Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d42374e authored by Bernardo Rufino's avatar Bernardo Rufino Committed by Android (Google) Code Review
Browse files

Merge "Update Roboletric to 3.5.1"

parents a1ac208a 90a88023
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -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
@@ -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
+17 −12
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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";
@@ -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);
@@ -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");
@@ -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) {
@@ -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(
+6 −12
Original line number Diff line number Diff line
@@ -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);
    }
}
+28 −21
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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));

@@ -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
@@ -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)
@@ -132,7 +139,7 @@ public class TransportClientTest {

        mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");

        connection.onServiceConnected(TRANSPORT_COMPONENT, mIBackupTransport);
        connection.onServiceConnected(mTransportComponent, mIBackupTransport);

        mShadowLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
@@ -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");

@@ -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");

@@ -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");

@@ -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));
@@ -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));
+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