xref: /aosp_15_r20/external/crosvm/base/src/test_utils.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::env::current_exe;
6 use std::process::Command;
7 
8 /// The tests below require root privileges.
9 /// Re-invoke the test binary to execute the specified test with sudo. The test will fail if
10 /// passwordless sudo is not available.
call_test_with_sudo(name: &str)11 pub fn call_test_with_sudo(name: &str) {
12     check_can_sudo();
13 
14     let result = Command::new("sudo")
15         .args([
16             "--preserve-env",
17             current_exe().unwrap().to_str().unwrap(),
18             "--nocapture",
19             "--ignored",
20             "--exact",
21             name,
22         ])
23         .status()
24         .unwrap();
25 
26     if !result.success() {
27         panic!("Test {name} failed in child process.");
28     }
29 }
30 
31 /// Checks to see if user has entered their password for sudo.
check_can_sudo()32 pub fn check_can_sudo() {
33     // Try a passwordless sudo first to provide a proper error message.
34     // Note: The combination of SUDO_ASKPASS and --askpass will fail if sudo has to ask for a
35     // password. When sudo needs to ask for a password, it will call "false" and fail without
36     // prompting.
37     let can_sudo = Command::new("sudo")
38         .args(["--askpass", "true"]) // Use an askpass program to ask for a password
39         .env("SUDO_ASKPASS", "false") // Set the askpass program to false
40         .output()
41         .unwrap();
42     if !can_sudo.status.success() {
43         panic!("This test need to be run as root or with passwordless sudo.");
44     }
45 }
46 
47 /// Assert repeatedly until it's true
48 ///
49 /// Runs the provided `$cond` closure until it returns true. If it does not return true after
50 /// `$tries` times, it will panic.
51 /// There is no delay between polls, but the `$cond` can sleep as needed.
52 #[macro_export]
53 macro_rules! poll_assert {
54     ($tries: tt, $cond:expr) => {
55         $crate::test_utils::poll_assert_impl(stringify!($cond), $tries, $cond)
56     };
57 }
58 
59 /// Implementation of [poll_assert]
poll_assert_impl(msg: &'static str, tries: usize, poll_fn: impl Fn() -> bool)60 pub fn poll_assert_impl(msg: &'static str, tries: usize, poll_fn: impl Fn() -> bool) {
61     for _ in 0..tries {
62         if poll_fn() {
63             return;
64         }
65     }
66     panic!("Still failing after {} tries: {}", tries, msg);
67 }
68