1# Testing 2 3Crosvm runs on a variety of platforms with a significant amount of platform-specific code. Testing 4on all the supported platforms is crucial to keep crosvm healthy. 5 6## Types of tests 7 8### Unit Tests 9 10Unit tests are your standard rust tests embedded with the rest of the code in `src/` and wrapped in 11a `#[cfg(test)]` attribute. 12 13Unit tests **cannot make any guarantees on the runtime environment**. Avoid doing the following in 14unit tests: 15 16- Avoid kernel features such as io_uring or userfaultfd, which may not be available on all kernels. 17- Avoid functionality that requires privileges (e.g. CAP_NET_ADMIN) 18- Avoid spawning threads or processes 19- Avoid accessing kernel devices 20- Avoid global state in unit tests 21 22This allows us to execute unit tests for any platform using emulators such as qemu-user-static or 23wine64. 24 25#### File Access in Unit Tests 26 27Some unit tests may need to access extra data files. The files should be accessed at build time 28using `include_str!()` macro, rather than at run time. The file is located relative to the current 29file (similarly to how modules are found). The contents of the file can be used directly in the test 30or at runtime the test can write this data to a temporary file. This approach is crucial because 31certain test environment may require to run the test binaries directly without access to the source 32code. Additionally, it ensures the test binary can be run manually within a debugger like GDB. 33 34These approaches ensure that units tests be able to find the correct paths in various build & 35execution environment. 36 37**Example:** 38 39```rust 40#[test] 41fn test_my_config() { 42 let temp_file = TempDir::new().unwrap(); 43 let path = temp_file.path().join("my_config.cfg"); 44 let test_config = include_str!("../../../data/config/my_config.cfg"); 45 fs::write(&path, test_config).expect("Unable to write test file"); 46 let config_file = File::open(path).expect("Failed to open config file"); 47 // ... rest of your test ... 48} 49``` 50 51### Documentation tests 52 53Rust's 54[documentation tests](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html) 55can be used to provide examples as part of the documentation that is verified by CI. 56 57Documentation tests are slow and not run as part of the usual workflows, but can be run locally 58with: 59 60```sh 61./tools/presubmit doc_tests 62``` 63 64### Integration tests 65 66Cargo has native support for 67[integration testing](https://doc.rust-lang.org/rust-by-example/testing/integration_testing.html). 68Integration tests are written just like unit tests, but live in a separate directory at `tests/`. 69 70Integration tests **guarantee that the test has privileged access to the test environment**. They 71are only executed when a device-under-test (DUT) is specified when running tests: 72 73```sh 74./tools/run_tests --dut=vm|host 75``` 76 77### End To End (E2E) tests 78 79End to end tests live in the `e2e_tests` crate. The crate provides a framework to boot a guest with 80crosvm and execut commands in the guest to validate functionality at a high level. 81 82E2E tests are executed just like integration tests. By giving 83[nextest's filter expressions](https://nexte.st/book/filter-expressions), you can run a subset of 84the tests. 85 86```sh 87# Run all e2e tests 88./tools/run_tests --dut=vm --filter-expr 'package(e2e_tests)' 89# Run e2e tests whose name contains the string 'boot'. 90./tools/run_tests --dut=vm --filter-expr 'package(e2e_tests) and test(boot)' 91``` 92 93### Downstream Product tests 94 95Each downstream product that uses crosvm is performing their own testing, e.g. ChromeOS is running 96high level testing of its VM features on ChromeOS hardware, while AOSP is running testing of their 97VM features on AOSP hardware. 98 99Upstream crosvm is not involved in these tests and they are not executed in crosvm CI. 100 101## Parallel test execution 102 103Crosvm tests are executed in parallel, each test case in its own process via 104[cargo nextest](http://nexte.st). 105 106This requires tests to be cautious about global state, especially integration tests which interact 107with system devices. 108 109If you require exclusive access to a device or file, you have to use 110[file-based locking](https://docs.rs/named-lock/latest/named_lock) to prevent access by other test 111processes. 112 113## Platforms tested 114 115The platforms below can all be tested using `tools/run_tests -p $platform`. The table indicates how 116these tests are executed: 117 118| Platform | Build | Unit Tests | Integration Tests | E2E Tests | 119| :-------------------------- | :---: | :------------------------: | :---------------: | :-------: | 120| x86_64 (linux) | ✅ | ✅ | ✅ | ✅ | 121| aarch64 (linux) | ✅ | ✅ (qemu-user[^qemu-user]) | ✅ (qemu[^qemu]) | ❌ | 122| armhf (linux) | ✅ | ✅ (qemu-user[^qemu-user]) | ❌ | ❌ | 123| mingw64[^windows] (linux) | | (wine64) | ❌ | ❌ | 124| mingw64[^windows] (windows) | | | | ❌ | 125 126Crosvm CI will use the same configuration as `tools/run_tests`. 127 128## Debugging Tips 129 130Here are some tips for developing or/and debugging crosvm tests. 131 132### Enter a test VM to see logs 133 134When you run a test on a VM with `./tools/run_tests --dut=vm`, if the test fails, you'll see 135extracted log messages. To see the full messages or monitor the test process during the runtime, you 136may want to enter the test VM. 137 138First, enter the VM's shell and start printing the syslog: 139 140```console 141$ ./tools/dev_container # Enter the dev_container 142$ ./tools/x86vm shell # Enter the test VM 143crosvm@testvm-x8664:~$ journalctl -f 144# syslog messages will be printed... 145``` 146 147Then, open another terminal and run a test: 148 149```console 150$ ./tools/run_tests --dut=vm --filter-expr 'package(e2e_tests) and test(boot)' 151``` 152 153So you'll see the crosvm log in the first terminal. 154 155[^qemu-user]: qemu-aarch64-static or qemu-arm-static translate instructions into x86 and executes them on the 156 host kernel. This works well for unit tests, but will fail when interacting with platform 157 specific kernel features. 158 159[^qemu]: run_tests will launch a VM for testing in the background. This VM is using full system 160 emulation, which causes tests to be slow. Also not all aarch64 features are properly emulated, 161 which prevents us from running e2e tests. 162 163[^windows]: Windows builds of crosvm are a work in progress. Some tests are executed via wine64 on linux 164