Skip to content

Commit 1760a99

Browse files
authored
Merge pull request #38 from sysprog21/test-suite
Harden test suite against silent-skip patterns
2 parents 382db3c + d3b4800 commit 1760a99

14 files changed

Lines changed: 1270 additions & 753 deletions

Makefile

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# elfuse aarch64-linux ELF executor on macOS Apple Silicon
1+
# elfuse -- aarch64-linux ELF executor on macOS Apple Silicon
22
#
33
# Copyright 2026 elfuse contributors
44
# SPDX-License-Identifier: Apache-2.0
@@ -8,7 +8,7 @@
88
#
99
# Example: make elfuse
1010
# make test-hello
11-
# make V=1 elfuse (verbose show full commands)
11+
# make V=1 elfuse (verbose -- show full commands)
1212

1313
.DEFAULT_GOAL := help
1414
.DELETE_ON_ERROR:
@@ -90,7 +90,7 @@ define link-and-sign
9090
mv "$$tmp" "$1"
9191
endef
9292

93-
# ── Main executable ──────────────────────────────────────────────
93+
# Main executable
9494
.PHONY: all elfuse
9595
.PHONY: gen-syscall-dispatch check-syscall-dispatch
9696

@@ -119,7 +119,7 @@ elfuse: $(ELFUSE_BIN)
119119
$(ELFUSE_BIN): $(OBJS) | $(BUILD_DIR)
120120
$(call link-and-sign,$@,$(OBJS))
121121

122-
# ── Native test binaries (macOS, Hypervisor.framework) ───────────
122+
# Native test binaries (macOS, Hypervisor.framework)
123123

124124
## Build the multi-vCPU HVF validation test (native macOS binary)
125125
$(BUILD_DIR)/test-multi-vcpu: $(BUILD_DIR)/test-multi-vcpu.o | $(BUILD_DIR)
@@ -129,7 +129,16 @@ $(BUILD_DIR)/test-multi-vcpu: $(BUILD_DIR)/test-multi-vcpu.o | $(BUILD_DIR)
129129
$(BUILD_DIR)/test-rwx: $(BUILD_DIR)/test-rwx.o | $(BUILD_DIR)
130130
$(call link-and-sign,$@,$<)
131131

132-
# ── Guest test binaries (cross-compiled, aarch64-linux) ──────────
132+
## Build the proctitle argv-tail regression test (native macOS binary)
133+
# Links against the project-built proctitle.o so the exact in-tree code is
134+
# exercised; no HVF entitlement is needed because the test only manipulates
135+
# mmap and PROT_NONE. The codesign step is skipped for the same reason.
136+
$(BUILD_DIR)/test-proctitle-host: $(BUILD_DIR)/test-proctitle-host.o \
137+
$(BUILD_DIR)/runtime/proctitle.o | $(BUILD_DIR)
138+
@echo " LD $@"
139+
$(Q)$(CC) $(CFLAGS) -o $@ $^
140+
141+
# Guest test binaries (cross-compiled, aarch64-linux)
133142
# Only used when GUEST_TEST_BINARIES is not set.
134143

135144
ifndef GUEST_TEST_BINARIES

mk/tests.mk

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
test-matrix test-matrix-elfuse-aarch64 test-matrix-qemu-aarch64 \
88
test-full test-multi-vcpu test-rwx test-sysroot-rename \
99
test-case-collision test-case-collision-fallback test-sysroot-create-paths \
10-
test-proctitle-low-stack \
10+
test-proctitle-host test-proctitle-low-stack \
1111
test-sysroot-procfs-exec test-timeout-disable test-fuse-alpine \
1212
test-sysroot-nofollow test-sysroot-chdir perf
1313

@@ -23,6 +23,8 @@ check-syscall-coverage:
2323
## Run the unit test suite plus busybox applet validation
2424
check: $(ELFUSE_BIN) $(TEST_DEPS) check-syscall-coverage
2525
@bash tests/driver.sh -e $(ELFUSE_BIN) -d $(TEST_DIR) -v
26+
@printf "\n$(BLUE)━━━ proctitle argv-tail regression ━━━$(RESET)\n"
27+
@$(MAKE) --no-print-directory test-proctitle-host
2628
@printf "\n$(BLUE)━━━ proctitle low-stack regression ━━━$(RESET)\n"
2729
@$(MAKE) --no-print-directory test-proctitle-low-stack
2830
@printf "\n$(BLUE)━━━ busybox applet validation ━━━$(RESET)\n"
@@ -35,7 +37,8 @@ check: $(ELFUSE_BIN) $(TEST_DEPS) check-syscall-coverage
3537
@$(MAKE) --no-print-directory test-timeout-disable
3638

3739
test-sysroot-rename: $(ELFUSE_BIN) $(BUILD_DIR)/test-sysroot-rename
38-
@tmpdir=$$(mktemp -d); \
40+
@set -e; \
41+
tmpdir=$$(mktemp -d); \
3942
trap 'rm -rf "$$tmpdir"; rm -f /tmp/elfuse-sysroot-rename-dst.txt' EXIT; \
4043
mkdir -p "$$tmpdir/tmp"; \
4144
printf 'inside-sysroot\n' > "$$tmpdir/tmp/elfuse-sysroot-rename-src.txt"; \
@@ -74,7 +77,8 @@ test-case-collision-fallback: $(ELFUSE_BIN) $(BUILD_DIR)/test-case-collision
7477
$(ELFUSE_BIN) --sysroot "$$tmpdir" $(BUILD_DIR)/test-case-collision
7578

7679
test-sysroot-create-paths: $(ELFUSE_BIN) $(BUILD_DIR)/test-sysroot-create-paths
77-
@tmpdir=$$(mktemp -d); \
80+
@set -e; \
81+
tmpdir=$$(mktemp -d); \
7882
guest_tmp="/tmp/elfuse-sysroot-create-paths/file.txt"; \
7983
mounted_tmp="$$tmpdir/case-sysroot/tmp/elfuse-sysroot-create-paths/file.txt"; \
8084
host_out_dir="$$tmpdir/host-out"; \
@@ -113,8 +117,7 @@ test-gdbstub: $(ELFUSE_BIN) $(TEST_DIR)/test-hello
113117
## Alias for check (backward compat)
114118
test-all: check
115119

116-
# ── Coreutils integration test ───────────────────────────────────
117-
120+
# Coreutils integration test
118121
FIXTURES_DIR ?= $(CURDIR)/externals/test-fixtures
119122

120123
ifeq ($(origin GUEST_COREUTILS), undefined)
@@ -171,8 +174,7 @@ test-coreutils: $(ELFUSE_BIN)
171174
bash tests/test-coreutils.sh $(ELFUSE_BIN) $(COREUTILS_BIN); \
172175
fi
173176

174-
# ── Busybox integration test ─────────────────────────────────────
175-
177+
# Busybox integration test
176178
ifneq ($(wildcard $(BUILD_DIR)/busybox),)
177179
BUSYBOX_BIN ?= $(BUILD_DIR)/busybox
178180
else ifdef GUEST_BUSYBOX
@@ -256,8 +258,7 @@ test-proctitle-low-stack: $(ELFUSE_BIN) $(BUSYBOX_DEPS)
256258
fi
257259
@bash tests/test-proctitle-low-stack.sh $(ELFUSE_BIN) $(BUSYBOX_BIN)
258260

259-
# ── Static binary integration tests ──────────────────────────────
260-
261+
# Static binary integration tests
261262
ifdef GUEST_STATIC_BINS
262263
ifneq ($(wildcard $(GUEST_STATIC_BINS)/bin),)
263264
STATIC_BINS_DIR ?= $(GUEST_STATIC_BINS)/bin
@@ -278,8 +279,7 @@ test-static-bins: $(ELFUSE_BIN)
278279
bash tests/test-static-bins.sh $(ELFUSE_BIN) $(STATIC_BINS_DIR); \
279280
fi
280281

281-
# ── Dynamic linking tests ────────────────────────────────────────
282-
282+
# Dynamic linking tests
283283
# Musl sysroot with dynamic linker + libc.so.
284284
SYSROOT_DIR ?= $(GUEST_SYSROOT)
285285
ifdef GUEST_DYNAMIC_COREUTILS
@@ -299,13 +299,24 @@ test-dynamic: $(ELFUSE_BIN)
299299
@printf "$(BLUE)▸ Running$(RESET) dynamic hello-dynamic (--sysroot)\n"
300300
$(ELFUSE_BIN) --sysroot $(SYSROOT_DIR) $(GUEST_DYNAMIC_TESTS)/bin/hello-dynamic
301301

302-
## Run guest FUSE validation against the Alpine musl sysroot
302+
## Run guest FUSE validation
303+
# test-fuse-basic is statically linked and accesses exactly one host path:
304+
# /mnt/fuse (open + access). /dev/fuse is intercepted by elfuse internally.
305+
# A minimal sysroot under build/ that contains only /mnt/fuse is therefore
306+
# sufficient coverage; the earlier dependency on the full Alpine fixture
307+
# tree was incidental and broke `make distclean && make check` whenever
308+
# the Alpine CDN pruned a pinned package version.
309+
#
310+
# An explicit SYSROOT_DIR override is still honored for users who want
311+
# the test to run against their own sysroot (e.g. the Alpine fixtures
312+
# fetched separately for the broader matrix runner).
303313
test-fuse-alpine: $(ELFUSE_BIN) $(BUILD_DIR)/test-fuse-basic
304-
@if [ -z "$(SYSROOT_DIR)" ] || [ ! -d "$(SYSROOT_DIR)" ]; then \
305-
printf "$(YELLOW)SKIP$(RESET) Alpine sysroot not found. Set SYSROOT_DIR=/path/to/sysroot or run tests/fetch-fixtures.sh.\n"; \
306-
exit 0; \
307-
fi
308-
@bash tests/test-fuse-alpine.sh $(ELFUSE_BIN) $(SYSROOT_DIR) $(BUILD_DIR)/test-fuse-basic
314+
@sysroot="$(SYSROOT_DIR)"; \
315+
if [ -z "$$sysroot" ] || [ ! -d "$$sysroot" ]; then \
316+
sysroot="$(BUILD_DIR)/fuse-scratch-sysroot"; \
317+
mkdir -p "$$sysroot/mnt/fuse"; \
318+
fi; \
319+
bash tests/test-fuse-alpine.sh $(ELFUSE_BIN) "$$sysroot" $(BUILD_DIR)/test-fuse-basic
309320

310321
## Run dynamically-linked coreutils tests (--sysroot)
311322
test-dynamic-coreutils: $(ELFUSE_BIN)
@@ -323,8 +334,7 @@ test-dynamic-coreutils: $(ELFUSE_BIN)
323334
bash tests/test-dynamic-coreutils.sh $(ELFUSE_BIN) $(SYSROOT_DIR) $(DYNAMIC_COREUTILS_BIN); \
324335
fi
325336

326-
# ── glibc dynamic linking tests ───────────────────────────────────
327-
337+
# glibc dynamic linking tests
328338
# glibc sysroot with dynamic linker + libc.so.
329339
GLIBC_SYSROOT_DIR ?= $(GUEST_GLIBC_SYSROOT)
330340
ifdef GUEST_GLIBC_DYNAMIC_COREUTILS
@@ -358,8 +368,7 @@ test-glibc-coreutils: $(ELFUSE_BIN)
358368
SUITE_SUMMARY="glibc results" \
359369
bash tests/test-dynamic-coreutils.sh $(ELFUSE_BIN) $(GLIBC_SYSROOT_DIR) $(GLIBC_DYNAMIC_COREUTILS_BIN)
360370

361-
# ── Performance benchmark ─────────────────────────────────────────
362-
371+
# Performance benchmark
363372
ifneq ($(wildcard $(BUILD_DIR)/busybox),)
364373
PERF_BIN ?= $(BUILD_DIR)/perf-bin
365374
PERF_DEPS := $(addprefix $(PERF_BIN)/,grep wc cat sort)
@@ -385,8 +394,7 @@ test-perf: $(ELFUSE_BIN) $(PERF_DEPS)
385394
## Alias for test-perf
386395
perf: test-perf
387396

388-
# ── Test matrix (elfuse + qemu, aarch64) ────────────────────────────────
389-
397+
# Test matrix (elfuse + qemu, aarch64)
390398
## Run full test matrix (all modes: elfuse + qemu, aarch64)
391399
test-matrix: $(ELFUSE_BIN) $(TEST_DEPS)
392400
@bash tests/test-matrix.sh all
@@ -399,8 +407,7 @@ test-matrix-elfuse-aarch64: $(ELFUSE_BIN) $(TEST_DEPS)
399407
test-matrix-qemu-aarch64: $(ELFUSE_BIN) $(TEST_DEPS)
400408
@bash tests/test-matrix.sh qemu-aarch64
401409

402-
# ── Full test suite ──────────────────────────────────────────────────
403-
410+
# Full test suite
404411
## Run the complete test suite (aarch64: unit + busybox + gdbstub + coreutils + static + dynamic)
405412
test-full: $(ELFUSE_BIN)
406413
@printf "\n$(CYAN)╔══════════════════════════════════════════════════════╗$(RESET)\n"
@@ -438,15 +445,19 @@ test-full: $(ELFUSE_BIN)
438445
printf "$(CYAN)╚══════════════════════════════════════════════════════╝$(RESET)\n"; \
439446
[ "$$fail" -eq 0 ]
440447

441-
# ── Multi-vCPU validation test ─────────────────────────────────────
448+
# Multi-vCPU validation test
442449
# Build rules in top-level Makefile; these are just run targets.
443450

444451
## Run multi-vCPU validation tests (5 tests)
445452
test-multi-vcpu: $(BUILD_DIR)/test-multi-vcpu
446453
$(BUILD_DIR)/test-multi-vcpu
447454

448-
# ── RWX page table entry test ───────────────────────────────────
449-
455+
# RWX page table entry test
450456
## Run RWX page table entry test (does HVF allow W+X?)
451457
test-rwx: $(BUILD_DIR)/test-rwx
452458
$(BUILD_DIR)/test-rwx
459+
460+
# Proctitle argv-tail regression
461+
## Run the deterministic argv-tail overshoot guard test
462+
test-proctitle-host: $(BUILD_DIR)/test-proctitle-host
463+
$(BUILD_DIR)/test-proctitle-host

src/syscall/fd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ int64_t signalfd_read(int guest_fd,
10281028
if (written == 0) {
10291029
/* No bytes transferred: surface EFAULT, leave the queue
10301030
* untouched so the signal is not lost. Matches the elfuse
1031-
* promise locked in by tests/test-tier-b's
1031+
* promise locked in by tests/test-fd-family's
10321032
* test_signalfd_efault_preserves_pending.
10331033
*/
10341034
if (pending != pending_stack)

tests/driver.sh

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ FILTER=""
2727
LIST_ONLY=0
2828
VERBOSE=0
2929
TAP=0
30-
ALLOW_MISSING_BINARIES="${ALLOW_MISSING_BINARIES:-auto}"
30+
# Three values: 0 (strict, default), 1 (skip missing), auto (legacy).
31+
# In strict mode any missing test binary is a FAIL. The legacy "auto"
32+
# value flips to skip when TESTDIR is not the canonical build/ or
33+
# build/bin tree, which used to silently turn a partial out-of-tree
34+
# fixture set into a wall of green skips. Callers that genuinely want
35+
# permissive-skip-mode behavior should set ALLOW_MISSING_BINARIES=1
36+
# explicitly.
37+
ALLOW_MISSING_BINARIES="${ALLOW_MISSING_BINARIES:-0}"
3138

3239
usage()
3340
{
@@ -230,15 +237,21 @@ evaluate_result()
230237
if [ "$rc" -eq 124 ]; then
231238
return 1
232239
fi
233-
if [ "$rc" -eq 0 ] || {
234-
[ -n "$expected" ] && [ "$rc" -eq "$expected" ]
235-
}; then
236-
if [ -n "$stdout_pat" ] && ! grep -qE "$stdout_pat" <<< "$output"; then
240+
# When the manifest declares expected_rc=N, only that exact rc passes.
241+
# Without this guard, a test that mistakenly exits 0 instead of its
242+
# declared non-zero code (e.g. test-complex with expected_rc=42)
243+
# would be reported PASS because rc=0 short-circuited the OR clause.
244+
if [ -n "$expected" ]; then
245+
if [ "$rc" -ne "$expected" ]; then
237246
return 1
238247
fi
239-
return 0
248+
elif [ "$rc" -ne 0 ]; then
249+
return 1
250+
fi
251+
if [ -n "$stdout_pat" ] && ! grep -qE "$stdout_pat" <<< "$output"; then
252+
return 1
240253
fi
241-
return 1
254+
return 0
242255
}
243256

244257
report_case()

tests/lib/coreutils-suite.sh

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,30 +47,24 @@ coreutils_suite_extended_text()
4747
coreutils_suite_basic_encoding()
4848
{
4949
coreutils_print_section "Encoding / hashing"
50-
if [ -e "$BIN/base32" ]; then
51-
run_check base32 "NBSWY" "$TMPDIR/hello.txt"
52-
fi
50+
# Optional binaries (base32/basenc/sha224sum/sha384sum/b2sum/sum) are
51+
# gated by test_skip_missing_tool inside run_check: when the wrapper
52+
# sets TEST_SKIP_MISSING_TOOLS=1 they report SKIP with accounting,
53+
# otherwise the missing binary surfaces as a hard FAIL. The previous
54+
# raw "if [ -e ... ]; then" blocks bypassed both paths, silently
55+
# erasing assertions whenever the binary was absent.
56+
run_check base32 "NBSWY" "$TMPDIR/hello.txt"
5357
run_check base64 "aGVsbG8" "$TMPDIR/hello.txt"
54-
if [ -e "$BIN/basenc" ]; then
55-
run_check basenc "aGVsbG8" "--base64" "$TMPDIR/hello.txt"
56-
fi
58+
run_check basenc "aGVsbG8" "--base64" "$TMPDIR/hello.txt"
5759
run_check md5sum "hello.txt" "$TMPDIR/hello.txt"
5860
run_check sha1sum "hello.txt" "$TMPDIR/hello.txt"
59-
if [ -e "$BIN/sha224sum" ]; then
60-
run_check sha224sum "95041d" "$TMPDIR/hello.txt"
61-
fi
61+
run_check sha224sum "95041d" "$TMPDIR/hello.txt"
6262
run_check sha256sum "hello.txt" "$TMPDIR/hello.txt"
63-
if [ -e "$BIN/sha384sum" ]; then
64-
run_check sha384sum "6b3b69" "$TMPDIR/hello.txt"
65-
fi
63+
run_check sha384sum "6b3b69" "$TMPDIR/hello.txt"
6664
run_check sha512sum "hello.txt" "$TMPDIR/hello.txt"
67-
if [ -e "$BIN/b2sum" ]; then
68-
run_check b2sum "hello.txt" "$TMPDIR/hello.txt"
69-
fi
65+
run_check b2sum "hello.txt" "$TMPDIR/hello.txt"
7066
run_check cksum "hello.txt" "$TMPDIR/hello.txt"
71-
if [ -e "$BIN/sum" ]; then
72-
run_check sum "[0-9]" "$TMPDIR/hello.txt"
73-
fi
67+
run_check sum "[0-9]" "$TMPDIR/hello.txt"
7468
}
7569

7670
coreutils_suite_basic_files()
@@ -164,9 +158,10 @@ coreutils_suite_basic_math()
164158
run_check seq "5" "1" "5"
165159
run_check expr "3" "1" "+" "2"
166160
run_check factor "2 2 3" "12"
167-
if [ -e "$BIN/numfmt" ]; then
168-
run_check numfmt "1\\.0[kK]" "--to=si" "1000"
169-
fi
161+
# numfmt is optional in some packages; rely on test_skip_missing_tool
162+
# so absence becomes a SKIP under TEST_SKIP_MISSING_TOOLS=1 and a FAIL
163+
# otherwise, rather than a silent omission.
164+
run_check numfmt "1\\.0[kK]" "--to=si" "1000"
170165
}
171166

172167
coreutils_suite_basic_sysinfo()

0 commit comments

Comments
 (0)