1# SDK Sandbox Test Scenario Test Utilities 2 3## Overview 4 5This directory contains utilities that allow you to run tests inside of an SDK. 6 7[design](http://go/sandbox-webview-cts-tests) (*only visible to Googlers) 8 9Three public class are provided: 10- SdkSandboxScenarioRule: This is a custom JUnit rule used for creating a test environment 11and invoking test methods 12- SdkSandboxTestScenarioRunner: This is a custom SandboxedSdkProvider that manages test 13invocations and reporting the results from within a SDK Sandbox 14 15## Creating new SDK Runtime tests 16 17A simple example of using SDK Runtime testscenario utilities can be found in 18//packages/modules/AdServices/sdksandbox/tests/testutils/testscenario/example. 19 20If you need to add an entirely new JUnit test suite, you need to add both a JUnit test 21suite, and a new testable SDK. 22 23### Create a new Test SDK 24 25You will first need to define a new Sandbox SDK with your 26test cases. Create a new module for your sdk side tests. 27Inside it, create three new files: 28- AndroidManifest.xml 29- Android.bp 30- src/<package/path>/\<TestName>TestSdk.java 31 32Write to the `Android.bp`: 33 34```soong 35android_test_helper_app { 36 name: "<TestName>TestSdk", 37 manifest: "AndroidManifest.xml", 38 // This is a certificate provided by the 39 // sandbox test utilities that will be used 40 // by your JUnit test suite to load 41 // this sdk. 42 certificate: ":sdksandbox-test", 43 srcs: [ 44 "src/**/*.java", 45 ], 46 platform_apis: true, 47 // This runner is used to execute SDK side tests. 48 static_libs: ["CtsSdkSandboxTestRunner"], 49 libs: ["android.test.base"], 50} 51``` 52 53Write to the `AndroidManifest.xml`: 54 55```xml 56<!-- This is all normal configuration for a Sandbox SDK provider --> 57<manifest xmlns:android="http://schemas.android.com/apk/res/android" 58 package="<your.test.package>"> 59 60 <application> 61 <sdk-library android:name="<your.test.package>" 62 android:versionMajor="1" /> 63 <property android:name="android.sdksandbox.PROPERTY_SDK_PROVIDER_CLASS_NAME" 64 android:value="<your.test.package>.<TestName>TestSdk" /> 65 </application> 66</manifest> 67``` 68 69Finally define a test within \<TestName>Sdk.java (eg: ExampleSandboxTestSdk): 70 71```java 72import com.android.sdkSandboxTestUtils.SdkSandboxTestScenarioRunner; 73 74// The SdkSandboxTestScenarioRunner will be responsible 75// for listening for commands to execute tests from 76// the JUnit test suite and returning results. 77public class ExampleSandboxTestSdk extends SdkSandboxTestScenarioRunner { 78 @Override 79 public View beforeEachTest(Context windowContext, Bundle params, int width, int height) { 80 // You can optionally override this method to return a view 81 // that should be added to the sandbox before each test. 82 // ... 83 } 84 85 @Test 86 public void testExample() { 87 // You write tests as you normally would. 88 // These test failures will be propagated back to the JUnit 89 // test suite. 90 assertTrue(true); 91 } 92 93 // You can optionally expect parameters 94 // as part of the test definitions. 95 // This is useful for when tests require 96 // setup externally and need configuration. 97 // For example, a local host server is set up 98 // outside this test and this test needs to know 99 // what port it's running on. 100 @Test 101 public void testExampleWithParam(Bundle params) { 102 params.getString("someParameter"); 103 } 104} 105``` 106 107These tests will not execute on their own. 108They need to be invoked from a JUnit test suite. 109We will add this in the next section. 110 111### Invoke from a JUnit test suite 112 113This guide will skip over defining a new JUnit test suite, 114as this is the same as any other JUnit test suite in CTS. 115 116Within the `AndroidManifest.xml`, specify that the 117JUnit test suite APK relies on the SDK you defined: 118 119```xml 120<application> 121 <!-- Note the certificate should be what is defined here - this is sdksandbox-test --> 122 <uses-sdk-library android:name="<your.test.sdk.package>" 123 android:versionMajor="1" 124 android:certDigest="0B:44:2D:88:FA:A7:B3:AD:23:8D:DE:29:8A:A1:9B:D5:62:03:92:0B:BF:D8:D3:EB:C8:99:33:2C:8E:E1:15:99" /> 125</application> 126``` 127 128In `AndroidTest.xml`, specify that your test needs to install 129the SDK you created: 130 131```xml 132<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> 133 <option name="test-file-name" value="<TestName>Sdk" /> 134</target_preparer> 135``` 136 137Finally, define that your SDK should 138be built and your JUnit test suite relies on 139CtsSdkSandboxTestScenario in your `Android.bp`: 140 141```soong 142... 143static_libs: [ 144 // This will be used to invoke test methods 145 // from within the test Sandbox SDK. 146 "CtsSdkSandboxTestScenario", 147], 148data: [ 149 // Define your test SDK as a data 150 // dependency so that it is built before 151 // this JUnit test suite is built. 152 ":<TestName>TestSdk", 153], 154... 155``` 156 157You can now invoke tests from your JUnit test suite as shown. Note that 158SdkSandboxScenarioRule, when annotated as a @ClassRule, will create a new 159activity for each test, but persist the same test SDK for all tests. If for some 160reason the SDK is not found by a test, it will attempt to reload it. 161 162If you wish to run your tests in a way such that the test SDK is reloaded after 163each test, you can simply annotate the SdkSandboxScenarioRule as a @Rule and 164everything else remains the same. 165 166```java 167import android.app.sdksandbox.testutils.testscenario.SdkSandboxScenarioRule; 168 169public class ExampleSandboxTest { 170 // This rule will automatically create a new test activity 171 // and for each test but persist the Sdk. 172 @ClassRule 173 public static final SdkSandboxScenarioRule sdkTester = new SdkSandboxScenarioRule( 174 "your.test.sdk.package"); 175 176 @Test 177 public void testExample() throws Throwable { 178 // This method will invoke a test and assert the results. 179 sdkTester.assertSdkTestRunPasses("testExample"); 180 181 // You can optionally provide parameters to your 182 // tests for setup. 183 Bundle params = new Bundle(); 184 params.put("someParameter", "A value"); 185 sdkTester.assertSdkTestRunPasses("testExampleWithParam", params); 186 } 187} 188``` 189 190## Custom test instances 191 192The `SdkSandboxTestScenarioRunner` supports invoking tests on different 193test instances from the class running your tests inside the test SDK. 194For example, you may have tests you wish to also run in a non SDK context. 195 196```java 197// You can define tests inside a separate class. 198public class ActuallyHasTests { 199 @Test 200 public void someTest() { 201 assertTrue(true); 202 } 203} 204 205public class ExampleSandboxTestSdk extends SdkSandboxTestScenarioRunner { 206 // And then optionally override the onLoadSdk method and use 207 // the API setTestInstance to set an instance to invoke tests from. 208 @Override 209 public SandboxedSdk onLoadSdk(Bundle params) { 210 setTestInstance(new ActuallyHasTests()); 211 return super.onLoadSdk(params); 212 } 213} 214``` 215 216## Custom setup 217 218There may be times when you need to pass through custom setup information to your sdk. 219 220You can optionally provide a Bundle 221(https://developer.android.com/reference/android/os/Bundle) to SdkSandboxScenarioRule 222that can be retrieved and used from inside test SDKs via the onLoadSdk method. 223 224One example is if you want to reuse an sdk in order to test multiple test instances. For this you 225could pass setup information to determine the specific test instance to use: 226 227```java 228public class ExampleSandboxTestSdk extends SdkSandboxTestScenarioRunner { 229 230 private ExampleParentTestClass mTestInstance; 231 232 @Override 233 public SandboxedSdk onLoadSdk(Bundle params) { 234 Bundle setupParams = params.getBundle(ISdkSandboxTestExecutor.TEST_SETUP_PARAMS); 235 if (setupParams != null) { 236 //logic for setting mTestInstance based on params 237 mTestInstance = new ExampleChildTestClass(); 238 } 239 240 setTestInstance(mTestInstance); 241 return super.onLoadSdk(params); 242 } 243} 244``` 245 246## Custom test binders 247 248There may be times where you want your tests to invoke behavior outside of 249the SDK. For example, you may want to perform a tap event using the instrumentation. 250 251You can optionally provide a custom [IBinder] 252(https://developer.android.com/reference/android/os/IBinder) to SdkSandboxScenarioRule 253that can be retrieved and used from inside test SDKs. 254 255In the example below, the following custom aidl will be used: 256 257```aidl 258interface SharedCustomBinder { 259 void doesSomethingOutsideSdk(); 260} 261``` 262 263```java 264public class ExampleSandboxTest { 265 // Provide a stub to the SdkSandboxScenarioRule. 266 @Rule 267 public final SdkSandboxScenarioRule sdkTester = new SdkSandboxScenarioRule( 268 "your.test.sdk.package", new SharedCustomBinder.Stub() { 269 public void doesSomethingOutsideSdk() { 270 } 271 }); 272} 273 274public class ExampleSandboxTestSdk extends SdkSandboxTestScenarioRunner { 275 @Test 276 public void exampleTest() throws Exception { 277 // The IBinder can be retrieved inside SDK tests using the API 278 // getCustomInterface(). 279 SharedCustomBinder binder = SharedCustomBinder.Stub.asInterface(getCustomInterface()); 280 // 281 binder.doesSomethingOutsideSdk(); 282 // ... 283 } 284} 285``` 286