Skip to content

Commit 668d90a

Browse files
committed
fix(report): count distinct tests, and assert the catalog is well-formed
Two defects a human auditor finds before anyone else does. 1. The header over-counted. `total` summed catalog ROWS, and two tests are deliberately catalogued twice -- test_eip1559_requires_chain_id is the replayable-signature refusal in the 7.14.2 defect narrative (J9) and a guard in the EVM catalog (VG2); test_contract_handler_streamed_calldata_signs_ full_data is J8 and VG6. Both entries earn their place: the same test carries two different arguments. But summing rows claimed 374 tests where the run contains 372, so anyone reconciling the header against the JUnit finds a two-test shortfall that is pure double-counting -- and a report whose own arithmetic does not survive a reconcile is not evidence, whatever the tests did. Count distinct (module, method), keep both rows. 2. VG4 had NO context. It rendered as a bare test name with no statement of what it proves, which is precisely the row an auditor cannot evaluate. Filled in: the 0x02 envelope prefix comes from msg.type but the fee fields from has_max_fee_per_gas, so a type-2 tx carrying only gas_price would hash a legacy fee into a 1559 field list -- refused, because a signature over a malformed field list is still a valid signature over SOMETHING. Then the check that finds the next one, run on every render: unique section letters, unique test ids, and no entry missing a title or a context. Fifteen lines, no new flag, no CI wiring -- it runs because the report runs.
1 parent 099a830 commit 668d90a

1 file changed

Lines changed: 44 additions & 5 deletions

File tree

scripts/generate-test-report.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,12 @@ def _arg_shown(a):
15251525
'absent priority fee must still hash and sign to the correct device address.',
15261526
[]),
15271527
('VG4', 'test_msg_ethereum_signing_guards', 'test_type2_without_max_fee_rejected',
1528-
'Type-2 tx without max_fee_per_gas rejected', '', []),
1528+
'Type-2 tx without max_fee_per_gas rejected',
1529+
'The 0x02 envelope prefix comes from msg.type but the fee fields come from '
1530+
'has_max_fee_per_gas, so a type-2 tx carrying only gas_price would hash a legacy '
1531+
'fee into a 1559 field list. Refused, because a signature over a malformed field '
1532+
'list is still a valid signature over SOMETHING.',
1533+
[]),
15291534
('VG5', 'test_msg_ethereum_signing_guards', 'test_legacy_with_max_fee_rejected',
15301535
'Legacy tx with max_fee_per_gas rejected',
15311536
'Mixing legacy gas_price semantics with EIP-1559 fee fields is refused rather than '
@@ -2791,7 +2796,30 @@ def _arg_shown(a):
27912796
# ---------------------------------------------------------------
27922797
# Render
27932798
# ---------------------------------------------------------------
2799+
def _audit_catalog():
2800+
"""Structural check on SECTIONS, run on every render.
2801+
2802+
A catalog entry with a blank context renders as a bare test name, which is
2803+
exactly the row a human auditor cannot evaluate -- VG4 shipped that way and
2804+
nothing complained. Duplicate ids or letters silently overwrite each other
2805+
in cross-references. Cheap to assert, and the report is evidence.
2806+
"""
2807+
letters, ids = set(), set()
2808+
for letter, title, mf, bg, notes, tests in SECTIONS:
2809+
assert letter not in letters, 'duplicate section letter %s' % letter
2810+
letters.add(letter)
2811+
assert (bg or '').strip(), 'section %s has no background' % letter
2812+
for t in tests:
2813+
assert len(t) == 6, 'malformed entry in section %s: %r' % (letter, t)
2814+
tid, mod, meth, ttl, ctx, scr = t
2815+
assert tid not in ids, 'duplicate test id %s' % tid
2816+
ids.add(tid)
2817+
assert (ttl or '').strip(), '%s has no title' % tid
2818+
assert (ctx or '').strip(), '%s has no context -- it would render as a bare name' % tid
2819+
2820+
27942821
def render(output_path, fw_version, results, screenshot_dir=None):
2822+
_audit_catalog()
27952823
pdf = PDF(); pb = PB(pdf)
27962824
_build_frame_census(screenshot_dir)
27972825
ts = datetime.now().strftime('%Y-%m-%d %H:%M')
@@ -2815,10 +2843,21 @@ def _section_state(s):
28152843
withheld = [s for s in active if s[5] and _section_state(s) == 'withheld']
28162844
pending = [s for s in active if s[5] and _section_state(s) == 'pending']
28172845
test_sections = tested + withheld + pending
2818-
total = sum(len(s[5]) for s in test_sections)
2819-
passed = sum(1 for s in test_sections for t in s[5] if _lookup(results, t[1], t[2]) == 'pass')
2820-
failed = sum(1 for s in test_sections for t in s[5] if _lookup(results, t[1], t[2]) in ('fail','error'))
2821-
skipped = sum(1 for s in test_sections for t in s[5] if _lookup(results, t[1], t[2]) == 'skip')
2846+
# Count DISTINCT tests, not catalog rows. A few tests are deliberately
2847+
# catalogued twice because they carry two different arguments -- e.g.
2848+
# test_eip1559_requires_chain_id is the replayable-signature refusal in the
2849+
# 7.14.2 defect narrative (J9) AND a guard in the EVM catalog (VG2). Both
2850+
# entries earn their place, but summing rows made the header claim more
2851+
# tests than the run contains, and an auditor reconciling the header
2852+
# against the JUnit finds a shortfall that is pure double-counting.
2853+
distinct = {}
2854+
for s in test_sections:
2855+
for t in s[5]:
2856+
distinct[(t[1], t[2])] = _lookup(results, t[1], t[2])
2857+
total = len(distinct)
2858+
passed = sum(1 for v in distinct.values() if v == 'pass')
2859+
failed = sum(1 for v in distinct.values() if v in ('fail', 'error'))
2860+
skipped = sum(1 for v in distinct.values() if v == 'skip')
28222861
missing = total - passed - failed - skipped
28232862

28242863
# Title

0 commit comments

Comments
 (0)