Skip to content

Commit a7e2bc3

Browse files
r41k0uclaude
andcommitted
Tests: Add a verifier level to the expected-failure tiers
test_config.toml could only declare a failure at the "ir" or "llc" level, and test_verifier.py dropped every declared-xfail case from the level-3 run outright. A program that generates IR and compiles cleanly but that the kernel verifier rejects therefore had no way to be declared: it was silently treated as must-pass at all three levels. Levels now form an ordered pipeline (ir < llc < verifier) and a declared level marks that level and every later one xfail, which is what the old ir-implies-llc special case was expressing. Level 3 runs every test file and reports declared failures as expected ones rather than skipping them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent edefe62 commit a7e2bc3

4 files changed

Lines changed: 50 additions & 17 deletions

File tree

tests/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,16 @@ Known-broken tests are declared in `tests/test_config.toml`:
6969
"failing_tests/my_test.py" = {reason = "...", level = "ir"}
7070
```
7171

72-
- `level = "ir"` — fails during IR generation; both IR and LLC tests are marked xfail.
73-
- `level = "llc"` — IR generates fine but `llc` rejects it; only the LLC test is marked xfail.
72+
- `level = "ir"` — fails during IR generation.
73+
- `level = "llc"` — IR generates fine but `llc` rejects it.
74+
- `level = "verifier"` — IR and `llc` both succeed, but the kernel verifier rejects the object.
75+
76+
A failure at one level implies failure at every later one, so the declared level marks
77+
that level **and all later ones** xfail. An `"ir"` entry is xfail at all three levels; a
78+
`"verifier"` entry is xfail at level 3 only and must still pass levels 1 and 2.
79+
80+
Every test file runs at every level, including the ones declared here — level 3 does not
81+
skip declared failures, it reports them as expected ones.
7482

7583
All xfails use `strict = True`: if a test starts **passing** it shows up as **XPASS** and is treated as a test failure. This is intentional — it means the bug was fixed and the test should be promoted to `passing_tests/`.
7684

tests/conftest.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import pytest
2121

22+
from tests.framework.bpf_test_case import level_index
2223
from tests.framework.collector import collect_all_test_files
2324

2425
# ── vmlinux availability ────────────────────────────────────────────────────
@@ -70,14 +71,19 @@ def pytest_collection_modifyitems(items):
7071

7172
# xfail (strict: XPASS counts as a test failure, alerting us to fixed bugs)
7273
if case.is_expected_fail:
73-
# Level "ir" → fails at IR generation: xfail both IR and LLC tests
74-
# Level "llc" → IR succeeds but LLC fails: only xfail the LLC test
75-
is_llc_test = item.nodeid.startswith("tests/test_llc_compilation.py")
76-
77-
apply_xfail = (case.xfail_level == "ir") or (
78-
case.xfail_level == "llc" and is_llc_test
79-
)
80-
if apply_xfail:
74+
# A failure at one level implies failure at every later one, so mark
75+
# this item xfail whenever the declared level is at or before it:
76+
# "ir" → IR, LLC and verifier
77+
# "llc" → LLC and verifier (IR is expected to succeed)
78+
# "verifier" → verifier only
79+
if item.nodeid.startswith("tests/test_verifier.py"):
80+
item_level = "verifier"
81+
elif item.nodeid.startswith("tests/test_llc_compilation.py"):
82+
item_level = "llc"
83+
else:
84+
item_level = "ir"
85+
86+
if level_index(case.xfail_level) <= level_index(item_level):
8187
item.add_marker(
8288
pytest.mark.xfail(
8389
reason=case.xfail_reason,

tests/framework/bpf_test_case.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
from dataclasses import dataclass
22
from pathlib import Path
33

4+
# The three test levels, in pipeline order. A test declared as failing at one
5+
# level is also expected to fail at every later level: a program that cannot
6+
# generate IR cannot reach llc, and one that llc rejects never reaches the
7+
# kernel. Used by conftest to decide which items to mark xfail.
8+
LEVELS = ("ir", "llc", "verifier")
9+
10+
11+
def level_index(level: str) -> int:
12+
"""Position of a level in the pipeline. Unknown levels sort first ("ir")."""
13+
try:
14+
return LEVELS.index(level)
15+
except ValueError:
16+
return 0
17+
418

519
@dataclass
620
class BpfTestCase:
721
path: Path
822
rel_path: str
923
is_expected_fail: bool = False
1024
xfail_reason: str = ""
11-
xfail_level: str = "ir" # "ir" or "llc"
25+
xfail_level: str = "ir" # one of LEVELS
1226
needs_vmlinux: bool = False
1327
skip_reason: str = ""
1428

tests/test_verifier.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
from tests.framework.verifier import verify_object
2525

2626

27-
def _passing_test_files():
28-
return [c.path for c in collect_all_test_files() if not c.is_expected_fail]
27+
def _verifier_test_files():
28+
return [c.path for c in collect_all_test_files()]
2929

3030

31-
def _passing_test_ids():
32-
return [c.rel_path for c in collect_all_test_files() if not c.is_expected_fail]
31+
def _verifier_test_ids():
32+
return [c.rel_path for c in collect_all_test_files()]
3333

3434

3535
def _get_rejection_reason(verifier_test_file: Path, output) -> str:
@@ -43,11 +43,16 @@ def _get_rejection_reason(verifier_test_file: Path, output) -> str:
4343
return errstr
4444

4545

46+
# Every test file runs at this level, including the ones declared in
47+
# test_config.toml. conftest marks those xfail, so an "ir"- or "llc"-level
48+
# failure is still reported as an expected failure rather than being silently
49+
# dropped from the level-3 run — and a "verifier"-level entry becomes possible
50+
# at all.
4651
@pytest.mark.verifier
4752
@pytest.mark.parametrize(
4853
"verifier_test_file",
49-
_passing_test_files(),
50-
ids=_passing_test_ids(),
54+
_verifier_test_files(),
55+
ids=_verifier_test_ids(),
5156
)
5257
def test_kernel_verifier(verifier_test_file: Path, tmp_path, caplog):
5358
"""Compile the BPF test and verify it passes the kernel verifier."""

0 commit comments

Comments
 (0)