From 395a56fc36f7c14d2ebf27cee0f4064e679dad41 Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Fri, 24 Jun 2022 00:57:34 +0800 Subject: [PATCH] add CI autotest, update README --- .github/workflows/build-doc.yml | 25 ------- .github/workflows/doc-and-test.yml | 69 ++++++++++++++++++ README.md | 82 +++++++++++++++------ os/Cargo.toml | 2 +- os/Makefile | 5 +- os/src/boards/k210.rs | 19 +++++ os/src/boards/qemu.rs | 88 ++++++++++++++++++++++- os/src/config.rs | 2 +- os/src/mm/memory_set.rs | 14 +++- os/src/task/mod.rs | 25 +++++++ user/Makefile | 8 ++- user/src/bin/usertests-simple.rs | 43 +++++++++++ user/src/bin/usertests.rs | 111 +++++++++++++++++++++++------ 13 files changed, 419 insertions(+), 74 deletions(-) delete mode 100644 .github/workflows/build-doc.yml create mode 100644 .github/workflows/doc-and-test.yml create mode 100644 user/src/bin/usertests-simple.rs diff --git a/.github/workflows/build-doc.yml b/.github/workflows/build-doc.yml deleted file mode 100644 index a48f1474..00000000 --- a/.github/workflows/build-doc.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Build Rust Doc - -on: [push] - -env: - CARGO_TERM_COLOR: always - -jobs: - build-doc: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Build doc - run: | - rustup target add riscv64gc-unknown-none-elf - rustup component add llvm-tools-preview - rustup component add rust-src - cd os - cargo doc --no-deps --verbose - - name: Deploy to Github Pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc - destination_dir: ${{ github.ref_name }} \ No newline at end of file diff --git a/.github/workflows/doc-and-test.yml b/.github/workflows/doc-and-test.yml new file mode 100644 index 00000000..0ddd1d3d --- /dev/null +++ b/.github/workflows/doc-and-test.yml @@ -0,0 +1,69 @@ +name: Build Rust Doc And Run tests + +on: [push] + +env: + CARGO_TERM_COLOR: always + +jobs: + build-doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2022-04-11 + components: rust-src, llvm-tools-preview + target: riscv64gc-unknown-none-elf + - name: Build doc + run: cd os && cargo doc --no-deps --verbose --features "board_qemu" + - name: Deploy to Github Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./os/target/riscv64gc-unknown-none-elf/doc + destination_dir: ${{ github.ref_name }} + + run-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly-2022-04-11 + components: rust-src, llvm-tools-preview + target: riscv64gc-unknown-none-elf + - uses: actions-rs/install@v0.1 + with: + crate: cargo-binutils + version: latest + use-tool-cache: true + - name: Cache QEMU + uses: actions/cache@v3 + with: + path: qemu-7.0.0 + key: qemu-7.0.0-x86_64-riscv64 + - name: Install QEMU + run: | + sudo apt-get update + sudo apt-get install ninja-build -y + if [ ! -d qemu-7.0.0 ]; then + wget https://download.qemu.org/qemu-7.0.0.tar.xz + tar -xf qemu-7.0.0.tar.xz + cd qemu-7.0.0 + ./configure --target-list=riscv64-softmmu + make -j + else + cd qemu-7.0.0 + fi + sudo make install + qemu-system-riscv64 --version + + - name: Run usertests + run: cd os && make run TEST=1 + timeout-minutes: 10 + + - name: Build for k210 + run: cd os && make build BOARD=k210 \ No newline at end of file diff --git a/README.md b/README.md index 5f27d55e..767cb147 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # rCore-Tutorial-v3 -rCore-Tutorial version 3.5. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/). +rCore-Tutorial version 3.6. See the [Documentation in Chinese](https://rcore-os.github.io/rCore-Tutorial-Book-v3/). rCore-Tutorial API Docs. See the [API Docs of Ten OSes ](#OS-API-DOCS) +If you don't know Rust Language and try to learn it, please visit [Rust Learning Resources](https://github.com/rcore-os/rCore/wiki/study-resource-of-system-programming-in-RUST) + Official QQ group number: 735045051 ## news -- 25/01/2022: Version 3.6.0 is on the way! Now we directly update the code on chX branches, please periodically check if there are any updates. +- 23/06/2022: Version 3.6.0 is on the way! Now we directly update the code on chX branches, please periodically check if there are any updates. ## Overview @@ -42,7 +44,7 @@ $ rustup component add rust-src ### Install Qemu -Here we manually compile and install Qemu 5.0.0. For example, on Ubuntu 18.04: +Here we manually compile and install Qemu 7.0.0. For example, on Ubuntu 18.04: ```sh # install dependency packages @@ -50,10 +52,10 @@ $ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev l gawk build-essential bison flex texinfo gperf libtool patchutils bc \ zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 python3-pip # download Qemu source code -$ wget https://download.qemu.org/qemu-5.0.0.tar.xz -# extract to qemu-5.0.0/ -$ tar xvJf qemu-5.0.0.tar.xz -$ cd qemu-5.0.0 +$ wget https://download.qemu.org/qemu-7.0.0.tar.xz +# extract to qemu-7.0.0/ +$ tar xvJf qemu-7.0.0.tar.xz +$ cd qemu-7.0.0 # build $ ./configure --target-list=riscv64-softmmu,riscv64-linux-user $ make -j$(nproc) @@ -62,9 +64,9 @@ $ make -j$(nproc) Then, add following contents to `~/.bashrc`(please adjust these paths according to your environment): ``` -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0 -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0/riscv64-softmmu -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-5.0.0/riscv64-linux-user +export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0 +export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0/riscv64-softmmu +export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0/riscv64-linux-user ``` Finally, update the current shell: @@ -77,7 +79,7 @@ Now we can check the version of Qemu: ```sh $ qemu-system-riscv64 --version -QEMU emulator version 5.0.0 +QEMU emulator version 7.0.0 Copyright (c) 2003-2020 Fabrice Bellard and the QEMU Project developers ``` @@ -188,6 +190,46 @@ $ make run BOARD=k210 Type `Ctrl+]` to disconnect from K210. + +## Show runtime debug info of OS kernel version +The branch of ch9-log contains a lot of debug info. You could try to run rcore tutorial +for understand the internal behavior of os kernel. + +```sh +$ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git +$ cd rCore-Tutorial-v3/os +$ git checkout ch9-log +$ make run +...... +[rustsbi] RustSBI version 0.2.0-alpha.10, adapting to RISC-V SBI v0.3 +.______ __ __ _______.___________. _______..______ __ +| _ \ | | | | / | | / || _ \ | | +| |_) | | | | | | (----`---| |----`| (----`| |_) || | +| / | | | | \ \ | | \ \ | _ < | | +| |\ \----.| `--' |.----) | | | .----) | | |_) || | +| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__| + +[rustsbi] Implementation: RustSBI-QEMU Version 0.0.2 +[rustsbi-dtb] Hart count: cluster0 with 1 cores +[rustsbi] misa: RV64ACDFIMSU +[rustsbi] mideleg: ssoft, stimer, sext (0x222) +[rustsbi] medeleg: ima, ia, bkpt, la, sa, uecall, ipage, lpage, spage (0xb1ab) +[rustsbi] pmp0: 0x10000000 ..= 0x10001fff (rw-) +[rustsbi] pmp1: 0x2000000 ..= 0x200ffff (rw-) +[rustsbi] pmp2: 0xc000000 ..= 0xc3fffff (rw-) +[rustsbi] pmp3: 0x80000000 ..= 0x8fffffff (rwx) +[rustsbi] enter supervisor 0x80200000 +[KERN] rust_main() begin +[KERN] clear_bss() begin +[KERN] clear_bss() end +[KERN] mm::init() begin +[KERN] mm::init_heap() begin +[KERN] mm::init_heap() end +[KERN] mm::init_frame_allocator() begin +[KERN] mm::frame_allocator::lazy_static!FRAME_ALLOCATOR begin +...... +``` + ## Rustdoc Currently it can only help you view the code since only a tiny part of the code has been documented. @@ -209,13 +251,9 @@ The API Docs for Ten OS ## Working in progress -Our first release 3.5.0 (chapter 1-7) has been published. +Our first release 3.6.0 (chapter 1-9) has been published, and we are still working on it. -There will be 9 chapters in our next release 3.6.0, where 2 new chapters will be added: -* chapter 8: synchronization on a uniprocessor -* chapter 9: I/O devices - -Current version is 3.6.0-alpha.1 and we are still working on it. +* chapter 9: need more descripts about different I/O devices Here are the updates since 3.5.0: @@ -237,18 +275,16 @@ Here are the updates since 3.5.0: * [x] switch the code of chapter 6 and chapter 7 * [x] support signal mechanism in chapter 7/8(only works for apps with a single thread) * [x] Add boards/ directory and support rustdoc, for example you can use `cargo doc --no-deps --open` to view the documentation of a crate - +* [x] code of chapter 9: device drivers based on interrupts, including UART, block, keyboard, mouse, gpu devices +* [x] add CI autotest and doc in github ### Todo(High priority) -* [ ] review documentation, current progress: 5/9 -* [ ] support user-level sync primitives in chapter 8 -* [ ] code of chapter 9: device drivers based on interrupts, including UART and block devices +* [ ] review documentation, current progress: 8/9 * [ ] use old fs image optionally, do not always rebuild the image -* [ ] add new system calls: getdents64/fstat * [ ] shell functionality improvement(to be continued...) * [ ] give every non-zero process exit code an unique and clear error type * [ ] effective error handling of mm module - +* [ ] add more os functions for understanding os conecpts and principles ### Todo(Low priority) * [ ] rewrite practice doc and remove some inproper questions diff --git a/os/Cargo.toml b/os/Cargo.toml index c690db67..d3f3f5b7 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -2,7 +2,7 @@ name = "os" version = "0.1.0" authors = ["Yifan Wu "] -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/os/Makefile b/os/Makefile index b972600b..d9b69196 100644 --- a/os/Makefile +++ b/os/Makefile @@ -34,6 +34,9 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64 # Disassembly DISASM ?= -x +# Run usertests or usershell +TEST ?= + build: env switch-check $(KERNEL_BIN) switch-check: @@ -53,7 +56,7 @@ $(KERNEL_BIN): kernel @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ kernel: - @cd ../user && make build + @cd ../user && make build TEST=$(TEST) @echo Platform: $(BOARD) @cp src/linker-$(BOARD).ld src/linker.ld @cargo build --release --features "board_$(BOARD)" diff --git a/os/src/boards/k210.rs b/os/src/boards/k210.rs index 5e7e047e..35b1c51b 100644 --- a/os/src/boards/k210.rs +++ b/os/src/boards/k210.rs @@ -1,2 +1,21 @@ pub const CLOCK_FREQ: usize = 403000000 / 62; pub const MEMORY_END: usize = 0x80600000; + +pub const MMIO: &[(usize, usize)] = &[ + // we don't need clint in S priv when running + // we only need claim/complete for target0 after initializing + (0x0C00_0000, 0x3000), /* PLIC */ + (0x0C20_0000, 0x1000), /* PLIC */ + (0x3800_0000, 0x1000), /* UARTHS */ + (0x3800_1000, 0x1000), /* GPIOHS */ + (0x5020_0000, 0x1000), /* GPIO */ + (0x5024_0000, 0x1000), /* SPI_SLAVE */ + (0x502B_0000, 0x1000), /* FPIOA */ + (0x502D_0000, 0x1000), /* TIMER0 */ + (0x502E_0000, 0x1000), /* TIMER1 */ + (0x502F_0000, 0x1000), /* TIMER2 */ + (0x5044_0000, 0x1000), /* SYSCTL */ + (0x5200_0000, 0x1000), /* SPI0 */ + (0x5300_0000, 0x1000), /* SPI1 */ + (0x5400_0000, 0x1000), /* SPI2 */ +]; \ No newline at end of file diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index a00a6835..1f3ee00e 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -1,2 +1,88 @@ +//! Constants used in rCore for qemu + pub const CLOCK_FREQ: usize = 12500000; -pub const MEMORY_END: usize = 0x80800000; +pub const MEMORY_END: usize = 0x81000000; + +pub const MMIO: &[(usize, usize)] = &[ + (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine +]; + +//ref:: https://github.com/andre-richter/qemu-exit +use core::arch::asm; + +const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit + +const EXIT_FAILURE_FLAG: u32 = 0x3333; +const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit +const EXIT_RESET: u32 = 0x7777; // qemu reset + +pub trait QEMUExit { + /// Exit with specified return code. + /// + /// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU. + fn exit(&self, code: u32) -> !; + + /// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible. + /// + /// Note: Not possible for `X86`. + fn exit_success(&self) -> !; + + /// Exit QEMU using `EXIT_FAILURE`, aka `1`. + fn exit_failure(&self) -> !; +} + +/// RISCV64 configuration +pub struct RISCV64 { + /// Address of the sifive_test mapped device. + addr: u64, +} + +/// Encode the exit code using EXIT_FAILURE_FLAG. +const fn exit_code_encode(code: u32) -> u32 { + (code << 16) | EXIT_FAILURE_FLAG +} + +impl RISCV64 { + /// Create an instance. + pub const fn new(addr: u64) -> Self { + RISCV64 { addr } + } +} + +impl QEMUExit for RISCV64 { + /// Exit qemu with specified exit code. + fn exit(&self, code: u32) -> ! { + // If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG. + let code_new = match code { + EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code, + _ => exit_code_encode(code), + }; + + unsafe { + asm!( + "sw {0}, 0({1})", + in(reg)code_new, in(reg)self.addr + ); + + // For the case that the QEMU exit attempt did not work, transition into an infinite + // loop. Calling `panic!()` here is unfeasible, since there is a good chance + // this function here is the last expression in the `panic!()` handler + // itself. This prevents a possible infinite loop. + loop { + asm!("wfi", options(nomem, nostack)); + } + } + } + + fn exit_success(&self) -> ! { + self.exit(EXIT_SUCCESS); + } + + fn exit_failure(&self) -> ! { + self.exit(EXIT_FAILURE); + } +} + +const VIRT_TEST: u64 = 0x100000; + +pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST); diff --git a/os/src/config.rs b/os/src/config.rs index 8d6d0458..03b0495e 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -9,4 +9,4 @@ pub const PAGE_SIZE_BITS: usize = 0xc; pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; -pub use crate::board::{CLOCK_FREQ, MEMORY_END}; +pub use crate::board::{CLOCK_FREQ, MEMORY_END, MMIO}; diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 917bb655..d6551bbe 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -3,7 +3,7 @@ use super::{frame_alloc, FrameTracker}; use super::{PTEFlags, PageTable, PageTableEntry}; use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; use super::{StepByOne, VPNRange}; -use crate::config::{MEMORY_END, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, USER_STACK_SIZE}; +use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, USER_STACK_SIZE}; use crate::sync::UPSafeCell; use alloc::collections::BTreeMap; use alloc::sync::Arc; @@ -150,6 +150,18 @@ impl MemorySet { ), None, ); + println!("mapping memory-mapped registers"); + for pair in MMIO { + memory_set.push( + MapArea::new( + (*pair).0.into(), + ((*pair).0 + (*pair).1).into(), + MapType::Identical, + MapPermission::R | MapPermission::W, + ), + None, + ); + } memory_set } /// Include sections in elf and trampoline and TrapContext and user stack, diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 483f62f8..39680b06 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -54,10 +54,35 @@ pub fn suspend_current_and_run_next() { // jump to scheduling cycle schedule(task_cx_ptr); } + +/// pid of usertests app in make run TEST=1 +pub const IDLE_PID: usize = 0; + +#[cfg(feature = "board_qemu")] +use crate::board::QEMUExit; + /// Exit the current 'Running' task and run the next task in task list. pub fn exit_current_and_run_next(exit_code: i32) { // take from Processor let task = take_current_task().unwrap(); + + #[cfg(feature = "board_qemu")] + let pid = task.getpid(); + #[cfg(feature = "board_qemu")] + if pid == IDLE_PID { + println!( + "[kernel] Idle process exit with exit_code {} ...", + exit_code + ); + if exit_code != 0 { + //crate::sbi::shutdown(255); //255 == -1 for err hint + crate::board::QEMU_EXIT_HANDLE.exit_failure(); + } else { + //crate::sbi::shutdown(0); //0 for success hint + crate::board::QEMU_EXIT_HANDLE.exit_success(); + } + } + // **** access current TCB exclusively let mut inner = task.inner_exclusive_access(); // Change status to Zombie diff --git a/user/Makefile b/user/Makefile index 5e0f87c3..c09b5e96 100644 --- a/user/Makefile +++ b/user/Makefile @@ -8,9 +8,15 @@ BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS)) OBJDUMP := rust-objdump --arch-name=riscv64 OBJCOPY := rust-objcopy --binary-architecture=riscv64 +CP := cp + +TEST ?= elf: $(APPS) @cargo build --release +ifeq ($(TEST), 1) + @$(CP) $(TARGET_DIR)/usertests $(TARGET_DIR)/initproc +endif binary: elf $(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));) @@ -20,4 +26,4 @@ build: binary clean: @cargo clean -.PHONY: elf binary build clean \ No newline at end of file +.PHONY: elf binary build clean diff --git a/user/src/bin/usertests-simple.rs b/user/src/bin/usertests-simple.rs new file mode 100644 index 00000000..62bc8388 --- /dev/null +++ b/user/src/bin/usertests-simple.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +static TESTS: &[&str] = &[ + "exit\0", + "fantastic_text\0", + "forktest\0", + "forktest2\0", + "forktest_simple\0", + "hello_world\0", + "matrix\0", + "sleep\0", + "sleep_simple\0", + "stack_overflow\0", + "yield\0", +]; + +use user_lib::{exec, fork, waitpid}; + +#[no_mangle] +pub fn main() -> i32 { + for test in TESTS { + println!("Usertests: Running {}", test); + let pid = fork(); + if pid == 0 { + exec(*test); + panic!("unreachable!"); + } else { + let mut exit_code: i32 = Default::default(); + let wait_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, wait_pid); + println!( + "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", + test, pid, exit_code + ); + } + } + println!("Usertests passed!"); + 0 +} diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs index 62bc8388..c60b6464 100644 --- a/user/src/bin/usertests.rs +++ b/user/src/bin/usertests.rs @@ -4,40 +4,111 @@ #[macro_use] extern crate user_lib; -static TESTS: &[&str] = &[ - "exit\0", - "fantastic_text\0", - "forktest\0", - "forktest2\0", - "forktest_simple\0", - "hello_world\0", - "matrix\0", - "sleep\0", - "sleep_simple\0", - "stack_overflow\0", - "yield\0", +// not in SUCC_TESTS & FAIL_TESTS +// count_lines, infloop, user_shell, usertests + +// item of TESTS : app_name(argv_0), argv_1, argv_2, argv_3, exit_code +static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[ + ("exit\0", "\0", "\0", "\0", 0), + ("fantastic_text\0", "\0", "\0", "\0", 0), + ("forktest_simple\0", "\0", "\0", "\0", 0), + ("forktest\0", "\0", "\0", "\0", 0), + ("forktest2\0", "\0", "\0", "\0", 0), + ("forktree\0", "\0", "\0", "\0", 0), + ("hello_world\0", "\0", "\0", "\0", 0), + ("matrix\0", "\0", "\0", "\0", 0), + ("sleep_simple\0", "\0", "\0", "\0", 0), + ("sleep\0", "\0", "\0", "\0", 0), + ("yield\0", "\0", "\0", "\0", 0), +]; + +static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[ + ("stack_overflow\0", "\0", "\0", "\0", -2), ]; use user_lib::{exec, fork, waitpid}; -#[no_mangle] -pub fn main() -> i32 { - for test in TESTS { - println!("Usertests: Running {}", test); +fn run_tests(tests: &[(&str, &str, &str, &str, i32)]) -> i32 { + let mut pass_num = 0; + let mut arr: [*const u8; 4] = [ + core::ptr::null::(), + core::ptr::null::(), + core::ptr::null::(), + core::ptr::null::(), + ]; + + for test in tests { + println!("Usertests: Running {}", test.0); + arr[0] = test.0.as_ptr(); + if test.1 != "\0" { + arr[1] = test.1.as_ptr(); + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + if test.2 != "\0" { + arr[2] = test.2.as_ptr(); + arr[3] = core::ptr::null::(); + if test.3 != "\0" { + arr[3] = test.3.as_ptr(); + } else { + arr[3] = core::ptr::null::(); + } + } else { + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + } + } else { + arr[1] = core::ptr::null::(); + arr[2] = core::ptr::null::(); + arr[3] = core::ptr::null::(); + } + let pid = fork(); if pid == 0 { - exec(*test); + exec(test.0); panic!("unreachable!"); } else { let mut exit_code: i32 = Default::default(); let wait_pid = waitpid(pid as usize, &mut exit_code); assert_eq!(pid, wait_pid); + if exit_code == test.4 { + // summary apps with exit_code + pass_num = pass_num + 1; + } println!( "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", - test, pid, exit_code + test.0, pid, exit_code ); } } - println!("Usertests passed!"); - 0 + pass_num +} + +#[no_mangle] +pub fn main() -> i32 { + let succ_num = run_tests(SUCC_TESTS); + let err_num = run_tests(FAIL_TESTS); + if succ_num == SUCC_TESTS.len() as i32 && err_num == FAIL_TESTS.len() as i32 { + println!( + "{} of sueecssed apps, {} of failed apps run correctly. \nUsertests passed!", + SUCC_TESTS.len(), + FAIL_TESTS.len() + ); + return 0; + } + if succ_num != SUCC_TESTS.len() as i32 { + println!( + "all successed app_num is {} , but only passed {}", + SUCC_TESTS.len(), + succ_num + ); + } + if err_num != FAIL_TESTS.len() as i32 { + println!( + "all failed app_num is {} , but only passed {}", + FAIL_TESTS.len(), + err_num + ); + } + println!(" Usertests failed!"); + return -1; } -- GitLab