Make test runner hang proof
This commit is contained in:
+1
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user