Skip to content

Commit

Permalink
Merge pull request #27 from hashmismatch/tests_staticlib_examples
Browse files Browse the repository at this point in the history
Ported tests to compile into static libraries using examples
  • Loading branch information
rudib authored Jun 16, 2019
2 parents 6f7cf41 + d9085d2 commit d52f8b8
Show file tree
Hide file tree
Showing 31 changed files with 1,024 additions and 893 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ dist: trusty

language: rust
rust:
- beta
- nightly

cache:
directories:
Expand All @@ -21,8 +21,8 @@ install:
- curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION
- source ~/.cargo/env
- export QEMU_ARCHIVE=$HOME/qemueclipse.tgz
- export QEMU_URL=https://github.com/gnu-mcu-eclipse/qemu/releases/download/v2.8.0-6-20190517/gnu-mcu-eclipse-qemu-2.8.0-6-20190517-1329-centos64.tgz
- export QEMU_DIR=$HOME/gnu-mcu-eclipse/qemu/2.8.0-6-20190517-1329
- export QEMU_URL=https://github.com/gnu-mcu-eclipse/qemu/releases/download/gae-2.7.0-20161128/gnuarmeclipse-qemu-debian64-2.7.0-201611282115-dev.tgz
- export QEMU_DIR=$HOME/qemu/2.7.0-201611282115-dev
- if [ ! -e $QEMU_DIR/bin/qemu-system-gnuarmeclipse ]; then wget $QEMU_URL -O $QEMU_ARCHIVE && tar xzf $QEMU_ARCHIVE -C $HOME ; fi
- export PATH=$PATH:$QEMU_DIR/bin:$HOME/.cargo/bin
- rustup target add thumbv7m-none-eabi
Expand Down
161 changes: 84 additions & 77 deletions qemu_runner/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,32 @@ use std::path::Path;
use std::process::{Command, Stdio};
use std::ffi::OsString;

fn is_cargo(path: &str) -> bool {
let output = Command::new(path).arg("-V").output();
if let Ok(output) = Command::new(path).arg("-V").output() {
let s = String::from_utf8_lossy(&output.stdout);
if s.contains("cargo") {
return true;
}
}

false
}

fn find_cargo_path() -> Option<String> {
let mut p: Vec<String> = vec!["cargo".to_string()];
if let Some(home) = env::home_dir() {
p.push(format!("{}/.cargo/bin/cargo", home.display()));
}

for path in p {
if is_cargo(&path) {
return Some(path.into());
}
}

None
}

#[derive(Clone, Debug)]
pub struct FoundFile {
Expand Down Expand Up @@ -47,104 +73,76 @@ pub struct CrossbuildOptions {
#[derive(Debug)]
pub struct CrossbuiltTests {
pub object_paths: Vec<String>,
pub tests: Vec<String>
pub tests: Vec<String>,
pub library_path: String
}

pub fn crossbuild_rust_tests(options: &CrossbuildOptions) -> CrossbuiltTests {

// check if we can find cargo for cross building
let cargo_path = find_cargo_path();
let cargo_path = cargo_path.expect("Cargo not found! Install Rust's package manager.");

let build_proj_root = {
let p = Path::new(&options.tests_project_path);
let mut absolute_path = ::std::env::current_dir().expect("Can't find current dir?");
absolute_path.push(p);
p.canonicalize().expect("Error canonicalizing")
};

// cross-build the tests library
let cargo_build = Command::new("cargo")
.current_dir(&options.tests_project_path)
.arg("build")
.arg("--verbose")
.arg("--target")
.arg(&options.target_arch)
.env("CARGO_INCREMENTAL", "")
.env("RUSTFLAGS", "--emit=obj")
.env("RUST_TARGET_PATH", &build_proj_root.to_str().expect("Missing path to proj root for target path?"))
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output();

let output = cargo_build.expect("Cargo build of the tests projects failed");
if !output.status.success() {
panic!("cargo build failed");
}

// grab the list of tests to compile binaries for
let tests = {
// slightly hackish way that requires each test entrypoint to be in its
// own source file with a matching name

let dir = format!("{}/src/", &options.tests_project_path);
let tests: Vec<_> = {
let dir = format!("{}/examples/", &options.tests_project_path);
let tests = find_files(&dir, |n| {
n.starts_with("test_") && n.ends_with(".rs")
}).iter().cloned().map(|f| f.name).map(|n| { n.replace(".rs", "") }).collect();

tests
};

let object_paths = {
let active_toolchain: String = {
let output = Command::new("rustup")
.arg("show")
.arg("active-toolchain")
.stderr(Stdio::inherit())
.output()
.expect("Can't get a current toolchain");

let active_toolchain = String::from_utf8_lossy(&output.stdout);
let mut split = active_toolchain.split_whitespace();
split.next().expect("active toolchain missing").trim().to_owned()
};

let rustup_sysroot = {
let home = env::home_dir().expect("missing profile home dir");
format!("{}/.rustup/toolchains/{}/lib/rustlib/{}/lib/",
home.to_str().unwrap(), active_toolchain, &options.target_arch)
};

let mut sysroot_rlibs: Vec<FoundFile> = find_files(&rustup_sysroot, |n| {
n.ends_with(".rlib")
}).iter().cloned().collect();

let tests_deps_dir = format!("{}/target/{}/debug/deps/", &options.tests_project_path, &options.target_arch);

for sysroot_rlib in sysroot_rlibs {
copy(sysroot_rlib.absolute_path, format!("{}/{}.o", tests_deps_dir, sysroot_rlib.name.trim_right_matches(".rlib")));
let mut built_tests = vec![];

for test in &tests {
// cross-build the tests library
let cargo_build = Command::new(&cargo_path)
.current_dir(&options.tests_project_path)
.arg("build")

.arg("--example")
.arg(test)
.arg("--verbose")
.arg("--target")
.arg(&options.target_arch)
//.env("RUSTFLAGS", "-C linker=arm-none-eabi-gcc -Z linker-flavor=gcc")

.env("CARGO_INCREMENTAL", "0")
//.env("RUSTFLAGS", "--emit=obj")
//.env("RUST_TARGET_PATH", &build_proj_root.to_str().expect("Missing path to proj root for target path?"))
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output();

let output = cargo_build.expect("Cargo build of the tests projects failed");
if !output.status.success() {
panic!("Cargo build failed");
}

let mut test_objects: Vec<_> = find_files(&tests_deps_dir, |n| {
n.ends_with(".o")
}).iter().cloned().collect();

test_objects.sort_by_key(|f| {
if f.name.contains("freertos_rs") { 0 }
else if f.name.contains("lazy_static") { 1 }
else if f.name.contains("liballoc") { 2 }
else if f.name.contains("libcompiler_builtins") { 3 }
else if f.name.contains("libcore") { 4 }
else if f.name.contains("librustc_std_workspace_core") { 5 }
else { 6 }
});

let mut test_objects: Vec<_> = test_objects.into_iter().map(|f| f.absolute_path).collect();
built_tests.push(test.clone());
}

let mut objects = vec![];
objects.append(&mut test_objects);
objects
let library_path = {
let p = format!("{}/target/{}/debug/examples/", &options.tests_project_path, &options.target_arch);
let p = Path::new(&p);
p.canonicalize().unwrap().to_str().unwrap().into()
};

CrossbuiltTests {
object_paths: object_paths,
tests: tests
object_paths: vec![],
tests: built_tests,
library_path: library_path
}
}

Expand All @@ -168,19 +166,28 @@ pub fn build_test_binaries(options: &CrossbuildOptions, tests: &CrossbuiltTests)
for test in &tests.tests {
let mut test_renames = "".to_string();

/*
if test.contains("isr_timer4") {
test_renames.push_str(&format!("testbed_timer4_isr = {}_timer4_isr;", test));
}
*/


let mut test_deps = vec![
format!("{}/lib{}.a", &tests.library_path, &test)
];

let test_binary_build = Command::new("make")
.current_dir(&gcc_proj_dir)
.env("TEST_ENTRY", test.clone())
.env("TEST_NAME", test.clone())
.env("TEST_LIBRARY_PATH", format!("-L {}", &tests.library_path))
.env("TEST_LIBRARY_PRE", format!("-l:lib{}.a", &test))
.env("TEST_OBJECTS", &test_objects)
.env("TEST_DEPS", test_deps.join(" "))
.env("TEST_RENAMES", test_renames)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output();

let output = test_binary_build.unwrap();
if !output.status.success() {
panic!(format!("GCC ARM build for '{}' failed", test));
Expand Down
43 changes: 41 additions & 2 deletions qemu_stm32_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,50 @@ authors = ["Rudi Benkovic <[email protected]>"]

[lib]
name = "qemu_stm32_tests"
crate-type = ["lib"]

[dependencies]
freertos_rs = { path = "../" }

[dependencies.lazy_static]
version = "1.3.0"
features = ["spin_no_std"]
features = ["spin_no_std"]

[[example]]
name = "test_basics"
crate-type = ["staticlib"]

[[example]]
name = "test_delay"
crate-type = ["staticlib"]

[[example]]
name = "test_mutex"
crate-type = ["staticlib"]

[[example]]
name = "test_mem_leaks1"
crate-type = ["staticlib"]

[[example]]
name = "test_timers"
crate-type = ["staticlib"]

[[example]]
name = "test_stats"
crate-type = ["staticlib"]

[[example]]
name = "test_processor"
crate-type = ["staticlib"]

[[example]]
name = "test_sample1"
crate-type = ["staticlib"]

[[example]]
name = "test_isr_timer4_notify"
crate-type = ["staticlib"]

[[example]]
name = "test_isr_timer4_queue"
crate-type = ["staticlib"]
24 changes: 24 additions & 0 deletions qemu_stm32_tests/examples/test_basics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![no_main]
#![no_std]

#[macro_use]
extern crate qemu_stm32_tests;

use qemu_stm32_tests::prelude::v1::*;

freertos_rs_test!(TestBasics);

pub struct TestBasics;
impl Test for TestBasics {
fn run<T: Testbed>(tb: &T) {
let check = shim_sanity_check();
if check.is_err() {
T::debug_print(&format!("Shim sanity check failed: {:?}", check));
T::exit_test(1);
}

T::debug_print("Type sizes are OK!");

T::exit_test(0);
}
}
77 changes: 77 additions & 0 deletions qemu_stm32_tests/examples/test_delay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#![no_main]
#![no_std]

#[macro_use]
extern crate qemu_stm32_tests;

use qemu_stm32_tests::prelude::v1::*;

freertos_rs_test!(TestDelay);

pub struct TestDelay;
impl Test for TestDelay {
fn run<T: Testbed>(tb: &T) {
let main_task = Task::new().name("main").start(|| {
let start = FreeRtosUtils::get_tick_count();

let counter = Arc::new(Mutex::new(0).unwrap());

{
let counter = counter.clone();
let delay_task = Task::new().name("delay").start(move || {
for _ in 0..10 {
CurrentTask::delay(Duration::ms(100));

// increase the counter and immediately release it
{
let mut counter = counter.lock(Duration::infinite()).unwrap();
*counter += 1;
}
}
}).unwrap();
}

CurrentTask::delay(Duration::ms(550));

{
let counter = counter.lock(Duration::infinite()).unwrap();
assert_eq!(*counter, 5);
}

CurrentTask::delay(Duration::ms(500));

{
let counter = counter.lock(Duration::infinite()).unwrap();
assert_eq!(*counter, 10);
}

// negative test: the counter should never increment
{
let counter = Arc::new(Mutex::new(0).unwrap());
{
let counter = counter.clone();
let task = Task::new().name("delay_long").start(move || {
for _ in 0..10 {
CurrentTask::delay(Duration::ms(1000));

// increase the counter and immediately release it
{
let mut counter = counter.lock(Duration::infinite()).unwrap();
*counter += 1;
}
}
});
}

CurrentTask::delay(Duration::ms(500));
let counter = counter.lock(Duration::infinite()).unwrap();
assert_eq!(*counter, 0);
}

T::exit_test(0);
});

let main_task = main_task.unwrap();
T::start_kernel();
}
}
Loading

0 comments on commit d52f8b8

Please sign in to comment.