diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9973f125e0..583af95b36 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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 diff --git a/Makefile b/Makefile index d06a41e73b..8a49bd401f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/check.sh b/check.sh index ceeeb4ba3f..e2f438ae62 100755 --- a/check.sh +++ b/check.sh @@ -98,8 +98,8 @@ 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 . --" - MAKE_ACTION="$MAKE_ACTION SKIP_REDOX=1" + export TEST_RUNNER="redoxer exec --folder ../sysroot/$TARGET:/usr --folder . --" + MAKE_ACTION="$MAKE_ACTION IS_REDOX=1" else REDOXER_ENV="" fi diff --git a/tests/Makefile b/tests/Makefile index 556706c193..961dc7eef6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,16 +1,19 @@ include ../config.mk -SKIP_REDOX?=0 +IS_REDOX?=0 -# If compiling for Redox, SKIP_REDOX must be 1 -ifeq ($(SKIP_REDOX),1) -REDOX_FAILING_TESTS= +# If compiling for Redox, IS_REDOX must be 1 +ifeq ($(IS_REDOX),1) +FAILING_TESTS=signals/killpg-child else -REDOX_FAILING_TESTS=tls \ +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 \ @@ -212,7 +211,6 @@ NAMES=\ signals/killpg0-self \ signals/kill-group \ signals/kill-child \ - signals/killpg-child \ signals/killpg-self \ signals/pthread_kill-invalid \ signals/pthread_kill-child \ @@ -293,29 +291,31 @@ NAMES=\ grp/gr_iter \ waitpid \ waitpid_multiple \ - sigqueue -# resource/getrusage -# time/times + sigqueue \ + $(FAILING_TESTS) # Tests run with `expect` (require a .c file and an .exp file # that takes the produced binary as the first argument) EXPECT_INPUT_NAMES=\ unistd/getpass -# TODO: Dynamic linking is currently broken on Redox -# TODO: Some tests in NAMES break the kernel -ifeq ($(SKIP_REDOX),0) -BINS=$(patsubst %,$(BUILD)/bins_static/%,$(NAMES)) -BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(NAMES)) -BINS+=$(patsubst %,$(BUILD)/bins_dynamic/%,$(DYNAMIC_ONLY_NAMES)) +# TODO: Dynamic linking doesn't work with NATIVE_RELIBC=0 +# TODO: Reduce bins_static tests, only execute it when static link difference matters +ifeq ($(IS_REDOX),0) +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 -EXPECT_BINS=$(patsubst %,$(BUILD)/bins_static/%,$(EXPECT_NAMES)) - - CARGO_TEST?=cargo TEST_RUNNER?= @@ -326,35 +326,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 \ @@ -375,6 +358,7 @@ DYNAMIC_FLAGS=\ -Wl,-export-dynamic SYSROOT?=$(abspath ../sysroot/$(TARGET)/) +SYSROOT_TARGET?=$(SYSROOT) $(SYSROOT): $(MAKE) -C .. sysroot @@ -389,17 +373,24 @@ FLAGS+=\ $(SYSROOT)/lib/crti.o \ $(SYSROOT)/lib/crtn.o -SYSROOT_LIB=$(shell realpath ../sysroot/$(TARGET)/lib/) - STATIC_FLAGS+=\ - $(SYSROOT_LIB)/libc.a + $(SYSROOT)/lib/libc.a +ifeq ($(IS_REDOX),0) DYNAMIC_FLAGS+=\ - -Wl,-dynamic-linker=$(SYSROOT)/$(LD_SO_PATH) \ - -Wl,-rpath=$(SYSROOT_LIB):\$$ORIGIN \ - -L $(SYSROOT_LIB) \ + -Wl,-dynamic-linker=$(SYSROOT_TARGET)/$(LD_SO_PATH) \ + -Wl,-rpath=$(SYSROOT_TARGET)/lib:\$$ORIGIN \ + -L $(SYSROOT)/lib \ -lc \ -fpic +else +# TODO: Make -dynamic-linker works +DYNAMIC_FLAGS+=\ + -Wl,-rpath=$(SYSROOT_TARGET)/lib:\$$ORIGIN \ + -L $(SYSROOT)/lib \ + -lc \ + -fpic +endif DEPS=../sysroot/$(TARGET) else diff --git a/tests/expected/bins_dynamic/stdlib/getsubopt.stderr b/tests/expected/bins_dynamic/stdlib/getsubopt.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_dynamic/stdlib/getsubopt.stdout b/tests/expected/bins_dynamic/stdlib/getsubopt.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/pthread/exit.c b/tests/pthread/exit.c index ce276b4239..14e1daee1a 100644 --- a/tests/pthread/exit.c +++ b/tests/pthread/exit.c @@ -9,7 +9,7 @@ void *routine(void *arg) { assert(arg == NULL); - sleep(1); + usleep(100); puts("Thread succeeded"); diff --git a/tests/pthread/extjoin.c b/tests/pthread/extjoin.c index 05203e9fd6..8f1737587d 100644 --- a/tests/pthread/extjoin.c +++ b/tests/pthread/extjoin.c @@ -15,7 +15,7 @@ struct arg2 { void *routine1(void *arg) { assert(arg == NULL); puts("Thread 1 spawned, waiting 1s."); - sleep(1); + usleep(100); puts("Thread 1 finished."); return strdup("message from thread 1"); } diff --git a/tests/pthread/main.c b/tests/pthread/main.c index fd9148076c..acf51adb48 100644 --- a/tests/pthread/main.c +++ b/tests/pthread/main.c @@ -20,7 +20,7 @@ int main(void) { int status; puts("Start, sleeping 1 second"); - sleep(1); + usleep(100); pthread_t thread; void *arg = NULL; if ((status = pthread_create(&thread, NULL, thread_main, arg)) != 0) { diff --git a/tests/ptrace.c b/tests/ptrace.c index 40123f47b1..66592f7ed2 100644 --- a/tests/ptrace.c +++ b/tests/ptrace.c @@ -26,7 +26,7 @@ int main() { if (pid == 0) { // Test behavior on Redox when TRACEME hasn't been activated // before waitpid is invoked! - sleep(1); + usleep(100); int result = ptrace(PTRACE_TRACEME, 0, NULL, NULL); ERROR_IF(ptrace, result, == -1); diff --git a/tests/signals/kill-child.c b/tests/signals/kill-child.c index 10e332374e..3db868c984 100644 --- a/tests/signals/kill-child.c +++ b/tests/signals/kill-child.c @@ -36,8 +36,8 @@ void child_proc(int signum) sigemptyset(&act.sa_mask); sigaction(signum, &act, NULL); - status = sleep(10); - ERROR_IF(sleep, status, == 0); + status = usleep(200); + ERROR_IF(usleep, status, == 0); assert(sig_handled != 0); @@ -48,7 +48,7 @@ void parent(int signum, pid_t pid) { int status; - sleep(1); + usleep(100); status = kill(pid, signum); ERROR_IF(kill, status, != 0); diff --git a/tests/signals/killpg-child.c b/tests/signals/killpg-child.c index 1291f13959..c6cfbe1851 100644 --- a/tests/signals/killpg-child.c +++ b/tests/signals/killpg-child.c @@ -37,7 +37,7 @@ int killpg_test2(int signum) int i; sigignore(signum); - sleep(1); + usleep(100); child_pgid = getpgid(child_pid); ERROR_IF(getpgid, child_pgid, == -1); diff --git a/tests/signals/pthread_kill-child.c b/tests/signals/pthread_kill-child.c index 842bd68e73..04eb8a68aa 100644 --- a/tests/signals/pthread_kill-child.c +++ b/tests/signals/pthread_kill-child.c @@ -36,7 +36,7 @@ int main() // Now the child_thread exited, it is an invalid tid memcpy(&invalid_tid, &child_thread, sizeof(pthread_t)); - sleep(3); + usleep(300); int status; status = pthread_kill(invalid_tid, 0); diff --git a/tests/signals/pthread_kill-self.c b/tests/signals/pthread_kill-self.c index 3e70746141..e53db627cf 100644 --- a/tests/signals/pthread_kill-self.c +++ b/tests/signals/pthread_kill-self.c @@ -38,7 +38,7 @@ void *a_thread_func( void *arg) sem1=INMAIN; while(sem1==INMAIN) - sleep(1); + usleep(100); // sleep(50); @@ -61,16 +61,16 @@ int pthread_kill_test1(int signum) ERROR_IF(pthread_create, status, != 0); while(sem1==INTHREAD) - sleep(1); + usleep(100); status = pthread_kill(new_th, signum); ERROR_IF(pthread_kill, status, != 0); - sleep(2); + usleep(200); sem1=INTHREAD; while(handler_called==0) - sleep(1); + usleep(100); ERROR_IF(pthread_kill, handler_called, == -1); ERROR_IF(pthread_kill, handler_called, == 0); diff --git a/tests/signals/sigpause-error.c b/tests/signals/sigpause-error.c index c770a1e9da..2a33a608c0 100644 --- a/tests/signals/sigpause-error.c +++ b/tests/signals/sigpause-error.c @@ -71,14 +71,14 @@ int sigpause_error(int signum){ status = pthread_create(&new_th, NULL, d_thread_func, (void *)&signum); ERROR_IF(pthread_create, status, != 0); - sleep(1); + usleep(100); status = pthread_kill(new_th, signum); ERROR_IF(pthread_kill, status, != 0); sem = INTHREAD; while (sem == INTHREAD) - sleep(1); + usleep(100); if(result == 2) { exit(EXIT_FAILURE); diff --git a/tests/signals/sigpause-pause.c b/tests/signals/sigpause-pause.c index 4caf0e62c8..85ec96f3d5 100644 --- a/tests/signals/sigpause-pause.c +++ b/tests/signals/sigpause-pause.c @@ -45,12 +45,12 @@ int sigpause_basic(int signum) status = pthread_create(&new_th, NULL, a_thread_func, (void *)&signum); ERROR_IF(pthread_create, status, != 0); - sleep(1); + usleep(100); status = pthread_kill(new_th, signum); ERROR_IF(pthread_kill, status, != 0); - sleep(1); + usleep(100); if (handler_called != 1){ prinft("handler wasn't called\n"); diff --git a/tests/signals/sigpause-revert.c b/tests/signals/sigpause-revert.c index fcb26ab89d..ba8f1d8cdd 100644 --- a/tests/signals/sigpause-revert.c +++ b/tests/signals/sigpause-revert.c @@ -77,7 +77,7 @@ int sigpause_revert(int signum) { exit(EXIT_FAILURE); } - sleep(1); + usleep(100); if((status = pthread_kill(new_th, signum)) != 0) { diff --git a/tests/signals/sigpause-suspend.c b/tests/signals/sigpause-suspend.c index 8a7583fde1..ab67c8380d 100644 --- a/tests/signals/sigpause-suspend.c +++ b/tests/signals/sigpause-suspend.c @@ -55,7 +55,7 @@ int sigpause_suspend(int signum) perror("failed to create thread"); return EXIT_FAILURE; } - sleep(1); + usleep(100); assert(!atomic_load(&completed)); if ((status = pthread_kill(new_th, signum)) != 0) { diff --git a/tests/src/main.rs b/tests/src/main.rs index 80aa38dcc8..31b5444d53 100644 --- a/tests/src/main.rs +++ b/tests/src/main.rs @@ -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,127 @@ 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" + || &bin == "./bins_dynamic/pthread/once" + || 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); } diff --git a/tests/sys_stat/chmod.c b/tests/sys_stat/chmod.c index c89f892cd8..5424669422 100644 --- a/tests/sys_stat/chmod.c +++ b/tests/sys_stat/chmod.c @@ -34,7 +34,7 @@ static bool check_mode( stderr, "Mode mismatch\n\tExpected: %o\n\tActual: %o\n", expected, - stat.st_mode + stat.st_mode & 0777 ); return false; } diff --git a/tests/unistd/sleep.c b/tests/unistd/sleep.c index 193f7c5f06..4ffb8f67d8 100644 --- a/tests/unistd/sleep.c +++ b/tests/unistd/sleep.c @@ -15,7 +15,7 @@ void handler(int _s) int main(void) { // sleep has no error codes and doesn't set errno - unsigned int unslept = sleep(2); + unsigned int unslept = sleep(1); printf("unslept: %u\n", unslept); struct sigaction sa; @@ -23,7 +23,7 @@ int main(void) sa.sa_handler = handler; sigaction(SIGALRM, &sa, NULL); // TODO: This test is unreliable, overlapping use of alarm and sleep is not recommended. - // alarm(2); + // alarm(1); // unslept = sleep(10); // if (unslept < 7) @@ -32,7 +32,7 @@ int main(void) // exit(EXIT_FAILURE); // } - int us_status = usleep(1000); + int us_status = usleep(100); ERROR_IF(usleep, us_status, == -1); UNEXP_IF(usleep, us_status, != 0); diff --git a/tests/waitpid.c b/tests/waitpid.c index 70eabbbfee..7f4aaf1213 100644 --- a/tests/waitpid.c +++ b/tests/waitpid.c @@ -12,7 +12,7 @@ void for_code(int code) { // Testing successful exit if (pid == 0) { // child - sleep(1); + usleep(100); _Exit(code); } printf("Testing waitpid of child %d for code %d\n", pid, code); diff --git a/tests/waitpid_multiple.c b/tests/waitpid_multiple.c index b872ed1fb5..7f1df48ff9 100644 --- a/tests/waitpid_multiple.c +++ b/tests/waitpid_multiple.c @@ -15,7 +15,7 @@ int main(void) { if (pid_samepgid == 0) { // child - sleep(2); + usleep(200); _Exit(2); } pid_t pid_diffpgids[2]; @@ -28,7 +28,7 @@ int main(void) { ERROR_IF(setpgid, ret, == -1); // child - sleep(1); + usleep(100); _Exit(i); } }