3.4 The gate suite
Fifty-eight tools/check-*.sh scripts, sixty-one module selftests and one window
selftest. They are not tests of the toolkit's results in the wild — no gate can
tell you whether the tower you are looking at is the one you think — they are
tests of the claims that would otherwise be quietly false: that every verb is
reachable, that every decoder agrees with a referee, that the document you are
reading describes the thing that exists.
Four kinds, and they fail differently.
Structural gates: does the interface still hold together
| Gate | What it asserts | The drift it prevents |
|---|---|---|
tools/check-paths.sh |
every path the tree assumes resolves: a directory a module writes into is created, a script a table names exists | a write into a directory nobody made, a card pointing at a deleted script |
tools/check-verbs.sh |
every documented verb sits in one of its submenu groups, or is a known modifier, or is exempt plumbing | a feature nobody can click |
tools/check-gui-text.sh |
no string literal beginning with -- is handed to an API that renders text |
a button labelled --tr069-audit, a verb moved onto the screen instead of a sentence |
tools/check-gui-pages.sh |
22 checks that the spec-driven pages keep a card's answer inside the card — no near-black OUTPUT scroller bolted to the bottom, which made every module look like a terminal |
the log well creeping back into a page |
tools/check-glossary.sh |
every key printed has a plain-words row; no row is dead; no label contains a verb token; every Workflows step names a verb its module documents | vocabulary invented at the call site, and a route pointing at nothing |
tools/check-theme.sh |
every theme sheet is named in the manifest, balanced, regenerable, and the manifest itself is current | the silent "Ember does nothing" bug |
tools/check-tui.sh |
the terminal workspace compiles, its keys are 01..N in table order and unique, it refuses a non-TTY, and it renders in a real pseudo-terminal |
duplicate menu keys, and a workspace that draws nothing |
tools/gen-module-index.sh --check |
module_index.tsv and native/src/module_index.c match the module table |
an index that describes a previous version of the tree |
tools/gen-theme.py --check |
the Ember sheet is exactly what today's base sheet generates | a theme edited by hand and then left behind |
Two of these deserve their reason written down, because both are gates that exist because of a bug rather than a plan.
check-verbs.sh insists that every non-modifier verb is placed in a submenu. Its
modifier list used to live inside the gate as one 237-name alternation; it is now
tools/verb-modifiers.txt, read by both the gate and the terminal workspace, so
the two cannot disagree about what counts as an action. The list is explicit
rather than a wildcard on purpose: a rule like "anything matching --.*-.* is a
modifier" would silently stop requiring a control for a real verb.
check-tui.sh asserts that the menu keys are 01..61 with no duplicates,
because the launcher's fn_for_key() returns the first match — a duplicate key
does not fail loudly, it makes a module unreachable.
Codec oracle gates: does the decoder agree with somebody else
The largest group, and the reason to trust a decoded field. Each one feeds a decoder in the tree and a referee outside it the same bytes, then compares.
The referees are named in each gate's header, and they are of four kinds:
tshark (a dissection of the same bytes), pycrate (an independent Python
implementation of the specification), published test vectors (3GPP's own, or the
values a specification prints), and a sibling module that implements the same
thing by a different route — which is the weakest of the four and is labelled as
such.
| Domain | Gates |
|---|---|
| GSM | check-gsm-si (tshark and pycrate, with five negative controls), check-auth-oracle, check-keystream |
| UMTS | check-umts-rrc, check-umts-sib, check-umts-bch, check-umts-phy, check-umts-sccpch, check-umts-uplink, check-umts-catcher, check-umts-catcher-pcap |
| LTE | check-lte-sib-codec, check-lte-nb-codec, check-lte-phy-oracle, check-lte-rm-oracle, check-lte-corpus, check-lpp-codec |
| 5G / NR | check-nas-vectors, check-nas-identity, check-suci-catcher, check-nr-phy-oracle, check-ngap-codec, check-xnap-codec |
| Core network | check-gtp-codec, check-pfcp-codec, check-diameter-codec, check-sba-codec, check-sip-codec, check-vowifi-codec |
| SS7 / RAN | check-ss7-codec, check-ss7-map, check-sgsap, check-ranap-codec, check-rua-codec, check-x2ap-codec, check-supl, check-scp03t, check-li-ber |
| SIM / device | check-sim-codec, check-apdu-codec, check-stk-codec, check-ota-codec, check-ota-packet, check-ota-response, check-ussd-codec, check-smpp |
| Physical layer | check-phy-seqs, check-media-leg, check-calls |
| Structure of the tree | check-paths, check-verbs, check-gui-text, check-gui-pages, check-glossary, check-theme, check-tui |
| Experiments | check-evil-twin-lab, check-ransacked-corpus |
A codec gate is only worth its runtime if it can fail. check-gsm-si carries five
negative controls: it copies the module, breaks its System Information
builders one at a time in five specific ways — drop the L2 pseudo-length, add a
LAPDm header to a BCCH block, put the LAI before the cell identity, put the cell
identity into SI4, count ARFCNs from the wrong end — and asserts that each broken
build produces the expected change. Without that, a gate that passes might be a
gate that compares two identical wrong things.
The SI framing fix in 1.0.78 is the worked example of the whole idea: the toolkit's own builder and its own decoder disagreed about where the cell identity goes, two independent referees sided with the decoder, and the gate that now covers it was written in the same pass as the fix, with the disagreement as one of its controls.
Module selftests: does this module's own arithmetic hold
Every module has --selftest, and it is the same idea one level down: fixture
input, printed expectation, exit non-zero on a mismatch. They are what you run
after a merge, on a machine with no radio, in under a minute each:
for m in modules/*.sh; do bash "$m" --selftest >/dev/null || echo "FAIL $m"; done
07_gsm_attack --selftest is 357 assertion rows covering the catcher sequence, the
length indicators, the message types, the ciphers and the observation store.
00_deps --selftest injects a stub tool directory to prove that detection is by
use rather than by file. 58_suci_catcher --selftest proves the record format
its live feed reads.
The window's selftest, and the gates that are not shell
imsisting-gui --selftest runs without a display and checks what a screenshot
cannot: the map's pan arithmetic and its zoom floor, the fleet page's column
lookup, the posture chips' reading rules (an algorithm the toolkit cannot place
never comes out strong; the newest row wins), and that both appearance settings
are deliverable — which is the row that caught the unpackaged theme.
Running them all
There is no single runner on purpose; each gate is a sentence you can read and a decision you can make. The order that catches the most in the fewest minutes:
tools/check-paths.sh # nothing points at nothing
tools/gen-module-index.sh --check
tools/check-verbs.sh # slow: it reads every module
tools/check-gui-pages.sh
tools/check-gui-text.sh
tools/check-glossary.sh
tools/check-theme.sh
tools/check-tui.sh
native/builddir/imsisting-gui --selftest
then the codec group for whichever protocol you touched.
A gate's number is not the point and neither is a green board. The point is the sentence each one asserts; when a gate fails, the useful move is to read its header comment, which states what it holds and — just as honestly — what it cannot see.
Part 3 has described the surfaces: the command line that is the truth, the terminal that indexes it, the window that draws it, and the gates that keep the drawing honest. Part 4 is the catalogue itself — the sixty-one modules, one page each, in the order the drawer lists them — followed by this window's ten pages described one at a time, so that "a page each" means the same thing for a module as it does for the interface that shows it.