Skip to content

Commit

Permalink
Merge pull request #380 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Oct 31, 2024
2 parents 6dfe010 + dcb1782 commit 2a4e78c
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 44 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]
### Fixed
- Issue [#379](https://github.com/reportportal/agent-python-pytest/issues/379): Fix TypeError when using pytest.skip() in fixtures, by @HardNorth

## [5.4.4]
### Fixed
- Issue [#375](https://github.com/reportportal/agent-python-pytest/issues/375): Fix max Item name length, by @HardNorth

## [5.4.3]
Expand Down
30 changes: 30 additions & 0 deletions examples/fixtures/test_fixture_skipped/test_fixture_skipped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2024 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest


@pytest.fixture(scope="session")
def base_fixture():
return False


@pytest.fixture()
def skip_fixture(base_fixture):
if not base_fixture:
pytest.skip("Skip if base condition is false")


def test_will_skip(skip_fixture):
pass
28 changes: 16 additions & 12 deletions pytest_reportportal/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,21 +295,32 @@ def pytest_runtest_makereport(item: Item) -> None:
service.process_results(item, report)


def report_fixture(request, name: str, error_msg: str) -> None:
def report_fixture(request, fixturedef, name: str, error_msg: str) -> None:
"""Report fixture setup and teardown.
:param request: Object of the FixtureRequest class
:param fixturedef: represents definition of the texture class
:param name: Name of the fixture
:param error_msg: Error message
"""
config = request.config
enabled = getattr(config, '_rp_enabled', False)
agent_config = getattr(config, '_reporter_config', None)
service = getattr(config, 'py_test_service', None)
if not enabled or not agent_config.rp_report_fixtures or not service:
agent_config = getattr(config, '_reporter_config', object())
report_fixtures = getattr(agent_config, 'rp_report_fixtures', False)
if not enabled or not service or not report_fixtures:
yield
return

cached_result = getattr(fixturedef, 'cached_result', None)
if cached_result and hasattr(cached_result, '__getitem__'):
result = fixturedef.cached_result[2]
if hasattr(result, '__getitem__'):
result = result[0]
if result and isinstance(result, BaseException):
yield
return

yield from service.report_fixture(name, error_msg)


Expand All @@ -322,7 +333,7 @@ def pytest_fixture_setup(fixturedef, request) -> None:
:param request: represents fixture execution metadata
"""
yield from report_fixture(
request, f'{fixturedef.scope} fixture setup: {fixturedef.argname}',
request, fixturedef, f'{fixturedef.scope} fixture setup: {fixturedef.argname}',
f'{fixturedef.scope} fixture setup failed: {fixturedef.argname}')


Expand All @@ -334,15 +345,8 @@ def pytest_fixture_post_finalizer(fixturedef, request) -> None:
:param fixturedef: represents definition of the texture class
:param request: represents fixture execution metadata
"""
cached_result = getattr(fixturedef, 'cached_result', None)
if cached_result and cached_result[2]:
exception = fixturedef.cached_result[2][0]
if exception and isinstance(exception, BaseException):
yield
return

yield from report_fixture(
request, f'{fixturedef.scope} fixture teardown: {fixturedef.argname}',
request, fixturedef, f'{fixturedef.scope} fixture teardown: {fixturedef.argname}',
f'{fixturedef.scope} fixture teardown failed: {fixturedef.argname}')


Expand Down
67 changes: 36 additions & 31 deletions pytest_reportportal/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
get_package_version
)

log = logging.getLogger(__name__)
LOGGER = logging.getLogger(__name__)

MAX_ITEM_NAME_LENGTH: int = 1024
TRUNCATION_STR: str = '...'
Expand Down Expand Up @@ -204,9 +204,9 @@ def start_launch(self) -> Optional[str]:
:return: item ID
"""
sl_pt = self._build_start_launch_rq()
log.debug('ReportPortal - Start launch: request_body=%s', sl_pt)
LOGGER.debug('ReportPortal - Start launch: request_body=%s', sl_pt)
self._launch_id = self.rp.start_launch(**sl_pt)
log.debug('ReportPortal - Launch started: id=%s', self._launch_id)
LOGGER.debug('ReportPortal - Launch started: id=%s', self._launch_id)
return self._launch_id

def _get_item_dirs(self, item: Item) -> List[str]:
Expand Down Expand Up @@ -366,7 +366,7 @@ def _get_item_name(self, name: str) -> str:
"""
if len(name) > MAX_ITEM_NAME_LENGTH:
name = name[:MAX_ITEM_NAME_LENGTH - len(TRUNCATION_STR)] + TRUNCATION_STR
log.warning(PytestWarning(
LOGGER.warning(PytestWarning(
f'Test leaf ID was truncated to "{name}" because of name size constrains on Report Portal'))
return name

Expand Down Expand Up @@ -411,8 +411,7 @@ def _build_start_suite_rq(self, leaf):
return payload

def _start_suite(self, suite_rq):
log.debug('ReportPortal - Start Suite: request_body=%s',
suite_rq)
LOGGER.debug('ReportPortal - Start Suite: request_body=%s', suite_rq)
return self.rp.start_test_item(**suite_rq)

def _create_suite(self, leaf):
Expand Down Expand Up @@ -655,7 +654,7 @@ def _build_start_step_rq(self, leaf):
return payload

def _start_step(self, step_rq):
log.debug('ReportPortal - Start TestItem: request_body=%s', step_rq)
LOGGER.debug('ReportPortal - Start TestItem: request_body=%s', step_rq)
return self.rp.start_test_item(**step_rq)

def __unique_id(self):
Expand Down Expand Up @@ -729,11 +728,11 @@ def _build_finish_step_rq(self, leaf):
return payload

def _finish_step(self, finish_rq):
log.debug('ReportPortal - Finish TestItem: request_body=%s', finish_rq)
LOGGER.debug('ReportPortal - Finish TestItem: request_body=%s', finish_rq)
self.rp.finish_test_item(**finish_rq)

def _finish_suite(self, finish_rq):
log.debug('ReportPortal - End TestSuite: request_body=%s', finish_rq)
LOGGER.debug('ReportPortal - End TestSuite: request_body=%s', finish_rq)
self.rp.finish_test_item(**finish_rq)

def _build_finish_suite_rq(self, leaf):
Expand Down Expand Up @@ -815,7 +814,7 @@ def _build_finish_launch_rq(self):
return finish_rq

def _finish_launch(self, finish_rq):
log.debug('ReportPortal - Finish launch: request_body=%s', finish_rq)
LOGGER.debug('ReportPortal - Finish launch: request_body=%s', finish_rq)
self.rp.finish_launch(**finish_rq)

@check_rp_enabled
Expand All @@ -828,8 +827,19 @@ def finish_launch(self):
# To finish launch session str parameter is needed
self._finish_launch(self._build_finish_launch_rq())

def _build_log(self, item_id: str, message: str, log_level: str, attachment: Optional[Any] = None):
sl_rq = {
'item_id': item_id,
'time': timestamp(),
'message': message,
'level': log_level,
}
if attachment:
sl_rq['attachment'] = attachment
return sl_rq

@check_rp_enabled
def post_log(self, test_item, message, log_level='INFO', attachment=None):
def post_log(self, test_item, message: str, log_level: str = 'INFO', attachment: Optional[Any] = None):
"""
Send a log message to the Report Portal.
Expand All @@ -841,16 +851,11 @@ def post_log(self, test_item, message, log_level='INFO', attachment=None):
:return: None
"""
if log_level not in self._log_levels:
log.warning('Incorrect loglevel = %s. Force set to INFO. '
'Available levels: %s.', log_level, self._log_levels)
LOGGER.warning('Incorrect loglevel = %s. Force set to INFO. '
'Available levels: %s.', log_level, self._log_levels)
item_id = self._tree_path[test_item][-1]['item_id']
sl_rq = {
'item_id': item_id,
'time': timestamp(),
'message': message,
'level': log_level,
'attachment': attachment
}

sl_rq = self._build_log(item_id, message, log_level, attachment)
self.rp.log(**sl_rq)

def report_fixture(self, name: str, error_msg: str) -> None:
Expand All @@ -864,15 +869,15 @@ def report_fixture(self, name: str, error_msg: str) -> None:

try:
outcome = yield
if outcome.exception:
log.error(error_msg)
log.exception(outcome.exception)
reporter.finish_nested_step(item_id, timestamp(), 'FAILED')
else:
reporter.finish_nested_step(item_id, timestamp(), 'PASSED')
exception = outcome.exception
status = 'PASSED'
if exception:
if type(exception).__name__ != 'Skipped':
status = 'FAILED'
reporter.finish_nested_step(item_id, timestamp(), status)
except Exception as e:
log.error('Failed to report fixture: %s', name)
log.exception(e)
LOGGER.error('Failed to report fixture: %s', name)
LOGGER.exception(e)
reporter.finish_nested_step(item_id, timestamp(), 'FAILED')

def start(self) -> None:
Expand All @@ -883,9 +888,9 @@ def start(self) -> None:
self._config.rp_ignore_attributes or []
).union({'parametrize'})
)
log.debug('ReportPortal - Init service: endpoint=%s, '
'project=%s, api_key=%s', self._config.rp_endpoint,
self._config.rp_project, self._config.rp_api_key)
LOGGER.debug('ReportPortal - Init service: endpoint=%s, '
'project=%s, api_key=%s', self._config.rp_endpoint,
self._config.rp_project, self._config.rp_api_key)
launch_id = self._launch_id
if self._config.rp_launch_id:
launch_id = self._config.rp_launch_id
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from setuptools import setup


__version__ = '5.4.4'
__version__ = '5.4.5'


def read_file(fname):
Expand Down
28 changes: 28 additions & 0 deletions tests/integration/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
from collections import defaultdict
from unittest import mock
Expand Down Expand Up @@ -522,3 +523,30 @@ def test_class_fixture_setup(mock_client_init):
assert teardown_call_args[0] == teardown_step_name
setup_call_kwargs = call_args[-1][1]
assert not setup_call_kwargs['has_stats']


@mock.patch(REPORT_PORTAL_SERVICE)
def test_fixture_setup_skip(mock_client_init):
mock_client = setup_mock_for_logging(mock_client_init)

test_path = 'examples/fixtures/test_fixture_skipped/test_fixture_skipped.py'
run_tests(test_path, False)

call_args = mock_client.start_test_item.call_args_list
setup_call_args = call_args[2][0]
fixture_name = 'skip_fixture'
step_name = f'function fixture setup: {fixture_name}'
assert setup_call_args[0] == step_name

setup_call_kwargs = call_args[2][1]
assert not setup_call_kwargs['has_stats']

log_count = mock_client.log.call_count
assert log_count == 1, 'Incorrect number of "log" calls'

call_args = mock_client.finish_test_item.call_args_list
finish_call_kwargs = call_args[1][1]
assert finish_call_kwargs['status'] == 'PASSED'

finish_call_kwargs = call_args[-1][1]
assert finish_call_kwargs['status'] == 'SKIPPED'

0 comments on commit 2a4e78c

Please sign in to comment.