Merge #167 167: Makefile: Support dumping stack size usage r=alistair23 a=alistair23 I have lots of issues of running out of stack, let's add a Makefile option to print stack usage. Signed-off-by: Alistair Francis <alistair.francis@wdc.com> Co-authored-by: Alistair Francis <alistair.francis@wdc.com>
diff --git a/.travis.yml b/.travis.yml index 7b6bf87..8efe4b7 100644 --- a/.travis.yml +++ b/.travis.yml
@@ -16,10 +16,10 @@ # Once Travis supports a version of Ubuntu Disco or newer we can apt install QEMU for RISC-V # Until then we need to build it ourselves before_install: -# - sudo apt-get -y install qemu-system-misc -# addons: -# apt: -# update: true + # - sudo apt-get -y install qemu-system-misc + # addons: + # apt: + # update: true - wget https://download.qemu.org/qemu-4.2.0.tar.xz - tar xJf qemu-4.2.0.tar.xz - pushd qemu-4.2.0 @@ -42,6 +42,7 @@ script: - make test # Run a QEMU instance of the HiFive1 app - - make flash-hifive1 EXAMPLE=hello_world - - timeout --foreground 10s qemu-system-riscv32 -M sifive_e -kernel ../tock/boards/hifive1/target/riscv32imac-unknown-none-elf/release/hifive1 -device loader,file=./target/riscv32imac-unknown-none-elf/tab/hifive1/hello_world/rv32imac.tbf,addr=0x20430000 -nographic | tee serial - - grep "Hello Tock World" serial + - PLATFORM=hifive1 cargo rrv32imac --example libtock_test --features=alloc --features=__internal_disable_gpio_in_integration_test + - pushd test-runner + - cargo run + - popd
diff --git a/Cargo.toml b/Cargo.toml index 5641d3c..eb56daa 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -9,6 +9,7 @@ alloc = ["libtock-core/alloc"] custom_panic_handler = ["libtock-core/custom_panic_handler"] custom_alloc_error_handler = ["libtock-core/custom_alloc_error_handler"] +__internal_disable_gpio_in_integration_test = [] [dependencies] core = { package = "async-support", path = "async-support" } @@ -59,5 +60,6 @@ members = [ "async-support", "codegen", - "core" + "core", + "test-runner" ]
diff --git a/examples-features/libtock_test.rs b/examples-features/libtock_test.rs index 26cccb4..5fe9e59 100644 --- a/examples-features/libtock_test.rs +++ b/examples-features/libtock_test.rs
@@ -2,7 +2,6 @@ // Requires P0.03 and P0.04 to be connected (on a nRF52 DK). #![no_std] - extern crate alloc; use alloc::string::String; @@ -36,6 +35,10 @@ } } +#[cfg_attr( + feature = "__internal_disable_gpio_in_integration_test", + allow(unused_variables) +)] async fn libtock_test( test: &mut LibtockTest, timer: &mut DriverContext, @@ -48,6 +51,7 @@ test.heap()?; test.drivers_only_instantiable_once()?; test.callbacks(timer).await?; + #[cfg(not(feature = "__internal_disable_gpio_in_integration_test"))] test.gpio(gpio)?; Ok(()) } @@ -113,7 +117,7 @@ let mut with_callback = timer_context.with_callback(|_, _| callback_hit = true); let mut timer = with_callback.init()?; - timer.set_alarm(Duration::from_ms(50))?; + timer.set_alarm(Duration::from_ms(1000))?; AlternatingFuture { yielded: false }.await; @@ -122,6 +126,10 @@ self.check_if_true(callback_hit, "Callbacks") } + #[cfg_attr( + feature = "__internal_disable_gpio_in_integration_test", + allow(dead_code) + )] fn gpio(&mut self, gpio: &mut GpioDriverFactory) -> TockResult<()> { let mut gpio_driver = gpio.init_driver().ok().unwrap();
diff --git a/test-runner/Cargo.toml b/test-runner/Cargo.toml new file mode 100644 index 0000000..b9769b9 --- /dev/null +++ b/test-runner/Cargo.toml
@@ -0,0 +1,19 @@ +[package] +name = "test-runner" +version = "0.1.0" +authors = ["torfmaster <briefe@kebes.de>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +structopt = { version = "0.3", default-features = false } +futures = "0.3.4" + +[dependencies.async-std] +version = "1.5.0" +features = ["attributes"] + +[dependencies.tokio] +version = "0.2.12" +features = ["process", "rt-threaded", "macros", "io-util", "time"]
diff --git a/test-runner/src/main.rs b/test-runner/src/main.rs new file mode 100644 index 0000000..0ee3455 --- /dev/null +++ b/test-runner/src/main.rs
@@ -0,0 +1,129 @@ +use std::fmt; +use std::process::Stdio; +use std::time::Duration; +use tokio::io::AsyncBufReadExt; +use tokio::io::BufReader; +use tokio::process::Command; +use tokio::time::timeout; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + timeout(Duration::from_secs(10), perform_tests()).await? +} + +async fn perform_tests() -> Result<(), Box<dyn std::error::Error>> { + let mut failed_tests = Vec::new(); + + let tests = Command::new("qemu-system-riscv32") + .arg("-M") + .arg("sifive_e") + .arg("-kernel") + .arg("../../tock/boards/hifive1/target/riscv32imac-unknown-none-elf/release/hifive1") + .arg("-device") + .arg("loader,file=./../target/riscv32imac-unknown-none-elf/tab/hifive1/libtock_test/rv32imac.tbf,addr=0x20430000") + .arg("-nographic") + .stdout(Stdio::piped()) + .kill_on_drop(true) + .spawn()?; + + let stdout = tests.stdout.unwrap(); + + let stdout_reader = BufReader::new(stdout); + let mut stdout_lines = stdout_reader.lines(); + + while let Some(line) = stdout_lines.next_line().await? { + println!("UART: {}", line); + let test_result = test_succeeded(line, &mut failed_tests); + if let Some(true) = test_result { + return Ok(()); + } + if let Some(false) = test_result { + return Err(Box::new(TestError::TestFailure(failed_tests))); + } + } + Err(Box::new(TestError::QemuExit)) +} + +fn test_succeeded(input: String, failed_tests: &mut Vec<String>) -> Option<bool> { + let success = input.find("[ OK ]").is_some(); + let failure = input.find("[ FAILURE ]").is_some(); + let input = input.replace("[ OK ]", ""); + let input = input.replace("[ FAILURE ]", ""); + let input = input.trim(); + if input == "Test suite finished with state SUCCESS" && success { + return Some(true); + } else if input == "Test suite finished with state FAILURE" && !success { + return Some(false); + } else if failure { + failed_tests.push(input.to_string()); + } + None +} + +#[derive(Debug)] +enum TestError { + TestFailure(Vec<String>), + QemuExit, +} + +impl fmt::Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TestError::TestFailure(failures) => { + writeln!(f, "A test failure occured. Failed Tests")?; + for test in failures { + writeln!(f, "Test failed\"{}\"", test)?; + } + Ok(()) + } + TestError::QemuExit => write!(f, "Qemu exited unexpectedly."), + } + } +} + +impl std::error::Error for TestError {} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn detects_success_of_test_suite() { + let mut test_results = Vec::new(); + assert_eq!( + test_succeeded( + "[ OK ] Test suite finished with state SUCCESS".into(), + &mut test_results + ), + Some(true) + ); + } + + #[test] + pub fn detects_failure_of_test_suite() { + let mut test_results = Vec::new(); + assert_eq!( + test_succeeded( + "[ FAILURE ] Test suite finished with state FAILURE".into(), + &mut test_results + ), + Some(false) + ); + } + + #[test] + pub fn detects_test_failures() { + let mut test_results = Vec::new(); + test_succeeded("[ FAILURE ] Another test".into(), &mut test_results); + assert_eq!(test_results, vec!["Another test"]); + } + + #[test] + pub fn ignores_other_tests() { + let mut test_results = Vec::new(); + assert_eq!( + test_succeeded("[ SUCCESS ] Another test".into(), &mut test_results), + None + ); + } +}