From 3dec17887724437d2355687aed82776dceb538a5 Mon Sep 17 00:00:00 2001 From: "David A. Ramos" Date: Sun, 12 Nov 2023 02:48:30 -0800 Subject: [PATCH] Report file paths as POSIX on Windows For consistency, we use forward slashes for file paths on all platforms and test frameworks. --- src/pytest_unflakable/_plugin.py | 14 +++++++------- tests/common.py | 5 +++-- tests/test_unflakable.py | 22 +++++++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/pytest_unflakable/_plugin.py b/src/pytest_unflakable/_plugin.py index 6bea264..98f83b8 100644 --- a/src/pytest_unflakable/_plugin.py +++ b/src/pytest_unflakable/_plugin.py @@ -59,11 +59,11 @@ def node_path(session: Union[pytest.Item, pytest.Session]) -> PathCompat: return session.fspath -def relative_to(path: PathCompat, base: PathCompat) -> str: +def posix_relative_to(path: PathCompat, base: PathCompat) -> str: if hasattr(path, 'relative_to'): - return str(path.relative_to(base)) + return path.relative_to(base).as_posix() else: - return str(path.relto(base)) # type: ignore + return Path(path.relto(base)).as_posix() # type: ignore def item_name(item: _pytest.nodes.Node) -> Tuple[str, ...]: @@ -243,7 +243,7 @@ def pytest_collection_modifyitems( ) -> None: self.logger.debug('called hook pytest_collection_modifyitems') for idx, item in enumerate(items): - test_path = relative_to(node_path(item), node_path(session)) + test_path = posix_relative_to(node_path(item), node_path(session)) test_name = item_name(item) if (test_path, test_name) in self.quarantined_tests: self.logger.info( @@ -261,14 +261,14 @@ def pytest_runtest_protocol(self, item: pytest.Item, nextitem: Optional[pytest.I ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) assert self.session - test_filename = relative_to(node_path(item), node_path(self.session)) + test_filename = posix_relative_to(node_path(item), node_path(self.session)) test_name = item_name(item) for attempt in range(item.config.option.unflakable_failure_retries + 1): if attempt > 0: self.logger.info( 'retrying test `%s` in file %s (attempt %d of %d)', '.'.join(test_name), - relative_to(node_path(item), node_path(item.session)), + posix_relative_to(node_path(item), node_path(item.session)), attempt + 1, item.config.option.unflakable_failure_retries + 1, ) @@ -304,7 +304,7 @@ def pytest_runtest_makereport( if item.parent: pass - test_filename = relative_to(node_path(item), node_path(item.session)) + test_filename = posix_relative_to(node_path(item), node_path(item.session)) test_name = item_name(item) is_quarantined = (test_filename, test_name) in self.quarantined_tests and ( self.quarantine_mode == QuarantineMode.IGNORE_FAILURES) diff --git a/tests/common.py b/tests/common.py index 88dc8e2..d3a4119 100644 --- a/tests/common.py +++ b/tests/common.py @@ -8,6 +8,7 @@ import re import subprocess from enum import Enum +from pathlib import Path from typing import (TYPE_CHECKING, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, cast) from unittest import mock @@ -422,11 +423,11 @@ def run_test_case( '^', r'\[gw[0-9]\]\x1b\[36m \[ *[0-9]+%\] \x1b\[0m', re.escape(VERBOSE_TEST_ATTEMPT_OUTCOME_CHARS[test_outcome]), - ' %s ' % (re.escape('::'.join((test_file,) + test_name))), + ' %s ' % (re.escape('::'.join((Path(test_file).as_posix(),) + test_name))), '$', ] if expect_xdist else [ '^', - '%s ' % (re.escape('::'.join((test_file,) + test_name))), + '%s ' % (re.escape('::'.join((Path(test_file).as_posix(),) + test_name))), re.escape(VERBOSE_TEST_ATTEMPT_OUTCOME_CHARS[test_outcome]), # Statuses may be truncated when test names are long (e.g., when there are parent # classes) to keep lines under 80 chars. Consequently, we assume anything can appear diff --git a/tests/test_unflakable.py b/tests/test_unflakable.py index f39f4bd..b1419f3 100644 --- a/tests/test_unflakable.py +++ b/tests/test_unflakable.py @@ -142,16 +142,20 @@ def test_quarantine_flaky( verbose: bool, xdist: bool, ) -> None: - pytester.makepyfile(test_input=""" + pytester.makepyfile(**{'folder/test_input': """ first_invocation = True + def test_pass(): + pass + + def test_flaky(): global first_invocation if first_invocation: first_invocation = False assert False - """) + """}) subprocess_mock.update(branch='MOCK_BRANCH', commit='MOCK_COMMIT') @@ -160,24 +164,28 @@ def test_flaky(): manifest={'quarantined_tests': [ { 'test_id': 'MOCK_TEST_ID', - 'filename': 'test_input.py', + 'filename': 'folder/test_input.py', 'name': ['test_flaky'] } ]}, expected_test_file_outcomes=[ ( - 'test_input.py', + os.path.join('folder', 'test_input.py'), [ + (('test_pass',), [ + _TestAttemptOutcome.PASSED, + ]), (('test_flaky',), [ _TestAttemptOutcome.QUARANTINED, _TestAttemptOutcome.RETRY_PASSED, - ]) + ]), ], ), ], - expected_test_result_counts=_TestResultCounts(num_quarantined=1), + expected_test_result_counts=_TestResultCounts(num_passed=1, num_quarantined=1), expected_uploaded_test_runs={ - ('test_input.py', ('test_flaky',)): ['quarantined', 'pass'], + ('folder/test_input.py', ('test_pass',)): ['pass'], + ('folder/test_input.py', ('test_flaky',)): ['quarantined', 'pass'], }, expected_exit_code=ExitCode.OK, expect_xdist=xdist,