https://github.com/python/cpython/commit/296f016e93002e91fdbca5732f9eb4a80bc1af39
commit: 296f016e93002e91fdbca5732f9eb4a80bc1af39
branch: main
author: Aniket <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-13T12:14:51+03:00
summary:

gh-109817: Add --single-process-per-case option to libregrtest (GH-151689)

files:
A Misc/NEWS.d/next/Tests/2026-08-12-20-52-16.gh-issue-109817.GZHUNg.rst
M Lib/test/libregrtest/cmdline.py
M Lib/test/libregrtest/findtests.py
M Lib/test/libregrtest/main.py
M Lib/test/libregrtest/run_workers.py
M Lib/test/libregrtest/runtests.py
M Lib/test/test_regrtest.py

diff --git a/Lib/test/libregrtest/cmdline.py b/Lib/test/libregrtest/cmdline.py
index 64c035307e66542..720b073e460127f 100644
--- a/Lib/test/libregrtest/cmdline.py
+++ b/Lib/test/libregrtest/cmdline.py
@@ -194,6 +194,7 @@ def __init__(self, **kwargs) -> None:
         self._add_python_opts = True
         self.xmlpath = None
         self.single_process = False
+        self.single_process_per_case = False
 
         super().__init__(**kwargs)
 
@@ -368,6 +369,11 @@ def _create_parser():
     group.add_argument('--list-cases', action='store_true',
                        help='only write the name of test cases that will be 
run, '
                             'don\'t execute them')
+    group.add_argument('--single-process-per-case', action='store_true',
+                       help='run each test case in its own process. '
+                            '(slow; for debugging order dependencies '
+                            "and environment leaks). Test cases from "
+                            'the same module run sequentially.')
     group.add_argument('-P', '--pgo', dest='pgo', action='store_true',
                        help='enable Profile Guided Optimization (PGO) 
training')
     group.add_argument('--pgo-extended', action='store_true',
@@ -492,6 +498,17 @@ def _parse_args(args, **kwargs):
     if ns.single_process:
         ns.use_mp = None
 
+    if ns.single_process_per_case:
+        if ns.rerun:
+            parser.error("--single-process-per-case and --rerun "
+                         "options don't go together")
+        if ns.pgo:
+            parser.error("--single-process-per-case and --pgo "
+                         "options don't go together")
+        if ns.single_process:
+            parser.error("--single-process-per-case and --single-process "
+                         "options don't go together")
+
     # When both --slow-ci and --fast-ci options are present,
     # --slow-ci has the priority
     if ns.slow_ci:
diff --git a/Lib/test/libregrtest/findtests.py 
b/Lib/test/libregrtest/findtests.py
index 6c0e50846a466bb..e7692c5156812e4 100644
--- a/Lib/test/libregrtest/findtests.py
+++ b/Lib/test/libregrtest/findtests.py
@@ -93,20 +93,58 @@ def list_cases(tests: TestTuple, *,
                match_tests: TestFilter | None = None,
                test_dir: StrPath | None = None) -> None:
     support.verbose = False
-    set_match_tests(match_tests)
+    cases_by_module, skipped = collect_cases(tests, match_tests=match_tests,
+                                             test_dir=test_dir)
+    for cases in cases_by_module.values():
+        for case_id in cases:
+            print(case_id)
+    if skipped:
+        sys.stdout.flush()
+        stderr = sys.stderr
+        print(file=stderr)
+        print(count(len(skipped), "test"), "skipped:", file=stderr)
+        printlist(skipped, file=stderr)
+
+class _ModuleLoadFailed(Exception):
+    """The test module failed to load; its test cases are unknown."""
+
 
-    skipped = []
+def collect_cases(tests: TestTuple, *,
+                  match_tests: TestFilter | None = None,
+                  test_dir: StrPath | None = None
+                  ) -> tuple[dict[TestName, list[str]], list[TestName]]:
+    # Install the filter unconditionally: passing None clears any
+    # previously installed global filter, so collection is not
+    # affected by unrelated state in this process.
+    set_match_tests(match_tests)
+    result: dict[TestName, list[str]] = {}
+    skipped: list[TestName] = []
     for test_name in tests:
         module_name = abs_module_name(test_name, test_dir)
+        cases: list[str] = []
         try:
             suite = unittest.defaultTestLoader.loadTestsFromName(module_name)
-            _list_cases(suite)
+            _collect_cases(suite, cases)
         except unittest.SkipTest:
             skipped.append(test_name)
+            continue
+        except _ModuleLoadFailed:
+            # The module failed to load. Run it as a whole, so that the
+            # error is reported as in the normal mode.
+            result[test_name] = [test_name]
+            continue
+        if cases:
+            result[test_name] = cases
+    return result, skipped
 
-    if skipped:
-        sys.stdout.flush()
-        stderr = sys.stderr
-        print(file=stderr)
-        print(count(len(skipped), "test"), "skipped:", file=stderr)
-        printlist(skipped, file=stderr)
+def _collect_cases(suite: unittest.TestSuite, out: list[str]) -> None:
+    for test in suite:
+        if isinstance(test, unittest.TestSuite):
+            _collect_cases(test, out)
+        elif isinstance(test, unittest.loader._FailedTest):  # type: 
ignore[attr-defined]
+            # The test module failed to load. Its test cases are
+            # unknown: let the caller run the whole module.
+            raise _ModuleLoadFailed
+        elif isinstance(test, unittest.TestCase):
+            if match_test(test):
+                out.append(test.id())
diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py
index 8773e9df73263b7..7391056627cd068 100644
--- a/Lib/test/libregrtest/main.py
+++ b/Lib/test/libregrtest/main.py
@@ -12,7 +12,7 @@
 from test.support import os_helper, MS_WINDOWS, flush_std_streams
 
 from .cmdline import _parse_args, Namespace
-from .findtests import findtests, split_test_packages, list_cases
+from .findtests import findtests, split_test_packages, list_cases, 
collect_cases
 from .logger import Logger
 from .pgo import setup_pgo_tests
 from .result import TestResult
@@ -73,6 +73,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = 
False):
         self.want_header: bool = ns.header
         self.want_list_tests: bool = ns.list_tests
         self.want_list_cases: bool = ns.list_cases
+        self.want_single_process_per_case: bool = ns.single_process_per_case
         self.want_wait: bool = ns.wait
         self.want_cleanup: bool = ns.cleanup
         self.want_rerun: bool = ns.rerun
@@ -99,6 +100,10 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = 
False):
         else:
             num_workers = ns.use_mp  # run in parallel
         self.num_workers: int = num_workers
+        if ns.single_process_per_case and ns.use_mp is None:
+            # Each test case runs in its own worker subprocess;
+            # default to one worker when -j was not given.
+            self.num_workers = 1
         self.worker_json: StrJSON | None = ns.worker_json
 
         # Options to run tests
@@ -521,6 +526,8 @@ def create_run_tests(self, tests: TestTuple) -> RunTests:
             randomize=self.randomize,
             random_seed=self.random_seed,
             parallel_threads=self.parallel_threads,
+            single_process_per_case=self.want_single_process_per_case,
+            case_groups=None,
         )
 
     def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
@@ -546,6 +553,23 @@ def _run_tests(self, selected: TestTuple, tests: TestList 
| None) -> int:
         print("Using random seed:", self.random_seed)
 
         runtests = self.create_run_tests(selected)
+        if self.want_single_process_per_case:
+            cases_by_module, _ = collect_cases(
+                selected,
+                match_tests=self.match_tests,
+                test_dir=self.test_dir)
+            groups = []
+            for module_name in selected:
+                cases = cases_by_module.get(module_name)
+                if cases:
+                    groups.append((module_name, tuple(cases)))
+                else:
+                    groups.append((module_name, (module_name,)))
+            case_groups = tuple(groups)
+            case_ids = tuple(
+                case_id for _, cases in case_groups for case_id in cases
+            )
+            runtests = runtests.copy(tests=case_ids, case_groups=case_groups)
         self.first_runtests = runtests
         self.logger.set_tests(runtests)
 
diff --git a/Lib/test/libregrtest/run_workers.py 
b/Lib/test/libregrtest/run_workers.py
index 7e6c7fa4cc5507a..a146a439d9d3eca 100644
--- a/Lib/test/libregrtest/run_workers.py
+++ b/Lib/test/libregrtest/run_workers.py
@@ -70,7 +70,6 @@ def stop(self):
         with self.lock:
             self.tests_iter = None
 
-
 @dataclasses.dataclass(slots=True, frozen=True)
 class MultiprocessResult:
     result: TestResult
@@ -269,16 +268,22 @@ def create_json_file(self, stack: contextlib.ExitStack) 
-> tuple[JsonFile, TextI
                 json_file = JsonFile(json_fd, JsonFileType.UNIX_FD)
         return (json_file, json_tmpfile)
 
-    def create_worker_runtests(self, test_name: TestName, json_file: JsonFile) 
-> WorkerRunTests:
-        tests = (test_name,)
-        if self.runtests.rerun:
-            match_tests = self.runtests.get_match_tests(test_name)
+    def create_worker_runtests(self, test_name: TestName,
+                               json_file: JsonFile,
+                               module_name: TestName | None = None,
+                               ) -> WorkerRunTests:
+        kwargs: dict[str, Any] = {}
+
+        if module_name is not None and test_name != module_name:
+            tests = (module_name,)
+            kwargs['match_tests'] = [(test_name, True)]
         else:
-            match_tests = None
+            tests = (test_name,)
+            if self.runtests.rerun:
+                match_tests = self.runtests.get_match_tests(test_name)
+                if match_tests:
+                    kwargs['match_tests'] = [(test, True) for test in 
match_tests]
 
-        kwargs: dict[str, Any] = {}
-        if match_tests:
-            kwargs['match_tests'] = [(test, True) for test in match_tests]
         if self.runtests.output_on_failure:
             kwargs['verbose'] = True
             kwargs['output_on_failure'] = False
@@ -356,11 +361,13 @@ def read_json(self, json_file: JsonFile, json_tmpfile: 
TextIO | None,
 
         return (result, stdout)
 
-    def _runtest(self, test_name: TestName) -> MultiprocessResult:
+    def _runtest(self, test_name: TestName,
+                 module_name: TestName | None = None) -> MultiprocessResult:
         with contextlib.ExitStack() as stack:
             stdout_file = self.create_stdout(stack)
             json_file, json_tmpfile = self.create_json_file(stack)
-            worker_runtests = self.create_worker_runtests(test_name, json_file)
+            worker_runtests = self.create_worker_runtests(
+                test_name, json_file, module_name=module_name)
 
             retcode: str | int | None
             retcode, tmp_files = self.run_tmp_files(worker_runtests,
@@ -393,26 +400,38 @@ def _runtest(self, test_name: TestName) -> 
MultiprocessResult:
     def run(self) -> None:
         fail_fast = self.runtests.fail_fast
         fail_env_changed = self.runtests.fail_env_changed
+        single_process_per_case = self.runtests.single_process_per_case
         try:
-            while not self._stopped:
+            stop = False
+            while not self._stopped and not stop:
                 try:
-                    test_name = next(self.pending)
+                    module_name, case_ids = next(self.pending)
                 except StopIteration:
                     break
 
-                self.start_time = time.monotonic()
-                self.test_name = test_name
-                try:
-                    mp_result = self._runtest(test_name)
-                except WorkerError as exc:
-                    mp_result = exc.mp_result
-                finally:
-                    self.test_name = _NOT_RUNNING
-                mp_result.result.duration = time.monotonic() - self.start_time
-                self.output.put((False, mp_result))
-
-                if mp_result.result.must_stop(fail_fast, fail_env_changed):
-                    break
+                # All cases of a group run sequentially on this thread
+                for test_name in case_ids:
+                    if self._stopped:
+                        break
+                    self.start_time = time.monotonic()
+                    self.test_name = test_name
+                    try:
+                        mp_result = self._runtest(
+                            test_name,
+                            module_name if single_process_per_case else None)
+                    except WorkerError as exc:
+                        mp_result = exc.mp_result
+                    finally:
+                        self.test_name = _NOT_RUNNING
+                    mp_result.result.duration = time.monotonic() - 
self.start_time
+                    if single_process_per_case:
+                        # Report the test case, not the test module
+                        mp_result.result.test_name = test_name
+                    self.output.put((False, mp_result))
+
+                    if mp_result.result.must_stop(fail_fast, fail_env_changed):
+                        stop = True
+                        break
         except ExitThread:
             pass
         except BaseException:
@@ -489,8 +508,7 @@ def __init__(self, num_workers: int, runtests: RunTests,
         self.live_worker_count = 0
 
         self.output: queue.Queue[QueueContent] = queue.Queue()
-        tests_iter = runtests.iter_tests()
-        self.pending = MultiprocessIterator(tests_iter)
+        self.pending = MultiprocessIterator(runtests.iter_case_groups())
         self.timeout = runtests.timeout
         if self.timeout is not None:
             # Rely on faulthandler to kill a worker process. This timouet is
diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py
index 0a9edce1085be54..fbb04b5b705ecec 100644
--- a/Lib/test/libregrtest/runtests.py
+++ b/Lib/test/libregrtest/runtests.py
@@ -101,6 +101,8 @@ class RunTests:
     randomize: bool
     random_seed: int | str
     parallel_threads: int | None
+    single_process_per_case: bool
+    case_groups: tuple[tuple[TestName, tuple[TestName, ...]], ...] | None
 
     def copy(self, **override) -> 'RunTests':
         state = dataclasses.asdict(self)
@@ -132,6 +134,20 @@ def iter_tests(self) -> Iterator[TestName]:
         else:
             yield from self.tests
 
+    def iter_case_groups(self) -> Iterator[tuple[TestName, tuple[TestName, 
...]]]:
+        """
+        Yield (module_name, case_ids) pairs. All case_ids in a group
+        must run sequentially on the same worker thread.
+        """
+        if self.case_groups is None:
+            for name in self.iter_tests():
+                yield (name, (name,))
+        elif self.forever:
+            while True:
+                yield from self.case_groups
+        else:
+            yield from self.case_groups
+
     def json_file_use_stdout(self) -> bool:
         # Use STDOUT in two cases:
         #
diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py
index 6ba440053089161..f05008bf8bc2c01 100644
--- a/Lib/test/test_regrtest.py
+++ b/Lib/test/test_regrtest.py
@@ -20,11 +20,15 @@
 import sys
 import sysconfig
 import tempfile
+import threading
 import textwrap
 import unittest
 import unittest.mock
 from xml.etree import ElementTree
 
+from test.libregrtest.findtests import collect_cases
+from test.libregrtest.filter import set_match_tests
+from test.libregrtest.run_workers import MultiprocessIterator
 from test import support
 from test.support import import_helper
 from test.support import os_helper
@@ -32,7 +36,7 @@
 from test.libregrtest import main
 from test.libregrtest import setup
 from test.libregrtest import utils
-from test.libregrtest.filter import get_match_tests, set_match_tests, 
match_test
+from test.libregrtest.filter import get_match_tests, match_test
 from test.libregrtest.result import TestStats
 from test.libregrtest.utils import normalize_test_name
 
@@ -574,6 +578,30 @@ def test_single_process(self):
         self.assertEqual(regrtest.num_workers, 0)
         self.assertTrue(regrtest.single_process)
 
+    def test_single_process_per_case(self):
+        ns = self.parse_args(['--single-process-per-case'])
+        self.assertTrue(ns.single_process_per_case)
+
+        # No -j given: default to one worker
+        regrtest = self.create_regrtest(['--single-process-per-case'])
+        self.assertEqual(regrtest.num_workers, 1)
+
+        # Explicit -j2 is respected
+        regrtest = self.create_regrtest(['-j2', '--single-process-per-case'])
+        self.assertEqual(regrtest.num_workers, 2)
+
+    def test_single_process_per_case_conflicts(self):
+        # --single-process-per-case doesn't compose with options that
+        # re-run or restrict test selection after collection (gh-109817)
+        self.checkError(['--single-process-per-case', '--rerun'],
+                        "don't go together")
+        self.checkError(['--single-process-per-case', '--single-process'],
+                        "don't go together")
+        self.checkError(['--single-process-per-case', '--pgo'],
+                        "don't go together")
+        self.checkError(['--single-process-per-case', '--fast-ci'],
+                        "don't go together")
+
     def test_pythoninfo(self):
         ns = self.parse_args([])
         self.assertFalse(ns.pythoninfo)
@@ -2332,6 +2360,94 @@ def test_crash(self):
             self.assertIn(f"Exit code {exitcode} (SIGSEGV)", output)
         self.check_line(output, "just before crash!", full=True, regex=False)
 
+    def test_single_process_per_case(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_one(self):
+                    pass
+                def test_two(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests('--single-process-per-case', '-v', testname)
+        # Each case is reported individually (progress lines have a
+        # timestamp prefix, so match mid-line with assertIn)
+        self.assertIn(f'{testname}.Tests.test_one passed', output)
+        self.assertIn(f'{testname}.Tests.test_two passed', output)
+        self.check_line(output, 'All 2 tests OK.', regex=False)
+
+    def test_single_process_per_case_order_independence(self):
+        # A module where test_b only passes if it runs in isolation.
+        # This simulates the real order-dependency bugs this feature
+        # is meant to catch (gh-109817): running each case in its own
+        # process means test_b can never observe state left behind
+        # by test_a.
+        code = textwrap.dedent("""
+            import unittest
+
+            counter = {'n': 0}
+
+            class Tests(unittest.TestCase):
+                def test_a(self):
+                    counter['n'] += 1
+                def test_b(self):
+                    self.assertEqual(counter['n'], 0)
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests('--single-process-per-case', '-v', testname)
+        self.assertIn(f'{testname}.Tests.test_a passed', output)
+        self.assertIn(f'{testname}.Tests.test_b passed', output)
+        self.check_line(output, 'All 2 tests OK.', regex=False)
+
+    def test_single_process_per_case_failure_isolated(self):
+        # One failing case must not abort or contaminate sibling
+        # cases in the same module.
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_pass(self):
+                    pass
+                def test_fail(self):
+                    self.fail("expected failure")
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests('--single-process-per-case', testname,
+                                 exitcode=EXITCODE_BAD_TEST)
+        # The failing case is reported by its case ID...
+        self.assertIn(f'{testname}.Tests.test_fail failed', output)
+        self.check_line(output, '1 test failed:', regex=False)
+        self.assertIn(f'    {testname}.Tests.test_fail', output)
+        # ...and the sibling case in the same module still ran and passed
+        self.assertIn(f'{testname}.Tests.test_pass passed', output)
+        self.check_line(output, '1 test OK.', regex=False)
+
+    def test_single_process_per_case_import_error(self):
+        testname = self.create_test(code='raise ImportError("boom")')
+
+        output = self.run_tests('--single-process-per-case', testname,
+                                exitcode=EXITCODE_BAD_TEST)
+        self.check_executed_tests(output, [testname], failed=[testname],
+                                  stats=0)
+        self.assertNotIn('_FailedTest', output)
+
+    def test_single_process_per_case_skipped_module(self):
+        code = textwrap.dedent("""
+            import unittest
+            raise unittest.SkipTest("nope")
+        """)
+        testname = self.create_test(code=code)
+
+        output = self.run_tests('--single-process-per-case', testname)
+        self.check_executed_tests(output, [testname],
+                                  skipped=[testname], stats=0)
+
+
     def test_verbose3(self):
         code = textwrap.dedent(r"""
             import unittest
@@ -2442,6 +2558,113 @@ def test_pythoninfo(self):
         self.assertIn("Python build information", output)
 
 
+class FindTestsTestCase(BaseTestCase):
+    def test_collect_cases_groups_by_module(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_one(self):
+                    pass
+                def test_two(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+        sys.path.insert(0, self.tmptestdir)
+        self.addCleanup(sys.path.remove, self.tmptestdir)
+        self.addCleanup(sys.modules.pop, testname, None)
+        self.addCleanup(set_match_tests, None)
+
+        cases_by_module, skipped = collect_cases((testname,),
+                                                   test_dir=self.tmptestdir)
+        self.assertIn(testname, cases_by_module)
+        self.assertEqual(len(cases_by_module[testname]), 2)
+        self.assertEqual(skipped, [])
+
+    def test_collect_cases_match_tests_filters(self):
+        code = textwrap.dedent("""
+            import unittest
+
+            class Tests(unittest.TestCase):
+                def test_keep(self):
+                    pass
+                def test_drop(self):
+                    pass
+        """)
+        testname = self.create_test(code=code)
+        sys.path.insert(0, self.tmptestdir)
+        self.addCleanup(sys.path.remove, self.tmptestdir)
+        self.addCleanup(sys.modules.pop, testname, None)
+        self.addCleanup(set_match_tests, None)
+
+        cases_by_module, _ = collect_cases(
+            (testname,),
+            match_tests=[('*test_keep*', True)],
+            test_dir=self.tmptestdir)
+        case_ids = cases_by_module.get(testname, [])
+        self.assertTrue(any('test_keep' in c for c in case_ids))
+        self.assertFalse(any('test_drop' in c for c in case_ids))
+
+    def test_collect_cases_skiptest(self):
+        code = textwrap.dedent("""
+            import unittest
+            raise unittest.SkipTest("module-level skip")
+        """)
+        testname = self.create_test(code=code)
+        sys.path.insert(0, self.tmptestdir)
+        self.addCleanup(sys.path.remove, self.tmptestdir)
+        self.addCleanup(sys.modules.pop, testname, None)
+        self.addCleanup(set_match_tests, None)
+
+        cases_by_module, skipped = collect_cases((testname,),
+                                                   test_dir=self.tmptestdir)
+        self.assertEqual(cases_by_module, {})
+        self.assertIn(testname, skipped)
+
+
+class MultiprocessIteratorTestCase(unittest.TestCase):
+    def test_yields_all_groups_once(self):
+        groups = [("mod_a", ("mod_a.A.t1", "mod_a.A.t2")),
+                  ("mod_b", ("mod_b.B.t1",))]
+        it = MultiprocessIterator(iter(groups))
+        seen = []
+        while (g := next(it, None)) is not None:
+            seen.append(g)
+        self.assertEqual(seen, groups)
+
+    def test_exhausted_returns_none(self):
+        it = MultiprocessIterator(iter([]))
+        self.assertIsNone(next(it, None))
+
+    def test_stop_halts_iteration(self):
+        groups = [("mod_a", ("mod_a.A.t1",)), ("mod_b", ("mod_b.B.t1",))]
+        it = MultiprocessIterator(iter(groups))
+        next(it, None)
+        it.stop()
+        self.assertIsNone(next(it, None))
+
+    def test_thread_safety_no_duplicate_or_lost_groups(self):
+        n = 200
+        groups = [(f"mod_{i}", (f"mod_{i}.T.t",)) for i in range(n)]
+        it = MultiprocessIterator(iter(groups))
+        results = []
+        results_lock = threading.Lock()
+
+        def worker():
+            while (g := next(it, None)) is not None:
+                with results_lock:
+                    results.append(g)
+
+        threads = [threading.Thread(target=worker) for _ in range(8)]
+        for t in threads:
+            t.start()
+        for t in threads:
+            t.join()
+
+        self.assertEqual(sorted(results), sorted(groups))
+        self.assertEqual(len(results), n)
+
+
 class TestUtils(unittest.TestCase):
     def test_format_duration(self):
         self.assertEqual(utils.format_duration(0),
diff --git 
a/Misc/NEWS.d/next/Tests/2026-08-12-20-52-16.gh-issue-109817.GZHUNg.rst 
b/Misc/NEWS.d/next/Tests/2026-08-12-20-52-16.gh-issue-109817.GZHUNg.rst
new file mode 100644
index 000000000000000..6064b331992d715
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2026-08-12-20-52-16.gh-issue-109817.GZHUNg.rst
@@ -0,0 +1,4 @@
+Add the ``--single-process-per-case`` option to libregrtest to run
+every test case in a separate process. Test cases from the same
+test module are run sequentially. This helps to detect order
+dependencies and environment leaks between test cases.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to