1# rdroidtest 2 3This is a custom Rust test harness which allows tests to be ignored at runtime based on arbitrary 4criteria. The built-in Rust test harness only allows tests to be ignored at compile time, but this 5is often not enough on Android, where we want to ignore tests based on system properties or other 6characteristics of the device on which the test is being run, which are not known at build time. 7 8## Usage 9 10Unfortunately without the built-in support that rustc provides to the standard test harness, this 11one is slightly more cumbersome to use. Firstly, add it to the `rust_test` build rule in your 12`Android.bp` by adding the defaults provided: 13 14```soong 15rust_test { 16 name: "mycrate.test", 17 defaults: ["rdroidtest.defaults"], 18 // ... 19} 20``` 21 22If you are testing a binary that has a `main` function, you'll need to remove it from the test 23build: 24 25```rust 26#[cfg(not(test))] 27fn main() { 28 // ... 29} 30``` 31 32(If you're testing a library or anything else which doesn't have a `main` function, you don't need 33to worry about this.) 34 35Each test case should be marked with the `rdroidtest` attribute, rather than the standard 36`#[test]` attribute: 37 38```rust 39use rdroidtest::rdroidtest; 40 41#[rdroidtest] 42fn one_plus_one() { 43 assert_eq!(1 + 1, 2); 44} 45``` 46 47To ignore a test, you can add an `ignore_if` attribute whose argument is an expression that 48evaluates to a boolean: 49 50```rust 51use rdroidtest::{ignore_if, rdroidtest}; 52 53#[rdroidtest] 54#[ignore_if(!feeling_happy())] 55fn clap_hands() { 56 assert!(HANDS.clap().is_ok()); 57} 58``` 59 60Somewhere in your main module, you need to use the `test_main` macro to generate an entry point for 61the test harness: 62 63```rust 64rdroidtest::test_main!(); 65``` 66 67You can then run your tests as usual with `atest`. 68 69 70## Parameterized Tests 71 72To run the same test multiple times with different parameter values, add an argument to the 73`rdroidtest` attribute: 74 75```rust 76use rdroidtest::rdroidtest; 77 78#[rdroidtest(my_instances())] 79fn is_even(param: u32) { 80 assert_eq!(param % 2, 0); 81} 82``` 83 84The initial argument to the `rdroidtest` attribute is an expression that generates the set of 85parameters to invoke the test with. This expression should evaluate to a vector of `(String, T)` 86values for some type `T`: 87 88```rust 89fn my_instances() -> Vec<(String, u32)> { 90 vec![ 91 ("one".to_string(), 1), 92 ("two".to_string(), 2), 93 ("three".to_string(), 3), 94 ] 95} 96``` 97 98The test method will be invoked with each of the parameter values in turn, passed in as the single 99argument of type `T`. 100 101Parameterized tests can also be ignored, using an `ignore_if` attribute. For a parameterized test, 102the argument is an expression that emits a boolean when invoked with a single argument, of type 103`&T`: 104 105```rust 106#[rdroidtest(my_instances())] 107#[ignore_if(feeling_odd)] 108fn is_even_too(param: u32) { 109 assert_eq!(param % 2, 0); 110} 111 112fn feeling_odd(param: &u32) -> bool { 113 *param % 2 == 1 114} 115``` 116 117## Summary Table 118 119| | Normal | Conditionally Ignore | 120|---------------|----------------------|-----------------------------------------------| 121| Normal | `#[rdroidtest]` | `#[rdroidtest]` <br> `#[ignore_if(<I>)]` | 122| Parameterized | `#[rdroidtest(<G>)]` | `#[rdroidtest(<G>)]` <br> `#[ignore_if(<C>)]` | 123 124Where: 125- `<I>` is an expression that evaluates to a `bool`. 126- `<G>` is an expression that evaluates to a `Vec<String, T>`. 127- `<C>` is an callable expression with signature `fn(&T) -> bool`. 128