v31.1-roots.1 · Commit 8: [PATCH 08/17] chore(roots): integrate v31 identity and maintenance tooling
ci/test/test_workflow_selection.py
Before: /dev/null · After: ci/test/test_workflow_selection.py
File metadata
new file mode 100644 index 0000000000000000000000000000000000000000..5520cc40bde154f28d6621f5682abdcb8f5abafc
Download original patch · Release notes
Diff rows 1–70 of 70. 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_workflow_selection.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
"""Execute the actual nightly matrix selector and PR result gate locally."""
|
| 5 |
+
import json
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import re
|
| 9 |
+
import subprocess
|
| 10 |
+
import tempfile
|
| 11 |
+
import unittest
|
| 12 |
+
|
| 13 |
+
ROOT = Path(__file__).resolve().parents[2]
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def run_block(file, name):
|
| 17 |
+
text = file.read_text()
|
| 18 |
+
start = text.index('- name: ' + name)
|
| 19 |
+
rest = text[start:]
|
| 20 |
+
match = re.search(r' run: \|\n((?: [^\n]*\n|\n)+)', rest)
|
| 21 |
+
if not match:
|
| 22 |
+
raise AssertionError(name)
|
| 23 |
+
return '\n'.join(line[10:] if line.startswith(' ') else line for line in match[1].splitlines())
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class WorkflowSelectionTest(unittest.TestCase):
|
| 27 |
+
def nightly(self, suite):
|
| 28 |
+
block = run_block(ROOT / '.github/workflows/nightly.yml', 'Select requested Linux assurance suites')
|
| 29 |
+
with tempfile.TemporaryDirectory() as directory:
|
| 30 |
+
output = Path(directory) / 'output'
|
| 31 |
+
result = subprocess.run(['bash', '-c', block], env=os.environ | {'SUITE': suite, 'GITHUB_OUTPUT': str(output)}, capture_output=True, text=True, timeout=5)
|
| 32 |
+
matrices = dict(line.split('=', 1) for line in output.read_text().splitlines()) if output.exists() else {}
|
| 33 |
+
return result, {key: json.loads(value) for key, value in matrices.items()}
|
| 34 |
+
|
| 35 |
+
def test_all_preserves_extended_platform_and_sanitizer_coverage(self):
|
| 36 |
+
result, matrices = self.nightly('all')
|
| 37 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 38 |
+
self.assertEqual(len(matrices['value']['include']), 4)
|
| 39 |
+
self.assertEqual(len(matrices['sanitizers']['include']), 3)
|
| 40 |
+
for matrix in matrices.values():
|
| 41 |
+
for row in matrix['include']:
|
| 42 |
+
self.assertTrue((ROOT / row['file-env']).is_file())
|
| 43 |
+
self.assertGreater(row['timeout'], 60)
|
| 44 |
+
|
| 45 |
+
def test_specific_suites_do_not_create_unrelated_matrix_jobs(self):
|
| 46 |
+
for suite in ['tsan', 'msan', 'sanitizers', 'tidy', 'i686', 'platforms', 'previous-releases', 'extended-functional', 'fuzz', 'macos-fuzz', 'windows-fuzz', 'arm32', 'kernel', 'macos-smoke', 'windows-smoke']:
|
| 47 |
+
with self.subTest(suite=suite):
|
| 48 |
+
result, matrices = self.nightly(suite)
|
| 49 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 50 |
+
self.assertLessEqual(sum(len(m['include']) for m in matrices.values()), 3)
|
| 51 |
+
result, _ = self.nightly('unrecognized-suite')
|
| 52 |
+
self.assertNotEqual(result.returncode, 0)
|
| 53 |
+
|
| 54 |
+
def test_selected_failures_cannot_pass_the_pr_result_gate(self):
|
| 55 |
+
block = run_block(ROOT / '.github/workflows/ci.yml', 'Enforce selected required results')
|
| 56 |
+
env = {'CLASSIFY': 'success', 'LINT': 'success'}
|
| 57 |
+
for key in ['GUI', 'SANITIZERS', 'FUZZ', 'NIGHTLY_SANITIZERS', 'NIGHTLY_FUZZ', 'NIGHTLY_PLATFORMS', 'NIGHTLY_FULL']:
|
| 58 |
+
env[key] = 'skipped'
|
| 59 |
+
env[key + '_SELECTED'] = 'false'
|
| 60 |
+
base = subprocess.run(['bash', '-c', block], env=os.environ | env, capture_output=True, timeout=5)
|
| 61 |
+
self.assertEqual(base.returncode, 0, base.stderr)
|
| 62 |
+
for name in ['GUI', 'SANITIZERS', 'FUZZ', 'NIGHTLY_FUZZ']:
|
| 63 |
+
for status in ['failure', 'cancelled', 'skipped']:
|
| 64 |
+
with self.subTest(job=name, status=status):
|
| 65 |
+
failed = subprocess.run(['bash', '-c', block], env=os.environ | env | {name + '_SELECTED': 'true', name: status}, capture_output=True, timeout=5)
|
| 66 |
+
self.assertNotEqual(failed.returncode, 0)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if __name__ == '__main__':
|
| 70 |
+
unittest.main()
|