Removed the Requirement to Install Python and NodeJS (Now Bundled with Borealis)
This commit is contained in:
185
Dependencies/Python/Lib/test/bisect_cmd.py
vendored
Normal file
185
Dependencies/Python/Lib/test/bisect_cmd.py
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Command line tool to bisect failing CPython tests.
|
||||
|
||||
Find the test_os test method which alters the environment:
|
||||
|
||||
./python -m test.bisect_cmd --fail-env-changed test_os
|
||||
|
||||
Find a reference leak in "test_os", write the list of failing tests into the
|
||||
"bisect" file:
|
||||
|
||||
./python -m test.bisect_cmd -o bisect -R 3:3 test_os
|
||||
|
||||
Load an existing list of tests from a file using -i option:
|
||||
|
||||
./python -m test --list-cases -m FileTests test_os > tests
|
||||
./python -m test.bisect_cmd -i tests test_os
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import os.path
|
||||
import math
|
||||
import random
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
|
||||
def write_tests(filename, tests):
|
||||
with open(filename, "w") as fp:
|
||||
for name in tests:
|
||||
print(name, file=fp)
|
||||
fp.flush()
|
||||
|
||||
|
||||
def write_output(filename, tests):
|
||||
if not filename:
|
||||
return
|
||||
print("Writing %s tests into %s" % (len(tests), filename))
|
||||
write_tests(filename, tests)
|
||||
return filename
|
||||
|
||||
|
||||
def format_shell_args(args):
|
||||
return ' '.join(args)
|
||||
|
||||
|
||||
def python_cmd():
|
||||
cmd = [sys.executable]
|
||||
cmd.extend(subprocess._args_from_interpreter_flags())
|
||||
cmd.extend(subprocess._optim_args_from_interpreter_flags())
|
||||
cmd.extend(('-X', 'faulthandler'))
|
||||
return cmd
|
||||
|
||||
|
||||
def list_cases(args):
|
||||
cmd = python_cmd()
|
||||
cmd.extend(['-m', 'test', '--list-cases'])
|
||||
cmd.extend(args.test_args)
|
||||
proc = subprocess.run(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
exitcode = proc.returncode
|
||||
if exitcode:
|
||||
cmd = format_shell_args(cmd)
|
||||
print("Failed to list tests: %s failed with exit code %s"
|
||||
% (cmd, exitcode))
|
||||
sys.exit(exitcode)
|
||||
tests = proc.stdout.splitlines()
|
||||
return tests
|
||||
|
||||
|
||||
def run_tests(args, tests, huntrleaks=None):
|
||||
tmp = tempfile.mktemp()
|
||||
try:
|
||||
write_tests(tmp, tests)
|
||||
|
||||
cmd = python_cmd()
|
||||
cmd.extend(['-u', '-m', 'test', '--matchfile', tmp])
|
||||
cmd.extend(args.test_args)
|
||||
print("+ %s" % format_shell_args(cmd))
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
proc = subprocess.run(cmd)
|
||||
return proc.returncode
|
||||
finally:
|
||||
if os.path.exists(tmp):
|
||||
os.unlink(tmp)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-i', '--input',
|
||||
help='Test names produced by --list-tests written '
|
||||
'into a file. If not set, run --list-tests')
|
||||
parser.add_argument('-o', '--output',
|
||||
help='Result of the bisection')
|
||||
parser.add_argument('-n', '--max-tests', type=int, default=1,
|
||||
help='Maximum number of tests to stop the bisection '
|
||||
'(default: 1)')
|
||||
parser.add_argument('-N', '--max-iter', type=int, default=100,
|
||||
help='Maximum number of bisection iterations '
|
||||
'(default: 100)')
|
||||
# FIXME: document that following arguments are test arguments
|
||||
|
||||
args, test_args = parser.parse_known_args()
|
||||
args.test_args = test_args
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
for opt in ('-w', '--rerun', '--verbose2'):
|
||||
if opt in args.test_args:
|
||||
print(f"WARNING: {opt} option should not be used to bisect!")
|
||||
print()
|
||||
|
||||
if args.input:
|
||||
with open(args.input) as fp:
|
||||
tests = [line.strip() for line in fp]
|
||||
else:
|
||||
tests = list_cases(args)
|
||||
|
||||
print("Start bisection with %s tests" % len(tests))
|
||||
print("Test arguments: %s" % format_shell_args(args.test_args))
|
||||
print("Bisection will stop when getting %s or less tests "
|
||||
"(-n/--max-tests option), or after %s iterations "
|
||||
"(-N/--max-iter option)"
|
||||
% (args.max_tests, args.max_iter))
|
||||
output = write_output(args.output, tests)
|
||||
print()
|
||||
|
||||
start_time = time.monotonic()
|
||||
iteration = 1
|
||||
try:
|
||||
while len(tests) > args.max_tests and iteration <= args.max_iter:
|
||||
ntest = len(tests)
|
||||
ntest = max(ntest // 2, 1)
|
||||
subtests = random.sample(tests, ntest)
|
||||
|
||||
print(f"[+] Iteration {iteration}/{args.max_iter}: "
|
||||
f"run {len(subtests)} tests/{len(tests)}")
|
||||
print()
|
||||
|
||||
exitcode = run_tests(args, subtests)
|
||||
|
||||
print("ran %s tests/%s" % (ntest, len(tests)))
|
||||
print("exit", exitcode)
|
||||
if exitcode:
|
||||
print("Tests failed: continuing with this subtest")
|
||||
tests = subtests
|
||||
output = write_output(args.output, tests)
|
||||
else:
|
||||
print("Tests succeeded: skipping this subtest, trying a new subset")
|
||||
print()
|
||||
iteration += 1
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
print("Bisection interrupted!")
|
||||
print()
|
||||
|
||||
print("Tests (%s):" % len(tests))
|
||||
for test in tests:
|
||||
print("* %s" % test)
|
||||
print()
|
||||
|
||||
if output:
|
||||
print("Output written into %s" % output)
|
||||
|
||||
dt = math.ceil(time.monotonic() - start_time)
|
||||
if len(tests) <= args.max_tests:
|
||||
print("Bisection completed in %s iterations and %s"
|
||||
% (iteration, datetime.timedelta(seconds=dt)))
|
||||
else:
|
||||
print("Bisection failed after %s iterations and %s"
|
||||
% (iteration, datetime.timedelta(seconds=dt)))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user