Merge pull request #1300 from brazilofmux/refactor/consolidate-dbt-tests

test(dbt): consolidate the DBT test islands into tests/dbt
This commit is contained in:
Stephen Dennis 2026-07-25 21:36:23 -06:00 committed by GitHub
commit e19db6b5e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 164 additions and 281 deletions

View file

@ -25,24 +25,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
via the accept path (nonzero exit on failure)
- netaddr subnet unit tests: `make test-netaddr` (also part of `make test`);
`mux_subnet::compare_to` + `parse_subnet` in `tests/netaddr/` (#799/#800)
- DBT chain patch encode/decode: `make test-dbt-chain` (also part of `make test`);
`tests/dbt_chain/` asserts `dbt_backend_decode_jmp_target` is the exact inverse
of `dbt_backend_backpatch_jmp` (#1152). Builds **all three** backends
(a64_sysv, x64_sysv, x64_win64) into one binary on every host via `-D` symbol
renames — #1152 survived because nothing exercised the affected backend, so
host-only coverage is not enough here. Compiles the backend sources directly:
needs neither `install` nor `--enable-jit`, and has no skip path.
- DBT block cache: `make test-dbt-cache` (also part of `make test`);
`tests/dbt_cache/` covers `dbt_cache_insert`/`dbt_cache_lookup` dedupe and
FIFO eviction (#1153). Compiles `dbt.cpp` against backend stubs — no
`install`, no `--enable-jit`, no skip path.
- RV64 execution tests: `make test-dbt-exec` (also part of `make test`);
`tests/dbt_exec/` builds `mux/modules/engine/dbt_test.cpp`, which had never
been wired into any build. Hand-assembled RV64 sequences run through the
interpreter and (for a subset) the DBT, plus a cross-compiled ELF through
both routes. Note only ~6 of 39 test functions drive the DBT directly — the
ELF leg carries most of the block-translation coverage. Builds the **host**
backend only, since it executes; skips loudly off x86_64/aarch64.
- DBT and RV64 tests: `make test-dbt` (also part of `make test`); `tests/dbt/`
builds three binaries, each needing a different link:
- **chain**`dbt_backend_decode_jmp_target` must invert
`dbt_backend_backpatch_jmp` (#1152). Compiles **all three** backends into
one binary via `-D` symbol renames, on every host: #1152 survived because
nothing exercised the affected backend, so host-only coverage is not enough.
- **cache**`dbt_cache_insert`/`dbt_cache_lookup` dedupe and FIFO eviction (#1153).
- **exec**`mux/modules/engine/dbt_test.cpp`, which had never been wired into
any build. Hand-assembled RV64 through the interpreter and (for ~6 of 39
test functions) the DBT, plus a cross-compiled ELF through both routes —
the ELF leg carries most of the block-translation coverage. Builds the
**host** backend only, since it executes; skips loudly off x86_64/aarch64.
- Wildcard-capture scenario: `make test-scenario` (opt-in, NOT in `make test`);
spins a throwaway netmux and drives `$`-command `%0..%9` captures over a
socket (`tests/scenario/`) — the path muxscript can't reach

View file

@ -8,7 +8,7 @@
# make test — run smoke tests (build + install first)
# make hooks — install git hooks (done automatically on first build)
.PHONY: all install clean realclean test test-ios test-ganl test-netaddr test-dbt-chain test-dbt-cache test-dbt-exec test-dbt-interp test-alarm test-scenario test-parity213 test-stress test-jit-qreg test-jit-ifelse hooks
.PHONY: all install clean realclean test test-ios test-ganl test-netaddr test-dbt test-alarm test-scenario test-parity213 test-stress test-jit-qreg test-jit-ifelse hooks
# Install git hooks on first build so all developers get protection
# against accidentally editing generated files.
@ -28,15 +28,12 @@ clean:
$(MAKE) -C mux clean
$(MAKE) -C testcases/tools clean
$(MAKE) -C mux/ganl/tests clean
$(MAKE) -C tests/dbt_chain clean
$(MAKE) -C tests/dbt_exec clean
$(MAKE) -C tests/dbt_interp clean
$(MAKE) -C tests/dbt_cache clean
$(MAKE) -C tests/dbt clean
realclean:
$(MAKE) -C mux distclean
test: install test-ganl test-netaddr test-dbt-chain test-dbt-cache test-dbt-exec test-dbt-interp test-alarm test-jit-qreg test-jit-ifelse test-ios
test: install test-ganl test-netaddr test-dbt test-alarm test-jit-qreg test-jit-ifelse test-ios
$(MAKE) -C testcases/tools
cd testcases && ./tools/Makesmoke && ./tools/Smoke
@ -80,37 +77,15 @@ test-netaddr:
@echo "==> Running netaddr subnet tests"
$(MAKE) -C tests/netaddr test
# DBT block-chaining patch encode/decode tests (#1152). Asserts that
# dbt_backend_decode_jmp_target is the exact inverse of
# dbt_backend_backpatch_jmp, which is what dbt_resolve_chains relies on to
# tell an unresolved site from a live one. Builds all three backends
# (a64_sysv, x64_sysv, x64_win64) into one binary on every host — the #1152
# bug survived precisely because nothing exercised the affected backend.
# Compiles the backend sources directly, so it needs neither `install` nor
# --enable-jit and cannot degrade into testing nothing.
test-dbt-chain:
@echo "==> Running DBT chain patch encode/decode tests"
$(MAKE) -C tests/dbt_chain test
# DBT block cache tests (#1153). The 4-way cache had no dedupe by guest_pc
# while intrinsic blocks are inserted twice (once by try_emit_intrinsic, once
# by every caller of dbt_backend_translate_block), so two intrinsics sharing a
# set filled all four ways with two distinct blocks and evicted live ones.
# Compiles dbt.cpp directly against backend stubs — no `install`, no
# --enable-jit, no skip path.
test-dbt-cache:
@echo "==> Running DBT block cache tests"
$(MAKE) -C tests/dbt_cache test
# RV64 execution tests (interpreter + DBT). mux/modules/engine/dbt_test.cpp
# had never been in any build — no Makefile.am entry, no CI — so it only ran
# if someone typed the compile line from its header by hand. That left no
# automated RV64 *execution* coverage at all, the same "nothing exercises it"
# gap behind #1152/#1153/#1151. Builds the host backend (this one executes
# translated code, unlike tests/dbt_chain) and skips loudly off x86_64/aarch64.
test-dbt-exec:
@echo "==> Running RV64 execution tests (interpreter + DBT)"
$(MAKE) -C tests/dbt_exec test
# 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
# RV64 interpreter guest-memory bounds: mem_check gated every guest read and
# write with `addr + len <= size`, which wraps for an addr near UINT64_MAX and

5
tests/dbt/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
dbt_test
*.o
test_cache
test_chain
test_interp

133
tests/dbt/Makefile Normal file
View file

@ -0,0 +1,133 @@
# Makefile — DBT and RV64 tests.
#
# Three binaries rather than one, because each needs a genuinely different
# link and they cannot coexist in a single image:
#
# chain all three backends at once, with their colliding strong symbols
# renamed via -D. It only inspects emitted bytes, so it does not
# need a host that can run them — which is the point: #1152 was a
# decode bug in a backend nobody compiled. No dbt.cpp.
# cache dbt.cpp against backend stubs that abort if called. Cannot be
# merged with `chain`, whose backends are renamed away, or with
# `exec`, which links the real ones.
# interp guest-memory bounds on the interpreter route. #includes
# dbt_interp.cpp to reach file-static mem_check, so it cannot
# share a link with `exec`, which compiles that TU normally.
# exec the RV64 execution harness: interpreter and DBT, host backend
# only, because this one actually runs the code it translates.
#
# All three compile engine sources directly, so none needs a prior
# `make install` or --enable-jit, and none has a skip path that could
# quietly degrade into testing nothing. `exec` is the exception on host
# support: it skips loudly off x86_64/aarch64 because it must execute.
#
# Build: make Run: make test
# Individually: make chain | make cache | make interp | make exec
CXX = g++
CXXFLAGS = -std=c++17 -g -O2
TESTFLAGS = -Wall -Wextra
INCDIR = ../../mux/include
ENGINE = ../../mux/modules/engine
DEFS = -DHAVE_CONFIG_H -DTINYMUX_JIT
COMPILE = $(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR)
# Engine sources are code under test, not code being written here; the
# engine build already compiles them under its own warning flags, so
# -Wall -Wextra applies to the drivers alone to keep real failures visible.
DRIVER = $(CXX) $(CXXFLAGS) $(TESTFLAGS) $(DEFS) -I$(INCDIR)
# Host backend for `exec`, mirroring configure.ac's @DBT_BACKEND@ mapping.
ARCH := $(shell uname -m)
ifneq (,$(filter $(ARCH),aarch64 arm64))
HOST_BACKEND = dbt_a64_sysv
else ifneq (,$(filter $(ARCH),x86_64 amd64))
HOST_BACKEND = dbt_x64_sysv
else
HOST_BACKEND =
endif
# The five strong symbols every backend defines; $(1) is the per-backend
# prefix test_chain.cpp declares.
rename = -Ddbt_backend_backpatch_jmp=$(1)_backpatch_jmp \
-Ddbt_backend_decode_jmp_target=$(1)_decode_jmp_target \
-Ddbt_backend_emit_trampoline=$(1)_emit_trampoline \
-Ddbt_backend_translate_block=$(1)_translate_block \
-Ddbt_register_intrinsic=$(1)_register_intrinsic
ELF = $(ENGINE)/dbt_rt/test_rv64.elf
all: test_chain test_cache test_interp dbt_test
# --- chain: all three backends, byte inspection only -----------------
test_chain.o: test_chain.cpp
$(DRIVER) -c -o $@ $<
be_a64.o: $(ENGINE)/dbt_a64_sysv.cpp
$(COMPILE) $(call rename,a64) -c -o $@ $<
be_x64sysv.o: $(ENGINE)/dbt_x64_sysv.cpp
$(COMPILE) $(call rename,x64sysv) -c -o $@ $<
be_win64.o: $(ENGINE)/dbt_x64_win64.cpp
$(COMPILE) $(call rename,win64) -c -o $@ $<
test_chain: test_chain.o be_a64.o be_x64sysv.o be_win64.o
$(CXX) $(CXXFLAGS) -o $@ $^
# --- cache: dbt.cpp against aborting backend stubs -------------------
test_cache.o: test_cache.cpp
$(DRIVER) -c -o $@ $<
dbt_shared.o: $(ENGINE)/dbt.cpp
$(COMPILE) -c -o $@ $<
test_cache: test_cache.o dbt_shared.o
$(CXX) $(CXXFLAGS) -o $@ $^
# --- interp: guest-memory bounds on the interpreter route ------------
# mem_check and the mem accessors are file-static, so the driver
# #includes dbt_interp.cpp rather than linking it. That also means no
# stubs: the TU needs only dbt_interp.h, dbt_decoder.h and the standard
# library. It cannot share a link with `exec`, which compiles the same
# TU normally, or with `cache`/`chain`, whose backends are stubbed or
# renamed (#1292).
test_interp.o: test_interp.cpp $(ENGINE)/dbt_interp.cpp
$(COMPILE) -I$(ENGINE) -c -o $@ $<
test_interp: test_interp.o
$(CXX) $(CXXFLAGS) -o $@ $^
# --- exec: real execution, host backend only -------------------------
EXEC_SRCS = $(ENGINE)/dbt_test.cpp $(ENGINE)/dbt_interp.cpp \
$(ENGINE)/dbt_elf64.cpp $(ENGINE)/dbt.cpp \
$(ENGINE)/$(HOST_BACKEND).cpp
dbt_test:
ifeq ($(HOST_BACKEND),)
@echo "SKIP: no DBT backend for host '$(ARCH)' (configure.ac supports"
@echo " x86_64 and aarch64). Execution tests cannot run here."
else
$(COMPILE) -o $@ $(EXEC_SRCS)
endif
chain: test_chain
./test_chain
cache: test_cache
./test_cache
interp: test_interp
./test_interp
exec: dbt_test
ifeq ($(HOST_BACKEND),)
@echo "==> Skipping DBT execution tests (unsupported host '$(ARCH)')"
else
@if [ -f "$(ELF)" ]; then \
./dbt_test "$(ELF)"; \
else \
echo "NOTE: $(ELF) missing — running hand-assembled cases only,"; \
echo " which drops the ELF leg's DBT block-translation coverage."; \
./dbt_test; \
fi
endif
test: chain cache interp exec
clean:
rm -f *.o test_chain test_cache dbt_test
.PHONY: all test chain cache exec clean

View file

@ -35,7 +35,7 @@
// backend fails loudly instead of quietly testing something else.
//
static void die_unreachable(const char *who) {
fprintf(stderr, "test_dbt_cache: %s was called from the cache path.\n"
fprintf(stderr, "test_cache: %s was called from the cache path.\n"
"These stubs are no longer safe.\n", who);
abort();
}

View file

@ -71,7 +71,7 @@ DECLARE_BACKEND(win64);
uint8_t g_dbt_safe_page[DBT_SAFE_PAGE_SIZE];
static void die_unreachable(const char *who) {
fprintf(stderr, "test_dbt_chain: %s was called from the chaining path.\n"
fprintf(stderr, "test_chain: %s was called from the chaining path.\n"
"The encode/decode pair is no longer leaf arithmetic; these "
"stubs are no longer safe.\n", who);
abort();

View file

@ -1,2 +0,0 @@
test_dbt_cache
*.o

View file

@ -1,41 +0,0 @@
# Makefile — unit tests for the DBT block cache (dbt_cache_insert /
# dbt_cache_lookup, #1153).
#
# Links dbt.cpp against a driver that stubs the four dbt_backend_* symbols
# the translation unit references. dbt.cpp is compiled here directly rather
# than taken from the engine build, so this test needs no prior
# `make install` and no --enable-jit.
#
# Build: make
# Run: make test
CXX = g++
CXXFLAGS = -std=c++17 -g -O2
TESTFLAGS = -Wall -Wextra
INCDIR = ../../mux/include
ENGINE = ../../mux/modules/engine
DEFS = -DHAVE_CONFIG_H -DTINYMUX_JIT
TARGET = test_dbt_cache
OBJS = test_dbt_cache.o dbt_shared.o
all: $(TARGET)
test_dbt_cache.o: test_dbt_cache.cpp
$(CXX) $(CXXFLAGS) $(TESTFLAGS) $(DEFS) -I$(INCDIR) -c -o $@ $<
# Code under test, built under the engine's own warning posture (see the
# same note in tests/dbt_chain/Makefile).
dbt_shared.o: $(ENGINE)/dbt.cpp
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) -c -o $@ $<
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
test: $(TARGET)
./$(TARGET)
clean:
rm -f *.o $(TARGET)
.PHONY: all test clean

View file

@ -1,2 +0,0 @@
test_dbt_chain
*.o

View file

@ -1,64 +0,0 @@
# Makefile — unit tests for the DBT block-chaining patch encode/decode pair
# (dbt_backend_backpatch_jmp / dbt_backend_decode_jmp_target, #1152).
#
# All three backends are built into one test binary, on every host, so that
# no backend's encoding can go untested the way x64_win64 otherwise would
# outside Windows. Each backend .cpp defines the same five strong symbols,
# so each is compiled with those five renamed on the command line; the
# rename rewrites both the declaration (dbt_internal.h) and the definition
# within that translation unit, and needs no change to the shipping source.
#
# The backend sources are compiled here directly rather than linked from the
# engine build, so this test needs no prior `make install` and no
# --enable-jit: it can never degrade into testing nothing.
#
# Build: make
# Run: make test
CXX = g++
CXXFLAGS = -std=c++17 -g -O2
TESTFLAGS = -Wall -Wextra
INCDIR = ../../mux/include
ENGINE = ../../mux/modules/engine
DEFS = -DHAVE_CONFIG_H -DTINYMUX_JIT
TARGET = test_dbt_chain
# The five strong symbols every backend defines. $(1) is the per-backend
# prefix the test declares in test_dbt_chain.cpp.
rename = -Ddbt_backend_backpatch_jmp=$(1)_backpatch_jmp \
-Ddbt_backend_decode_jmp_target=$(1)_decode_jmp_target \
-Ddbt_backend_emit_trampoline=$(1)_emit_trampoline \
-Ddbt_backend_translate_block=$(1)_translate_block \
-Ddbt_register_intrinsic=$(1)_register_intrinsic
OBJS = test_dbt_chain.o be_a64.o be_x64sysv.o be_win64.o
all: $(TARGET)
test_dbt_chain.o: test_dbt_chain.cpp
$(CXX) $(CXXFLAGS) $(TESTFLAGS) $(DEFS) -I$(INCDIR) -c -o $@ $<
# The backend sources are code under test, not code being written here, and
# the engine build already compiles them under its own warning flags. Adding
# a different posture here would only emit noise unrelated to what this test
# checks, so -Wall -Wextra is applied to the driver alone.
be_a64.o: $(ENGINE)/dbt_a64_sysv.cpp
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) $(call rename,a64) -c -o $@ $<
be_x64sysv.o: $(ENGINE)/dbt_x64_sysv.cpp
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) $(call rename,x64sysv) -c -o $@ $<
be_win64.o: $(ENGINE)/dbt_x64_win64.cpp
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) $(call rename,win64) -c -o $@ $<
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
test: $(TARGET)
./$(TARGET)
clean:
rm -f *.o $(TARGET)
.PHONY: all test clean

View file

@ -1,2 +0,0 @@
dbt_test
*.o

View file

@ -1,71 +0,0 @@
# Makefile — RV64 execution tests for the interpreter and the DBT.
#
# mux/modules/engine/dbt_test.cpp has existed for a long time and has never
# been in any build: no Makefile.am entry, no CI, no `make test`. It only
# ran if a developer typed the compile line out of its header comment by
# hand. That left the project with no automated RV64 *execution* coverage
# at all, which is the same "nothing exercises it" gap that produced #1152
# (an x86 decode applied to AArch64), #1153 and #1151 — all three found by
# reading rather than by running.
#
# Unlike tests/dbt_chain, which compiles all three backends because it only
# inspects emitted bytes, this harness EXECUTES translated code and so must
# build the backend matching this host. The selection mirrors configure.ac;
# an unrecognised host skips loudly rather than building a wrong backend.
#
# Build: make
# Run: make test
CXX = g++
CXXFLAGS = -std=c++17 -g -O2
INCDIR = ../../mux/include
ENGINE = ../../mux/modules/engine
DEFS = -DHAVE_CONFIG_H -DTINYMUX_JIT
# Host backend, same mapping configure.ac uses for @DBT_BACKEND@.
ARCH := $(shell uname -m)
ifneq (,$(filter $(ARCH),aarch64 arm64))
BACKEND = dbt_a64_sysv
else ifneq (,$(filter $(ARCH),x86_64 amd64))
BACKEND = dbt_x64_sysv
else
BACKEND =
endif
TARGET = dbt_test
SRCS = $(ENGINE)/dbt_test.cpp $(ENGINE)/dbt_interp.cpp \
$(ENGINE)/dbt_elf64.cpp $(ENGINE)/dbt.cpp \
$(ENGINE)/$(BACKEND).cpp
# The cross-compiled RV64 ELF carries most of the DBT block-translation
# coverage: the hand-assembled cases are mostly interpreter-only, while the
# ELF leg runs through both routes and translates ~54 blocks.
ELF = $(ENGINE)/dbt_rt/test_rv64.elf
all: $(TARGET)
$(TARGET):
ifeq ($(BACKEND),)
@echo "SKIP: no DBT backend for host '$(ARCH)' (configure.ac supports"
@echo " x86_64 and aarch64). Execution tests cannot run here."
else
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) -o $@ $(SRCS)
endif
test: $(TARGET)
ifeq ($(BACKEND),)
@echo "==> Skipping DBT execution tests (unsupported host '$(ARCH)')"
else
@if [ -f "$(ELF)" ]; then \
./$(TARGET) "$(ELF)"; \
else \
echo "NOTE: $(ELF) missing — running hand-assembled cases only,"; \
echo " which drops the ELF leg's DBT block-translation coverage."; \
./$(TARGET); \
fi
endif
clean:
rm -f *.o $(TARGET)
.PHONY: all test clean

View file

@ -1,2 +0,0 @@
test_dbt_interp
*.o

View file

@ -1,40 +0,0 @@
# Makefile — guest memory bounds checks for the RV64 interpreter (#1292).
#
# mem_check() and the mem_read*/mem_write* accessors are file-static, so the
# test #includes dbt_interp.cpp rather than linking against it. That means a
# single translation unit and no stubs: dbt_interp.cpp depends only on
# dbt_interp.h, dbt_decoder.h and the standard library.
#
# Compiled here directly rather than taken from the engine build, so this
# needs no prior `make install` and no --enable-jit, and has no skip path.
#
# Build: make
# Run: make test
CXX = g++
CXXFLAGS = -std=c++17 -g -O2
INCDIR = ../../mux/include
ENGINE = ../../mux/modules/engine
DEFS = -DHAVE_CONFIG_H -DTINYMUX_JIT
TARGET = test_dbt_interp
OBJS = test_dbt_interp.o
all: $(TARGET)
# The engine's own warning posture applies here: the test TU includes the
# code under test, so -Wall -Wextra would report on engine source rather
# than on the test (same reasoning as tests/dbt_cache's dbt_shared.o).
test_dbt_interp.o: test_dbt_interp.cpp $(ENGINE)/dbt_interp.cpp
$(CXX) $(CXXFLAGS) $(DEFS) -I$(INCDIR) -I$(ENGINE) -c -o $@ $<
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $(OBJS)
test: $(TARGET)
./$(TARGET)
clean:
rm -f *.o $(TARGET)
.PHONY: all test clean