1# Ravenwood for Test Authors
2
3The 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
5Ravenwood 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
7When 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
9## Manually running tests
10
11To run all Ravenwood tests, use:
12
13```
14./frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh
15```
16
17To run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as:
18
19```
20atest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative
21```
22
23## Typical test structure
24
25Below are the typical steps needed to add a straightforward “small” unit test:
26
27* Define an `android_ravenwood_test` rule in your `Android.bp` file:
28
29```
30android_ravenwood_test {
31    name: "MyTestsRavenwood",
32    static_libs: [
33        "androidx.annotation_annotation",
34        "androidx.test.ext.junit",
35        "androidx.test.rules",
36    ],
37    srcs: [
38        "src/com/example/MyCode.java",
39        "tests/src/com/example/MyCodeTest.java",
40    ],
41    sdk_version: "test_current",
42    auto_gen_config: true,
43}
44```
45
46* Write your unit test just like you would for an Android device:
47
48```
49import android.platform.test.annotations.IgnoreUnderRavenwood;
50import android.platform.test.ravenwood.RavenwoodRule;
51
52import androidx.test.ext.junit.runners.AndroidJUnit4;
53
54import org.junit.Test;
55import org.junit.runner.RunWith;
56
57@RunWith(AndroidJUnit4.class)
58public class MyCodeTest {
59    @Test
60    public void testSimple() {
61        // ...
62    }
63}
64```
65
66* 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
68```
69import android.platform.test.annotations.IgnoreUnderRavenwood;
70import android.platform.test.ravenwood.RavenwoodRule;
71
72import androidx.test.runner.AndroidJUnit4;
73
74import org.junit.Test;
75import org.junit.runner.RunWith;
76
77@RunWith(AndroidJUnit4.class)
78public class MyCodeTest {
79    @Rule
80    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
81            .setProcessApp()
82            .setProvideMainThread(true)
83            .build();
84```
85
86Once you’ve defined your test, you can use typical commands to execute it locally:
87
88```
89$ atest --host MyTestsRavenwood
90```
91
92> **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
94You can also run your new tests automatically via `TEST_MAPPING` rules like this:
95
96```
97{
98  "ravenwood-presubmit": [
99    {
100      "name": "MyTestsRavenwood",
101      "host": true
102    }
103  ]
104}
105```
106
107> **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
109## Strategies for feature flags
110
111Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team:
112
113```
114import android.platform.test.flag.junit.SetFlagsRule;
115
116@RunWith(AndroidJUnit4.class)
117public class MyCodeTest {
118    @Rule
119    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);
120
121    @Test
122    public void testEnabled() {
123        mSetFlagsRule.enableFlags(Flags.FLAG_MY_FLAG);
124        // verify test logic that depends on flag being enabled
125    }
126```
127
128This naturally composes together well with any `RavenwoodRule` that your test may need.
129
130While `SetFlagsRule` is generally a best-practice (as it can explicitly confirm behaviors for both "on" and "off" states), you may need to write tests that use `CheckFlagsRule` (such as when writing CTS).  Ravenwood currently supports `CheckFlagsRule` by offering "all-on" and "all-off" behaviors:
131
132```
133import android.platform.test.flag.junit.CheckFlagsRule;
134import android.platform.test.flag.junit.DeviceFlagsValueProvider;
135import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
136import android.platform.test.ravenwood.RavenwoodRule;
137
138@RunWith(AndroidJUnit4.class)
139public class MyCodeTest {
140    @Rule
141    public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood()
142            ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
143            : DeviceFlagsValueProvider.createCheckFlagsRule();
144```
145
146Ravenwood currently doesn't have knowledge of the "default" value of any flags, so using `createAllOnCheckFlagsRule()` is recommended to verify the widest possible set of behaviors.  The example code above falls back to using default values from `DeviceFlagsValueProvider` when not running on Ravenwood.
147
148## Strategies for migration/bivalent tests
149
150Ravenwood 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.
151
152In 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.
153
154Test 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.
155
156```
157@RunWith(AndroidJUnit4.class)
158public class MyCodeTest {
159    @Rule
160    public final RavenwoodRule mRavenwood = new RavenwoodRule();
161
162    @Test
163    public void testSimple() {
164        // Simple test that runs on both devices and Ravenwood
165    }
166
167    @Test
168    @IgnoreUnderRavenwood(blockedBy = PackageManager.class)
169    public void testComplex() {
170        // Complex test that runs on devices, but is ignored under Ravenwood
171    }
172}
173```
174
175At 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:
176
177```
178android_test {
179    name: "MyTestsDevice",
180    resource_dirs: ["res"],
181...
182
183android_ravenwood_test {
184    name: "MyTestsRavenwood",
185    srcs: [
186        ":MyTestsDevice{.aapt.srcjar}",
187...
188```
189
190## Strategies for unsupported APIs
191
192As 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:
193
194* 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`.)
195    * 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:
196
197```
198import java.nio.file.Files;
199
200@RunWith(AndroidJUnit4.class)
201public class MyTest {
202    @Before
203    public void setUp() throws Exception {
204        File tempDir = Files.createTempDirectory("MyTest").toFile();
205...
206```
207
208* 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.
209
210## Strategies for debugging test development
211
212When 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:
213
214```
215$ atest MyTestsRavenwood
216...
217Test Logs have saved in /tmp/atest_result/20231128_094010_0e90t8v8/log
218Run 'atest --history' to review test result history.
219```
220
221The 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:
222
223```
224$ less /tmp/atest_result/20231128_133105_h9al__79/log/i*/i*/isolated-java-logs*
225```
226
227Here are some common known issues and recommended workarounds:
228
229* 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()`.
230* 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.
231    * This may also manifest as very odd behavior, such as test not being executed at all, tracked by bug #312517322
232    * This may also manifest as an obscure Mockito error claiming “Mockito can only mock non-private & non-final classes”
233