Loading services/robotests/Android.mk +3 −4 Original line number Diff line number Diff line Loading @@ -40,15 +40,15 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) # Include the testing libraries (JUnit4 + Robolectric libs). LOCAL_STATIC_JAVA_LIBRARIES := \ platform-robolectric-android-all-stubs \ android-support-test \ mockito-robolectric-prebuilt \ platform-test-annotations \ truth-prebuilt # TODO(b/69254249): Migrate to Robolectric 3.4.2 LOCAL_JAVA_LIBRARIES := \ junit \ platform-robolectric-3.1.1-prebuilt platform-robolectric-3.5.1-prebuilt LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib LOCAL_MODULE := FrameworksServicesRoboTests Loading @@ -73,5 +73,4 @@ LOCAL_TEST_PACKAGE := FrameworksServicesLib LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java # TODO(b/69254249): Migrate to Robolectric 3.4.2 include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk services/robotests/src/com/android/server/backup/TransportManagerTest.java +17 −12 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.robolectric.shadow.api.Shadow.extract; import android.app.backup.BackupManager; import android.content.ComponentName; Loading @@ -31,24 +32,25 @@ import android.os.IBinder; import android.platform.test.annotations.Presubmit; import com.android.server.backup.testing.BackupTransportStub; import com.android.server.backup.testing.DefaultPackageManagerWithQueryIntentServicesAsUser; import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.res.builder.RobolectricPackageManager; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.util.ArrayList; import java.util.Arrays; Loading @@ -56,15 +58,17 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; @RunWith(RobolectricTestRunner.class) @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, sdk = 23, sdk = 26, shadows = { ShadowContextImplForBackup.class, ShadowBackupTransportStub.class ShadowBackupTransportStub.class, ShadowPackageManagerForBackup.class } ) @SystemLoaderClasses({TransportManager.class}) @Presubmit public class TransportManagerTest { private static final String PACKAGE_NAME = "some.package.name"; Loading @@ -73,7 +77,7 @@ public class TransportManagerTest { private TransportInfo mTransport1; private TransportInfo mTransport2; private RobolectricPackageManager mPackageManager; private ShadowPackageManager mPackageManagerShadow; private final TransportBoundListenerStub mTransportBoundListenerStub = new TransportBoundListenerStub(true); Loading @@ -86,9 +90,10 @@ public class TransportManagerTest { MockitoAnnotations.initMocks(this); ShadowLog.stream = System.out; mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser( RuntimeEnvironment.getAppResourceLoader()); RuntimeEnvironment.setRobolectricPackageManager(mPackageManager); mPackageManagerShadow = (ShadowPackageManagerForBackup) extract(RuntimeEnvironment.application.getPackageManager()); mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name"); mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name"); Loading Loading @@ -539,7 +544,7 @@ public class TransportManagerTest { packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.privateFlags = flags; mPackageManager.addPackage(packageInfo); mPackageManagerShadow.addPackage(packageInfo); List<ResolveInfo> transportsInfo = new ArrayList<>(); for (TransportInfo transport : transports) { Loading @@ -553,7 +558,7 @@ public class TransportManagerTest { Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST); intent.setPackage(packageName); mPackageManager.addResolveInfoForIntent(intent, transportsInfo); mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo); } private TransportManager createTransportManagerAndSetUpTransports( Loading services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java→services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java +6 −12 Original line number Diff line number Diff line Loading @@ -16,28 +16,22 @@ package com.android.server.backup.testing; import android.app.ApplicationPackageManager; import android.content.Intent; import android.content.pm.ResolveInfo; import org.robolectric.res.ResourceLoader; import org.robolectric.res.builder.DefaultPackageManager; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplicationPackageManager; import java.util.List; /** * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser(). */ public class DefaultPackageManagerWithQueryIntentServicesAsUser extends DefaultPackageManager { /* package */ public DefaultPackageManagerWithQueryIntentServicesAsUser( ResourceLoader appResourceLoader) { super(appResourceLoader); } @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager { @Override public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) { return super.queryIntentServices(intent, flags); return queryIntentServices(intent, flags); } } services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +28 −21 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.backup.transport; import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -37,31 +38,34 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import com.android.internal.backup.IBackupTransport; import com.android.server.backup.TransportManager; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 23) @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @SystemLoaderClasses({TransportManager.class, TransportClient.class}) @Presubmit public class TransportClientTest { private static final String PACKAGE_NAME = "some.package.name"; private static final ComponentName TRANSPORT_COMPONENT = new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport"); private static final String TRANSPORT_DIR_NAME = TRANSPORT_COMPONENT.toString(); @Mock private Context mContext; @Mock private TransportConnectionListener mTransportConnectionListener; @Mock private TransportConnectionListener mTransportConnectionListener2; @Mock private IBackupTransport.Stub mIBackupTransport; private TransportClient mTransportClient; private ComponentName mTransportComponent; private String mTransportDirName; private Intent mBindIntent; private ShadowLooper mShadowLooper; Loading @@ -71,13 +75,16 @@ public class TransportClientTest { Looper mainLooper = Looper.getMainLooper(); mShadowLooper = shadowOf(mainLooper); mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(TRANSPORT_COMPONENT); mTransportComponent = new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport"); mTransportDirName = mTransportComponent.toString(); mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent); mTransportClient = new TransportClient( mContext, mBindIntent, TRANSPORT_COMPONENT, TRANSPORT_DIR_NAME, mTransportComponent, mTransportDirName, "1", new Handler(mainLooper)); Loading @@ -91,12 +98,12 @@ public class TransportClientTest { @Test public void testGetTransportDirName_returnsTransportDirName() { assertThat(mTransportClient.getTransportDirName()).isEqualTo(TRANSPORT_DIR_NAME); assertThat(mTransportClient.getTransportDirName()).isEqualTo(mTransportDirName); } @Test public void testGetTransportComponent_returnsTransportComponent() { assertThat(mTransportClient.getTransportComponent()).isEqualTo(TRANSPORT_COMPONENT); assertThat(mTransportClient.getTransportComponent()).isEqualTo(mTransportComponent); } @Test Loading @@ -117,7 +124,7 @@ public class TransportClientTest { // Simulate framework connecting ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mShadowLooper.runToEndOfTasks(); verify(mTransportConnectionListener) Loading @@ -132,7 +139,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mShadowLooper.runToEndOfTasks(); verify(mTransportConnectionListener) Loading @@ -145,7 +152,7 @@ public class TransportClientTest { public void testConnectAsync_whenAlreadyConnected_callsListener() throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); Loading Loading @@ -190,8 +197,8 @@ public class TransportClientTest { throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceDisconnected(TRANSPORT_COMPONENT); connection.onServiceConnected(mTransportComponent, mIBackupTransport); connection.onServiceDisconnected(mTransportComponent); mTransportClient.connectAsync(mTransportConnectionListener2, "caller1"); Loading @@ -204,9 +211,9 @@ public class TransportClientTest { throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceDisconnected(TRANSPORT_COMPONENT); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); connection.onServiceDisconnected(mTransportComponent); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mTransportClient.connectAsync(mTransportConnectionListener2, "caller1"); Loading @@ -221,7 +228,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportListener, "caller"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onBindingDied(TRANSPORT_COMPONENT); connection.onBindingDied(mTransportComponent); mShadowLooper.runToEndOfTasks(); verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient)); Loading @@ -235,7 +242,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportListener2, "caller2"); connection.onBindingDied(TRANSPORT_COMPONENT); connection.onBindingDied(mTransportComponent); mShadowLooper.runToEndOfTasks(); verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient)); Loading services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java 0 → 100644 +162 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.testing; import com.google.common.collect.ImmutableSet; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.robolectric.RobolectricTestRunner; import org.robolectric.internal.SandboxFactory; import org.robolectric.internal.SdkEnvironment; import org.robolectric.internal.bytecode.InstrumentationConfiguration; import org.robolectric.internal.bytecode.SandboxClassLoader; import org.robolectric.util.Util; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URL; import java.util.Arrays; import java.util.Set; import javax.annotation.Nonnull; /** * HACK * Robolectric loads up Android environment from prebuilt android jars before running a method. * These jars are versioned according to the SDK level configured for the method (or class). The * jars represent a snapshot of the Android APIs in that SDK level. For Robolectric tests that are * testing Android components themselves we don't want certain classes (usually the * class-under-test) to be loaded from the prebuilt jar, we want it instead to be loaded from the * dependencies of our test target, i.e. the system class loader. That way we can write tests * against the actual classes that are in the tree, not a past version of them. Ideally we would * have a locally built jar referenced by Robolectric, but until that happens one can use this * class. * This class reads the {@link SystemLoaderClasses} annotation on test classes and for each class * in that annotation value it will bypass the android jar and load it from the system class loader. * Allowing the test to test the actual class in the tree. * * Implementation note: One could think about overriding * {@link RobolectricTestRunner#createClassLoaderConfig(FrameworkMethod)} method and putting the * classes in the annotation in the {@link InstrumentationConfiguration} list of classes not to * acquire. Unfortunately, this will not work because we will not be instrumenting the class. * Instead, we have to load the class bytes from the system class loader but still instrument it, we * do this by overriding {@link SandboxClassLoader#getByteCode(String)} and loading the class bytes * from the system class loader if it in the {@link SystemLoaderClasses} annotation. This way the * {@link SandboxClassLoader} still instruments the class, but it's not loaded from the android jar. * Finally, we inject the custom class loader in place of the default one. * * TODO: Remove this when we are using locally built android jars in the method's environment. */ public class FrameworkRobolectricTestRunner extends RobolectricTestRunner { private final SandboxFactory mSandboxFactory; public FrameworkRobolectricTestRunner(Class<?> testClass) throws InitializationError { super(testClass); SystemLoaderClasses annotation = testClass.getAnnotation(SystemLoaderClasses.class); Class<?>[] systemLoaderClasses = (annotation != null) ? annotation.value() : new Class<?>[0]; Set<String> systemLoaderClassNames = classesToClassNames(systemLoaderClasses); mSandboxFactory = new FrameworkSandboxFactory(systemLoaderClassNames); } @Nonnull @Override protected SdkEnvironment getSandbox(FrameworkMethod method) { // HACK: Calling super just to get SdkConfig via sandbox.getSdkConfig(), because // RobolectricFrameworkMethod, the runtime class of method, is package-protected SdkEnvironment sandbox = super.getSandbox(method); return mSandboxFactory.getSdkEnvironment( createClassLoaderConfig(method), getJarResolver(), sandbox.getSdkConfig()); } private static class FrameworkClassLoader extends SandboxClassLoader { private final Set<String> mSystemLoaderClasses; private FrameworkClassLoader( Set<String> systemLoaderClasses, ClassLoader systemClassLoader, InstrumentationConfiguration instrumentationConfig, URL... urls) { super(systemClassLoader, instrumentationConfig, urls); mSystemLoaderClasses = systemLoaderClasses; } @Override protected byte[] getByteCode(String className) throws ClassNotFoundException { String classFileName = className.replace('.', '/') + ".class"; if (shouldLoadFromSystemLoader(className)) { try (InputStream classByteStream = getResourceAsStream(classFileName)) { if (classByteStream == null) { throw new ClassNotFoundException(className); } return Util.readBytes(classByteStream); } catch (IOException e) { throw new ClassNotFoundException( "Couldn't load " + className + " from system class loader", e); } } return super.getByteCode(className); } /** * Classes like com.package.ClassName$InnerClass should also be loaded from the system class * loader, so we test if the classes in the annotation are prefixes of the class to load. */ private boolean shouldLoadFromSystemLoader(String className) { for (String classNamePrefix : mSystemLoaderClasses) { if (className.startsWith(classNamePrefix)) { return true; } } return false; } } private static class FrameworkSandboxFactory extends SandboxFactory { private final Set<String> mSystemLoaderClasses; private FrameworkSandboxFactory(Set<String> systemLoaderClasses) { mSystemLoaderClasses = systemLoaderClasses; } @Nonnull @Override public ClassLoader createClassLoader( InstrumentationConfiguration instrumentationConfig, URL... urls) { return new FrameworkClassLoader( mSystemLoaderClasses, ClassLoader.getSystemClassLoader(), instrumentationConfig, urls); } } private static Set<String> classesToClassNames(Class<?>[] classes) { ImmutableSet.Builder<String> builder = ImmutableSet.builder(); for (Class<?> classObject : classes) { builder.add(classObject.getName()); } return builder.build(); } } Loading
services/robotests/Android.mk +3 −4 Original line number Diff line number Diff line Loading @@ -40,15 +40,15 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) # Include the testing libraries (JUnit4 + Robolectric libs). LOCAL_STATIC_JAVA_LIBRARIES := \ platform-robolectric-android-all-stubs \ android-support-test \ mockito-robolectric-prebuilt \ platform-test-annotations \ truth-prebuilt # TODO(b/69254249): Migrate to Robolectric 3.4.2 LOCAL_JAVA_LIBRARIES := \ junit \ platform-robolectric-3.1.1-prebuilt platform-robolectric-3.5.1-prebuilt LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib LOCAL_MODULE := FrameworksServicesRoboTests Loading @@ -73,5 +73,4 @@ LOCAL_TEST_PACKAGE := FrameworksServicesLib LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java # TODO(b/69254249): Migrate to Robolectric 3.4.2 include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk include prebuilts/misc/common/robolectric/3.5.1/run_robotests.mk
services/robotests/src/com/android/server/backup/TransportManagerTest.java +17 −12 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.backup; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.robolectric.shadow.api.Shadow.extract; import android.app.backup.BackupManager; import android.content.ComponentName; Loading @@ -31,24 +32,25 @@ import android.os.IBinder; import android.platform.test.annotations.Presubmit; import com.android.server.backup.testing.BackupTransportStub; import com.android.server.backup.testing.DefaultPackageManagerWithQueryIntentServicesAsUser; import com.android.server.backup.testing.ShadowBackupTransportStub; import com.android.server.backup.testing.ShadowContextImplForBackup; import com.android.server.backup.testing.ShadowPackageManagerForBackup; import com.android.server.backup.testing.TransportBoundListenerStub; import com.android.server.backup.testing.TransportReadyCallbackStub; import com.android.server.backup.transport.TransportClient; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.res.builder.RobolectricPackageManager; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowPackageManager; import java.util.ArrayList; import java.util.Arrays; Loading @@ -56,15 +58,17 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; @RunWith(RobolectricTestRunner.class) @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, sdk = 23, sdk = 26, shadows = { ShadowContextImplForBackup.class, ShadowBackupTransportStub.class ShadowBackupTransportStub.class, ShadowPackageManagerForBackup.class } ) @SystemLoaderClasses({TransportManager.class}) @Presubmit public class TransportManagerTest { private static final String PACKAGE_NAME = "some.package.name"; Loading @@ -73,7 +77,7 @@ public class TransportManagerTest { private TransportInfo mTransport1; private TransportInfo mTransport2; private RobolectricPackageManager mPackageManager; private ShadowPackageManager mPackageManagerShadow; private final TransportBoundListenerStub mTransportBoundListenerStub = new TransportBoundListenerStub(true); Loading @@ -86,9 +90,10 @@ public class TransportManagerTest { MockitoAnnotations.initMocks(this); ShadowLog.stream = System.out; mPackageManager = new DefaultPackageManagerWithQueryIntentServicesAsUser( RuntimeEnvironment.getAppResourceLoader()); RuntimeEnvironment.setRobolectricPackageManager(mPackageManager); mPackageManagerShadow = (ShadowPackageManagerForBackup) extract(RuntimeEnvironment.application.getPackageManager()); mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name"); mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name"); Loading Loading @@ -539,7 +544,7 @@ public class TransportManagerTest { packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.privateFlags = flags; mPackageManager.addPackage(packageInfo); mPackageManagerShadow.addPackage(packageInfo); List<ResolveInfo> transportsInfo = new ArrayList<>(); for (TransportInfo transport : transports) { Loading @@ -553,7 +558,7 @@ public class TransportManagerTest { Intent intent = new Intent(TransportManager.SERVICE_ACTION_TRANSPORT_HOST); intent.setPackage(packageName); mPackageManager.addResolveInfoForIntent(intent, transportsInfo); mPackageManagerShadow.addResolveInfoForIntent(intent, transportsInfo); } private TransportManager createTransportManagerAndSetUpTransports( Loading
services/robotests/src/com/android/server/backup/testing/DefaultPackageManagerWithQueryIntentServicesAsUser.java→services/robotests/src/com/android/server/backup/testing/ShadowPackageManagerForBackup.java +6 −12 Original line number Diff line number Diff line Loading @@ -16,28 +16,22 @@ package com.android.server.backup.testing; import android.app.ApplicationPackageManager; import android.content.Intent; import android.content.pm.ResolveInfo; import org.robolectric.res.ResourceLoader; import org.robolectric.res.builder.DefaultPackageManager; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplicationPackageManager; import java.util.List; /** * Implementation of PackageManager for Robolectric which handles queryIntentServicesAsUser(). */ public class DefaultPackageManagerWithQueryIntentServicesAsUser extends DefaultPackageManager { /* package */ public DefaultPackageManagerWithQueryIntentServicesAsUser( ResourceLoader appResourceLoader) { super(appResourceLoader); } @Implements(value = ApplicationPackageManager.class, inheritImplementationMethods = true) public class ShadowPackageManagerForBackup extends ShadowApplicationPackageManager { @Override public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) { return super.queryIntentServices(intent, flags); return queryIntentServices(intent, flags); } }
services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +28 −21 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.backup.transport; import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -37,31 +38,34 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import com.android.internal.backup.IBackupTransport; import com.android.server.backup.TransportManager; import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderClasses; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 23) @RunWith(FrameworkRobolectricTestRunner.class) @Config(manifest = Config.NONE, sdk = 26) @SystemLoaderClasses({TransportManager.class, TransportClient.class}) @Presubmit public class TransportClientTest { private static final String PACKAGE_NAME = "some.package.name"; private static final ComponentName TRANSPORT_COMPONENT = new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport"); private static final String TRANSPORT_DIR_NAME = TRANSPORT_COMPONENT.toString(); @Mock private Context mContext; @Mock private TransportConnectionListener mTransportConnectionListener; @Mock private TransportConnectionListener mTransportConnectionListener2; @Mock private IBackupTransport.Stub mIBackupTransport; private TransportClient mTransportClient; private ComponentName mTransportComponent; private String mTransportDirName; private Intent mBindIntent; private ShadowLooper mShadowLooper; Loading @@ -71,13 +75,16 @@ public class TransportClientTest { Looper mainLooper = Looper.getMainLooper(); mShadowLooper = shadowOf(mainLooper); mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(TRANSPORT_COMPONENT); mTransportComponent = new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport"); mTransportDirName = mTransportComponent.toString(); mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent); mTransportClient = new TransportClient( mContext, mBindIntent, TRANSPORT_COMPONENT, TRANSPORT_DIR_NAME, mTransportComponent, mTransportDirName, "1", new Handler(mainLooper)); Loading @@ -91,12 +98,12 @@ public class TransportClientTest { @Test public void testGetTransportDirName_returnsTransportDirName() { assertThat(mTransportClient.getTransportDirName()).isEqualTo(TRANSPORT_DIR_NAME); assertThat(mTransportClient.getTransportDirName()).isEqualTo(mTransportDirName); } @Test public void testGetTransportComponent_returnsTransportComponent() { assertThat(mTransportClient.getTransportComponent()).isEqualTo(TRANSPORT_COMPONENT); assertThat(mTransportClient.getTransportComponent()).isEqualTo(mTransportComponent); } @Test Loading @@ -117,7 +124,7 @@ public class TransportClientTest { // Simulate framework connecting ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mShadowLooper.runToEndOfTasks(); verify(mTransportConnectionListener) Loading @@ -132,7 +139,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mShadowLooper.runToEndOfTasks(); verify(mTransportConnectionListener) Loading @@ -145,7 +152,7 @@ public class TransportClientTest { public void testConnectAsync_whenAlreadyConnected_callsListener() throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); Loading Loading @@ -190,8 +197,8 @@ public class TransportClientTest { throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceDisconnected(TRANSPORT_COMPONENT); connection.onServiceConnected(mTransportComponent, mIBackupTransport); connection.onServiceDisconnected(mTransportComponent); mTransportClient.connectAsync(mTransportConnectionListener2, "caller1"); Loading @@ -204,9 +211,9 @@ public class TransportClientTest { throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceDisconnected(TRANSPORT_COMPONENT); connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport); connection.onServiceConnected(mTransportComponent, mIBackupTransport); connection.onServiceDisconnected(mTransportComponent); connection.onServiceConnected(mTransportComponent, mIBackupTransport); mTransportClient.connectAsync(mTransportConnectionListener2, "caller1"); Loading @@ -221,7 +228,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportListener, "caller"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); connection.onBindingDied(TRANSPORT_COMPONENT); connection.onBindingDied(mTransportComponent); mShadowLooper.runToEndOfTasks(); verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient)); Loading @@ -235,7 +242,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportListener2, "caller2"); connection.onBindingDied(TRANSPORT_COMPONENT); connection.onBindingDied(mTransportComponent); mShadowLooper.runToEndOfTasks(); verify(mTransportListener).onTransportBound(isNull(), eq(mTransportClient)); Loading
services/robotests/src/com/android/server/testing/FrameworkRobolectricTestRunner.java 0 → 100644 +162 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.server.testing; import com.google.common.collect.ImmutableSet; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.robolectric.RobolectricTestRunner; import org.robolectric.internal.SandboxFactory; import org.robolectric.internal.SdkEnvironment; import org.robolectric.internal.bytecode.InstrumentationConfiguration; import org.robolectric.internal.bytecode.SandboxClassLoader; import org.robolectric.util.Util; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URL; import java.util.Arrays; import java.util.Set; import javax.annotation.Nonnull; /** * HACK * Robolectric loads up Android environment from prebuilt android jars before running a method. * These jars are versioned according to the SDK level configured for the method (or class). The * jars represent a snapshot of the Android APIs in that SDK level. For Robolectric tests that are * testing Android components themselves we don't want certain classes (usually the * class-under-test) to be loaded from the prebuilt jar, we want it instead to be loaded from the * dependencies of our test target, i.e. the system class loader. That way we can write tests * against the actual classes that are in the tree, not a past version of them. Ideally we would * have a locally built jar referenced by Robolectric, but until that happens one can use this * class. * This class reads the {@link SystemLoaderClasses} annotation on test classes and for each class * in that annotation value it will bypass the android jar and load it from the system class loader. * Allowing the test to test the actual class in the tree. * * Implementation note: One could think about overriding * {@link RobolectricTestRunner#createClassLoaderConfig(FrameworkMethod)} method and putting the * classes in the annotation in the {@link InstrumentationConfiguration} list of classes not to * acquire. Unfortunately, this will not work because we will not be instrumenting the class. * Instead, we have to load the class bytes from the system class loader but still instrument it, we * do this by overriding {@link SandboxClassLoader#getByteCode(String)} and loading the class bytes * from the system class loader if it in the {@link SystemLoaderClasses} annotation. This way the * {@link SandboxClassLoader} still instruments the class, but it's not loaded from the android jar. * Finally, we inject the custom class loader in place of the default one. * * TODO: Remove this when we are using locally built android jars in the method's environment. */ public class FrameworkRobolectricTestRunner extends RobolectricTestRunner { private final SandboxFactory mSandboxFactory; public FrameworkRobolectricTestRunner(Class<?> testClass) throws InitializationError { super(testClass); SystemLoaderClasses annotation = testClass.getAnnotation(SystemLoaderClasses.class); Class<?>[] systemLoaderClasses = (annotation != null) ? annotation.value() : new Class<?>[0]; Set<String> systemLoaderClassNames = classesToClassNames(systemLoaderClasses); mSandboxFactory = new FrameworkSandboxFactory(systemLoaderClassNames); } @Nonnull @Override protected SdkEnvironment getSandbox(FrameworkMethod method) { // HACK: Calling super just to get SdkConfig via sandbox.getSdkConfig(), because // RobolectricFrameworkMethod, the runtime class of method, is package-protected SdkEnvironment sandbox = super.getSandbox(method); return mSandboxFactory.getSdkEnvironment( createClassLoaderConfig(method), getJarResolver(), sandbox.getSdkConfig()); } private static class FrameworkClassLoader extends SandboxClassLoader { private final Set<String> mSystemLoaderClasses; private FrameworkClassLoader( Set<String> systemLoaderClasses, ClassLoader systemClassLoader, InstrumentationConfiguration instrumentationConfig, URL... urls) { super(systemClassLoader, instrumentationConfig, urls); mSystemLoaderClasses = systemLoaderClasses; } @Override protected byte[] getByteCode(String className) throws ClassNotFoundException { String classFileName = className.replace('.', '/') + ".class"; if (shouldLoadFromSystemLoader(className)) { try (InputStream classByteStream = getResourceAsStream(classFileName)) { if (classByteStream == null) { throw new ClassNotFoundException(className); } return Util.readBytes(classByteStream); } catch (IOException e) { throw new ClassNotFoundException( "Couldn't load " + className + " from system class loader", e); } } return super.getByteCode(className); } /** * Classes like com.package.ClassName$InnerClass should also be loaded from the system class * loader, so we test if the classes in the annotation are prefixes of the class to load. */ private boolean shouldLoadFromSystemLoader(String className) { for (String classNamePrefix : mSystemLoaderClasses) { if (className.startsWith(classNamePrefix)) { return true; } } return false; } } private static class FrameworkSandboxFactory extends SandboxFactory { private final Set<String> mSystemLoaderClasses; private FrameworkSandboxFactory(Set<String> systemLoaderClasses) { mSystemLoaderClasses = systemLoaderClasses; } @Nonnull @Override public ClassLoader createClassLoader( InstrumentationConfiguration instrumentationConfig, URL... urls) { return new FrameworkClassLoader( mSystemLoaderClasses, ClassLoader.getSystemClassLoader(), instrumentationConfig, urls); } } private static Set<String> classesToClassNames(Class<?>[] classes) { ImmutableSet.Builder<String> builder = ImmutableSet.builder(); for (Class<?> classObject : classes) { builder.add(classObject.getName()); } return builder.build(); } }