From 42964b76d08693582f6a56c5bfd4b9d89453dd48 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 21 Oct 2023 00:01:20 +0200 Subject: [PATCH] gh-110932: Fix regrtest for SOURCE_DATE_EPOCH If the SOURCE_DATE_EPOCH environment variable is defined, use its value as a string as the random seed. --- Lib/test/libregrtest/main.py | 16 +++++++++------- Lib/test/libregrtest/runtests.py | 2 +- Lib/test/test_regrtest.py | 30 ++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index cb60d5af732b437..595f76e4e139814 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -129,14 +129,16 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False): # Randomize self.randomize: bool = ns.randomize - self.random_seed: int | None = ( - ns.random_seed - if ns.random_seed is not None - else random.getrandbits(32) - ) if 'SOURCE_DATE_EPOCH' in os.environ: self.randomize = False - self.random_seed = None + # SOURCE_DATE_EPOCH should be an integer, but use a string to not + # fail if it's not integer. random.seed() accepts a string. + # https://reproducible-builds.org/docs/source-date-epoch/ + self.random_seed: int | str = os.environ['SOURCE_DATE_EPOCH'] + elif ns.random_seed is None: + self.random_seed = random.getrandbits(32) + else: + self.random_seed = ns.random_seed # tests self.first_runtests: RunTests | None = None @@ -441,7 +443,7 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: or tests or self.cmdline_args)): display_header(self.use_resources, self.python_cmd) - print("Using random seed", self.random_seed) + print("Using random seed:", self.random_seed) runtests = self.create_run_tests(selected) self.first_runtests = runtests diff --git a/Lib/test/libregrtest/runtests.py b/Lib/test/libregrtest/runtests.py index 4da312db4cb02e5..893b311c31297cd 100644 --- a/Lib/test/libregrtest/runtests.py +++ b/Lib/test/libregrtest/runtests.py @@ -91,7 +91,7 @@ class RunTests: use_resources: tuple[str, ...] python_cmd: tuple[str, ...] | None randomize: bool - random_seed: int | None + random_seed: int | str json_file: JsonFile | None def copy(self, **override): diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index e65d9a89ff8a2f5..5ef2801445aee15 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -149,12 +149,12 @@ def test_randomize(self): self.assertTrue(ns.randomize) with os_helper.EnvironmentVarGuard() as env: - env['SOURCE_DATE_EPOCH'] = '1' + env['SOURCE_DATE_EPOCH'] = '1697839080' ns = self.parse_args(['--randomize']) regrtest = main.Regrtest(ns) self.assertFalse(regrtest.randomize) - self.assertIsNone(regrtest.random_seed) + self.assertEqual(regrtest.random_seed, '1697839080') def test_randseed(self): ns = self.parse_args(['--randseed', '12345']) @@ -388,7 +388,13 @@ def check_ci_mode(self, args, use_resources, rerun=True): # Check Regrtest attributes which are more reliable than Namespace # which has an unclear API - regrtest = main.Regrtest(ns) + with os_helper.EnvironmentVarGuard() as env: + # Ignore SOURCE_DATE_EPOCH env var if it's set + if 'SOURCE_DATE_EPOCH' in env: + del env['SOURCE_DATE_EPOCH'] + + regrtest = main.Regrtest(ns) + self.assertEqual(regrtest.num_workers, -1) self.assertEqual(regrtest.want_rerun, rerun) self.assertTrue(regrtest.randomize) @@ -662,7 +668,7 @@ def list_regex(line_format, tests): self.check_line(output, f'Result: {state}', full=True) def parse_random_seed(self, output): - match = self.regex_search(r'Using random seed ([0-9]+)', output) + match = self.regex_search(r'Using random seed: ([0-9]+)', output) randseed = int(match.group(1)) self.assertTrue(0 <= randseed, randseed) return randseed @@ -672,10 +678,17 @@ def run_command(self, args, input=None, exitcode=0, **kw): input = '' if 'stderr' not in kw: kw['stderr'] = subprocess.STDOUT + + env = kw.pop('env', None) + if env is None: + env = dict(os.environ) + env.pop('SOURCE_DATE_EPOCH', None) + proc = subprocess.run(args, text=True, input=input, stdout=subprocess.PIPE, + env=env, **kw) if proc.returncode != exitcode: msg = ("Command %s failed with exit code %s, but exit code %s expected!\n" @@ -955,6 +968,15 @@ def test_random(self): output = self.run_tests(test, exitcode=EXITCODE_NO_TESTS_RAN) self.assertIsInstance(self.parse_random_seed(output), int) + # check SOURCE_DATE_EPOCH + timestamp = 1697839080 + env = dict(os.environ, SOURCE_DATE_EPOCH=str(timestamp)) + output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN, + env=env) + randseed = self.parse_random_seed(output) + self.assertEqual(randseed, timestamp) + self.check_line(output, 'TESTRANDOM: 520') + def test_fromfile(self): # test --fromfile tests = [self.create_test() for index in range(5)]