Skip to content

Commit 7ef4be5

Browse files
authored
Merge pull request #2 from ppapou/master
Pytest-Report Portal Logging
2 parents e0fb0b7 + c85572d commit 7ef4be5

File tree

6 files changed

+170
-103
lines changed

6 files changed

+170
-103
lines changed

CONTRIBUTING.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
.. highlight:: shell
2-
31
============
42
Contributing
53
============

README.md

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
# agent-python-pytest
2-
Framework integration with PyTest
2+
Pytest plugin for reporting test results of Pytest to the 'Reportal Portal'.
33

4-
Description:
5-
Plugin for reporting items results of Pytest to the 'Reportal Portal'.
6-
7-
Install Plugin
4+
## Usage:
5+
### Install the plugin
6+
```bash
87
pip install pytest-reportportal
8+
```
99

10-
Usage:
11-
12-
Prepare the config file pytest.ini in root dir of tests.
10+
### Prepare the config file pytest.ini in root dir of tests.
1311
Content of the pytest.ini file:
14-
[pytest]
15-
rp_uuid = uid reportportal
16-
rp_endpoint = http://ip:port
17-
rp_project = Project of ReportPortal
18-
19-
Run tests:
20-
py.test ./tests --rp_launch AnyLaunchName
12+
```
13+
[pytest]
14+
mandatory fields
15+
rp_uuid = uid reportportal
16+
rp_endpoint = http://ip:port
17+
rp_project = Project of ReportPortal
18+
```
19+
also launch tags could be added. But this parapmeter if not mandatory `rp_launch_tags = 'PyTest' 'Report_Portal'`.
2120

22-
Features:
23-
- add logging
24-
- add Tags parameter to config file
21+
### For logging of the test item flow please use the python logging util.
22+
```python
23+
# import ReportPortal handler in the test module
24+
from pytest_reportportal import RPlogHandler
25+
# get logger
26+
logger = logging.getLogger()
27+
# create hanler, set log level add it to the logger
28+
rp_handler = RPlogHandler()
29+
rp_handler.setLevel(logging.INFO)
30+
logger.addHandler(rp_handler)
31+
# In this case only INFO messages will be sent to the RP
32+
def test_one(self):
33+
logger.info("Case1. Step1")
34+
x = "this"
35+
logger.info("Case1. Step2")
36+
assert 'h' in x
37+
```
38+
### Run tests:
39+
```bash
40+
py.test ./tests --rp-launch AnyLaunchName
41+
```
42+
## Copyright Notice
43+
Licensed under the [GPLv3](https://www.gnu.org/licenses/quick-guide-gplv3.html)
44+
license (see the LICENSE file).

pytest_reportportal/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import logging
2+
from .service import PyTestService
3+
4+
5+
class RPlogHandler(logging.Handler):
6+
def __init__(self, level=logging.NOTSET):
7+
super(RPlogHandler, self).__init__(level)
8+
9+
def emit(self, record):
10+
try:
11+
msg = self.format(record)
12+
except (KeyboardInterrupt, SystemExit):
13+
raise
14+
except:
15+
self.handleError(record)
16+
17+
return PyTestService.post_log(msg, log_level=self.level)

pytest_reportportal/pytest_rp_plugin.py renamed to pytest_reportportal/plugin.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# This program is free software: you can redistribute it and/or modify iit under the terms of the GPL licence
21
import pytest
2+
# This program is free software: you can redistribute it
3+
# and/or modify it under the terms of the GPL licence
34
import logging
4-
from service import PyTestService
5+
from .service import PyTestService
56

67

78
class RP_Report_Listener(object):
@@ -17,8 +18,9 @@ class RP_Report_Listener(object):
1718
def pytest_runtest_makereport(self, item, call):
1819
report = (yield).get_result()
1920
if report.when == "setup":
21+
2022
# when function pytest_setup is called,
21-
# test item session is started in RP
23+
# test item session will be started in RP
2224
PyTestService.start_pytest_item(item)
2325

2426
if report.when == "call":
@@ -41,65 +43,74 @@ def pytest_runtest_makereport(self, item, call):
4143
# the TestItem will reported as SKIPPED
4244
item_result = "SKIPPED"
4345
PyTestService.finish_pytest_item(item_result)
46+
self.called = None
4447

4548

4649
def pytest_sessionstart(session):
4750
config = session.config
4851
if config.option.rp_launch:
4952
# get config parameters if rp_launch option is set
50-
rp_uuid = config.getini('rp_uuid')
51-
rp_project = config.getini('rp_project')
52-
rp_endpoint = config.getini('rp_endpoint')
53+
rp_uuid = config.getini("rp_uuid")
54+
rp_project = config.getini("rp_project")
55+
rp_endpoint = config.getini("rp_endpoint")
56+
rp_launch_tags = config.getini("rp_launch_tags")
5357
# initialize PyTest
5458
PyTestService.init_service(
5559
project=rp_project,
5660
endpoint=rp_endpoint,
5761
uuid=rp_uuid)
58-
launch_name = config.getoption('rp_launch')
62+
launch_name = config.getoption("rp_launch")
5963

60-
PyTestService.start_launch(launch_name)
64+
PyTestService.start_launch(launch_name, tags=rp_launch_tags)
6165

6266

6367
def pytest_sessionfinish(session):
6468
config = session.config
6569
if config.option.rp_launch:
66-
PyTestService.finish_launch("RP_Launch")
70+
# FixMe: currently method of RP api takes the string parameter
71+
# so it is hardcoded
72+
PyTestService.finish_launch(status="RP_Launch")
6773

6874

6975
def pytest_configure(config):
70-
rp_launch = config.getoption('rp_launch')
76+
rp_launch = config.getoption("rp_launch")
7177
if rp_launch:
7278
# set Pytest_Reporter and configure it
7379
config._reporter = RP_Report_Listener()
7480

75-
if hasattr(config, '_reporter'):
81+
if hasattr(config, "_reporter"):
7682
config.pluginmanager.register(config._reporter)
7783

7884

7985
def pytest_unconfigure(config):
80-
if hasattr(config, '_reporter'):
86+
if hasattr(config, "_reporter"):
8187
reporter = config._reporter
8288
del config._reporter
8389
config.pluginmanager.unregister(reporter)
84-
logging.debug('ReportPortal is unconfigured')
90+
logging.debug("RP is unconfigured")
8591

8692

8793
def pytest_addoption(parser):
8894
group = parser.getgroup("reporting")
8995
group.addoption(
90-
'--rp_launch',
91-
action='store',
92-
dest=None,
93-
help="The Launch name for ReportPortal, i.e PyTest")
96+
"--rp-launch",
97+
action="store",
98+
dest="rp_launch",
99+
help="The Launch name for RP")
94100

95101
parser.addini(
96-
'rp_uuid',
102+
"rp_uuid",
97103
help="Uid of RP user")
98104

99105
parser.addini(
100-
'rp_endpoint',
101-
help="Report portal server")
106+
"rp_endpoint",
107+
help="RP server")
102108

103109
parser.addini(
104110
"rp_project",
105-
help='Report Portal Project')
111+
help="RP Project")
112+
113+
parser.addini(
114+
"rp_launch_tags",
115+
type="args",
116+
help="Tags for of RP Launch, i.e Perfomance Regression")

pytest_reportportal/service.py

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from time import time
3+
from six import with_metaclass
34
from reportportal_client import (
45
ReportPortalService, FinishExecutionRQ, StartLaunchRQ, StartTestItemRQ,
56
FinishTestItemRQ, SaveLogRQ)
@@ -9,127 +10,141 @@ def timestamp():
910
return str(int(time() * 1000))
1011

1112

12-
class PyTestService(object):
13+
class Singleton(type):
14+
_instances = {}
1315

14-
RP = None
15-
TEST_ITEM_STACK = []
16-
launch_id = None
16+
def __call__(cls, *args, **kwargs):
17+
if cls not in cls._instances:
18+
cls._instances[cls] = super(Singleton, cls).__call__(
19+
*args, **kwargs)
20+
return cls._instances[cls]
1721

18-
@staticmethod
19-
def init_service(endpoint, project, uuid):
2022

21-
if PyTestService.RP is None:
23+
class PyTestServiceClass(with_metaclass(Singleton, object)):
24+
25+
def __init__(self):
26+
27+
self.RP = None
28+
self.TEST_ITEM_STACK = []
29+
self.launch_id = None
30+
self.loglevel_map = {
31+
0: "TRACE",
32+
10: "DEBUG",
33+
20: "INFO",
34+
30: "WARNING",
35+
40: "ERROR"}
36+
37+
def init_service(self, endpoint, project, uuid):
38+
39+
if self.RP is None:
2240
logging.debug(
2341
msg="ReportPortal - Init service: "
2442
"endpoint={0}, project={1}, uuid={2}".
2543
format(endpoint, project, uuid))
26-
PyTestService.RP = ReportPortalService(
44+
self.RP = ReportPortalService(
2745
endpoint=endpoint,
2846
project=project,
2947
token=uuid)
3048
else:
31-
raise Exception("PyTest is initialized")
32-
return PyTestService.RP
49+
logging.debug("The pytest is already initialized")
50+
return self.RP
3351

34-
@staticmethod
35-
def start_launch(launch_name=None, mode=None):
36-
# TODO: Tags, values could be moved to separated module like helper
37-
tags = ["PyTest"]
52+
def start_launch(
53+
self, launch_name=None, mode=None, tags=None, launch=None):
54+
# In next versions launch object(suite, testcase)
55+
# could be set as parameter
3856
sl_pt = StartLaunchRQ(
3957
name=launch_name,
4058
start_time=timestamp(),
41-
description="PyTest_Launch",
59+
description='Pytest Launch',
4260
mode=mode,
4361
tags=tags)
4462
logging.debug(msg="ReportPortal - Start launch: "
4563
"request_body={0}".format(sl_pt.data))
46-
req_data = PyTestService.RP.start_launch(sl_pt)
64+
req_data = self.RP.start_launch(sl_pt)
4765
logging.debug(msg="ReportPortal - Launch started: "
4866
"response_body={0}".format(req_data.raw))
49-
PyTestService.launch_id = req_data.id
67+
self.launch_id = req_data.id
5068

51-
PyTestService.TEST_ITEM_STACK.append((None, "SUITE"))
69+
self.TEST_ITEM_STACK.append((None, "SUITE"))
5270
logging.debug(
5371
msg="ReportPortal - Stack: {0}".
54-
format(PyTestService.TEST_ITEM_STACK))
72+
format(self.TEST_ITEM_STACK))
5573

56-
@staticmethod
57-
def start_pytest_item(test_item=None):
74+
def start_pytest_item(self, test_item=None):
5875
try:
5976
# for common items
6077
item_description = test_item.function.__doc__
6178
except AttributeError:
6279
# doctest has no `function` attribute
6380
item_description = test_item.reportinfo()[2]
64-
6581
start_rq = StartTestItemRQ(
6682
name=test_item.name,
6783
description=item_description,
6884
tags=['PyTest Item Tag'],
6985
start_time=timestamp(),
70-
launch_id=PyTestService.launch_id,
86+
launch_id=self.launch_id,
7187
type="TEST")
7288

73-
parent_item_id = PyTestService._get_top_id_from_stack()
89+
parent_item_id = self._get_top_id_from_stack()
7490

7591
logging.debug(
7692
msg="ReportPortal - Start TestItem: "
7793
"request_body={0}, parent_item={1}".format(
7894
start_rq.data, parent_item_id))
7995

80-
req_data = PyTestService.RP.start_test_item(
96+
req_data = self.RP.start_test_item(
8197
parent_item_id=parent_item_id, start_test_item_rq=start_rq)
8298

83-
PyTestService.TEST_ITEM_STACK.append((req_data.id, "TEST"))
99+
self.TEST_ITEM_STACK.append((req_data.id, "TEST"))
84100
logging.debug(
85101
msg="ReportPortal - Stack: {0}".
86-
format(PyTestService.TEST_ITEM_STACK))
102+
format(self.TEST_ITEM_STACK))
87103

88-
@staticmethod
89-
def finish_pytest_item(status, issue=None):
104+
def finish_pytest_item(self, status, issue=None):
90105
fta_rq = FinishTestItemRQ(end_time=timestamp(),
91106
status=status,
92107
issue=issue)
93108

94-
test_item_id = PyTestService._get_top_id_from_stack()
109+
test_item_id = self._get_top_id_from_stack()
95110
logging.debug(
96111
msg="ReportPortal - Finish TetsItem:"
97112
" request_body={0}, test_id={1}".
98113
format(fta_rq.data, test_item_id))
99-
PyTestService.RP.finish_test_item(
114+
self.RP.finish_test_item(
100115
item_id=test_item_id,
101116
finish_test_item_rq=fta_rq)
102-
PyTestService.TEST_ITEM_STACK.pop()
117+
self.TEST_ITEM_STACK.pop()
103118
logging.debug(
104119
msg="ReportPortal - Stack: {0}".
105-
format(PyTestService.TEST_ITEM_STACK))
120+
format(self.TEST_ITEM_STACK))
106121

107-
@staticmethod
108-
def finish_launch(status):
122+
def finish_launch(self, launch=None, status="rp_launch"):
123+
# TO finish launch session str parameter is needed
109124
fl_rq = FinishExecutionRQ(
110125
end_time=timestamp(),
111126
status=status)
112-
launch_id = PyTestService.launch_id
127+
launch_id = self.launch_id
113128
logging.debug(msg="ReportPortal - Finish launch: "
114129
"request_body={0}, launch_id={1}".format(fl_rq.data,
115130
launch_id))
116-
PyTestService.RP.finish_launch(launch_id, fl_rq)
117-
PyTestService.TEST_ITEM_STACK.pop()
131+
self.RP.finish_launch(launch_id, fl_rq)
132+
self.TEST_ITEM_STACK.pop()
118133
logging.debug(
119134
msg="ReportPortal - Stack: {0}".
120-
format(PyTestService.TEST_ITEM_STACK))
135+
format(self.TEST_ITEM_STACK))
121136

122-
@staticmethod
123-
def _get_top_id_from_stack():
137+
def _get_top_id_from_stack(self):
124138
try:
125-
return PyTestService.TEST_ITEM_STACK[-1][0]
139+
return self.TEST_ITEM_STACK[-1][0]
126140
except IndexError:
127141
return None
128142

129-
@staticmethod
130-
def post_log(message, log_level="DEBUG"):
131-
sl_rq = SaveLogRQ(item_id=PyTestService._get_top_id_from_stack(),
143+
def post_log(self, message, log_level="INFO"):
144+
sl_rq = SaveLogRQ(item_id=self._get_top_id_from_stack(),
132145
time=timestamp(), message=message,
133-
level=log_level)
134-
PyTestService.RP.log(sl_rq)
146+
level=self.loglevel_map[log_level])
147+
self.RP.log(sl_rq)
148+
135149

150+
PyTestService = PyTestServiceClass()

0 commit comments

Comments
 (0)