|
| 1 | +# PythonBPF Test Suite |
| 2 | + |
| 3 | +## Quick start |
| 4 | + |
| 5 | +```bash |
| 6 | +# Activate the venv and install test deps (once) |
| 7 | +source .venv/bin/activate |
| 8 | +uv pip install -e ".[test]" |
| 9 | + |
| 10 | +# Run the full suite (IR + LLC levels, no sudo required) |
| 11 | +make test |
| 12 | + |
| 13 | +# Run with coverage report |
| 14 | +make test-cov |
| 15 | +``` |
| 16 | + |
| 17 | +## Test levels |
| 18 | + |
| 19 | +Tests are split into three levels, each in a separate file: |
| 20 | + |
| 21 | +| Level | File | What it checks | Needs sudo? | |
| 22 | +|---|---|---|---| |
| 23 | +| 1 — IR generation | `test_ir_generation.py` | `compile_to_ir()` completes without exception or `logging.ERROR` | No | |
| 24 | +| 2 — LLC compilation | `test_llc_compilation.py` | Level 1 + `llc` produces a non-empty `.o` file | No | |
| 25 | +| 3 — Kernel verifier | `test_verifier.py` | `bpftool prog load -d` exits 0 | Yes | |
| 26 | + |
| 27 | +Levels 1 and 2 run together with `make test`. Level 3 is opt-in: |
| 28 | + |
| 29 | +```bash |
| 30 | +make test-verifier # requires bpftool and sudo |
| 31 | +``` |
| 32 | + |
| 33 | +## Running a single test |
| 34 | + |
| 35 | +Tests are parametrized by file path. Use `-k` to filter: |
| 36 | + |
| 37 | +```bash |
| 38 | +# By file name |
| 39 | +pytest tests/ -v -k "and.py" -m "not verifier" |
| 40 | + |
| 41 | +# By category |
| 42 | +pytest tests/ -v -k "conditionals" -m "not verifier" |
| 43 | + |
| 44 | +# One specific level only |
| 45 | +pytest tests/test_ir_generation.py -v -k "hash_map.py" |
| 46 | +``` |
| 47 | + |
| 48 | +## Coverage report |
| 49 | + |
| 50 | +```bash |
| 51 | +make test-cov |
| 52 | +``` |
| 53 | + |
| 54 | +- **Terminal**: shows per-file coverage with missing lines after the test run. |
| 55 | +- **HTML**: written to `htmlcov/index.html` — open in a browser for line-by-line detail. |
| 56 | + |
| 57 | +```bash |
| 58 | +xdg-open htmlcov/index.html |
| 59 | +``` |
| 60 | + |
| 61 | +`htmlcov/` and `.coverage` are excluded from git (listed in `.gitignore` if not already). |
| 62 | + |
| 63 | +## Expected failures (`test_config.toml`) |
| 64 | + |
| 65 | +Known-broken tests are declared in `tests/test_config.toml`: |
| 66 | + |
| 67 | +```toml |
| 68 | +[xfail] |
| 69 | +"failing_tests/my_test.py" = {reason = "...", level = "ir"} |
| 70 | +``` |
| 71 | + |
| 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. |
| 74 | + |
| 75 | +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/`. |
| 76 | + |
| 77 | +## Adding a new test |
| 78 | + |
| 79 | +1. Create a `.py` file in `tests/passing_tests/<category>/` with the usual `@bpf` decorators and a `compile()` call at the bottom. |
| 80 | +2. Run `make test` — the file is discovered and tested automatically at all levels. |
| 81 | +3. If the test is expected to fail, add it to `tests/test_config.toml` instead of `passing_tests/`. |
| 82 | + |
| 83 | +## Directory structure |
| 84 | + |
| 85 | +``` |
| 86 | +tests/ |
| 87 | +├── README.md ← you are here |
| 88 | +├── conftest.py ← pytest config: discovery, xfail/skip injection, fixtures |
| 89 | +├── test_config.toml ← expected-failure list |
| 90 | +├── test_ir_generation.py ← Level 1 |
| 91 | +├── test_llc_compilation.py ← Level 2 |
| 92 | +├── test_verifier.py ← Level 3 (opt-in, sudo) |
| 93 | +├── framework/ |
| 94 | +│ ├── bpf_test_case.py ← BpfTestCase dataclass |
| 95 | +│ ├── collector.py ← discovers test files, reads test_config.toml |
| 96 | +│ ├── compiler.py ← wrappers around compile_to_ir() + _run_llc() |
| 97 | +│ └── verifier.py ← bpftool subprocess wrapper |
| 98 | +├── passing_tests/ ← programs that should compile and verify cleanly |
| 99 | +└── failing_tests/ ← programs with known issues (declared in test_config.toml) |
| 100 | +``` |
| 101 | + |
| 102 | +## Known regressions (as of compilation-context PR merge) |
| 103 | + |
| 104 | +Three tests in `passing_tests/` currently fail — these are bugs to fix, not xfails: |
| 105 | + |
| 106 | +| Test | Error | |
| 107 | +|---|---| |
| 108 | +| `passing_tests/assign/comprehensive.py` | `TypeError: cannot store i64* to i64*` | |
| 109 | +| `passing_tests/helpers/bpf_probe_read.py` | `ValueError: 'ctx' not in local symbol table` | |
| 110 | +| `passing_tests/vmlinux/register_state_dump.py` | `KeyError: 'cs'` | |
| 111 | + |
| 112 | +Nine tests in `failing_tests/` were fixed by the compilation-context PR (they show as XPASS). They can be moved to `passing_tests/` when convenient: |
| 113 | + |
| 114 | +`assign/retype.py`, `conditionals/helper_cond.py`, `conditionals/oneline.py`, |
| 115 | +`direct_assign.py`, `globals.py`, `if.py`, `license.py` (IR only), `named_arg.py`, |
| 116 | +`xdp/xdp_test_1.py` |
0 commit comments