Merge branch 'doc-test' into 'master'

Enhance single test run, document check.sh

See merge request redox-os/relibc!862
This commit is contained in:
Jeremy Soller
2026-01-05 06:20:52 -07:00
17 changed files with 92 additions and 68 deletions
+1 -1
View File
@@ -66,6 +66,6 @@ test:aarch64:
needs: [aarch64]
image: "redoxos/redoxer:aarch64"
script:
- ./check.sh --arch=aarch64 --test
- timeout -s KILL 9m ./check.sh --arch=aarch64 --test
#TODO: Enable more arch once dynamic linker working
+13 -35
View File
@@ -108,44 +108,22 @@ Note: Do not edit `relibc` inside `prefix` folder! Do your work on `relibc` fold
## Tests
This section explain how to build and run the tests.
Relibc has a test suite that also runs every time a new commit get pushed. You can see `.gitlab-ci.yml` to see how it's being executed. That being said, `./check.sh` is the recommended way to run tests. Here's few examples:
### Build
+ `./check.sh` - Run build, without running the test
+ `./check.sh --test` - Run all tests in x86_64 Redox using Redoxer
+ `./check.sh --test --host` - Run all tests in host (Linux)
+ `./check.sh --test --arch=aarch64` - Run all tests in specified arch
- Arch can be `x86_64`, `aarch64`, `i586`, or `riscv64gc`
+ `./check.sh --test=stdio/printf` - Run a single test
- Can be combined with `--host` or `--arch`
- Will run statically linked test in Linux, dynamically linked in Redox
To build the tests run `make all` on the `tests` folder, it will store the executables at `tests/bins_static`
Couple of notes:
If you did changes to your tests, run `make clean all` to rebuild the executables.
### Redox OS Testing
To test on Redox do the following steps:
- Add the `relibc-tests` recipe on your filesystem configuration at `config/your-cpu/your-config.toml` (generally `desktop.toml`)
- Run the following commands to rebuild relibc with your changes, update the `relibc-tests` recipe and update your QEMU image:
```sh
touch relibc
```
```sh
make prefix cr.relibc-tests image
```
- Run the tests
```sh
/usr/share/relibc-tests/bins_static/test-name
```
### Linux Testing
Run `make test` on the relibc directory.
If you want to run one test, run the following command:
```sh
tests/bins_static/test-name
```
- Relibc and its tests will rebuild if files changed, however switching between arch or host requires you to run `make clean`
- Redoxer is needed to run tests for Redox. You can install it using `cargo install redoxer`
- Tests can hangs, the test runner can anticipate this, assuming the kernel doesn't hang too.
## Issues
+1 -1
View File
@@ -9,7 +9,7 @@
void *routine(void *arg) {
assert(arg == NULL);
usleep(100);
usleep(100000);
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.");
usleep(100);
usleep(100000);
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");
usleep(100);
usleep(100000);
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!
usleep(100);
usleep(100000);
int result = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
ERROR_IF(ptrace, result, == -1);
+2 -2
View File
@@ -36,7 +36,7 @@ void child_proc(int signum)
sigemptyset(&act.sa_mask);
sigaction(signum, &act, NULL);
status = usleep(200);
status = usleep(200000);
ERROR_IF(usleep, status, == 0);
assert(sig_handled != 0);
@@ -48,7 +48,7 @@ void parent(int signum, pid_t pid)
{
int status;
usleep(100);
usleep(100000);
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);
usleep(100);
usleep(100000);
child_pgid = getpgid(child_pid);
ERROR_IF(getpgid, child_pgid, == -1);
+4 -4
View File
@@ -38,7 +38,7 @@ void *a_thread_func( void *arg)
sem1=INMAIN;
while(sem1==INMAIN)
usleep(100);
usleep(100000);
// sleep(50);
@@ -61,16 +61,16 @@ int pthread_kill_test1(int signum)
ERROR_IF(pthread_create, status, != 0);
while(sem1==INTHREAD)
usleep(100);
usleep(100000);
status = pthread_kill(new_th, signum);
ERROR_IF(pthread_kill, status, != 0);
usleep(200);
usleep(100000);
sem1=INTHREAD;
while(handler_called==0)
usleep(100);
usleep(100000);
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);
usleep(100);
usleep(100000);
status = pthread_kill(new_th, signum);
ERROR_IF(pthread_kill, status, != 0);
sem = INTHREAD;
while (sem == INTHREAD)
usleep(100);
usleep(100000);
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);
usleep(100);
usleep(100000);
status = pthread_kill(new_th, signum);
ERROR_IF(pthread_kill, status, != 0);
usleep(100);
usleep(100000);
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);
}
usleep(100);
usleep(100000);
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;
}
usleep(100);
usleep(100000);
assert(!atomic_load(&completed));
if ((status = pthread_kill(new_th, signum)) != 0) {
+53 -7
View File
@@ -1,3 +1,5 @@
#![feature(exit_status_error)]
use std::{
env, fs,
io::{self, Read, Write},
@@ -48,12 +50,28 @@ fn expected(bin: &str, kind: &str, generated: &[u8], status: ExitStatus) -> Resu
const STATUS_ONLY: &str = "-s";
fn print_tabbed(output: Vec<u8>, name: &str) {
if let Ok(stdout) = String::from_utf8(output) {
let stdout: Vec<String> = stdout.trim().lines().map(|p| format!(" {}", p)).collect();
let stdout = stdout.join("\n");
if stdout.as_str() == "" {
println!("{name}: empty content");
} else {
println!("{name}:\n{}", stdout);
}
} else {
println!("can't print out {name}: not utf8");
}
}
fn main() {
let mut failures = Vec::new();
let timeout = Duration::from_secs(10);
let slowtime = Duration::from_secs(1);
let bins: Vec<String> = env::args().skip(1).collect();
let single_test = bins.len() == 1;
for bin in env::args().skip(1) {
for bin in bins {
let status_only = bin.starts_with(STATUS_ONLY);
let bin = if bin.starts_with(STATUS_ONLY) {
bin.strip_prefix(STATUS_ONLY).unwrap().to_string()
@@ -152,13 +170,30 @@ fn main() {
}
match (status, child) {
(Some(exit_status), Some(child)) => {
if !status_only {
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();
(exit_status, Some(mut child)) => {
if exit_status.is_none() {
match child.kill() {
Ok(_) => {}
Err(e) => {
// if can't be killed then getting the output will hang
println!("Unable to kill, can't get output: {:?}", e);
continue;
}
}
}
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();
let Some(exit_status) = exit_status else {
// hangs
print_tabbed(stdout, "stdout");
print_tabbed(stderr, "stderr");
continue;
};
if !status_only {
if let Err(failure) = expected(&bin, "stdout", &stdout, exit_status) {
println!("{}", failure);
failures.push(failure);
@@ -168,6 +203,17 @@ fn main() {
failures.push(failure);
}
}
if let Err(e) = exit_status.exit_ok() {
let failure = format!("# {}: {}", bin, e);
println!("{}", failure);
failures.push(failure);
}
if single_test {
print_tabbed(stdout, "stdout");
print_tabbed(stderr, "stderr");
}
}
(_, _) => {
continue;
+1 -1
View File
@@ -32,7 +32,7 @@ int main(void)
// exit(EXIT_FAILURE);
// }
int us_status = usleep(100);
int us_status = usleep(100000);
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
usleep(100);
usleep(100000);
_Exit(code);
}
printf("Testing waitpid of child %d for code %d\n", pid, code);
+6 -6
View File
@@ -1,3 +1,4 @@
#include <stdio.h>
#include <assert.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -6,16 +7,15 @@
#include "test_helpers.h"
int main(void) {
// Spawn two children, one with same pgid and one with pgid set to its own
// pid, so that the one with different pgid completes first. Then test
// waitpid both for 'any child' and for 'any child with same pgid'.
// Spawn three childrens, one with same pgid and two with pgid set to its own
// pid, so the exit order is two different pgid, then followed with same pgid
pid_t pid_samepgid = fork();
ERROR_IF(fork, pid_samepgid, == -1);
if (pid_samepgid == 0) {
// child
usleep(200);
usleep(300000);
_Exit(2);
}
pid_t pid_diffpgids[2];
@@ -28,7 +28,7 @@ int main(void) {
ERROR_IF(setpgid, ret, == -1);
// child
usleep(100);
usleep((i + 1) * 100000);
_Exit(i);
}
}
@@ -49,7 +49,7 @@ int main(void) {
assert(WIFEXITED(status));
assert(WEXITSTATUS(status) == 2);
// Finally, the last same-pgid must have completed.
// Finally, the last different-pgid must have completed.
wid = waitpid(-1, &status, WNOHANG);
ERROR_IF(waitpid, wid, == -1);
assert(wid == pid_diffpgids[1]);