Skip to content

Commit 32361bb

Browse files
authored
Merge pull request #164 from iivanou/rp_v5_support
Report Portal V5 support.
2 parents 47e3fe3 + 6165346 commit 32361bb

File tree

6 files changed

+97
-84
lines changed

6 files changed

+97
-84
lines changed

README.rst

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22
agent-python-pytest
33
===================
44

5-
6-
**Important:** this is BETA2 version. Please post issue in case if any found
7-
8-
Pytest plugin for reporting test results of Pytest to the 'Reportal Portal'.
5+
Pytest plugin for reporting test results of Pytest to the Reportal Portal.
96

107
* Usage
8+
* Installation
119
* Configuration
10+
* Contribution
1211
* Examples
1312
* Launching
1413
* Send attachement (screenshots)
@@ -27,6 +26,22 @@ To install pytest plugin execute next command in a terminal:
2726
2827
pip install pytest-reportportal
2928
29+
**IMPORTANT!**
30+
The latest version **does not** support Report Portal versions below 5.0.0.
31+
32+
Specify the last one release of the client version 1 to install or update the client for other versions of Report Portal below 5.0.0:
33+
34+
.. code-block:: bash
35+
36+
pip install pytest-reportportal~=1.0
37+
38+
39+
Contribution
40+
~~~~~~~~~~~~~
41+
42+
All the fixes for the agent that supports Report Portal versions below 5.0.0 should go into the v1 branch.
43+
The master branch will store the code base for the agent for Report Portal versions 5 and above.
44+
3045

3146
Configuration
3247
~~~~~~~~~~~~~
@@ -105,7 +120,6 @@ in conftest.py:
105120
106121
@pytest.fixture(scope="session")
107122
def rp_logger(request):
108-
import logging
109123
logger = logging.getLogger(__name__)
110124
logger.setLevel(logging.DEBUG)
111125
# Create handler for Report Portal if the service has been

pytest_reportportal/listener.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def pytest_runtest_protocol(self, item):
3838
if not update_supported:
3939
self._add_issue_id_marks(item)
4040

41-
self.PyTestService.start_pytest_item(item)
41+
item_id = self.PyTestService.start_pytest_item(item)
4242
if PYTEST_HAS_LOGGING_PLUGIN:
4343
# This check can go away once we support pytest >= 3.3
4444
with patching_logger_class():
@@ -53,7 +53,8 @@ def pytest_runtest_protocol(self, item):
5353
self._add_issue_id_marks(item)
5454
self.PyTestService.update_pytest_item(item)
5555
# Finishing item in RP
56-
self.PyTestService.finish_pytest_item(item, self.result or 'SKIPPED', self.issue or None)
56+
self.PyTestService.finish_pytest_item(
57+
item, item_id, self.result or 'SKIPPED', self.issue or None)
5758

5859
@pytest.hookimpl(hookwrapper=True)
5960
def pytest_runtest_makereport(self, item):

pytest_reportportal/plugin.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import logging
55
import dill as pickle
6+
import pkg_resources
67
import pytest
78
import requests
89
import time
@@ -12,9 +13,15 @@
1213

1314
try:
1415
# This try/except can go away once we support pytest >= 3.3
15-
import _pytest.logging
16+
pkg_resources.get_distribution('pytest >= 3.3.0')
1617
PYTEST_HAS_LOGGING_PLUGIN = True
17-
except ImportError:
18+
try:
19+
# This try/except can go away once we support pytest >= 5.4.0
20+
from _pytest.logging import get_actual_log_level
21+
except ImportError:
22+
from _pytest.logging import get_log_level_for_setting as \
23+
get_actual_log_level
24+
except pkg_resources.VersionConflict:
1825
PYTEST_HAS_LOGGING_PLUGIN = False
1926

2027
log = logging.getLogger(__name__)
@@ -59,7 +66,7 @@ def pytest_sessionstart(session):
5966
description=session.config.option.rp_launch_description
6067
)
6168
if session.config.pluginmanager.hasplugin('xdist'):
62-
wait_launch(session.config.py_test_service.RP.rp_client)
69+
wait_launch(session.config.py_test_service.rp)
6370

6471

6572
@pytest.hookimpl(trylast=True)
@@ -101,14 +108,8 @@ def pytest_sessionfinish(session):
101108
# Stop now if the plugin is not properly configured
102109
return
103110

104-
shouldfail = getattr(session, 'shouldfail', False)
105-
nowait = True if shouldfail else False
106-
107111
if is_master(session.config):
108-
session.config.py_test_service.finish_launch(
109-
status='RP_Launch', force=nowait)
110-
111-
session.config.py_test_service.terminate_service(nowait=nowait)
112+
session.config.py_test_service.finish_launch()
112113

113114

114115
def pytest_configure(config):
@@ -152,12 +153,11 @@ def pytest_configure(config):
152153
config.py_test_service = PyTestServiceClass()
153154
else:
154155
config.py_test_service = pickle.loads(config.slaveinput['py_test_service'])
155-
config.py_test_service.RP.listener.start()
156156

157157
# set Pytest_Reporter and configure it
158158
if PYTEST_HAS_LOGGING_PLUGIN:
159159
# This check can go away once we support pytest >= 3.3
160-
log_level = _pytest.logging.get_actual_log_level(config, 'rp_log_level')
160+
log_level = get_actual_log_level(config, 'rp_log_level')
161161
if log_level is None:
162162
log_level = logging.NOTSET
163163
else:

pytest_reportportal/service.py

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from _pytest.python import Class, Function, Instance, Module
2525
from _pytest.unittest import TestCaseFunction, UnitTestCase
2626

27-
from reportportal_client import ReportPortalServiceAsync
27+
from reportportal_client import ReportPortalService
2828
from six import with_metaclass
2929
from six.moves import queue
3030

@@ -74,12 +74,15 @@ def __call__(cls, *args, **kwargs):
7474
class PyTestServiceClass(with_metaclass(Singleton, object)):
7575

7676
def __init__(self):
77-
self.RP = None
77+
self.rp = None
78+
self.rp_supports_parameters = True
7879
try:
7980
pkg_resources.get_distribution('reportportal_client >= 3.2.0')
80-
self.RP_SUPPORTS_PARAMETERS = True
8181
except pkg_resources.VersionConflict:
82-
self.RP_SUPPORTS_PARAMETERS = False
82+
self.rp_supports_parameters = False
83+
84+
self.log_item_id = None
85+
self.parent_item_id = None
8386

8487
self.ignore_errors = True
8588
self.ignored_tags = []
@@ -93,64 +96,57 @@ def init_service(self, endpoint, project, uuid, log_batch_size,
9396
ignore_errors, ignored_tags, verify_ssl=True,
9497
retries=0):
9598
self._errors = queue.Queue()
96-
if self.RP is None:
99+
if self.rp is None:
97100
self.ignore_errors = ignore_errors
98-
if self.RP_SUPPORTS_PARAMETERS:
101+
if self.rp_supports_parameters:
99102
self.ignored_tags = list(set(ignored_tags).union({'parametrize'}))
100103
else:
101104
self.ignored_tags = ignored_tags
102105
log.debug('ReportPortal - Init service: endpoint=%s, '
103106
'project=%s, uuid=%s', endpoint, project, uuid)
104-
self.RP = ReportPortalServiceAsync(
107+
self.rp = ReportPortalService(
105108
endpoint=endpoint,
106109
project=project,
107110
token=uuid,
108-
error_handler=self.async_error_handler,
109111
retries=retries,
110-
log_batch_size=log_batch_size,
111112
verify_ssl=verify_ssl
112113
)
113-
if self.RP and hasattr(self.RP.rp_client, "get_project_settings"):
114-
self.project_settings = self.RP.rp_client.get_project_settings()
114+
if self.rp and hasattr(self.rp, "get_project_settings"):
115+
self.project_settings = self.rp.get_project_settings()
115116
else:
116117
self.project_settings = None
117118
self.issue_types = self.get_issue_types()
118119
else:
119120
log.debug('The pytest is already initialized')
120-
return self.RP
121+
return self.rp
121122

122123
def async_error_handler(self, exc_info):
123-
self.terminate_service(nowait=True)
124-
self.RP = None
124+
self.rp = None
125125
self._errors.put_nowait(exc_info)
126126

127-
def terminate_service(self, nowait=False):
128-
if self.RP is not None:
129-
self.RP.terminate(nowait)
130-
self.RP = None
131-
132-
def start_launch(self, launch_name,
127+
def start_launch(self,
128+
launch_name,
133129
mode=None,
134-
tags=None,
135-
description=None):
130+
description=None,
131+
**kwargs):
136132
self._stop_if_necessary()
137-
if self.RP is None:
133+
if self.rp is None:
138134
return
139135

140136
sl_pt = {
141137
'name': launch_name,
142138
'start_time': timestamp(),
143139
'description': description,
144140
'mode': mode,
145-
'tags': tags
146141
}
147142
log.debug('ReportPortal - Start launch: equest_body=%s', sl_pt)
148-
req_data = self.RP.start_launch(**sl_pt)
149-
log.debug('ReportPortal - Launch started: response_body=%s', req_data)
143+
item_id = self.rp.start_launch(**sl_pt)
144+
log.debug('ReportPortal - Launch started: id=%s', item_id)
145+
return item_id
150146

151147
def collect_tests(self, session):
152148
self._stop_if_necessary()
153-
if self.RP is None:
149+
if self.rp is None:
154150
return
155151

156152
hier_dirs = False
@@ -209,7 +205,7 @@ def collect_tests(self, session):
209205

210206
def start_pytest_item(self, test_item=None):
211207
self._stop_if_necessary()
212-
if self.RP is None:
208+
if self.rp is None:
213209
return
214210

215211
for part in self._item_parts[test_item]:
@@ -220,37 +216,42 @@ def start_pytest_item(self, test_item=None):
220216
payload = {
221217
'name': self._get_item_name(part),
222218
'description': self._get_item_description(part),
223-
'tags': self._get_item_tags(part),
224219
'start_time': timestamp(),
225-
'item_type': 'SUITE'
220+
'item_type': 'SUITE',
221+
'parent_item_id': self.parent_item_id
226222
}
227223
log.debug('ReportPortal - Start Suite: request_body=%s', payload)
228-
self.RP.start_test_item(**payload)
224+
item_id = self.rp.start_test_item(**payload)
225+
self.log_item_id = item_id
226+
self.parent_item_id = item_id
227+
self._hier_parts[part]["item_id"] = item_id
229228

230229
start_rq = {
231230
'name': self._get_item_name(test_item),
232231
'description': self._get_item_description(test_item),
233-
'tags': self._get_item_tags(test_item),
234232
'start_time': timestamp(),
235-
'item_type': 'STEP'
233+
'item_type': 'TEST',
234+
'parent_item_id': self.parent_item_id
236235
}
237-
if self.RP_SUPPORTS_PARAMETERS:
236+
if self.rp_supports_parameters:
238237
start_rq['parameters'] = self._get_parameters(test_item)
239238

240239
log.debug('ReportPortal - Start TestItem: request_body=%s', start_rq)
241-
self.RP.start_test_item(**start_rq)
240+
item_id = self.rp.start_test_item(**start_rq)
241+
self.log_item_id = item_id
242+
return item_id
242243

243244
def is_item_update_supported(self):
244245
"""Check item update API call client support."""
245-
return hasattr(self.RP, "update_test_item")
246+
return hasattr(self.rp, "update_test_item")
246247

247248
def update_pytest_item(self, test_item=None):
248249
"""Make item update API call.
249250
250251
:param test_item: pytest test item
251252
"""
252253
self._stop_if_necessary()
253-
if self.RP is None:
254+
if self.rp is None:
254255
return
255256

256257
# if update_test_item is not supported in client
@@ -263,23 +264,25 @@ def update_pytest_item(self, test_item=None):
263264
'tags': self._get_item_tags(test_item),
264265
}
265266
log.debug('ReportPortal - Update TestItem: request_body=%s', start_rq)
266-
self.RP.update_test_item(**start_rq)
267+
self.rp.update_test_item(**start_rq)
267268

268-
def finish_pytest_item(self, test_item, status, issue=None):
269+
def finish_pytest_item(self, test_item, item_id, status, issue=None):
269270
self._stop_if_necessary()
270-
if self.RP is None:
271+
if self.rp is None:
271272
return
272273

273274
fta_rq = {
274275
'end_time': timestamp(),
275276
'status': status,
276-
'issue': issue
277+
'issue': issue,
278+
'item_id': item_id
277279
}
278280

279281
log.debug('ReportPortal - Finish TestItem: request_body=%s', fta_rq)
280-
self.RP.finish_test_item(**fta_rq)
281282

282283
parts = self._item_parts[test_item]
284+
if not parts:
285+
self.rp.finish_test_item(**fta_rq)
283286
while len(parts) > 0:
284287
part = parts.pop()
285288
if status == "FAILED":
@@ -290,14 +293,15 @@ def finish_pytest_item(self, test_item, status, issue=None):
290293
payload = {
291294
'end_time': timestamp(),
292295
'issue': issue,
296+
'item_id': self._hier_parts[part]["item_id"],
293297
'status': part._rp_result
294298
}
295299
log.debug('ReportPortal - End TestSuite: request_body=%s', payload)
296-
self.RP.finish_test_item(**payload)
300+
self.rp.finish_test_item(**payload)
297301

298-
def finish_launch(self, status='rp_launch', force=False):
302+
def finish_launch(self, status=None, **kwargs):
299303
self._stop_if_necessary()
300-
if self.RP is None:
304+
if self.rp is None:
301305
return
302306

303307
# To finish launch session str parameter is needed
@@ -306,14 +310,11 @@ def finish_launch(self, status='rp_launch', force=False):
306310
'status': status
307311
}
308312
log.debug('ReportPortal - Finish launch: request_body=%s', fl_rq)
309-
if not force:
310-
self.RP.finish_launch(**fl_rq)
311-
else:
312-
self.RP.stop_launch(**fl_rq)
313+
self.rp.finish_launch(**fl_rq)
313314

314315
def post_log(self, message, loglevel='INFO', attachment=None):
315316
self._stop_if_necessary()
316-
if self.RP is None:
317+
if self.rp is None:
317318
return
318319

319320
if loglevel not in self._loglevels:
@@ -327,7 +328,7 @@ def post_log(self, message, loglevel='INFO', attachment=None):
327328
'level': loglevel,
328329
'attachment': attachment,
329330
}
330-
self.RP.log(**sl_rq)
331+
self.rp.log_batch((sl_rq,), item_id=self.log_item_id)
331332

332333
def _stop_if_necessary(self):
333334
try:

setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ def read_file(fname):
88
return f.read()
99

1010

11-
version = '1.10.0'
11+
version = '5.0.0'
1212

1313

1414
requirements = [
15-
'reportportal-client~=3.0',
15+
'reportportal-client~=5.0.0',
1616
'pytest>=3.0.7',
1717
'six>=1.10.0',
1818
'dill>=0.2.7.1',
@@ -25,7 +25,6 @@ def read_file(fname):
2525
description='Agent for Reporting results of tests to the Report Portal',
2626
long_description=read_file('README.rst'),
2727
long_description_content_type='text/markdown',
28-
author='Pavel Papou',
2928
author_email='[email protected]',
3029
url='https://github.com/reportportal/agent-python-pytest',
3130
packages=['pytest_reportportal'],

0 commit comments

Comments
 (0)