# rdroidtest This is a custom Rust test harness which allows tests to be ignored at runtime based on arbitrary criteria. The built-in Rust test harness only allows tests to be ignored at compile time, but this is often not enough on Android, where we want to ignore tests based on system properties or other characteristics of the device on which the test is being run, which are not known at build time. ## Usage Unfortunately without the built-in support that rustc provides to the standard test harness, this one is slightly more cumbersome to use. Firstly, add it to the `rust_test` build rule in your `Android.bp` by adding the defaults provided: ```soong rust_test { name: "mycrate.test", defaults: ["rdroidtest.defaults"], // ... } ``` If you are testing a binary that has a `main` function, you'll need to remove it from the test build: ```rust #[cfg(not(test))] fn main() { // ... } ``` (If you're testing a library or anything else which doesn't have a `main` function, you don't need to worry about this.) Each test case should be marked with the `rdroidtest` attribute, rather than the standard `#[test]` attribute: ```rust use rdroidtest::rdroidtest; #[rdroidtest] fn one_plus_one() { assert_eq!(1 + 1, 2); } ``` To ignore a test, you can add an `ignore_if` attribute whose argument is an expression that evaluates to a boolean: ```rust use rdroidtest::{ignore_if, rdroidtest}; #[rdroidtest] #[ignore_if(!feeling_happy())] fn clap_hands() { assert!(HANDS.clap().is_ok()); } ``` Somewhere in your main module, you need to use the `test_main` macro to generate an entry point for the test harness: ```rust rdroidtest::test_main!(); ``` You can then run your tests as usual with `atest`. ## Parameterized Tests To run the same test multiple times with different parameter values, add an argument to the `rdroidtest` attribute: ```rust use rdroidtest::rdroidtest; #[rdroidtest(my_instances())] fn is_even(param: u32) { assert_eq!(param % 2, 0); } ``` The initial argument to the `rdroidtest` attribute is an expression that generates the set of parameters to invoke the test with. This expression should evaluate to a vector of `(String, T)` values for some type `T`: ```rust fn my_instances() -> Vec<(String, u32)> { vec![ ("one".to_string(), 1), ("two".to_string(), 2), ("three".to_string(), 3), ] } ``` The test method will be invoked with each of the parameter values in turn, passed in as the single argument of type `T`. Parameterized tests can also be ignored, using an `ignore_if` attribute. For a parameterized test, the argument is an expression that emits a boolean when invoked with a single argument, of type `&T`: ```rust #[rdroidtest(my_instances())] #[ignore_if(feeling_odd)] fn is_even_too(param: u32) { assert_eq!(param % 2, 0); } fn feeling_odd(param: &u32) -> bool { *param % 2 == 1 } ``` ## Summary Table | | Normal | Conditionally Ignore | |---------------|----------------------|-----------------------------------------------| | Normal | `#[rdroidtest]` | `#[rdroidtest]`
`#[ignore_if()]` | | Parameterized | `#[rdroidtest()]` | `#[rdroidtest()]`
`#[ignore_if()]` | Where: - `` is an expression that evaluates to a `bool`. - `` is an expression that evaluates to a `Vec`. - `` is an callable expression with signature `fn(&T) -> bool`.