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()
|