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