Skip to content
Bitcoin Roots

v31.1-roots.1 · Commit 8: [PATCH 08/17] chore(roots): integrate v31 identity and maintenance tooling

ci/test/test_roots_regressions.py

Before: /dev/null · After: ci/test/test_roots_regressions.py

File metadata
new file mode 100644
index 0000000000000000000000000000000000000000..0c65dd7a2a5e2335be1dc1dee906f6ecd548fa19

Download original patch · Release notes

Diff rows 1–84 of 84. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

ci/test/test_roots_regressions.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) 2026-present The Bitcoin Roots developers
3
+ # Distributed under the MIT software license, see the accompanying file COPYING.
4
+ """Exercise actual affected selections, execution budgets and failure artifacts."""
5
+ import importlib.util
6
+ import json
7
+ from pathlib import Path
8
+ import sys
9
+ import tempfile
10
+ import unittest
11
+
12
+ ROOT = Path(__file__).resolve().parents[2]
13
+ SPEC = importlib.util.spec_from_file_location('roots_regressions', ROOT / 'ci/test/roots-regressions.py')
14
+ MODULE = importlib.util.module_from_spec(SPEC)
15
+ SPEC.loader.exec_module(MODULE)
16
+
17
+
18
+ class RootsRegressionTest(unittest.TestCase):
19
+ def test_affected_wallet_retains_authorization_regressions(self):
20
+ plan = MODULE.selection(['src/wallet/spend.cpp'], 'fast')
21
+ self.assertIn('wallet_sweepprivkeys.py', plan['functional'])
22
+ self.assertIn('wallet_signer.py', plan['functional'])
23
+ self.assertNotIn('rpc_net.py', plan['functional'])
24
+ self.assertEqual(plan['budgets']['fuzz'], 5)
25
+ self.assertLess(plan['budgets']['functional'], MODULE.selection(None, 'nightly')['budgets']['functional'])
26
+
27
+ def test_unknown_incomplete_and_ambiguous_fail_open(self):
28
+ baseline = MODULE.selection(None, 'fast')
29
+ for files in [[], ['unclassified/file'], ['/src/policy/a.cpp'], ['src//policy/a.cpp'], ['src/../policy/a.cpp'], [None], 'src/policy/a.cpp']:
30
+ with self.subTest(files=files):
31
+ self.assertEqual(MODULE.selection(files, 'fast'), baseline)
32
+ self.assertEqual(MODULE.selection(['doc/man/bitcoind.1'], 'fast'), baseline)
33
+ self.assertEqual(MODULE.selection(['share/examples/bitcoin.conf'], 'fast'), baseline)
34
+
35
+ def test_mempool_args_selects_policy_and_runtime_dependencies(self):
36
+ plan = MODULE.selection(['src/node/mempool_args.cpp'], 'fast')
37
+ self.assertEqual(plan['groups'], ['mining', 'policy', 'runtime'])
38
+ self.assertIn('feature_policy_args.py', plan['functional'])
39
+ self.assertIn('feature_config_args.py', plan['functional'])
40
+ self.assertNotIn('wallet_signer.py', plan['functional'])
41
+
42
+ def test_mixed_changes_take_union_and_all_tests_exist(self):
43
+ wallet = MODULE.selection(['src/wallet/wallet.cpp'], 'fast')
44
+ peers = MODULE.selection(['src/net_processing.cpp'], 'fast')
45
+ mixed = MODULE.selection(['src/wallet/wallet.cpp', 'src/net_processing.cpp'], 'fast')
46
+ self.assertEqual(set(mixed['functional']), set(wallet['functional']) | set(peers['functional']))
47
+ for name in MODULE.selection(None, 'fast')['functional']:
48
+ self.assertTrue((ROOT / 'test/functional' / name).is_file(), name)
49
+ sources = '\n'.join(path.read_text() for path in (ROOT / 'src/test/fuzz').glob('*.cpp'))
50
+ for target in MODULE.selection(None, 'fast')['fuzz']:
51
+ self.assertIn('FUZZ_TARGET(' + target, sources)
52
+
53
+ def test_execution_uses_time_budgets_and_keeps_failed_output(self):
54
+ with tempfile.TemporaryDirectory() as directory:
55
+ build = Path(directory)
56
+ (build / 'bin').mkdir()
57
+ fake = build / 'bin/fuzz'
58
+ fake.write_text('#!' + sys.executable + '\nimport os,sys\nprint(os.environ["FUZZ"], *sys.argv[1:], flush=True)\nsys.exit(3)\n')
59
+ fake.chmod(0o755)
60
+ for cadence, expected in [('fast', 5), ('nightly', 60)]:
61
+ artifacts = build / cadence
62
+ plan = MODULE.selection(['src/wallet/wallet.cpp'], cadence)
63
+ self.assertEqual(MODULE.execute(plan, 'fuzz', build, artifacts, []), 1)
64
+ results = json.loads((artifacts / 'fuzz-results.json').read_text())['results']
65
+ self.assertEqual(len(results), 2)
66
+ for record in results:
67
+ self.assertEqual(record['exit'], 3)
68
+ self.assertEqual(record['timeout'], expected + 10)
69
+ self.assertIn('-max_total_time=' + str(expected), record['command'])
70
+ self.assertNotIn('-runs=', ' '.join(record['command']))
71
+ self.assertIn(record['target'], (artifacts / (record['target'] + '.log')).read_text())
72
+
73
+ def test_real_timeout_is_failure_with_retained_log(self):
74
+ with tempfile.TemporaryDirectory() as directory:
75
+ log = Path(directory) / 'timeout.log'
76
+ result = MODULE.run_bounded([sys.executable, '-c', 'import time; print("owned slow fixture", flush=True); time.sleep(30)'], .2, log)
77
+ self.assertEqual(result, 124)
78
+ self.assertIn('owned slow fixture', log.read_text())
79
+ self.assertEqual(MODULE.run_bounded([str(Path(directory) / 'absent')], 1, log), 127)
80
+ self.assertTrue(log.stat().st_size)
81
+
82
+
83
+ if __name__ == '__main__':
84
+ unittest.main()