diff options
author | David Drysdale <drysdale@google.com> | 2023-12-20 16:41:13 +0000 |
---|---|---|
committer | David Drysdale <drysdale@google.com> | 2024-01-04 18:17:49 +0000 |
commit | e7658a36e18057aba73cd69f0054484006ab1f4c (patch) | |
tree | b71e943107093e4c9ccba0ba634c096f2d8815f2 | |
parent | ee78aadd2c3a2d6fd8d5565dabe4276465663423 (diff) | |
download | platform_testing-e7658a36e18057aba73cd69f0054484006ab1f4c.tar.gz |
rdroidtest: add support for parameterized tests
Test: librdroidtest_test
Change-Id: I1b473a810181598ef9d94bc5ff4eb38ffa9744a3
-rw-r--r-- | libraries/rdroidtest/Android.bp | 10 | ||||
-rw-r--r-- | libraries/rdroidtest/README.md | 51 | ||||
l--------- | libraries/rdroidtest/rustfmt.toml | 1 | ||||
-rw-r--r-- | libraries/rdroidtest/src/lib.rs | 56 | ||||
-rw-r--r-- | libraries/rdroidtest/src/runner.rs | 15 | ||||
-rw-r--r-- | libraries/rdroidtest/tests/raw.rs | 74 |
6 files changed, 202 insertions, 5 deletions
diff --git a/libraries/rdroidtest/Android.bp b/libraries/rdroidtest/Android.bp index a18681abe..cd0bbec2d 100644 --- a/libraries/rdroidtest/Android.bp +++ b/libraries/rdroidtest/Android.bp @@ -35,3 +35,13 @@ rust_defaults { "nostart-stop-gc", ], } + +rust_test { + name: "librdroidtest_test", + srcs: ["tests/raw.rs"], + test_suites: [ + "general-tests", + ], + host_supported: true, + defaults: ["rdroidtest.defaults"], +} diff --git a/libraries/rdroidtest/README.md b/libraries/rdroidtest/README.md index 779bb21c2..199e79d6d 100644 --- a/libraries/rdroidtest/README.md +++ b/libraries/rdroidtest/README.md @@ -39,7 +39,7 @@ Each test case should be marked with the `rdroidtest::test!` macro, rather than use rdroidtest::test; test!(one_plus_one); -fn one_plus_one { +fn one_plus_one() { assert_eq!(1 + 1, 2); } ``` @@ -50,7 +50,7 @@ To ignore a test, you can add an `ignore_if` clause with a boolean expression: use rdroidtest::test; test!(clap_hands, ignore_if: !feeling_happy()); -fn clap_hands { +fn clap_hands() { assert!(HANDS.clap().is_ok()); } ``` @@ -59,8 +59,53 @@ Somewhere in your main module, you need to use the `test_main` macro to generate the test harness: ```rust -#[cfg(test)] 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, use the `rdroidtest::ptest!` +macro: + +```rust +use rdroidtest::ptest; + +ptest!(is_even, my_instances()); +fn is_even(param: u32) { + assert_eq!(param % 2, 0); +} +``` + +The second argument to the `ptest!` macro is an expression that is called at runtime to generate +the set of parameters to invoke the test with. This expression should emit a vector of +`(String, T)` values: + +```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` clause that accepts the parameter +value (this time as type `&T`) and returns a boolean: + +```rust +ptest!(is_even_too, my_instances(), ignore_if: |p| feeling_odd(p)); +fn is_even_too(param: u32) { + assert_eq!(param % 2, 0); +} + +fn feeling_odd(param: &u32) -> bool { + *param % 2 == 1 +} +``` diff --git a/libraries/rdroidtest/rustfmt.toml b/libraries/rdroidtest/rustfmt.toml new file mode 120000 index 000000000..475ba8fde --- /dev/null +++ b/libraries/rdroidtest/rustfmt.toml @@ -0,0 +1 @@ +../../../build/soong/scripts/rustfmt.toml
\ No newline at end of file diff --git a/libraries/rdroidtest/src/lib.rs b/libraries/rdroidtest/src/lib.rs index 42675c63b..94761a342 100644 --- a/libraries/rdroidtest/src/lib.rs +++ b/libraries/rdroidtest/src/lib.rs @@ -25,6 +25,8 @@ macro_rules! test_main { /// # Usage /// /// ``` +/// use rdroidtest::test; +/// /// test!(test_string_equality); /// fn test_string_equality() { /// assert_eq!("", ""); @@ -55,3 +57,57 @@ macro_rules! test { ); }; } + +/// Macro to generate a wrapper function for a parameterized test. +/// +/// # Usage +/// +/// ``` +/// use rdroidtest::ptest; +/// +/// /// Return (param name, param value) tuples of type `(String, T)`. +/// /// The parameter value can be any type T (not just `u32`). +/// fn my_instances() -> Vec<(String, u32)> { +/// vec![ +/// ("one".to_string(), 1), +/// ("two".to_string(), 2), +/// ("three".to_string(), 3), +/// ] +/// } +/// +/// ptest!(is_even, my_instances()); +/// fn is_even(param: u32) { +/// // Test method takes a parameter of type T. +/// assert_eq!(param % 2, 0); +/// } +/// ``` +#[macro_export] +macro_rules! ptest { + ($test_name:ident, $param_gen:expr) => { + $crate::_paste::paste!( + #[$crate::_linkme::distributed_slice($crate::runner::RDROIDTEST_PTESTS)] + fn [< __ptest_ $test_name >]() -> Vec<$crate::_libtest_mimic::Trial> { + $param_gen.into_iter().map(|(name, val)| { + $crate::_libtest_mimic::Trial::test( + format!("{}/{}", ::std::stringify!($test_name), name), + move || $crate::runner::run(|| $test_name(val)), + ) + }).collect() + } + ); + }; + ($test_name:ident, $param_gen:expr, ignore_if: $ignore_expr:expr) => { + $crate::_paste::paste!( + #[$crate::_linkme::distributed_slice($crate::runner::RDROIDTEST_PTESTS)] + fn [< __ptest_ $test_name >]() -> Vec<$crate::_libtest_mimic::Trial> { + $param_gen.into_iter().map(|(name, val)| { + let ignored = $ignore_expr(&val); + $crate::_libtest_mimic::Trial::test( + format!("{}/{}", ::std::stringify!($test_name), name), + move || $crate::runner::run(|| $test_name(val)), + ).with_ignored_flag(ignored) + }).collect() + } + ); + }; +} diff --git a/libraries/rdroidtest/src/runner.rs b/libraries/rdroidtest/src/runner.rs index 81199e5e3..268b70d77 100644 --- a/libraries/rdroidtest/src/runner.rs +++ b/libraries/rdroidtest/src/runner.rs @@ -9,16 +9,27 @@ use std::env; /// Command-line arguments to ignore, because they are not supported by libtest-mimic. const IGNORED_ARGS: [&str; 2] = ["-Zunstable-options", "--report-time"]; -/// The collection of all tests to run. +/// The collection of all non-parameterized tests to run. #[doc(hidden)] #[distributed_slice] pub static RDROIDTEST_TESTS: [fn() -> Trial] = [..]; +/// The collection of all parameterized tests to run. +#[doc(hidden)] +#[distributed_slice] +pub static RDROIDTEST_PTESTS: [fn() -> Vec<Trial>] = [..]; + /// Runs all tests. pub fn main() { logger::init(logger::Config::default().with_min_level(Level::Debug)); let args = Arguments::from_iter(env::args().filter(|arg| !IGNORED_ARGS.contains(&arg.deref()))); - let tests = RDROIDTEST_TESTS.iter().map(|test| test()).collect(); + + let tests = RDROIDTEST_TESTS + .iter() + .map(|test| test()) + .chain(RDROIDTEST_PTESTS.iter().flat_map(|test| test())) + .collect(); + libtest_mimic::run(&args, tests).exit(); } diff --git a/libraries/rdroidtest/tests/raw.rs b/libraries/rdroidtest/tests/raw.rs new file mode 100644 index 000000000..e22305f0e --- /dev/null +++ b/libraries/rdroidtest/tests/raw.rs @@ -0,0 +1,74 @@ +//! Test use of `rdroidtest`. + +use rdroidtest::{ptest, test}; + +// Tests using raw declarative macros. + +test!(one_plus_one); +fn one_plus_one() { + let result = 1 + 1; + assert_eq!(result, 2); +} + +test!(clap_hands, ignore_if: !feeling_happy()); +fn clap_hands() { + let result = 1 + 1; + assert_eq!(result, 3); +} + +fn feeling_happy() -> bool { + false +} + +ptest!(is_less_than_five, my_instances()); +fn is_less_than_five(param: u32) { + assert!(param < 5); +} + +ptest!(is_even, my_instances(), ignore_if: feeling_odd); +fn is_even(param: u32) { + assert_eq!(param % 2, 0); +} + +ptest!(is_odd, my_instances(), ignore_if: |p| !feeling_odd(p)); +fn is_odd(param: u32) { + assert_eq!(param % 2, 1); +} + +fn feeling_odd(param: &u32) -> bool { + *param % 2 == 1 +} + +fn my_instances() -> Vec<(String, u32)> { + vec![("one".to_string(), 1), ("two".to_string(), 2), ("three".to_string(), 3)] +} + +ptest!(is_odder, wrapped_instances(), ignore_if: |p| !feeling_odder(p)); +fn is_odder(param: Param) { + assert_eq!(param.0 % 2, 1); +} + +fn feeling_odder(param: &Param) -> bool { + param.0 % 2 == 1 +} + +struct Param(u32); + +fn wrapped_instances() -> Vec<(String, Param)> { + vec![ + ("one".to_string(), Param(1)), + ("two".to_string(), Param(2)), + ("three".to_string(), Param(3)), + ] +} + +ptest!(is_the_one, more_instances(), ignore_if: |p| p != "one"); +fn is_the_one(param: String) { + assert_eq!(param, "one"); +} + +fn more_instances() -> Vec<(String, String)> { + vec![("one".to_string(), "one".to_string()), ("two".to_string(), "two".to_string())] +} + +rdroidtest::test_main!(); |