From b2ccce622efe9abb75f86592e44ac3f886231aa2 Mon Sep 17 00:00:00 2001 From: Mark Adams Date: Wed, 7 May 2025 16:53:15 -0500 Subject: [PATCH 1/2] Fixed a bug where randomization logic was being incorrectly triggred (#31) Previously, a call to config.getoption() passed in a default argument of 'False'. Unfortunately, per [the docs](https://github.com/pytest-dev/pytest/blob/89b84cb56295c46e1d8834b599e858bc65c15a5b/src/_pytest/config/__init__.py#L1733-L1734) the default argument is only for if the argument isn't declared, not if the argument is not present. As a result, the absence of `--test-group-random-seed` would always result in a value of `None` which is not `False` so it would trigger the randomization logic with a seed of `None`. When the seed is `None`, a random seed is chosen so each xdist process winds up coming up with a different list. The fix is simple: check to see if the seed is not `None` in order to trigger the activation logic. --- CHANGELOG.md | 1 + pytest_test_groups/__init__.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cda764e..19ae0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added ### Changed +- Fixed a bug where randomization logic was being applied even when no random seed was provided (#31) ### Removed diff --git a/pytest_test_groups/__init__.py b/pytest_test_groups/__init__.py index 94c9fee..8f0dde3 100644 --- a/pytest_test_groups/__init__.py +++ b/pytest_test_groups/__init__.py @@ -77,14 +77,14 @@ def pytest_collection_modifyitems(session, config, items): group_count = config.getoption('test-group-count') group_id = config.getoption('test-group') group_by = config.getoption("test-group-by") - seed = config.getoption('random-seed', False) + seed = config.getoption('random-seed') if not group_count or not group_id: return original_items = items[:] - if seed is not False: + if seed is not None: seeded = Random(seed) seeded.shuffle(items) @@ -94,7 +94,7 @@ def pytest_collection_modifyitems(session, config, items): if len(items) == 0: raise pytest.UsageError('Invalid test-group argument') - if seed is not False: + if seed is not None: items = _sort_in_original_order(items, original_items) terminal_reporter = config.pluginmanager.get_plugin('terminalreporter') From 27032e4acac767d0bd8f14b3708dbd6d48aa015a Mon Sep 17 00:00:00 2001 From: Mark Adams Date: Thu, 8 May 2025 10:56:03 -0500 Subject: [PATCH 2/2] Fix linting errors --- pytest_test_groups/__init__.py | 12 ++++++++++-- tests/test_pytest.py | 5 ++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pytest_test_groups/__init__.py b/pytest_test_groups/__init__.py index 8f0dde3..d913a9c 100644 --- a/pytest_test_groups/__init__.py +++ b/pytest_test_groups/__init__.py @@ -8,20 +8,24 @@ # Import 3rd-party libs from _pytest.config import create_terminal_writer + class StrEnum(str, Enum): """Custom StrEnum for Python < 3.11 compatibility.""" def __str__(self): return self.value - + + class GroupBy(StrEnum): DEFAULT = "" FILENAME = "filename" + def get_group_default(items, group_count, group_id): """Get the items from the passed in group based on group count.""" start = _get_start(group_id, group_count) return items[start:len(items):group_count] + def get_group_by_filename(items, group_count, group_id): """Get the items from the passed in group, split by files, based on group count.""" start = _get_start(group_id, group_count) @@ -45,16 +49,19 @@ def get_group_by_filename(items, group_count, group_id): return group_to_items[start] + def _get_start(group_id, group_count): if not (1 <= group_id <= group_count): raise pytest.UsageError('Invalid test-group argument') return group_id - 1 + groupByHandlers = { GroupBy.DEFAULT: get_group_default, GroupBy.FILENAME: get_group_by_filename } + def pytest_addoption(parser): group = parser.getgroup('split your tests into groups and run them') group.addoption('--test-group-count', dest='test-group-count', type=int, @@ -71,6 +78,7 @@ def _sort_in_original_order(items, orig_items): items.sort(key=original_order.__getitem__) return items + @pytest.hookimpl(hookwrapper=True) def pytest_collection_modifyitems(session, config, items): yield @@ -81,7 +89,7 @@ def pytest_collection_modifyitems(session, config, items): if not group_count or not group_id: return - + original_items = items[:] if seed is not None: diff --git a/tests/test_pytest.py b/tests/test_pytest.py index 0ed704b..ab645a2 100644 --- a/tests/test_pytest.py +++ b/tests/test_pytest.py @@ -72,6 +72,7 @@ def test_z(): pass assert set(group_1 + group_2) == set(all_tests) + def test_group_by_files(testdir): testdir.makepyfile(test_file_1=""" def test_a(): pass @@ -161,6 +162,7 @@ def test_e(): pass ) result.assertoutcome(passed=5) + def test_group_runs_after_std_item_collection(testdir): """If @pytest.hookimpl(hookwrapper=True) is not used, the plugin will split the item list of items before other filtering (like -k) has been applied. This could potentially @@ -180,6 +182,7 @@ def test_y(): pass result = testdir.runpytest_subprocess('-k', 'test_y', '--test-group-count', '2', '--test-group', '2') assert 'Invalid test-group argument' in result.stderr.str() + def test_random_group_runs_in_original_order(testdir): """When running tests with a random seed, check test order is unchanged""" testdir.makepyfile(""" @@ -197,4 +200,4 @@ def test_b(): pass '--test-group', '1', '--test-group-random-seed', '5') group_1 = [x.item.name for x in result.calls if x._name == 'pytest_runtest_call'] - assert group_1 == sorted(group_1, reverse=True) \ No newline at end of file + assert group_1 == sorted(group_1, reverse=True)