Make test runner hang proof

This commit is contained in:
Wildan M
2025-12-18 10:16:08 +07:00
parent fb01e89508
commit 42d5369d41
5 changed files with 130 additions and 50 deletions
+1 -2
View File
@@ -58,8 +58,7 @@ test:x86_64:
stage: test
needs: [x86_64]
script:
# TODO: sigaction and sigaltstack hangs with multi core
- REDOXER_QEMU_ARGS="-smp 1" ./check.sh --arch=x86_64 --test
- ./check.sh --arch=x86_64 --test
test:aarch64:
stage: test
-1
View File
@@ -114,7 +114,6 @@ test: sysroot/$(TARGET)
# TODO: Fix SIGILL when running cargo test
# $(CARGO_TEST) test
$(MAKE) -C tests run
$(MAKE) -C tests verify
$(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/libopenlibm.a
+1 -1
View File
@@ -98,7 +98,7 @@ run_redoxer() {
redoxer toolchain || { echo -e "${RED}Fail: redoxer toolchain for: $target.${NC}" && exit 1; }
export CARGOFLAGS=""
export CARGO_TEST="redoxer"
export TEST_RUNNER="redoxer exec --folder . --"
export TEST_RUNNER="redoxer exec --folder ../sysroot/$TARGET:/usr --folder . --"
MAKE_ACTION="$MAKE_ACTION SKIP_REDOX=1 NATIVE_RELIBC=1"
else
REDOXER_ENV=""
+17 -34
View File
@@ -10,7 +10,10 @@ REDOX_FAILING_TESTS=tls \
futimens \
mkfifo \
stdlib/ptsname \
sys_mman
sys_mman \
# resource/getrusage
# time/times
# netdb/netdb
endif
@@ -178,10 +181,7 @@ EXPECT_NAMES=\
wctype/towlower \
wctype/towupper \
mknod \
mknodat \
$(REDOX_FAILING_TESTS)
# TODO: Fix this
# netdb/netdb \
mknodat
BUILD?=.
@@ -190,8 +190,7 @@ DYNAMIC_ONLY_NAMES=\
dlopen_scopes
# Binaries that may generate varied output
NAMES=\
$(EXPECT_NAMES) \
VARIED_NAMES=\
dirent/main \
dirent/posix_getdents \
kill-waitpid \
@@ -293,9 +292,8 @@ NAMES=\
grp/gr_iter \
waitpid \
waitpid_multiple \
sigqueue
# resource/getrusage
# time/times
sigqueue \
$(REDOX_FAILING_TESTS)
# Tests run with `expect` (require a .c file and an .exp file
# that takes the produced binary as the first argument)
@@ -303,18 +301,20 @@ EXPECT_INPUT_NAMES=\
unistd/getpass
# TODO: Dynamic linking doesn't work with NATIVE_RELIBC=0
# TODO: Some tests in NAMES break the kernel
# TODO: Reduce bins_static tests?
# TODO: Reduce bins_static tests, only execute it when static link difference matters
ifeq ($(SKIP_REDOX),0)
BINS=$(patsubst %,$(BUILD)/bins_static/%,$(NAMES))
BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(NAMES))
BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(DYNAMIC_ONLY_NAMES))
BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(VARIED_NAMES))
BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(VARIED_NAMES))
EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_static/%,$(EXPECT_NAMES))
EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(EXPECT_NAMES))
EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(DYNAMIC_ONLY_NAMES))
EXPECT_INPUT_BINS=$(patsubst %,$(BUILD)/bins_expect_input/%,$(EXPECT_INPUT_NAMES))
else
# TODO: Not compiling static tests, too heavy for redoxerd
BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(VARIED_NAMES))
EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(EXPECT_NAMES))
EXPECT_BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(DYNAMIC_ONLY_NAMES))
# TODO: EXPECT_INPUT_BINS
endif
CARGO_TEST?=cargo
@@ -327,35 +327,18 @@ all: $(BINS)
clean:
rm -rf bins_* gen *.out
run: | $(BINS) $(EXPECT_INPUT_BINS)
run: bins_verify/relibc-tests $(BINS) $(EXPECT_BINS) $(EXPECT_INPUT_BINS)
@echo "\033[1;36;49mRunning tests\033[0m"
@for bin in $(BINS); \
do \
echo "\033[0;96;49m# $${bin} #\033[0m"; \
${TEST_RUNNER} "$${bin}" test args || exit $$?; \
done
$(TEST_RUNNER) $< $(patsubst %,-s%,$(BINS)) $(EXPECT_BINS)
for exp in $(EXPECT_INPUT_BINS); \
do \
echo "# expect $$(readlink -e $${exp}.exp) $$(readlink -e $${exp}) #"; \
expect "$$(readlink -e $${exp}.exp)" "$$(readlink -e $${exp})" test args || exit $$?; \
done
expected: | $(EXPECT_BINS)
rm -rf expected
mkdir -p expected
for bin in $(EXPECT_BINS); \
do \
echo "# $${bin} #"; \
mkdir -p expected/`dirname $${bin}`; \
"$${bin}" test args > "expected/$${bin}.stdout" 2> "expected/$${bin}.stderr" || exit $$?; \
done
bins_verify/relibc-tests: src/main.rs
$(CARGO_TEST) build --release --bin relibc-tests --out-dir bins_verify -Z unstable-options
verify: bins_verify/relibc-tests | $(EXPECT_BINS)
$(TEST_RUNNER) $< $(EXPECT_BINS)
FLAGS=\
-std=c11 \
-fno-builtin \
+111 -12
View File
@@ -1,8 +1,11 @@
use std::{
env, fs,
io::{self, Write},
io::{self, Read, Write},
path::PathBuf,
process::{self, Command, ExitStatus},
process::{self, Command, ExitStatus, Stdio},
sync::mpsc,
thread,
time::{Duration, Instant},
};
fn expected(bin: &str, kind: &str, generated: &[u8], status: ExitStatus) -> Result<(), String> {
@@ -47,6 +50,8 @@ const STATUS_ONLY: &str = "-s";
fn main() {
let mut failures = Vec::new();
let timeout = Duration::from_secs(10);
let slowtime = Duration::from_secs(1);
for bin in env::args().skip(1) {
let status_only = bin.starts_with(STATUS_ONLY);
@@ -58,30 +63,124 @@ fn main() {
println!("# {} #", bin);
match Command::new(&bin).arg("test").arg("args").output() {
Ok(output) => {
let start_time = Instant::now();
let (tx, rx) = mpsc::channel();
let bin_for_spawn = bin.clone();
// There's an issue when pthread hangs, spawn() also hangs, so let's use separate thread to spawn
thread::spawn(move || {
let result = Command::new(&bin_for_spawn)
.arg("test")
.arg("args")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn();
tx.send(result).expect("Can't send");
});
let mut status = None;
let mut child = None;
loop {
if start_time.elapsed() > timeout {
let failure = format!(
"\x1b[0;91;49m{}: assumed hangs after {}ms\x1b[0m",
bin,
start_time.elapsed().as_millis()
);
println!("{}", failure);
failures.push(failure);
break;
}
// A pretty rare hang on pthread/barrier which also stops this loop :(
// https://gitlab.redox-os.org/redox-os/relibc/-/issues/238
if &bin == "./bins_dynamic/pthread/barrier" || start_time.elapsed() > slowtime {
println!("# waiting {}ms", start_time.elapsed().as_millis());
}
let c = match &mut child {
Some(child) => child,
None => match rx.try_recv() {
Ok(Ok(c)) => {
child = Some(c);
continue;
}
Ok(Err(err)) => {
let failure = format!("{}: failed to execute: {}", bin, err);
println!("{}", failure);
failures.push(failure);
break;
}
Err(mpsc::TryRecvError::Disconnected) => {
let failure = format!("{}: failed to execute: thread died", bin);
println!("{}", failure);
failures.push(failure);
break;
}
Err(mpsc::TryRecvError::Empty) => {
thread::sleep(Duration::from_millis(if start_time.elapsed() < slowtime {
25
} else {
500
}));
continue;
}
},
};
match c.try_wait() {
Ok(Some(s)) => {
status = Some(s);
break;
}
Ok(None) => {
thread::sleep(Duration::from_millis(if start_time.elapsed() < slowtime {
25
} else {
500
}));
}
Err(e) => {
failures.push(format!("{}: error waiting: {}", bin, e));
break;
}
}
}
match (status, child) {
(Some(exit_status), Some(child)) => {
if !status_only {
if let Err(failure) = expected(&bin, "stdout", &output.stdout, output.status) {
let mut stdout = Vec::new();
let mut stderr = Vec::new();
child.stdout.unwrap().read_to_end(&mut stdout).unwrap();
child.stderr.unwrap().read_to_end(&mut stderr).unwrap();
if let Err(failure) = expected(&bin, "stdout", &stdout, exit_status) {
println!("{}", failure);
failures.push(failure);
}
if let Err(failure) = expected(&bin, "stderr", &output.stderr, output.status) {
if let Err(failure) = expected(&bin, "stderr", &stderr, exit_status) {
println!("{}", failure);
failures.push(failure);
}
}
}
Err(err) => {
let failure = format!("{}: failed to execute: {}", bin, err);
println!("{}", failure);
failures.push(failure);
(_, _) => {
continue;
}
}
if start_time.elapsed() > slowtime {
println!(
"\x1b[0;93;49m test exection took too long: {}ms\x1b[0m",
start_time.elapsed().as_millis()
);
}
}
if !failures.is_empty() {
println!("# FAILURES #");
println!("\x1b[1;91;49m# FAILURES #\x1b[0m");
for failure in failures {
println!("{}", failure);
}