summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Drysdale <drysdale@google.com>2023-12-20 16:41:13 +0000
committerDavid Drysdale <drysdale@google.com>2024-01-04 18:17:49 +0000
commite7658a36e18057aba73cd69f0054484006ab1f4c (patch)
treeb71e943107093e4c9ccba0ba634c096f2d8815f2
parentee78aadd2c3a2d6fd8d5565dabe4276465663423 (diff)
downloadplatform_testing-e7658a36e18057aba73cd69f0054484006ab1f4c.tar.gz
rdroidtest: add support for parameterized tests
Test: librdroidtest_test Change-Id: I1b473a810181598ef9d94bc5ff4eb38ffa9744a3
-rw-r--r--libraries/rdroidtest/Android.bp10
-rw-r--r--libraries/rdroidtest/README.md51
l---------libraries/rdroidtest/rustfmt.toml1
-rw-r--r--libraries/rdroidtest/src/lib.rs56
-rw-r--r--libraries/rdroidtest/src/runner.rs15
-rw-r--r--libraries/rdroidtest/tests/raw.rs74
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!();