mirror of
https://gitlab.com/buildroot.org/buildroot.git
synced 2026-08-01 21:23:51 -09:00
Commit 3d2141bcee("support/testing/run-tests: specify multiprocessing
method") added a call to multiprocessing.set_start_method('fork') as a
workaround for python 3.14, which changed the default start method to
forkserver - Which is incompatible with the nose2 setup.
multiprocessing.set_start_method() is only supposed to be called a maximum
of 1 time per process and throws a RuntimeError if called more than that
(even with the same arguments):
>>> import multiprocessing
>>> multiprocessing.set_start_method('fork')
>>> multiprocessing.set_start_method('fork')
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
multiprocessing.set_start_method('fork')
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "/usr/lib/python3.13/multiprocessing/context.py", line 247, in set_start_method
raise RuntimeError('context has already been set')
Debian included a similar patch in python3-nose2 0.51.1-2 (currently in
testing/unstable) which adds its own call to set_start_method():
https://salsa.debian.org/python-team/packages/nose2/-/blob/debian/0.15.1-2/debian/patches/0004-plugins-mp-set-context-to-fork-for-Python-3.14-mp-AP.patch?ref_type=tags
Which comes from:
https://github.com/nose-devs/nose2/pull/644
As discussed in the upstream PR, this is not a correct fix is wrong and
breaks various use cases. An issue has been opened to get this fixed in the
Debian packaging at:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1129350
But until that is done, rework the patch to:
- Only override set_start_method() if needed to limit impact
- Monkey patch set_start_method() so additional calls are ignored
To unbreak run-test on affected Debian systems and add some documentation to
make it clear why this is done.
[Peter: use allow_none / force optional arguments as pointed out by Julien]
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
157 lines
5.3 KiB
Python
Executable File
157 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse
|
|
import multiprocessing
|
|
import os
|
|
import sys
|
|
|
|
import nose2
|
|
|
|
from infra.basetest import BRConfigTest
|
|
|
|
import infra
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Run Buildroot tests')
|
|
parser.add_argument('testname', nargs='*',
|
|
help='list of test cases to execute')
|
|
parser.add_argument('-l', '--list', action='store_true',
|
|
help='list of available test cases')
|
|
parser.add_argument('-a', '--all', action='store_true',
|
|
help='execute all test cases')
|
|
parser.add_argument('-s', '--stdout', action='store_true',
|
|
help='log everything to stdout')
|
|
parser.add_argument('-o', '--output',
|
|
help='output directory')
|
|
parser.add_argument('-d', '--download',
|
|
help='download directory')
|
|
parser.add_argument('-p', '--prepare-only', action='store_true',
|
|
help='download emulator builtin binaries')
|
|
parser.add_argument('-k', '--keep',
|
|
help='keep build directories',
|
|
action='store_true')
|
|
parser.add_argument('-t', '--testcases', type=int, default=1,
|
|
help='number of testcases to run simultaneously')
|
|
parser.add_argument('-j', '--jlevel', type=int,
|
|
help='BR2_JLEVEL to use for each testcase')
|
|
parser.add_argument('--timeout-multiplier', type=int, default=1,
|
|
help='increase timeouts (useful for slow machines)')
|
|
parser.add_argument('-D', '--debug', action='store_true',
|
|
help='enable debug log')
|
|
|
|
args = parser.parse_args()
|
|
|
|
script_path = os.path.realpath(__file__)
|
|
test_dir = os.path.dirname(script_path)
|
|
|
|
if args.stdout:
|
|
BRConfigTest.logtofile = False
|
|
|
|
if args.list:
|
|
print("List of tests")
|
|
nose2_args = [
|
|
script_path,
|
|
"-s", test_dir,
|
|
"-v",
|
|
"--collect-only"
|
|
]
|
|
if args.debug:
|
|
nose2_args += ["--log-level", "debug"]
|
|
nose2.discover(argv=nose2_args,
|
|
plugins=["nose2.plugins.collect"])
|
|
return 0
|
|
|
|
if args.download is None:
|
|
args.download = os.getenv("BR2_DL_DIR")
|
|
if args.download is None:
|
|
print("Missing download directory, please use -d/--download")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
|
|
BRConfigTest.downloaddir = os.path.abspath(args.download)
|
|
|
|
if args.prepare_only:
|
|
emulator_builtin_binaries = ["kernel-vexpress-5.10.202",
|
|
"vexpress-v2p-ca9-5.10.202.dtb",
|
|
"kernel-versatile-5.10.202",
|
|
"versatile-pb-5.10.202.dtb"]
|
|
print("Downloading emulator builtin binaries")
|
|
for binary in emulator_builtin_binaries:
|
|
infra.download(BRConfigTest.downloaddir, binary)
|
|
return 0
|
|
|
|
if args.output is None:
|
|
print("Missing output directory, please use -o/--output")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
|
|
if not os.path.exists(args.output):
|
|
os.mkdir(args.output)
|
|
|
|
BRConfigTest.outputdir = os.path.abspath(args.output)
|
|
|
|
if args.all is False and not args.testname:
|
|
print("No test selected")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
|
|
BRConfigTest.keepbuilds = args.keep
|
|
|
|
if args.testcases != 1:
|
|
if args.testcases < 1:
|
|
print("Invalid number of testcases to run simultaneously")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
# same default BR2_JLEVEL as package/Makefile.in
|
|
br2_jlevel = 1 + multiprocessing.cpu_count()
|
|
each_testcase = int((br2_jlevel + args.testcases) / args.testcases)
|
|
BRConfigTest.jlevel = each_testcase
|
|
|
|
if args.jlevel:
|
|
if args.jlevel < 0:
|
|
print("Invalid BR2_JLEVEL to use for each testcase")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
# the user can override the auto calculated value
|
|
BRConfigTest.jlevel = args.jlevel
|
|
|
|
if args.timeout_multiplier < 1:
|
|
print("Invalid multiplier for timeout values")
|
|
print("")
|
|
parser.print_help()
|
|
return 1
|
|
BRConfigTest.timeout_multiplier = args.timeout_multiplier
|
|
|
|
nose2_args = ["-v",
|
|
"-N", str(args.testcases),
|
|
"-s", test_dir,
|
|
"-c", os.path.join(test_dir, "conf/unittest.cfg")]
|
|
|
|
if args.debug:
|
|
nose2_args += ["--log-level", "debug"]
|
|
|
|
if args.testname:
|
|
nose2_args += args.testname
|
|
|
|
nose2.discover(argv=nose2_args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# python 3.14 changed default start method from fork to
|
|
# fork-server, which is not compatible with the nose2 setup
|
|
if multiprocessing.get_start_method(allow_none=True) != "fork":
|
|
multiprocessing.set_start_method("fork", force=True)
|
|
# set_start_method throws a RuntimeError if called more than
|
|
# once.
|
|
# Debian python3-nose2 0.15.1-2 includes a patch adding
|
|
# another set_start_method() call, so monkey patch it out to
|
|
# get rid of this
|
|
multiprocessing.set_start_method = lambda *args: None
|
|
|
|
sys.exit(main())
|