# Top-level convenience Makefile. # Delegates to the autotools build system in mux/. # # Usage: # make — build everything (libmux, netmux, engine, modules) # make install — build + create symlinks in mux/game/bin # make clean — clean all build artifacts # make test — run smoke tests (build + install first) # make hooks — install git hooks (done automatically on first build) # Keep test-lua-jit (added on master after this branch was cut) alongside # the new dual-route smoke targets. .PHONY: all install clean realclean test test-buildconfig test-db test-ios test-ganl test-netaddr test-nfc test-digest test-shacrypt test-libmux test-color-ops test-table test-slave test-stubslave-teardown test-hir test-format test-dbt test-alarm test-blob test-codiff test-codiff-2019 test-smoke test-smoke-ast test-smoke-builtin test-comsys-handoff test-comsys-mogrify test-comsys-conformance test-comsys-cmdparity test-scenario test-poison test-perf test-growth test-parity213 test-stress test-jit-qreg test-jit-ifelse test-jit-recursion test-lua-jit test-lua-ecall test-vacuous test-narrowing test-config test-nls test-nls-plural test-nls-runtime test-nls-ko test-asan hooks # Install git hooks on first build so all developers get protection # against accidentally editing generated files. hooks: @if git rev-parse --git-dir >/dev/null 2>&1 && [ -d hooks ]; then \ git config core.hooksPath hooks; \ echo "Git hooks installed (core.hooksPath = hooks)"; \ fi all: hooks $(MAKE) -C mux install: all $(MAKE) -C mux install clean: $(MAKE) -C mux clean $(MAKE) -C testcases/tools clean $(MAKE) -C mux/ganl/tests clean $(MAKE) -C tests/dbt clean realclean: $(MAKE) -C mux distclean # The suite `make test` runs. Kept as a variable so the runner below and # anyone wanting a subset both work from one list. # # Adding a suite means adding it here. test-libmux / test-color-ops / # test-table are #1919's three formerly-orphan suites (460 assertions that # only ran if someone remembered to). test-db is the fourth (#1953), and it # stayed hidden longest because `test-dbt` contains its name as a substring, # so a grep for it appears to succeed. test-blob is the same shape one # level down: softlib.rv64 is a checked-in binary nothing regenerates, # so its source can drift with the suite staying green (#1924). # TEST_TARGETS = \ test-ganl test-netaddr test-digest test-shacrypt test-libmux test-color-ops test-table \ test-db \ test-slave test-stubslave-teardown test-hir test-format test-nfc \ test-nls test-nls-plural test-nls-runtime test-nls-ko \ test-vacuous test-narrowing test-config test-dbt test-alarm \ test-blob test-codiff \ test-jit-qreg test-jit-ifelse test-jit-recursion test-lua-ecall test-ios \ test-smoke test-smoke-ast test-smoke-builtin \ test-comsys-handoff test-comsys-mogrify test-comsys-conformance \ test-comsys-cmdparity TEST_LOG_DIR = test-logs # Run every target, then report. Deliberately NOT a prerequisite list. # # As prerequisites, make stops at the first failure. test-slave was 3rd of # 25, so one fragile guard hid smoke and 21 other targets — and a suite that # reports nothing about 22 targets trains people to ignore red (#1912). # # `install` stays a real prerequisite: there is no point testing a tree that # did not build, and that failure *should* stop everything. # # SKIP is reported separately from PASS. Several harnesses here exit 0 when # their binary is missing, so "green" could mean "never ran" — that is # exactly how a vacuous control got produced against this very target. # `make test STRICT=1` turns any skip into a failure, for CI or a release # gate where a silently-unrun suite is not acceptable. # # The build-configuration banner runs first and can abort the whole run # (#1946). Several features default to NO and their tests skip cleanly when # absent, so "green" means different things on different boxes and nothing # used to say which. It aborts rather than counting as one target among 31 # because a tree that was reconfigured without a rebuild misattributes # *every* result below it -- better to learn that in two seconds than after # fifteen minutes. `EXPECT_CONFIG="jit=yes"` makes a box assert the job it # was set up to do. # test: install @./tests/buildconfig/report.sh @rm -rf $(TEST_LOG_DIR) && mkdir -p $(TEST_LOG_DIR); \ pass=0; fail=0; skip=0; failed=""; skipped=""; \ for t in $(TEST_TARGETS); do \ log="$(TEST_LOG_DIR)/$$t.log"; \ printf '==> %-26s ' "$$t"; \ if $(MAKE) --no-print-directory "$$t" >"$$log" 2>&1; then \ if grep -qE '^[[:space:]]*SKIP:' "$$log"; then \ skip=$$((skip + 1)); skipped="$$skipped $$t"; \ printf 'SKIP\n'; \ sed -n 's/^[[:space:]]*\(SKIP:.*\)/ \1/p' "$$log" | head -3; \ else \ pass=$$((pass + 1)); printf 'PASS\n'; \ fi; \ else \ fail=$$((fail + 1)); failed="$$failed $$t"; \ printf 'FAIL\n'; \ sed -n 's/^/ | /p' "$$log" | tail -12; \ fi; \ done; \ total=$$((pass + skip + fail)); \ echo; \ echo "======================================================================"; \ printf ' make test: %d targets — %d passed, %d skipped, %d failed\n' \ "$$total" "$$pass" "$$skip" "$$fail"; \ echo " config: $$(./tests/buildconfig/report.sh --oneline)"; \ echo "======================================================================"; \ if [ -n "$$skipped" ]; then echo " skipped:$$skipped"; fi; \ if [ -n "$$failed" ]; then echo " FAILED: $$failed"; fi; \ echo " logs: $(TEST_LOG_DIR)/"; \ if [ "$$fail" -ne 0 ]; then exit 1; fi; \ if [ -n "$$skipped" ] && [ -n "$(STRICT)" ]; then \ echo " STRICT=1: skipped targets are failures."; exit 1; \ fi; \ exit 0 # The banner on its own -- no build, no tests, answers "what would a green # run here actually mean?" in about a second. Deliberately NOT in # TEST_TARGETS: `test` runs it up front and aborts on it, and a second run # as target 31 of 31 would report the same thing after the fact. test-buildconfig: @./tests/buildconfig/report.sh # Smoke on the compiled route (jit_eval_brackets defaults on). test-smoke: $(MAKE) -C testcases/tools @echo "==> Smoke: compiled route (jit_eval_brackets default)" cd testcases && ./tools/Makesmoke && ./tools/Smoke # Smoke again on the interpreted route (#1243). # # The default run cannot fail on an AST-route-only defect: with # jit_eval_brackets on, bracketed expressions are compiled, so a bug that # exists only in the interpreter never executes. Three fixes in one day # (#1214, #1238, #1246) passed `make test` with the fix reverted and were # caught only by this second pass -- testcases/disarm_fn.mux carries a # ROUTE: header saying so, because the file cannot defend itself under the # default configuration. # # The interpreted route is not legacy: it handles every expression the JIT # declines, so it runs in production on every server. # # Whole corpus rather than a route-sensitive subset -- a second pass costs # ~30s against a ~30s first pass, which is not worth the bookkeeping of # deciding which files are route-sensitive (and getting that wrong # silently loses coverage). # # Depends on test-smoke so smoke.flat is already built and the two passes # report in a fixed order. test-smoke-ast: test-smoke @echo "==> Smoke: interpreted route (jit_eval_brackets 0)" cd testcases && SMOKE_EXTRA_CONF="jit_eval_brackets 0" ./tools/Smoke # The same corpus against the engine's BUILT-IN comsys/mail instead of the # modules (#1589 stage 0). # # Both implementations ship, both are reachable, and they demonstrably differ: # #1564, #1585 and #1620 are each a bug that only exists when the two # disagree. Every run before this one exercised whichever implementation the # platform resolved -- Windows the built-in, Unix the modules -- so half the # shipped code had no coverage on any given box. # # Reuses smoke.flat, so this costs a run of the corpus and no rebuild. test-smoke-builtin: test-smoke @echo "==> Smoke: engine built-in comsys/mail (no modules)" cd testcases && SMOKE_OMIT_MODULES="comsys_mod mail_mod" ./tools/Smoke # comsys/mail state written by one implementation and read by the other # (#1589 stage 0b). # # test-smoke-builtin makes the other implementation reachable; it does not # make a divergence detectable. The corpus scores 1561/1561 against BOTH, # so it cannot tell them apart -- and every bug in this area (#1564, #1585, # #1620) exists only when one implementation reads state the other wrote, # which no single run can produce. # # Carries TODO markers for the two divergences that are still open. A TODO # that starts PASSING fails the run, so the fix cannot land silently. test-comsys-handoff: @echo "==> Running comsys/mail cross-implementation handoff tests" bash tests/comsys_handoff/run.sh # MOGRIFY hooks and per-player CHATFORMAT, compared across both comsys # implementations (#1572). # # Separate from test-comsys-handoff because delivery cannot be tested by that # driver's shape: bConnected is runtime state set when a player joins during # that process, so a run inheriting membership delivers to nobody. Each side # joins and speaks within one process here. test-comsys-mogrify: @echo "==> Running comsys MOGRIFY/CHATFORMAT comparison" bash tests/comsys_mogrify/run.sh # Whole-output differential across both implementations (#1614 step 4). # # The third shape, and the one the other two cannot cover. handoff tests # STORED state; mogrify tests DELIVERY. Neither runs a command and compares # what it printed -- so the module ignoring @clist/full, dropping comtitle # from speech and join/leave, and answering @mail/stats, /dstats and /fstats # with one identical line all sat in master unreported (#1631, #1640), along # with two engine-side defects found the same way (#1637, #1639). # # Assertions can only catch divergences someone thought to write a case for, # which is how those survived. This diffs the entire output against a # recorded baseline, so a divergence in a command nobody was thinking about # still fails the run -- and a known divergence that DISAPPEARS fails it too, # because the baseline is then lying. Regenerate with --bless, after reading # the delta. test-comsys-conformance: @echo "==> Running comsys/mail whole-output conformance diff" bash tests/comsys_conformance/run.sh # The comsys COMMAND SURFACE, compared command-for-command (#1640). test-comsys-cmdparity: @echo "==> Running comsys command-surface parity comparison" bash tests/comsys_cmdparity/run.sh # Static guard: no smoke case may be incapable of failing (#1434 family). # # A tr.tc* label with a Succeeded branch and no non-success branch reports the # same verdict every run -- it counts toward the total and cannot go red. Eight # separate findings in one day were that exact shape (#1413, #1426, #1434, # #1438, #1460/#1498, #1495), each found by a person noticing rather than by a # check. Same move as the format guard: turn a sweep someone remembers to run # into something the build does. # # Source-only, so it needs no build and runs before the suites. test-vacuous: @echo "==> Checking for smoke cases that cannot fail" cd testcases && python3 tools/check_vacuous.py # Runtime oracle for the xx pseudo-locale (#1523). # # test-nls above is the static half -- markings, catalogue coverage, .pot # freshness -- and that is where the risk that has actually bitten lives. This # is the half that shows translation happens at all: nothing else in the suite # observes notify() prose, so the whole translatable surface is otherwise # invisible to it. # # The point is the pairing, not the LANGUAGE=xx run on its own. A smoke run # with LANGUAGE=xx is green whether the catalogue is correct, corrupt or absent # -- measured on #1523 with the catalogue in a directory the server never opens # -- so it is only evidence if something also asserts that removing the # catalogue takes the translations away. Both directions are checked. # # Skips cleanly without --enable-nls or without msgfmt. # Unit test for the Plural-Forms evaluator in the built-in catalogue reader # (#1702). # # #1702 made that reader the only catalogue path on every platform, which # meant implementing plural selection rather than borrowing ngettext(3). A # bug in that expression evaluator is now everybody's bug, not just Windows'. # # test-nls-runtime drives plurals through the server, but only reaches the # counts a scenario can produce and only the rules the shipped catalogues # carry -- neither exercises chained ternaries, %, or the malformed-input # paths. Russian only gets interesting at n=21, and digging 21 exits to test # a parser is the wrong shape. # # Source-only: it compiles mux_nls.cpp directly, so it needs no build and no # gettext on the box. test-nls-plural: @echo "==> Running NLS Plural-Forms evaluator unit test" @g++ -std=c++17 -O2 -Wall -I mux/include -I mux/lib \ -o tests/nls/test_plural tests/nls/test_plural.cpp @./tests/nls/test_plural test-nls-runtime: @echo "==> Running NLS runtime oracle (xx pseudo-locale)" bash tests/nls/run.sh # Runtime oracle for a real translated locale (#1419). # # test-nls-runtime above uses xx, which is English with a prefix. It proves # the gettext plumbing works and cannot prove anything about translation: # because xx preserves argument order, every message it renders would render # identically under a broken implementation of argument handling. That blind # spot is why Korean was chosen as the second locale over Spanish -- Korean is # SOV with postpositions and reorders arguments where an SVO language does not. # # One of the four cases deliberately asserts a DEFECT: msgfmt -c accepts %N$ # positional specs, so a translator who reorders correctly gets a clean build, # and then mux_vsnprintf stops at the '$' and echoes the format literally. The # case is written to FAIL once positional arguments are supported, which is the # signal to drop the fuzzy markers in ko.po. # # Skips cleanly without --enable-nls or without msgfmt. test-nls-ko: @echo "==> Running NLS runtime oracle (ko, real locale)" bash tests/nls/run_ko.sh # 64-bit parse into a narrower destination (#1402). # # mux_atoi64() returns int64_t; storing that somewhere narrower truncates # BEFORE anything can judge the value, so a range check placed after it passes # on input it was written to reject. #1404 fixed three instances (justify # width, printf %d, fun_shl's count); these cover @poor and hasquota(). # # Not smoke cases, for two different reasons: @poor is CA_GOD and walks the # whole database, so a tr.tc* case would be refused and would also rewrite # every other test's money; hasquota() needs `quotas yes`, which smoke # deliberately runs without (powersee_fn.mux TC005 asserts the disabled path). # Each case therefore gets a throwaway game, as tests/luajit does. # # Worth knowing where these can fail: `int` is 32-bit everywhere, so these run # meaningfully on every host. The rest of #1402 is `long`, which is 64-bit on # LP64 -- that half cannot fail on Linux or macOS at all, so its coverage only # means something on Windows. test-narrowing: @echo "==> Running narrowing-destination tests" bash tests/narrowing/run.sh # An unreadable configuration file must be fatal (#1601). cf_read()'s return # was discarded in LoadGame, so netmux came up on compiled-in defaults -- # listening, on the wrong database -- and muxscript reported success, which # made every harness probe indistinguishable from the thing under test. # # Pins the two deliberately non-fatal cases too (unknown directive, empty # file); those are the ones a later "make config errors fatal" change would # break. Needs only muxscript, so it runs on any built tree. test-config: @echo "==> Running config display guard" python3 tests/config/check_display.py @echo "==> Running configuration-error tests" bash tests/config/run.sh # JIT q-register scope oracle (docs/plan-jit-evalbracket-lift.md). # Compares forced-JIT vs AST results for the scope/ordering shapes fixed # in plan Phases 2-3. Skips cleanly on builds without --enable-jit # (the script exits 2 when jitstats()/the JIT blob is unavailable). test-jit-qreg: @echo "==> Running JIT q-register scope oracle" @sh testcases/tools/jit_qreg/oracle.sh; rc=$$?; \ if [ $$rc -eq 2 ]; then \ echo "==> Skipping (build has no JIT)"; \ else \ exit $$rc; \ fi # JIT ifelse()/if() condition-truth oracle (#1157). # Compares JIT vs AST for the condition shapes where xlate() disagrees # with "atol() != 0" or with an integer truncation: bare %0-%9 cargs, # non-numeric and dbref literals, and fractional floats. # Skips cleanly on builds without --enable-jit (the script exits 2). test-jit-ifelse: @echo "==> Running JIT ifelse() condition oracle" @sh testcases/tools/jit_ifelse/oracle.sh; rc=$$?; \ if [ $$rc -eq 2 ]; then \ echo "==> Skipping (build has no JIT)"; \ else \ exit $$rc; \ fi # Runaway-recursion termination cost oracle (#1994). # Terminating a runaway self-recursive ufun on the compiled route was # exponential in function_recursion_limit: the shared heap's single DBT # context was reset by a nested eval(), the outer run then failed, and # jit_eval handed the whole subtree back to the AST to redo. Asserts # eval_attempts stays linear, because the ANSWER is correct either way -- # a result-equality check cannot see this defect. # Skips cleanly on builds without --enable-jit (the script exits 2). test-jit-recursion: @echo "==> Running JIT runaway-recursion cost oracle" @sh testcases/tools/jit_recursion/oracle.sh; rc=$$?; \ if [ $$rc -eq 2 ]; then \ echo "==> Skipping (build has no JIT)"; \ else \ exit $$rc; \ fi # Full smoke with mudconf.lua_jit forced on (#1309). Default `make test` # keeps lua_jit off so production configs stay safe until Phase 4 default-on. # Requires --enable-jit (same as the rest of the Lua JIT path). # Opt-in: not part of `make test`. # make test-lua-jit # # or: cd testcases && SMOKE_EXTRA_CONF='lua_jit 1' ./tools/Smoke test-lua-jit: install @echo "==> Running smoke with lua_jit 1 (Lua bytecode→HIR→DBT path)" $(MAKE) -C testcases/tools cd testcases && ./tools/Makesmoke && SMOKE_EXTRA_CONF='lua_jit 1' ./tools/Smoke # Lua JIT differential harness (#1423 / #1426 / #1512): SURVIVE / AGREE / EXEC. # Part of `make test`. Cheap, and the only place that requires lua_run_ok to # advance so a decline cannot masquerade as a pass (#1426). # # Unlike test-lua-jit above, this sets jit_eval_brackets 0 as well. That # matters: with eval brackets compiled, fun_lua is ECALLed from inside a DBT # program, run_cached_program refuses the nested run (#1326), and the Lua JIT # executes nothing at all -- so `lua_jit 1` on its own cannot reach any of # this code. test-lua-ecall: install @echo "==> Running Lua JIT differential harness (survive/agree/exec)" bash tests/luajit/run.sh # GANL engine + ConnectionBase harness (epoll/select on Linux, kqueue/select # on macOS/BSD). Windows: mux/ganl/tests/run-msvc.bat (wselect/iocp + same # ConnectionBase fakes; #1857/#1858). test-ganl: @echo "==> Running GANL engine + ConnectionBase tests" $(MAKE) -C mux/ganl/tests check # netaddr unit tests: mux_subnet::compare_to (subnet/address, #799/#800) and # parse_subnet rejection/normalization paths. Links the netmux-side # netmux-netaddr.o (from install) against libmux. test-netaddr: @echo "==> Running netaddr subnet tests" $(MAKE) -C tests/netaddr test # #1963: known-answer vectors for the OS-backed digest entry points. # Pins byte-identical SHA-1 across backends (OpenSSL EVP / Windows CNG) # for the surfaces whose output may never change: RFC 6455 # Sec-WebSocket-Accept, $SHA1$/$P6H$ password verification, sha1() # softcode. The same vectors build and run manually on Windows against # the CNG backend (see tests/digest/test_digest.cpp header). test-digest: @echo "==> Running digest known-answer tests (#1963)" $(MAKE) -C tests/digest test # #1962: the portable sha-crypt ($5$/$6$) implementation, pinned against # `openssl passwd` oracle vectors. Byte-compatibility with glibc/musl # output IS the feature — a $6$ password hash must verify identically on # every platform — so any drift here is a cross-platform login breakage. test-shacrypt: @echo "==> Running sha-crypt known-answer tests (#1962)" $(MAKE) -C tests/shacrypt test # #1917: libmux/color_ops unit suite. It existed and was RED for four # days (a #1649 behaviour change vs a stale expectation) purely because # nothing ran it -- `make -C tests/libmux test` by hand was the only # path. A suite outside `make test` is a suite that pins nothing. test-libmux: @echo "==> Running libmux / color_ops unit tests (#1917)" $(MAKE) -C tests/libmux test # #1917 sweep: two more suites that existed outside `make test`. Both # were green when wired in (392 and 16 assertions) -- but so was libmux # until #1649 moved a behaviour under it, and nothing noticed for four # days. Coverage that nothing runs is coverage that pins nothing. test-color-ops: @echo "==> Running color_ops unit tests" $(MAKE) -C tests/color_ops test test-table: @echo "==> Running table formatting tests" $(MAKE) -C tests/table test # SQLite storage backend (#1953). The fourth orphan suite after #1917's # three: it built, passed 11 assertions, and nothing ran it. # # NOTE FOR GREPPERS: this is test-db (tests/db), NOT test-dbt (tests/dbt). # `test-dbt` contains `test-db` as a substring, which is exactly why this # suite read as wired for as long as it did -- a grep for the shorter name # matches the longer target and stops looking. # # Compiles sqlite3.c itself, so a cold build is ~23s; warm it is ~1s and the # suite is the only coverage the persistence layer has outside smoke. test-db: @echo "==> Running SQLite storage-backend tests (#1953)" $(MAKE) -C tests/db test # #1853 / #1827: DNS slave child-cap burst with a forced stall. Not a # platform item — plain waitpid + spawnSlavePosix on every POSIX engine. test-slave: install @echo "==> Running slave child-cap burst (#1853)" $(MAKE) -C tests/slave test # #1939: muxscript stubslave-teardown recursion. Kills the stubslave child # and drives @shutdown so ShutdownSlave's pump write fails -- pre-fix that # recursed to a stack-overflow SIGSEGV. Skips green on builds without # --enable-stubslave (nothing to exercise there). test-stubslave-teardown: install @echo "==> Running muxscript stubslave-teardown regression (#1939)" $(MAKE) -C tests/stubslave test # #1863: HIR block-table exhaustion must not OOB-write via add_edge(-1,…). test-hir: @echo "==> Running HIR CFG capacity tests (#1863)" $(MAKE) -C tests/hir test # Run the high-coverage suites against a sanitizer build (#1440). # # Deliberately does NOT reconfigure. Silently replacing the tree's build # settings would be rude, and a sanitizer build is not what anyone wants left # behind. Configure one yourself first: # # cd mux && ./configure --enable-sanitizers # # or, to pick the set: # # cd mux && ./configure --enable-sanitizers=address # # then `make clean && make install` from the repo root. The clean matters: # objects left from a non-sanitizer build link fine but are not instrumented, # which reads as a clean run. # # The value is concentrated in the suites that execute the most engine code. # ASan reports a bad access only when something reaches it, so this multiplies # the coverage already there rather than substituting for it. # # Everything below was verified to run clean under -fsanitize=address,undefined # before being added, including test-ganl and test-scenario -- the only legs # that exercise the live network path, which muxscript cannot reach at all. # # rvbench_fn is excluded from the smoke legs. It issues 55 rvbench() calls at # 10000 iterations each; instrumented, that is ~25ms per iteration, so the file # alone runs for hours and the harness reports an idle-hang at ~260 of 315 # files -- which is how test-asan came to report FAILED for instrumentation # cost rather than for a defect. The timeouts are raised as well, since an # instrumented smoke run legitimately takes several times longer. ASAN_SMOKE_EXCLUDE = rvbench_fn # LeakSanitizer is fatal to the harness, not merely noisy. A long-lived # server legitimately does not free everything at exit, and LSan's nonzero # exit status at shutdown makes Makesmoke report "ERROR: muxscript failed" # before a single test runs. Measured on a sanitizer build of this tree: # # default ASAN_OPTIONS muxscript exit=1, "detected memory leaks" # ASAN_OPTIONS=detect_leaks=0 exit=0 # # Leak hunting stays available and opt-in -- there are ~500 KB in ~460 # allocations to look at when someone wants them: # # ASAN_OPTIONS=detect_leaks=1 make test-asan # # Both honour a value already in the environment, so either can be overridden. ASAN_ENV = ASAN_OPTIONS=$${ASAN_OPTIONS:-detect_leaks=0} \ UBSAN_OPTIONS=$${UBSAN_OPTIONS:-print_stacktrace=1} # The tests/ islands build their own binaries from their own Makefiles, so # they are NOT sanitizer-instrumented even when libmux is, and ASan refuses # to start: # # ASan runtime does not come first in initial library list; you should # either link runtime to your application or manually preload it # # test-format and test-dbt both died on this immediately. Preloading the # runtime is the documented remedy and keeps the islands out of the configure # plumbing. Resolved from the compiler rather than hardcoded, and empty when # unavailable so an unusual toolchain degrades to the original error rather # than a confusing LD_PRELOAD failure. ASAN_PRELOAD = $(shell $(CC) -print-file-name=libasan.so 2>/dev/null | grep / || true) ASAN_ISLAND_ENV = $(if $(ASAN_PRELOAD),LD_PRELOAD=$(ASAN_PRELOAD),) $(ASAN_ENV) test-asan: @if ! grep -q 'fsanitize' mux/config.status 2>/dev/null; then \ echo "==> test-asan: this tree is not configured with sanitizers."; \ echo " See the recipe above this target in the Makefile."; \ exit 1; \ fi @echo "==> Running the suites under sanitizers" $(ASAN_ISLAND_ENV) $(MAKE) test-format $(ASAN_ISLAND_ENV) $(MAKE) test-nfc $(ASAN_ISLAND_ENV) $(MAKE) test-netaddr $(ASAN_ISLAND_ENV) $(MAKE) test-digest $(ASAN_ISLAND_ENV) $(MAKE) test-shacrypt $(ASAN_ISLAND_ENV) $(MAKE) test-alarm $(ASAN_ISLAND_ENV) $(MAKE) test-dbt $(ASAN_ISLAND_ENV) $(MAKE) test-ganl $(ASAN_ISLAND_ENV) $(MAKE) test-jit-qreg $(ASAN_ISLAND_ENV) $(MAKE) test-jit-ifelse $(ASAN_ISLAND_ENV) $(MAKE) test-jit-recursion $(ASAN_ISLAND_ENV) $(MAKE) test-lua-ecall $(ASAN_ISLAND_ENV) $(MAKE) test-scenario cd testcases && $(ASAN_ENV) SMOKE_EXCLUDE="$(ASAN_SMOKE_EXCLUDE)" ./tools/Makesmoke \ && $(ASAN_ENV) SMOKE_EXCLUDE="$(ASAN_SMOKE_EXCLUDE)" ./tools/Smoke \ --activity-timeout 300 --wallclock-timeout 3600 cd testcases && $(ASAN_ENV) SMOKE_EXCLUDE="$(ASAN_SMOKE_EXCLUDE)" \ SMOKE_EXTRA_CONF="jit_eval_brackets 0" ./tools/Smoke \ --activity-timeout 300 --wallclock-timeout 3600 # mux_vsnprintf differential tests: %i, %o and the floating-point conversions # against the platform snprintf as an oracle. These conversions used to fall # through to mux_assert(0) and abort the process (#1382, and the same shape in # @list), so they are implemented rather than forbidden -- and the float path # assembles mux_dtoa digits by hand, which is precisely the code that needs an # oracle rather than a few spot checks. test-format: @echo "==> Running mux_vsnprintf format tests" $(MAKE) -C tests/format test # NFC normalization (tests/nfc): composition, canonical ordering by combining # class, and idempotence of the normal form. Nothing covered normalization # before this -- the combining-character smoke cases reach the code but assert # nothing about it, which is the coverage #1998 had to be reviewed against. test-nfc: @echo "==> Running NFC normalization tests" $(MAKE) -C tests/nfc test # NLS marking and catalogue guard (tests/nls): softcode ABI tokens and printf # conversions must never become translatable, a literal must not be M_() in one # place and T() in another within one file, every catalogue must cover the .pot # with no fuzzy entries (msgfmt drops those silently), and the .pot must match # what the sources actually mark. Static -- no build, no server, no catalogue # needs installing (#1505). # # Runs whether or not the tree was configured --enable-nls: the marking is in # the sources either way, and a slice that breaks it should not be able to hide # behind an English-only build. test-nls: @echo "==> Running NLS marking/catalogue guard" python3 tests/nls/check_nls.py # DBT and RV64 tests (tests/dbt): chain patch encode/decode across all three # backends (#1152), block cache dedupe and eviction (#1153), and the RV64 # execution harness -- interpreter plus DBT, host backend only since it runs # what it translates. Three binaries because each needs a different link. # All compile engine sources directly: no `install`, no --enable-jit, and no # skip path except `exec` on a host with no backend. test-dbt: @echo "==> Running DBT and RV64 tests" $(MAKE) -C tests/dbt test # mux_alarm unit tests: the per-command wall-clock abort. Guards the lazy # worker-thread start — alarm_clock is a libmux global whose constructor used # to spawn a thread during static init, deadlocking before main in ~14% of # runs (which is what made `make test` hang here intermittently). test-alarm: @echo "==> Running mux_alarm tests" $(MAKE) -C tests/alarm test # softlib.rv64 is a checked-in binary the JIT loads at run time, and nothing # in the normal build regenerates it — so an edit to mux/rv64/src/ is inert # until someone rebuilds by hand, while the suite stays green. #1915's first # fix shipped exactly that way, and the blob build had been broken since #1402 # with no way to notice. Rebuild and compare when a cross-toolchain is here; # skip cleanly when it is not. test-blob: @echo "==> Checking softlib.rv64 against its source" @tests/blob/run.sh # Does color_ops.c mean the same thing on every route that executes it? # color_ops.c is compiled twice (host libmux, rv64 blob) and the blob is then # run by two engines of our own, so there are four implementations of one # source. #2019 is what happens when they disagree: correct C, correct RV64, # wrong in the artifact production runs. qemu-riscv64 is the external oracle # -- the only route here neither we nor the tree wrote. Skips loudly without # a RISC-V cross-compiler. test-codiff: @echo "==> Differential: color_ops across host/qemu/interp/DBT" @tests/codiff/run.sh # Reproducer for the open DBT defect #2019. NOT part of `make test`: it is # expected to fail, that being the point, and it is intermittent so it loops. test-codiff-2019: @echo "==> Reproducing #2019 (DBT block chaining)" @tests/codiff/repro/run-2019.sh # Performance battery (#2046). Opt-in, NOT part of `make test`: it reads the # rvbench timings from a previous smoke run, so it needs `make test-smoke` # first, and its comparison is against a PER-MACHINE baseline that only exists # on boxes where someone recorded one. # # Report-only by default. PERF_GATE=1 fails on a regression, but only on the # `compile` leg -- see tests/perf/compare.py for why the other two are not # trustworthy to gate on yet. test-perf: @echo "==> Performance battery (rvbench)" @tests/perf/run.sh # Algorithmic growth battery: asserts the COMPLEXITY CLASS of evaluation, not # its speed. Doubling N costs 2.0x if an implementation is linear and 4.0x if # it is quadratic, on every machine -- the hardware cancels out of the ratio -- # so unlike test-perf this needs no per-machine baseline and no calibrated # tolerance. It cannot see a 10% regression; it can see O(n) become O(n^2), # which is the failure that actually ruins a live game. # # Opt-in, NOT part of `make test`: it takes minutes, and timings are timings. # Known defects are xfail'd against their issue number in tests/growth/ # driver.py, and an xfail that starts PASSING fails the run. test-growth: install @echo "==> Running algorithmic growth battery" bash tests/growth/run.sh # Smoke suite against a hostile allocator: every large malloc comes back # filled with 0xAA instead of the fresh zeroed pages the kernel usually # supplies. This exists for one bug class -- a read past the count something # wrote into a buffer handed over uninitialized (#2145 made eleven list-builtin # tables uninitialized by contract, and the #2136 arc keeps touching those # functions). That read is invisible to every other gate here: large # allocations are normally zero-filled, so the stale slot reads as zero and # behaves exactly like the value-initialized code it replaced, biting only # later once the allocator recycles a dirty block. Measured, the recycled # case yields a stale but VALID pointer 199 times in 200 -- silent wrong # output, not a crash. Poisoning makes it a crash. # # The shim is verified before it is trusted: an injected past-count read must # be invisible unpoisoned and fatal poisoned, or the target fails rather than # blessing everything (#2133's genre -- an instrument must not return an # answer it cannot stand behind). # # Opt-in, NOT part of `make test`: Linux/glibc only, and it pays a memset on # every large allocation. test-poison: install @echo "==> Running the smoke suite under a poisoned allocator" bash tests/poison/run.sh # Live scenario test: the wildcard capture path ($-command %0..%9), which # muxscript cannot drive. Opt-in (NOT part of `make test`) because it spins a # throwaway netmux and drives it over a socket — timing-sensitive by nature. test-scenario: install @echo "==> Running wildcard-capture scenario test" bash tests/scenario/run.sh # 2.13 <-> 2.14 parser parity jig. MUSH function-call recognition is # context sensitive in ways a tokenizer cannot decide (`add(` is a call, # `foo(` is not — only a function-table lookup separates them), so the # grammar is not well-formed and there is no rule to validate against. # The specification is what 2.13 actually does, and this measures it. # # Compares 2.14's JIT and AST routes against each other (always), and # against a built 2.13 tree when one is available (MUX213_ROOT, or a # conventional location). All three are driven identically through a # live netmux over a socket — 2.13 has no muxscript, so the 2.14 legs # use the same path rather than the convenient one. # # Opt-in, NOT part of `make test`: divergences currently exist (#1214), # so this is a measurement tool rather than a pass/fail gate. test-parity213: install @echo "==> Checking the adjudicator itself (#1368)" bash tests/parity213/selftest_adjudicate.sh @echo "==> Running 2.13/2.14 parser parity jig" sh tests/parity213/run.sh # Live network + queue stress harness: concurrent connections, bulk queue # fan-outs, and an over-cap burst that provokes the runaway shed. Defensive # — pushes the accept/read/write path and the scheduler to catch problems # before a live game does. Opt-in (NOT part of `make test`): live-socket, # timing-sensitive, and multi-second by design. test-stress: install @echo "==> Running network+queue stress harness" bash tests/stress/run.sh # Headless iOS Titan parser/model tests via SPM. Skipped off Darwin # or when swift is unavailable. test-ios: @if [ "$$(uname -s)" = "Darwin" ] && command -v swift >/dev/null 2>&1; then \ echo "==> Running iOS Titan parser/model tests"; \ cd client/ios && swift test; \ else \ echo "==> Skipping iOS tests (not on Darwin or no swift available)"; \ fi