1*d57664e9SAndroid Build Coastguard Worker# Ravenwood for Test Authors 2*d57664e9SAndroid Build Coastguard Worker 3*d57664e9SAndroid Build Coastguard WorkerThe Ravenwood testing environment runs inside a single Java process on the host side, and provides a limited yet growing set of Android API functionality. 4*d57664e9SAndroid Build Coastguard Worker 5*d57664e9SAndroid Build Coastguard WorkerRavenwood explicitly does not support “large” integration tests that expect a fully booted Android OS. Instead, it’s more suited for “small” and “medium” tests where your code-under-test has been factored to remove dependencies on a fully booted device. 6*d57664e9SAndroid Build Coastguard Worker 7*d57664e9SAndroid Build Coastguard WorkerWhen writing tests under Ravenwood, all Android API symbols associated with your declared `sdk_version` are available to link against using, but unsupported APIs will throw an exception. This design choice enables mocking of unsupported APIs, and supports sharing of test code to build “bivalent” test suites that run against either Ravenwood or a traditional device. 8*d57664e9SAndroid Build Coastguard Worker 9*d57664e9SAndroid Build Coastguard Worker## Manually running tests 10*d57664e9SAndroid Build Coastguard Worker 11*d57664e9SAndroid Build Coastguard WorkerTo run all Ravenwood tests, use: 12*d57664e9SAndroid Build Coastguard Worker 13*d57664e9SAndroid Build Coastguard Worker``` 14*d57664e9SAndroid Build Coastguard Worker./frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh 15*d57664e9SAndroid Build Coastguard Worker``` 16*d57664e9SAndroid Build Coastguard Worker 17*d57664e9SAndroid Build Coastguard WorkerTo run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as: 18*d57664e9SAndroid Build Coastguard Worker 19*d57664e9SAndroid Build Coastguard Worker``` 20*d57664e9SAndroid Build Coastguard Workeratest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative 21*d57664e9SAndroid Build Coastguard Worker``` 22*d57664e9SAndroid Build Coastguard Worker 23*d57664e9SAndroid Build Coastguard Worker## Typical test structure 24*d57664e9SAndroid Build Coastguard Worker 25*d57664e9SAndroid Build Coastguard WorkerBelow are the typical steps needed to add a straightforward “small” unit test: 26*d57664e9SAndroid Build Coastguard Worker 27*d57664e9SAndroid Build Coastguard Worker* Define an `android_ravenwood_test` rule in your `Android.bp` file: 28*d57664e9SAndroid Build Coastguard Worker 29*d57664e9SAndroid Build Coastguard Worker``` 30*d57664e9SAndroid Build Coastguard Workerandroid_ravenwood_test { 31*d57664e9SAndroid Build Coastguard Worker name: "MyTestsRavenwood", 32*d57664e9SAndroid Build Coastguard Worker static_libs: [ 33*d57664e9SAndroid Build Coastguard Worker "androidx.annotation_annotation", 34*d57664e9SAndroid Build Coastguard Worker "androidx.test.ext.junit", 35*d57664e9SAndroid Build Coastguard Worker "androidx.test.rules", 36*d57664e9SAndroid Build Coastguard Worker ], 37*d57664e9SAndroid Build Coastguard Worker srcs: [ 38*d57664e9SAndroid Build Coastguard Worker "src/com/example/MyCode.java", 39*d57664e9SAndroid Build Coastguard Worker "tests/src/com/example/MyCodeTest.java", 40*d57664e9SAndroid Build Coastguard Worker ], 41*d57664e9SAndroid Build Coastguard Worker sdk_version: "test_current", 42*d57664e9SAndroid Build Coastguard Worker auto_gen_config: true, 43*d57664e9SAndroid Build Coastguard Worker} 44*d57664e9SAndroid Build Coastguard Worker``` 45*d57664e9SAndroid Build Coastguard Worker 46*d57664e9SAndroid Build Coastguard Worker* Write your unit test just like you would for an Android device: 47*d57664e9SAndroid Build Coastguard Worker 48*d57664e9SAndroid Build Coastguard Worker``` 49*d57664e9SAndroid Build Coastguard Workerimport android.platform.test.annotations.DisabledOnRavenwood; 50*d57664e9SAndroid Build Coastguard Workerimport android.platform.test.ravenwood.RavenwoodRule; 51*d57664e9SAndroid Build Coastguard Worker 52*d57664e9SAndroid Build Coastguard Workerimport androidx.test.ext.junit.runners.AndroidJUnit4; 53*d57664e9SAndroid Build Coastguard Worker 54*d57664e9SAndroid Build Coastguard Workerimport org.junit.Test; 55*d57664e9SAndroid Build Coastguard Workerimport org.junit.runner.RunWith; 56*d57664e9SAndroid Build Coastguard Worker 57*d57664e9SAndroid Build Coastguard Worker@RunWith(AndroidJUnit4.class) 58*d57664e9SAndroid Build Coastguard Workerpublic class MyCodeTest { 59*d57664e9SAndroid Build Coastguard Worker @Test 60*d57664e9SAndroid Build Coastguard Worker public void testSimple() { 61*d57664e9SAndroid Build Coastguard Worker // ... 62*d57664e9SAndroid Build Coastguard Worker } 63*d57664e9SAndroid Build Coastguard Worker} 64*d57664e9SAndroid Build Coastguard Worker``` 65*d57664e9SAndroid Build Coastguard Worker 66*d57664e9SAndroid Build Coastguard Worker* APIs available under Ravenwood are stateless by default. If your test requires explicit states (such as defining the UID you’re running under, or requiring a main `Looper` thread), add a `RavenwoodRule` to declare that: 67*d57664e9SAndroid Build Coastguard Worker 68*d57664e9SAndroid Build Coastguard Worker``` 69*d57664e9SAndroid Build Coastguard Workerimport android.platform.test.annotations.DisabledOnRavenwood; 70*d57664e9SAndroid Build Coastguard Workerimport android.platform.test.ravenwood.RavenwoodRule; 71*d57664e9SAndroid Build Coastguard Worker 72*d57664e9SAndroid Build Coastguard Workerimport androidx.test.runner.AndroidJUnit4; 73*d57664e9SAndroid Build Coastguard Worker 74*d57664e9SAndroid Build Coastguard Workerimport org.junit.Test; 75*d57664e9SAndroid Build Coastguard Workerimport org.junit.runner.RunWith; 76*d57664e9SAndroid Build Coastguard Worker 77*d57664e9SAndroid Build Coastguard Worker@RunWith(AndroidJUnit4.class) 78*d57664e9SAndroid Build Coastguard Workerpublic class MyCodeTest { 79*d57664e9SAndroid Build Coastguard Worker @Rule 80*d57664e9SAndroid Build Coastguard Worker public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() 81*d57664e9SAndroid Build Coastguard Worker .setProcessApp() 82*d57664e9SAndroid Build Coastguard Worker .setProvideMainThread(true) 83*d57664e9SAndroid Build Coastguard Worker .build(); 84*d57664e9SAndroid Build Coastguard Worker``` 85*d57664e9SAndroid Build Coastguard Worker 86*d57664e9SAndroid Build Coastguard WorkerOnce you’ve defined your test, you can use typical commands to execute it locally: 87*d57664e9SAndroid Build Coastguard Worker 88*d57664e9SAndroid Build Coastguard Worker``` 89*d57664e9SAndroid Build Coastguard Worker$ atest --host MyTestsRavenwood 90*d57664e9SAndroid Build Coastguard Worker``` 91*d57664e9SAndroid Build Coastguard Worker 92*d57664e9SAndroid Build Coastguard Worker> **Note:** There's a known bug #312525698 where `atest` currently requires a connected device to run Ravenwood tests, but that device isn't used for testing. Using the `--host` argument above is a way to bypass this requirement until the bug is fixed. 93*d57664e9SAndroid Build Coastguard Worker 94*d57664e9SAndroid Build Coastguard WorkerYou can also run your new tests automatically via `TEST_MAPPING` rules like this: 95*d57664e9SAndroid Build Coastguard Worker 96*d57664e9SAndroid Build Coastguard Worker``` 97*d57664e9SAndroid Build Coastguard Worker{ 98*d57664e9SAndroid Build Coastguard Worker "ravenwood-presubmit": [ 99*d57664e9SAndroid Build Coastguard Worker { 100*d57664e9SAndroid Build Coastguard Worker "name": "MyTestsRavenwood", 101*d57664e9SAndroid Build Coastguard Worker "host": true 102*d57664e9SAndroid Build Coastguard Worker } 103*d57664e9SAndroid Build Coastguard Worker ] 104*d57664e9SAndroid Build Coastguard Worker} 105*d57664e9SAndroid Build Coastguard Worker``` 106*d57664e9SAndroid Build Coastguard Worker 107*d57664e9SAndroid Build Coastguard Worker> **Note:** There's a known bug #308854804 where `TEST_MAPPING` is not being applied, so we're currently planning to run all Ravenwood tests unconditionally in presubmit for changes to `frameworks/base/` and `cts/` until there is a better path forward. 108*d57664e9SAndroid Build Coastguard Worker 109*d57664e9SAndroid Build Coastguard Worker## Strategies for migration/bivalent tests 110*d57664e9SAndroid Build Coastguard Worker 111*d57664e9SAndroid Build Coastguard WorkerRavenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment. 112*d57664e9SAndroid Build Coastguard Worker 113*d57664e9SAndroid Build Coastguard WorkerIn situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly “ignore” that test under Ravenwood, while continuing to validate that test on real devices. The annotation can be applied to either individual methods or to an entire test class. Please note that your test class must declare a `RavenwoodRule` for the annotation to take effect. 114*d57664e9SAndroid Build Coastguard Worker 115*d57664e9SAndroid Build Coastguard WorkerTest authors are encouraged to provide a `blockedBy` or `reason` argument to help future maintainers understand why a test is being ignored, and under what conditions it might be supported in the future. 116*d57664e9SAndroid Build Coastguard Worker 117*d57664e9SAndroid Build Coastguard Worker``` 118*d57664e9SAndroid Build Coastguard Worker@RunWith(AndroidJUnit4.class) 119*d57664e9SAndroid Build Coastguard Workerpublic class MyCodeTest { 120*d57664e9SAndroid Build Coastguard Worker @Rule 121*d57664e9SAndroid Build Coastguard Worker public final RavenwoodRule mRavenwood = new RavenwoodRule(); 122*d57664e9SAndroid Build Coastguard Worker 123*d57664e9SAndroid Build Coastguard Worker @Test 124*d57664e9SAndroid Build Coastguard Worker public void testSimple() { 125*d57664e9SAndroid Build Coastguard Worker // Simple test that runs on both devices and Ravenwood 126*d57664e9SAndroid Build Coastguard Worker } 127*d57664e9SAndroid Build Coastguard Worker 128*d57664e9SAndroid Build Coastguard Worker @Test 129*d57664e9SAndroid Build Coastguard Worker @DisabledOnRavenwood(blockedBy = PackageManager.class) 130*d57664e9SAndroid Build Coastguard Worker public void testComplex() { 131*d57664e9SAndroid Build Coastguard Worker // Complex test that runs on devices, but is ignored under Ravenwood 132*d57664e9SAndroid Build Coastguard Worker } 133*d57664e9SAndroid Build Coastguard Worker} 134*d57664e9SAndroid Build Coastguard Worker``` 135*d57664e9SAndroid Build Coastguard Worker 136*d57664e9SAndroid Build Coastguard WorkerAt the moment, the `android.content.res.Resources` subsystem isn't yet supported under Ravenwood, but you may still want to dual-compile test suites that depend on references to resources. Below is a strategy for supporting dual-compiliation, where you can "borrow" the generated resource symbols from your traditional `android_test` target: 137*d57664e9SAndroid Build Coastguard Worker 138*d57664e9SAndroid Build Coastguard Worker``` 139*d57664e9SAndroid Build Coastguard Workerandroid_test { 140*d57664e9SAndroid Build Coastguard Worker name: "MyTestsDevice", 141*d57664e9SAndroid Build Coastguard Worker resource_dirs: ["res"], 142*d57664e9SAndroid Build Coastguard Worker... 143*d57664e9SAndroid Build Coastguard Worker 144*d57664e9SAndroid Build Coastguard Workerandroid_ravenwood_test { 145*d57664e9SAndroid Build Coastguard Worker name: "MyTestsRavenwood", 146*d57664e9SAndroid Build Coastguard Worker srcs: [ 147*d57664e9SAndroid Build Coastguard Worker ":MyTestsDevice{.aapt.srcjar}", 148*d57664e9SAndroid Build Coastguard Worker... 149*d57664e9SAndroid Build Coastguard Worker``` 150*d57664e9SAndroid Build Coastguard Worker 151*d57664e9SAndroid Build Coastguard Worker## Strategies for unsupported APIs 152*d57664e9SAndroid Build Coastguard Worker 153*d57664e9SAndroid Build Coastguard WorkerAs you write tests against Ravenwood, you’ll likely discover API dependencies that aren’t supported yet. Here’s a few strategies that can help you make progress: 154*d57664e9SAndroid Build Coastguard Worker 155*d57664e9SAndroid Build Coastguard Worker* Your code-under-test may benefit from subtle dependency refactoring to reduce coupling. (For example, providing a specific `File` argument instead of deriving paths internally from a `Context` or `Environment`.) 156*d57664e9SAndroid Build Coastguard Worker * One common use-case is providing a directory for your test to store temporary files, which can easily be accomplished using the `Files.createTempDirectory()` API which works on both physical devices and under Ravenwood: 157*d57664e9SAndroid Build Coastguard Worker 158*d57664e9SAndroid Build Coastguard Worker``` 159*d57664e9SAndroid Build Coastguard Workerimport java.nio.file.Files; 160*d57664e9SAndroid Build Coastguard Worker 161*d57664e9SAndroid Build Coastguard Worker@RunWith(AndroidJUnit4.class) 162*d57664e9SAndroid Build Coastguard Workerpublic class MyTest { 163*d57664e9SAndroid Build Coastguard Worker @Before 164*d57664e9SAndroid Build Coastguard Worker public void setUp() throws Exception { 165*d57664e9SAndroid Build Coastguard Worker File tempDir = Files.createTempDirectory("MyTest").toFile(); 166*d57664e9SAndroid Build Coastguard Worker... 167*d57664e9SAndroid Build Coastguard Worker``` 168*d57664e9SAndroid Build Coastguard Worker 169*d57664e9SAndroid Build Coastguard Worker* Although mocking code that your team doesn’t own is a generally discouraged testing practice, it can be a valuable pressure relief valve when a dependency isn’t yet supported. 170*d57664e9SAndroid Build Coastguard Worker 171*d57664e9SAndroid Build Coastguard Worker## Strategies for debugging test development 172*d57664e9SAndroid Build Coastguard Worker 173*d57664e9SAndroid Build Coastguard WorkerWhen writing tests you may encounter odd or hard to debug behaviors. One good place to start is at the beginning of the logs stored by atest: 174*d57664e9SAndroid Build Coastguard Worker 175*d57664e9SAndroid Build Coastguard Worker``` 176*d57664e9SAndroid Build Coastguard Worker$ atest MyTestsRavenwood 177*d57664e9SAndroid Build Coastguard Worker... 178*d57664e9SAndroid Build Coastguard WorkerTest Logs have saved in /tmp/atest_result/20231128_094010_0e90t8v8/log 179*d57664e9SAndroid Build Coastguard WorkerRun 'atest --history' to review test result history. 180*d57664e9SAndroid Build Coastguard Worker``` 181*d57664e9SAndroid Build Coastguard Worker 182*d57664e9SAndroid Build Coastguard WorkerThe most useful logs are in the `isolated-java-logs` text file, which can typically be tab-completed by copy-pasting the logs path mentioned in the atest output: 183*d57664e9SAndroid Build Coastguard Worker 184*d57664e9SAndroid Build Coastguard Worker``` 185*d57664e9SAndroid Build Coastguard Worker$ less /tmp/atest_result/20231128_133105_h9al__79/log/i*/i*/isolated-java-logs* 186*d57664e9SAndroid Build Coastguard Worker``` 187*d57664e9SAndroid Build Coastguard Worker 188*d57664e9SAndroid Build Coastguard WorkerHere are some common known issues and recommended workarounds: 189*d57664e9SAndroid Build Coastguard Worker 190*d57664e9SAndroid Build Coastguard Worker* Some code may unconditionally interact with unsupported APIs, such as via static initializers. One strategy is to shift the logic into `@Before` methods and make it conditional by testing `RavenwoodRule.isUnderRavenwood()`. 191*d57664e9SAndroid Build Coastguard Worker* Some code may reference API symbols not yet present in the Ravenwood runtime, such as ART or ICU internals, or APIs from Mainline modules. One strategy is to refactor to avoid these internal dependencies, but Ravenwood aims to better support them soon. 192*d57664e9SAndroid Build Coastguard Worker * This may also manifest as very odd behavior, such as test not being executed at all, tracked by bug #312517322 193*d57664e9SAndroid Build Coastguard Worker * This may also manifest as an obscure Mockito error claiming “Mockito can only mock non-private & non-final classes” 194