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. The two line-number columns refer to before and after.
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()
|