Merge branch 'dyn-tests' into 'master'

Use dynamic link and add more tests

See merge request redox-os/relibc!814
This commit is contained in:
Jeremy Soller
2025-12-18 21:42:12 -07:00
23 changed files with 184 additions and 93 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
+2 -2
View File
@@ -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
+41 -50
View File
@@ -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
+1 -1
View File
@@ -9,7 +9,7 @@
void *routine(void *arg) {
assert(arg == NULL);
sleep(1);
usleep(100);
puts("Thread succeeded");
+1 -1
View File
@@ -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");
}
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+4 -4
View File
@@ -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);
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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");
+1 -1
View File
@@ -77,7 +77,7 @@ int sigpause_revert(int signum) {
exit(EXIT_FAILURE);
}
sleep(1);
usleep(100);
if((status = pthread_kill(new_th, signum)) != 0)
{
+1 -1
View File
@@ -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) {
+114 -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,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);
}
+1 -1
View File
@@ -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;
}
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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);
}
}