Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501# `test/functional/test_runner.py test/functional/wallet*`502# `test/functional/test_runner.py ./test/functional/wallet*`503# `test_runner.py wallet*`504# but not:505# `test/functional/test_runner.py wallet*`506# Multiple wildcards can be passed:507# `test_runner.py tool* mempool*`508for test in tests:509script = test.split("/")[-1]510script = script + ".py" if ".py" not in script else script511matching_scripts = [s for s in ALL_SCRIPTS if s.startswith(script)]512if matching_scripts:513test_list += matching_scripts514else:515print("{}WARNING!{} Test '{}' not found in full test list.".format(BOLD[1], BOLD[0], test))516elif args.extended:517# Include extended tests518test_list += ALL_SCRIPTS519else:520# Run base tests only521test_list += BASE_SCRIPTS523# Remove the test cases that the user has explicitly asked to exclude.524# The user can specify a test case with or without the .py extension.525if args.exclude:527def print_warning_missing_test(test_name):528print("{}WARNING!{} Test '{}' not found in current test list. Check the --exclude options.".format(BOLD[1], BOLD[0], test_name))529if fail_on_warn:530sys.exit(1)532def remove_tests(exclude_list):533if not exclude_list:534print_warning_missing_test(exclude_test)535for exclude_item in exclude_list:536print("Excluding %s" % exclude_item)537test_list.remove(exclude_item)539for exclude_test in args.exclude:540if ',' in exclude_test:541print("{}WARNING!{} --exclude '{}' contains a comma. Use --exclude for each test.".format(BOLD[1], BOLD[0], exclude_test))542if fail_on_warn:543sys.exit(1)545for exclude_test in args.exclude:546# A space in the name indicates it has arguments such as "rpc_bind.py --ipv4"547if ' ' in exclude_test:548remove_tests([test for test in test_list if test.replace('.py', '') == exclude_test.replace('.py', '')])549else:550# Exclude all variants of a test551remove_tests([test for test in test_list if test.split('.py')[0] == exclude_test.split('.py')[0]])553if config.getboolean("components", "BUILD_BENCH") and TOOL_BENCH_SANITY_CHECK in test_list:554# Remove it, and expand it for each bench in the list555test_list.remove(TOOL_BENCH_SANITY_CHECK)556bench_cmd = Binaries(get_binary_paths(config), bin_dir=None).bench_argv() + ["-list"]557bench_list = subprocess.check_output(bench_cmd, text=True).splitlines()558bench_list = [f"{TOOL_BENCH_SANITY_CHECK} --bench={b}" for b in bench_list]559# Start with special scripts (variable, unknown runtime)560test_list.extendleft(reversed(bench_list))562if args.filter:563test_list = deque(filter(re.compile(args.filter).search, test_list))565if not test_list:566print("No valid test scripts specified. Check that your test is in one "567"of the test lists in test_runner.py, or run test_runner.py with no arguments to run all tests")568sys.exit(1)570if args.help:571# Print help for test_runner.py, then print help of the first script (with args removed) and exit.572parser.print_help()573subprocess.check_call([sys.executable, os.path.join(config["environment"]["SRCDIR"], 'test', 'functional', test_list[0].split()[0]), '-h'])574sys.exit(0)576# Warn if there is not enough space on tmpdir to run the tests with --nocleanup577if args.nocleanup:578if shutil.disk_usage(tmpdir).free < MIN_NO_CLEANUP_SPACE:579print(f"{BOLD[1]}WARNING!{BOLD[0]} There may be insufficient free space in {tmpdir} to run the functional test suite with --nocleanup. "580f"A minimum of {MIN_NO_CLEANUP_SPACE // (1024 * 1024 * 1024)} GB of free space is required.")581passon_args.append("--nocleanup")583check_script_list(src_dir=config["environment"]["SRCDIR"], fail_on_warn=fail_on_warn)584check_script_prefixes()586run_tests(587test_list=test_list,588build_dir=config["environment"]["BUILDDIR"],589tmpdir=tmpdir,590jobs=args.jobs,591enable_coverage=args.coverage,592args=passon_args,593combined_logs_len=args.combinedlogslen,594failfast=args.failfast,595use_term_control=args.ansi,596results_filepath=results_filepath,597)599def run_tests(*, test_list, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, use_term_control, results_filepath=None):600args = args or []602# Some optional Python dependencies (e.g. pycapnp) may emit warnings or fail under603# CPython free-threaded builds when the GIL is disabled. Force it on for all604# functional tests so every child process inherits PYTHON_GIL=1.605os.environ["PYTHON_GIL"] = "1"607# Warn if bitcoind is already running608try:609# pgrep exits with code zero when one or more matching processes found610if subprocess.run(["pgrep", "-x", "bitcoind"], stdout=subprocess.DEVNULL).returncode == 0:611print("%sWARNING!%s There is already a bitcoind process running on this system. Tests may fail unexpectedly due to resource contention!" % (BOLD[1], BOLD[0]))612except OSError:613# pgrep not supported614pass616# Warn if there is not enough space on the testing dir617min_space = MIN_FREE_SPACE + (jobs - 1) * ADDITIONAL_SPACE_PER_JOB618if shutil.disk_usage(tmpdir).free < min_space:619print(f"{BOLD[1]}WARNING!{BOLD[0]} There may be insufficient free space in {tmpdir} to run the Bitcoin functional test suite. "620f"Running the test suite with fewer than {min_space // (1024 * 1024)} MB of free space might cause tests to fail.")622tests_dir = f"{build_dir}/test/functional/"623# This allows `test_runner.py` to work from an out-of-source build directory using a symlink,624# a hard link or a copy on any platform. See https://github.com/bitcoin/bitcoin/pull/27561.625sys.path.append(tests_dir)627cache_tmp_dir = tempfile.TemporaryDirectory(prefix="functional_test_cache")628flags = [f"--cachedir={cache_tmp_dir.name}"] + args630if enable_coverage:631coverage = RPCCoverage()632flags.append(coverage.flag)633logging.debug("Initializing coverage directory at %s" % coverage.dir)634else:635coverage = None637if len(test_list) > 1 and jobs > 1:638# Populate cache639try:640subprocess.check_output([sys.executable, tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir])641except subprocess.CalledProcessError as e:642sys.stdout.buffer.write(e.output)643raise645# Run Tests646job_queue = TestHandler(647num_tests_parallel=jobs,648tests_dir=tests_dir,649tmpdir=tmpdir,650test_list=test_list,651flags=flags,652use_term_control=use_term_control,653)654start_time = time.time()655test_results = []657max_len_name = len(max(test_list, key=len))658test_count = len(test_list)659all_passed = True660while not job_queue.done():661if failfast and not all_passed:662break663for test_result, testdir, stdout, stderr, exit_code, skip_reason in job_queue.get_next():664test_results.append(test_result)665done_str = f"{len(test_results)}/{test_count} - {BOLD[1]}{test_result.name}{BOLD[0]}"666if test_result.status == "Passed":667logging.debug("%s passed, Duration: %s s" % (done_str, test_result.time))668elif test_result.status == "Skipped":669logging.debug(f"{done_str} skipped ({skip_reason})")670else:671all_passed = False672print(f"{done_str} failed (exit code {exit_code}), Duration: {test_result.time} s\n")673print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n')674print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n')675if combined_logs_len and os.path.isdir(testdir):676# Print the final `combinedlogslen` lines of the combined logs677print('{}Combine the logs and print the last {} lines ...{}'.format(BOLD[1], combined_logs_len, BOLD[0]))678print('\n============')679print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0]))680print('============\n')681combined_logs_args = [sys.executable, os.path.join(tests_dir, 'combine_logs.py'), testdir]682if BOLD[0]:683combined_logs_args += ['--color']684combined_logs, _ = subprocess.Popen(combined_logs_args, text=True, stdout=subprocess.PIPE).communicate()685print("\n".join(deque(combined_logs.splitlines(), combined_logs_len)))687if failfast:688logging.debug("Early exiting after test failure")689break691if "[Errno 28] No space left on device" in stdout:692sys.exit(f"Early exiting after test failure due to insufficient free space in {tmpdir}\n"693f"Test execution data left in {tmpdir}.\n"694f"Additional storage is needed to execute testing.")696runtime = int(time.time() - start_time)697print_results(test_results, max_len_name, runtime)698if results_filepath:699write_results(test_results, results_filepath, runtime)701if coverage:702coverage_passed = coverage.report_rpc_coverage()703else:704coverage_passed = True706# Clear up the temp directory if all subdirectories are gone707if not os.listdir(tmpdir):708os.rmdir(tmpdir)710all_passed = all_passed and coverage_passed712# Clean up dangling processes if any. This may only happen with --failfast option.713# Killing the process group will also terminate the current process but that is714# not an issue715if not os.getenv("CI_FAILFAST_TEST_LEAVE_DANGLING") and len(job_queue.jobs):716os.killpg(os.getpgid(0), signal.SIGKILL)718sys.exit(not all_passed)721def print_results(test_results, max_len_name, runtime):722results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]724test_results.sort(key=TestResult.sort_key)725all_passed = True726time_sum = 0728for test_result in test_results:729all_passed = all_passed and test_result.was_successful730time_sum += test_result.time731test_result.padding = max_len_name732results += str(test_result)734status = TICK + "Passed" if all_passed else CROSS + "Failed"735if not all_passed:736results += RED[1]737results += BOLD[1] + "\n%s | %s | %s s (accumulated) \n" % ("ALL".ljust(max_len_name), status.ljust(9), time_sum) + BOLD[0]738if not all_passed:739results += RED[0]740results += "Runtime: %s s\n" % (runtime)741print(results)744def write_results(test_results, filepath, total_runtime):745with open(filepath, mode="w") as results_file:746results_writer = csv.writer(results_file)747results_writer.writerow(['test', 'status', 'duration(seconds)'])748all_passed = True749for test_result in test_results:750all_passed = all_passed and test_result.was_successful
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.