diff options
author | 2025-03-20 14:32:44 -0700 | |
---|---|---|
committer | 2025-03-20 14:33:10 -0700 | |
commit | 88ee98465f42fc45271de6b7df00a757dff6ccc0 (patch) | |
tree | 6f72fb2e3af8d6132c0cabdfd82580febb24c6ea | |
parent | 0fa17c8466315b592ee4bd6310b88f75cde07a71 (diff) |
[Ravenwood] Update documentation and Javadocs
Bug: 340867849
Flag: EXEMPT host side change only
Test: document change only
Change-Id: Ie19d7188cae73cf2dc7ebb49d9f4e64abfb1c353
-rw-r--r-- | ravenwood/api-maintainers.md | 66 | ||||
-rw-r--r-- | ravenwood/test-authors.md | 90 |
2 files changed, 83 insertions, 73 deletions
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md index 4b2f96804c97..142492eace98 100644 --- a/ravenwood/api-maintainers.md +++ b/ravenwood/api-maintainers.md @@ -60,8 +60,51 @@ public class MyStruct { // doComplex$ravenwood() method implementation under Ravenwood } - public void doComplex$ravenwood() { - // This method implementation only runs under Ravenwood + private void doComplex$ravenwood() { + // This method implementation only runs under Ravenwood. + // The visibility of this replacement method does not need to match + // the original method, so it's recommmended to always use + // private methods so that these methods won't be accidentally used + // by unexpected users. + } +} +``` + +## Redirect a complex method to a separate class when under Ravenwood + +``` +@RavenwoodKeepPartialClass +@RavenwoodRedirectionClass("MyComplexClass_ravenwood") +public class MyComplexClass { + @RavenwoodRedirect + public void doComplex(int i, int j, int k) { + // This method implementation runs as-is on devices, but calls to this + // method is redirected to MyComplexClass_ravenwood#doComplex() + // under Ravenwood. + } + + @RavenwoodRedirect + public static void staticDoComplex(int i, int j, int k) { + // This method implementation runs as-is on devices, but calls to this + // method is redirected to MyComplexClass_ravenwood#staticDoComplex() + // under Ravenwood. + } + +/** + * This class is only available in the Ravenwood environment. + */ +class MyComplexClass_ravenwood { + public static void doComplex(MyComplexClass obj, int i, int j, int k) { + // Because the original method is a non-static method, the current + // object instance of the original method (the "this" reference) + // will be passed over as the first argument of the redirection method, + // with the remaining arguments to follow. + } + + public static void staticDoComplex(int i, int j, int k) { + // Because the original method is a static method, there is no current + // object instance, so the parameter list of the redirection method + // is the same as the original method. } } ``` @@ -74,21 +117,6 @@ For example, consider a constructor or static initializer that relies on unsuppo ## Strategies for JNI -At the moment, JNI isn't yet supported under Ravenwood, but you may still want to support APIs that are partially implemented with JNI. The current approach is to use the “replace” strategy to offer a pure-Java alternative implementation for any JNI-provided logic. +At the moment, JNI support is considered "experimental". To ensure that teams are well-supported, for any JNI usage, please reach out to ravenwood@ so we can offer design advice and help you onboard native methods. If a native method can be trivially re-implemented in pure-Java, using the replacement or redirection mechanisms described above is also a viable option. -Since this approach requires potentially complex re-implementation, it should only be considered for core infrastructure that is critical to unblocking widespread testing use-cases. Other less-common usages of JNI should instead wait for offical JNI support in the Ravenwood environment. - -When a pure-Java implementation grows too large or complex to host within the original class, the `@RavenwoodNativeSubstitutionClass` annotation can be used to host it in a separate source file: - -``` -@RavenwoodKeepWholeClass -@RavenwoodNativeSubstitutionClass("com.android.platform.test.ravenwood.nativesubstitution.MyComplexClass_host") -public class MyComplexClass { - private static native void nativeDoThing(long nativePtr); -... - -public class MyComplexClass_host { - public static void nativeDoThing(long nativePtr) { - // ... - } -``` +The main caveat regarding JNI support on Ravenwood is due to how native code is built for Ravenwood. Unlike Java/Kotlin code where Ravenwood directly uses the same intermediate artifacts when building the real target for Android, Ravenwood uses the "host" variant of native libraries. The "host" variant of native libraries usually either don't exist, are extremely limited, or behave drastically different compared to the Android variant. diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md index 6d82a744bc4f..b1030ff4e7ed 100644 --- a/ravenwood/test-authors.md +++ b/ravenwood/test-authors.md @@ -17,7 +17,7 @@ To run all Ravenwood tests, use: To run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as: ``` -atest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative +atest CtsOsTestCasesRavenwood:ParcelTest#testSetDataCapacityNegative ``` ## Typical test structure @@ -63,63 +63,63 @@ public class MyCodeTest { } ``` -* 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: +Once you’ve defined your test, you can use typical commands to execute it locally: ``` -import android.platform.test.annotations.DisabledOnRavenwood; -import android.platform.test.ravenwood.RavenwoodRule; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class MyCodeTest { - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() - .setProcessApp() - .setProvideMainThread(true) - .build(); +$ atest MyTestsRavenwood ``` -Once you’ve defined your test, you can use typical commands to execute it locally: +You can also run your new tests automatically via `TEST_MAPPING` rules like this: ``` -$ atest --host MyTestsRavenwood +{ + "ravenwood-presubmit": [ + { + "name": "MyTestsRavenwood", + "host": true + } + ] +} ``` -> **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. +## Using resources -You can also run your new tests automatically via `TEST_MAPPING` rules like this: +At the moment, the `android_ravenwood_test` module type cannot directly build resources yet. In order to use resources in Ravenwood tests, you have to build the resource APK in a separate `android_app` module and "borrow" the resources and R classes: ``` -{ - "ravenwood-presubmit": [ - { - "name": "MyTestsRavenwood", - "host": true - } - ] +android_app { + name: "MyTests-res", + resource_dirs: ["res"], + // This has to be set to false, or ".aapt.srcjar" will not be generated + use_resource_processor: false, } -``` -> **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. +android_ravenwood_test { + name: "MyTestsRavenwood", + srcs: [ + "src/**/*.java", + // ... + + // Include R.java from the resource APK + ":MyTests-res{.aapt.srcjar}", + ], + // Set the resource APK + resource_apk: "MyTests-res", + // ... +} +``` ## Strategies for migration/bivalent tests Ravenwood 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. -In 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. +In situations where a test method depends on API functionality not yet available under Ravenwood, we provide an annotation to quietly skip 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. Test 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. ``` @RunWith(AndroidJUnit4.class) public class MyCodeTest { - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule(); - @Test public void testSimple() { // Simple test that runs on both devices and Ravenwood @@ -128,26 +128,11 @@ public class MyCodeTest { @Test @DisabledOnRavenwood(blockedBy = PackageManager.class) public void testComplex() { - // Complex test that runs on devices, but is ignored under Ravenwood + // Complex test that runs on devices, but is disabled under Ravenwood } } ``` -At 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: - -``` -android_test { - name: "MyTestsDevice", - resource_dirs: ["res"], -... - -android_ravenwood_test { - name: "MyTestsRavenwood", - srcs: [ - ":MyTestsDevice{.aapt.srcjar}", -... -``` - ## Strategies for unsupported APIs As 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: @@ -187,7 +172,4 @@ $ less /tmp/atest_result/20231128_133105_h9al__79/log/i*/i*/isolated-java-logs* Here are some common known issues and recommended workarounds: -* 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()`. -* 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. - * This may also manifest as very odd behavior, such as test not being executed at all, tracked by bug #312517322 - * This may also manifest as an obscure Mockito error claiming “Mockito can only mock non-private & non-final classes” +* 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.isOnRavenwood()`. |