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.
751results_writer.writerow([test_result.name, test_result.status, str(test_result.time)])752results_writer.writerow(['ALL', ("Passed" if all_passed else "Failed"), str(total_runtime)])754class TestHandler:755"""756Trigger the test scripts passed in via the list.757"""758def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, use_term_control):759assert num_tests_parallel >= 1760self.executor = futures.ThreadPoolExecutor(max_workers=num_tests_parallel)761self.num_jobs = num_tests_parallel762self.tests_dir = tests_dir763self.tmpdir = tmpdir764self.test_list = test_list765self.flags = flags766self.jobs = {}767self.use_term_control = use_term_control769def done(self):770return not (self.jobs or self.test_list)772def get_next(self):773while len(self.jobs) < self.num_jobs and self.test_list:774# Add tests775test = self.test_list.popleft()776portseed = len(self.test_list)777portseed_arg = ["--portseed={}".format(portseed)]778log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)779log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)780test_argv = test.split()781testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)782tmpdir_arg = ["--tmpdir={}".format(testdir)]784def proc_wait(task):785task[2].wait()786return task788task = [789test,790time.time(),791subprocess.Popen(792[sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,793text=True,794stdout=log_stdout,795stderr=log_stderr,796),797testdir,798log_stdout,799log_stderr,800]801fut = self.executor.submit(proc_wait, task)802self.jobs[fut] = test803assert self.jobs # Must not be empty here805# Print remaining running jobs when all jobs have been started.806if not self.test_list:807print("Remaining jobs: [{}]".format(", ".join(sorted(self.jobs.values()))))809dot_count = 0810while True:811# Return all procs that have finished, if any. Otherwise sleep until there is one.812procs = futures.wait(self.jobs.keys(), timeout=.5, return_when=futures.FIRST_COMPLETED)813self.jobs = {fut: self.jobs[fut] for fut in procs.not_done}814ret = []815for job in procs.done:816(name, start_time, proc, testdir, log_out, log_err) = job.result()818log_out.seek(0), log_err.seek(0)819[stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]820log_out.close(), log_err.close()821skip_reason = None822if proc.returncode == TEST_EXIT_PASSED and stderr == "":823status = "Passed"824elif proc.returncode == TEST_EXIT_SKIPPED:825status = "Skipped"826skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1).strip()827else:828status = "Failed"830if self.use_term_control:831clearline = '\r' + (' ' * dot_count) + '\r'832print(clearline, end='', flush=True)833dot_count = 0834ret.append((TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr, proc.returncode, skip_reason))835if ret:836return ret837if self.use_term_control:838print('.', end='', flush=True)839dot_count += 1842class TestResult():843def __init__(self, name, status, time):844self.name = name845self.status = status846self.time = time847self.padding = 0849def sort_key(self):850if self.status == "Passed":851return 0, self.name.lower()852elif self.status == "Failed":853return 2, self.name.lower()854elif self.status == "Skipped":855return 1, self.name.lower()857def __repr__(self):858if self.status == "Passed":859color = GREEN860glyph = TICK861elif self.status == "Failed":862color = RED863glyph = CROSS864elif self.status == "Skipped":865color = DEFAULT866glyph = CIRCLE868return color[1] + "%s | %s%s | %s s\n" % (self.name.ljust(self.padding), glyph, self.status.ljust(7), self.time) + color[0]870@property871def was_successful(self):872return self.status != "Failed"875def check_script_prefixes():876"""Check that test scripts start with one of the allowed name prefixes."""878good_prefixes_re = re.compile("^(example|feature|interface|mempool|mining|p2p|rpc|wallet|tool)_")879bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None]881if bad_script_names:882print("%sERROR:%s %d tests not meeting naming conventions:" % (BOLD[1], BOLD[0], len(bad_script_names)))883print(" %s" % ("\n ".join(sorted(bad_script_names))))884raise AssertionError("Some tests are not following naming convention!")887def check_script_list(*, src_dir, fail_on_warn):888"""Check scripts directory.890Check that all python files in this directory are categorized891as a test script or meta script."""892script_dir = src_dir + '/test/functional/'893python_files = set([test_file for test_file in os.listdir(script_dir) if test_file.endswith(".py")])894missed_tests = list(python_files - set(map(lambda x: x.split()[0], ALL_SCRIPTS + NON_SCRIPTS)))895if len(missed_tests) != 0:896print("%sWARNING!%s The following scripts are not being run: %s. Check the test lists in test_runner.py." % (BOLD[1], BOLD[0], str(missed_tests)))897if fail_on_warn:898sys.exit(1)901class RPCCoverage():902"""903Coverage reporting utilities for test_runner.905Coverage calculation works by having each test script subprocess write906coverage files into a particular directory. These files contain the RPC907commands invoked during testing, as well as a complete listing of RPC908commands per `bitcoin-cli help` (`rpc_interface.txt`).910After all tests complete, the commands run are combined and diff'd against911the complete list to calculate uncovered RPC commands.913See also: test/functional/test_framework/coverage.py915"""916def __init__(self):917self.temp_dir = tempfile.TemporaryDirectory(prefix="coverage")918self.dir = self.temp_dir.name919self.flag = '--coveragedir=%s' % self.dir921def report_rpc_coverage(self):922"""923Print out RPC commands that were unexercised by tests.925"""926uncovered = self._get_uncovered_rpc_commands()928if uncovered:929print("Uncovered RPC commands:")930print("".join((" - %s\n" % command) for command in sorted(uncovered)))931return False932else:933print("All RPC commands covered.")934return True936def _get_uncovered_rpc_commands(self):937"""938Return a set of currently untested RPC commands.940"""941# This is shared from `test/functional/test_framework/coverage.py`942reference_filename = 'rpc_interface.txt'943coverage_file_prefix = 'coverage.'945coverage_ref_filename = os.path.join(self.dir, reference_filename)946coverage_filenames = set()947all_cmds = set()948# Consider RPC generate covered, because it is overloaded in949# test_framework/test_node.py and not seen by the coverage check.950covered_cmds = set({'generate'})952if not os.path.isfile(coverage_ref_filename):953raise RuntimeError("No coverage reference found")955with open(coverage_ref_filename, 'r') as coverage_ref_file:956all_cmds.update([line.strip() for line in coverage_ref_file.readlines()])958for root, _, files in os.walk(self.dir):959for filename in files:960if filename.startswith(coverage_file_prefix):961coverage_filenames.add(os.path.join(root, filename))963for filename in coverage_filenames:964with open(filename, 'r') as coverage_file:965covered_cmds.update([line.strip() for line in coverage_file.readlines()])967return all_cmds - covered_cmds970if __name__ == '__main__':971main()
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.