Skip to content

Commit 47670fb

Browse files
[3.11] gh-110932: Fix regrtest for SOURCE_DATE_EPOCH (GH-111143) (#111153)
gh-110932: Fix regrtest for SOURCE_DATE_EPOCH (GH-111143) If the SOURCE_DATE_EPOCH environment variable is defined, use its value as the random seed. (cherry picked from commit 7237fb5) Co-authored-by: Victor Stinner <[email protected]>
1 parent 9addf2c commit 47670fb

File tree

4 files changed

+75
-22
lines changed

4 files changed

+75
-22
lines changed

Lib/test/libregrtest/main.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,19 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False):
129129

130130
# Randomize
131131
self.randomize: bool = ns.randomize
132-
self.random_seed: int | None = (
133-
ns.random_seed
134-
if ns.random_seed is not None
135-
else random.getrandbits(32)
136-
)
137-
if 'SOURCE_DATE_EPOCH' in os.environ:
132+
if ('SOURCE_DATE_EPOCH' in os.environ
133+
# don't use the variable if empty
134+
and os.environ['SOURCE_DATE_EPOCH']
135+
):
138136
self.randomize = False
139-
self.random_seed = None
137+
# SOURCE_DATE_EPOCH should be an integer, but use a string to not
138+
# fail if it's not integer. random.seed() accepts a string.
139+
# https://reproducible-builds.org/docs/source-date-epoch/
140+
self.random_seed: int | str = os.environ['SOURCE_DATE_EPOCH']
141+
elif ns.random_seed is None:
142+
self.random_seed = random.getrandbits(32)
143+
else:
144+
self.random_seed = ns.random_seed
140145

141146
# tests
142147
self.first_runtests: RunTests | None = None
@@ -441,7 +446,7 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
441446
or tests or self.cmdline_args)):
442447
display_header(self.use_resources, self.python_cmd)
443448

444-
print("Using random seed", self.random_seed)
449+
print("Using random seed:", self.random_seed)
445450

446451
runtests = self.create_run_tests(selected)
447452
self.first_runtests = runtests

Lib/test/libregrtest/runtests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class RunTests:
9191
use_resources: tuple[str, ...]
9292
python_cmd: tuple[str, ...] | None
9393
randomize: bool
94-
random_seed: int | None
94+
random_seed: int | str
9595
json_file: JsonFile | None
9696

9797
def copy(self, **override):

Lib/test/test_regrtest.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,26 @@ def test_header(self):
143143
self.assertTrue(ns.header)
144144

145145
def test_randomize(self):
146-
for opt in '-r', '--randomize':
146+
for opt in ('-r', '--randomize'):
147147
with self.subTest(opt=opt):
148148
ns = self.parse_args([opt])
149149
self.assertTrue(ns.randomize)
150150

151151
with os_helper.EnvironmentVarGuard() as env:
152-
env['SOURCE_DATE_EPOCH'] = '1'
153-
152+
# with SOURCE_DATE_EPOCH
153+
env['SOURCE_DATE_EPOCH'] = '1697839080'
154154
ns = self.parse_args(['--randomize'])
155155
regrtest = main.Regrtest(ns)
156156
self.assertFalse(regrtest.randomize)
157-
self.assertIsNone(regrtest.random_seed)
157+
self.assertIsInstance(regrtest.random_seed, str)
158+
self.assertEqual(regrtest.random_seed, '1697839080')
159+
160+
# without SOURCE_DATE_EPOCH
161+
del env['SOURCE_DATE_EPOCH']
162+
ns = self.parse_args(['--randomize'])
163+
regrtest = main.Regrtest(ns)
164+
self.assertTrue(regrtest.randomize)
165+
self.assertIsInstance(regrtest.random_seed, int)
158166

159167
def test_randseed(self):
160168
ns = self.parse_args(['--randseed', '12345'])
@@ -388,7 +396,13 @@ def check_ci_mode(self, args, use_resources, rerun=True):
388396

389397
# Check Regrtest attributes which are more reliable than Namespace
390398
# which has an unclear API
391-
regrtest = main.Regrtest(ns)
399+
with os_helper.EnvironmentVarGuard() as env:
400+
# Ignore SOURCE_DATE_EPOCH env var if it's set
401+
if 'SOURCE_DATE_EPOCH' in env:
402+
del env['SOURCE_DATE_EPOCH']
403+
404+
regrtest = main.Regrtest(ns)
405+
392406
self.assertEqual(regrtest.num_workers, -1)
393407
self.assertEqual(regrtest.want_rerun, rerun)
394408
self.assertTrue(regrtest.randomize)
@@ -662,21 +676,26 @@ def list_regex(line_format, tests):
662676
state = f'{state} then {new_state}'
663677
self.check_line(output, f'Result: {state}', full=True)
664678

665-
def parse_random_seed(self, output):
666-
match = self.regex_search(r'Using random seed ([0-9]+)', output)
667-
randseed = int(match.group(1))
668-
self.assertTrue(0 <= randseed, randseed)
669-
return randseed
679+
def parse_random_seed(self, output: str) -> str:
680+
match = self.regex_search(r'Using random seed: (.*)', output)
681+
return match.group(1)
670682

671683
def run_command(self, args, input=None, exitcode=0, **kw):
672684
if not input:
673685
input = ''
674686
if 'stderr' not in kw:
675687
kw['stderr'] = subprocess.STDOUT
688+
689+
env = kw.pop('env', None)
690+
if env is None:
691+
env = dict(os.environ)
692+
env.pop('SOURCE_DATE_EPOCH', None)
693+
676694
proc = subprocess.run(args,
677695
text=True,
678696
input=input,
679697
stdout=subprocess.PIPE,
698+
env=env,
680699
**kw)
681700
if proc.returncode != exitcode:
682701
msg = ("Command %s failed with exit code %s, but exit code %s expected!\n"
@@ -752,7 +771,9 @@ def setUp(self):
752771
self.regrtest_args.append('-n')
753772

754773
def check_output(self, output):
755-
self.parse_random_seed(output)
774+
randseed = self.parse_random_seed(output)
775+
self.assertTrue(randseed.isdigit(), randseed)
776+
756777
self.check_executed_tests(output, self.tests,
757778
randomize=True, stats=len(self.tests))
758779

@@ -943,7 +964,7 @@ def test_random(self):
943964
test_random = int(match.group(1))
944965

945966
# try to reproduce with the random seed
946-
output = self.run_tests('-r', '--randseed=%s' % randseed, test,
967+
output = self.run_tests('-r', f'--randseed={randseed}', test,
947968
exitcode=EXITCODE_NO_TESTS_RAN)
948969
randseed2 = self.parse_random_seed(output)
949970
self.assertEqual(randseed2, randseed)
@@ -954,7 +975,32 @@ def test_random(self):
954975

955976
# check that random.seed is used by default
956977
output = self.run_tests(test, exitcode=EXITCODE_NO_TESTS_RAN)
957-
self.assertIsInstance(self.parse_random_seed(output), int)
978+
randseed = self.parse_random_seed(output)
979+
self.assertTrue(randseed.isdigit(), randseed)
980+
981+
# check SOURCE_DATE_EPOCH (integer)
982+
timestamp = '1697839080'
983+
env = dict(os.environ, SOURCE_DATE_EPOCH=timestamp)
984+
output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN,
985+
env=env)
986+
randseed = self.parse_random_seed(output)
987+
self.assertEqual(randseed, timestamp)
988+
self.check_line(output, 'TESTRANDOM: 520')
989+
990+
# check SOURCE_DATE_EPOCH (string)
991+
env = dict(os.environ, SOURCE_DATE_EPOCH='XYZ')
992+
output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN,
993+
env=env)
994+
randseed = self.parse_random_seed(output)
995+
self.assertEqual(randseed, 'XYZ')
996+
self.check_line(output, 'TESTRANDOM: 22')
997+
998+
# check SOURCE_DATE_EPOCH (empty string): ignore the env var
999+
env = dict(os.environ, SOURCE_DATE_EPOCH='')
1000+
output = self.run_tests('-r', test, exitcode=EXITCODE_NO_TESTS_RAN,
1001+
env=env)
1002+
randseed = self.parse_random_seed(output)
1003+
self.assertTrue(randseed.isdigit(), randseed)
9581004

9591005
def test_fromfile(self):
9601006
# test --fromfile
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix regrtest if the ``SOURCE_DATE_EPOCH`` environment variable is defined:
2+
use the variable value as the random seed. Patch by Victor Stinner.

0 commit comments

Comments
 (0)