Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7128373
tests: fail the gate on an nsedge fixture with no goldens
VeaaC Aug 12, 2026
a700c33
check.sh: drive stages from one table, keep logs, name failures
VeaaC Aug 12, 2026
59a47fe
gate: move the corpus sweep to deep, parallelise the differential
VeaaC Aug 12, 2026
156fb02
parser: split the enum/reserved grammar into its own TU
VeaaC Aug 12, 2026
76a419a
gate: close the review's findings (fixture bypass, silent stage, unli…
VeaaC Aug 12, 2026
c7b3427
parser: revert the enum/reserved TU split
VeaaC Aug 13, 2026
cae8c11
gate: make every stage able to fail, and say why
VeaaC Aug 13, 2026
5d906b5
gate: key verdicts on exit status, not on output text
VeaaC Aug 13, 2026
59df449
gate: check the coverage profile and build, not just the run
VeaaC Aug 13, 2026
6ebaa20
gate: fail the tidy fan-out, and always say why a test run failed
VeaaC Aug 13, 2026
6b2ccea
gate: make the tidy and regen error paths reachable
VeaaC Aug 13, 2026
94a0d9a
gate: check the cxx20 smoke's own write, and split stale from absent
VeaaC Aug 13, 2026
fc3bd6f
gate: check the log dir's own creation, and normalize shard digits
VeaaC Aug 13, 2026
ab711e3
gate: check the log symlink, the seed dirs and the bench's tempdir
VeaaC Aug 13, 2026
bc0b359
gate: fail on an unbuilt example, and name the seed case that was silent
VeaaC Aug 13, 2026
e819607
gate: pin BUILD_TESTS on both presets, and refuse REGEN_GOLDEN
VeaaC Aug 13, 2026
1916ed3
gate: fix the REGEN guard, and four more ways to pass without working
VeaaC Aug 14, 2026
c0a6776
gate: check the binary the stage actually grades, not just the examples
VeaaC Aug 14, 2026
bf71438
gate: guard rapidprotoc in the regen scripts, and quick's test binary
VeaaC Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# three-shard matrix (tidy is ~80% of the gate's CPU, so it gets its own runners), a corpus job that
# sweeps ~8000 real third-party schemas (its own runner: it needs a ~100 MB fetch), an arm64
# build/test job (the SWAR decode kernels lower to native AArch64 instructions x86 can't exercise),
# the heavy "deep" tier (sanitizers + coverage floor + fuzz smoke), a Release -O3 -Werror build on
# the heavy "deep" tier (sanitizers + coverage floor + corpus sweep + fuzz smoke), a Release -O3 -Werror build on
# both compilers, and the consumer job (install -> find_package -> newer-standard header compiles) --
# so wall-clock is the slowest runner, not the sum.
name: CI
Expand All @@ -28,7 +28,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-toolchain
- name: Quality gate minus tidy (./check.sh)
run: RAPIDPROTO_GATE_STAGES='format docs gcc clang cf fuzz differential' ./check.sh
run: RAPIDPROTO_GATE_STAGES='format docs fixtures gcc clang cf fuzz cxx20 differential' ./check.sh

# The real-world compatibility check (see tests/corpus_gate.py for what it covers and why).
# Its own job because it needs a ~100 MB fetch the other jobs don't, keeping that off the main
Expand All @@ -37,7 +37,7 @@ jobs:
# tests/fetch_corpus.py), and the 'gcc' stage is here to produce the rapidprotoc the sweep drives
# -- which re-runs the unit tests and consumer examples the `gate` job already ran. Splitting a
# build-only stage out of job_build_test would fix that if it ever matters.
# Expect minutes rather than the ~40s a dev box sees: the sweep is ~11 CPU-minutes of work, so a
# Expect minutes rather than the ~3 min a 20-core dev box sees: the sweep is ~38 CPU-minutes, so a
# 2-core runner is the constraint.
corpus:
name: corpus (real-world schema sweep)
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
./check.sh

deep:
name: deep (ASan/UBSan + coverage + fuzz smoke)
name: deep (ASan/UBSan + coverage + corpus + fuzz smoke)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
Expand Down
17 changes: 13 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ cmake --build --preset gcc
differential against protobuf.
- `./check.sh fix`: apply clang-format first, then run the full gate.
- `./check.sh quick`: gcc-only build + test for the inner loop (not the commit bar).
- `./check.sh deep` is the heavy tier: ASan + UBSan, a library coverage floor, and a fuzz smoke over
the four targets (see [Fuzzing](#fuzzing)).
- `./check.sh deep` is the heavy tier: ASan + UBSan, a library coverage floor, the real-world corpus
sweep, and a fuzz smoke over the four targets (see [Fuzzing](#fuzzing)).

CI runs `./check.sh`, `./check.sh deep`, and a Release `-O3 -Werror` build on **every push and pull
request**.
Expand All @@ -60,8 +60,10 @@ python3 tests/fetch_corpus.py --list # what it fetches, and why, without tou
Nothing is vendored and nothing is redistributed, and everything corpus-related **skips** when
*nothing* has been fetched, so you never have to download it to run the gate. A **partially** fetched
corpus is a hard failure instead: sweeping a fraction of the schemas while reporting the same green
result is worse than not sweeping at all. Once fetched, `./check.sh` gains a `corpus` stage (~40s)
that drives every schema through `rapidprotoc`; CI runs it in its own job.
result is worse than not sweeping at all. Once fetched, `./check.sh deep` sweeps every schema
through `rapidprotoc` (~3 min on 20 cores, ~38 CPU-minutes of work). It is out of the default gate
deliberately -- it is a compatibility check, not fast feedback -- and CI runs it in its own job on
every PR.

Failures are diffed against `tests/corpus_expected_failures.txt`, and that list is strict in three
directions: an unlisted schema that fails is a regression, a listed one that starts **passing** fails
Expand Down Expand Up @@ -146,6 +148,13 @@ arena layout plan — all dumped to text and compared byte-for-byte). After an *
to a generator or a dumper, regenerate with `tests/regen_goldens.sh`, then run `./check.sh` and
review the diff by hand. Never hand-edit a file under `tests/*_golden/`.

Adding a schema to `tests/corpus/nsedge/` needs one extra step: the regen scripts only *overwrite*
goldens that already exist, so seed each one once by running `rapidprotoc` directly, and add the
fixture to `tests/regen_goldens.sh`, `tests/regen_arenagen_goldens.sh`, and the case list plus
`#include` in the matching test file. `tests/check_fixture_coverage.sh` (a gate stage) fails until
the fixture is referenced by both regen scripts and both goldens exist — without it a new fixture
is silently unpinned, since a package/namespace shape can fail in ways no compiler reports.

## Style & scope

- All hand-written code is `clang-format`ed; the gate enforces it, and nothing is exempt. Comments
Expand Down
3 changes: 2 additions & 1 deletion architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,8 @@ reflected (a documented simplification; decoders accept both wire forms).
googleapis — `tests/fetch_corpus.py`, nothing vendored). Because RapidProto parses `.proto` itself
rather than consuming a protoc `FileDescriptorSet`, that corpus is the only available check that
the
front-end accepts what protoc accepts. `check.sh`'s **`corpus` stage** drives every fetched schema
front-end accepts what protoc accepts. `check.sh`'s **`corpus` stage** -- in the `deep` tier and its own CI job, not the default gate --
drives every fetched schema
through `rapidprotoc` — parse → resolve → analyze → generate, all three emitters — and diffs the
outcome against `tests/corpus_expected_failures.txt` (the policy for that list is in
[CONTRIBUTING.md](CONTRIBUTING.md)). **Compiling** the generated code is deliberately not swept:
Expand Down
Loading
Loading