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

Commit 548c1e8c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add transport tests"

parents 2a6d979f c3ec538d
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPOR
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -30,6 +31,7 @@ import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;

import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderPackages;
import org.junit.Before;
@@ -45,7 +47,6 @@ import org.robolectric.annotation.Config;
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
public class TransportClientManagerTest {

    private static final String PACKAGE_NAME = "random.package.name";
    private static final String CLASS_NAME = "random.package.name.transport.Transport";

@@ -71,16 +72,31 @@ public class TransportClientManagerTest {
                .thenReturn(true);
    }

    @Test
    public void testGetTransportClient() {
        TransportClient transportClient =
                mTransportClientManager.getTransportClient(mTransportComponent, "caller");

        // Connect to be able to extract the intent
        transportClient.connectAsync(mTransportConnectionListener, "caller");
        verify(mContext)
                .bindServiceAsUser(
                        argThat(matchesIntentAndExtras(mBindIntent)),
                        any(ServiceConnection.class),
                        anyInt(),
                        any(UserHandle.class));
    }

    @Test
    public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
        Bundle extras = new Bundle();
        extras.putBoolean("random_extra", true);
        mBindIntent.putExtras(extras);

        TransportClient transportClient =
                mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");

        transportClient.connectAsync(mTransportConnectionListener, "caller");
        mBindIntent.putExtras(extras);
        verify(mContext)
                .bindServiceAsUser(
                        argThat(matchesIntentAndExtras(mBindIntent)),
@@ -89,6 +105,17 @@ public class TransportClientManagerTest {
                        any(UserHandle.class));
    }

    @Test
    public void testDisposeOfTransportClient() {
        TransportClient transportClient =
                spy(mTransportClientManager.getTransportClient(mTransportComponent, "caller"));

        mTransportClientManager.disposeOfTransportClient(transportClient, "caller");

        verify(transportClient).unbind(any());
        verify(transportClient).markAsDisposed();
    }

    private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
        return (Intent actualIntent) -> {
            if (!expectedIntent.filterEquals(actualIntent)) {
+107 −18
Original line number Diff line number Diff line
@@ -26,16 +26,21 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.expectThrows;

import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -43,10 +48,9 @@ import android.util.Log;

import com.android.internal.backup.IBackupTransport;
import com.android.server.EventLogTags;
import com.android.server.backup.TransportManager;
import com.android.server.testing.FrameworkRobolectricTestRunner;
import com.android.server.testing.SystemLoaderClasses;
import com.android.server.testing.SystemLoaderPackages;
import com.android.server.testing.shadows.FrameworkShadowLooper;
import com.android.server.testing.shadows.ShadowCloseGuard;
import com.android.server.testing.shadows.ShadowEventLog;
import com.android.server.testing.shadows.ShadowSlog;
@@ -61,11 +65,19 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;

import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

@RunWith(FrameworkRobolectricTestRunner.class)
@Config(
    manifest = Config.NONE,
    sdk = 26,
    shadows = {ShadowEventLog.class, ShadowCloseGuard.class, ShadowSlog.class}
    shadows = {
        ShadowEventLog.class,
        ShadowCloseGuard.class,
        ShadowSlog.class,
        FrameworkShadowLooper.class
    }
)
@SystemLoaderPackages({"com.android.server.backup"})
@Presubmit
@@ -80,14 +92,16 @@ public class TransportClientTest {
    private ComponentName mTransportComponent;
    private String mTransportString;
    private Intent mBindIntent;
    private ShadowLooper mShadowLooper;
    private FrameworkShadowLooper mShadowMainLooper;
    private ShadowLooper mShadowWorkerLooper;
    private Handler mWorkerHandler;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        Looper mainLooper = Looper.getMainLooper();
        mShadowLooper = shadowOf(mainLooper);
        mShadowMainLooper = extract(mainLooper);
        mTransportComponent =
                new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport");
        mTransportString = mTransportComponent.flattenToShortString();
@@ -107,6 +121,11 @@ public class TransportClientTest {
                        anyInt(),
                        any(UserHandle.class)))
                .thenReturn(true);

        HandlerThread workerThread = new HandlerThread("worker");
        workerThread.start();
        mShadowWorkerLooper = shadowOf(workerThread.getLooper());
        mWorkerHandler = workerThread.getThreadHandler();
    }

    @Test
@@ -129,12 +148,11 @@ public class TransportClientTest {
    @Test
    public void testConnectAsync_callsListenerWhenConnected() throws Exception {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller");

        // Simulate framework connecting
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);

        connection.onServiceConnected(mTransportComponent, mTransportBinder);

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
                .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
    }
@@ -148,8 +166,7 @@ public class TransportClientTest {
        mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");

        connection.onServiceConnected(mTransportComponent, mTransportBinder);

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
                .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
        verify(mTransportConnectionListener2)
@@ -164,7 +181,7 @@ public class TransportClientTest {

        mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener2)
                .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient));
    }
@@ -180,7 +197,7 @@ public class TransportClientTest {

        mTransportClient.connectAsync(mTransportConnectionListener, "caller");

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
                .onTransportConnectionResult(isNull(), eq(mTransportClient));
    }
@@ -233,11 +250,11 @@ public class TransportClientTest {
    @Test
    public void testConnectAsync_callsListenerIfBindingDies() throws Exception {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller");

        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);

        connection.onBindingDied(mTransportComponent);

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
                .onTransportConnectionResult(isNull(), eq(mTransportClient));
    }
@@ -251,8 +268,7 @@ public class TransportClientTest {
        mTransportClient.connectAsync(mTransportConnectionListener2, "caller2");

        connection.onBindingDied(mTransportComponent);

        mShadowLooper.runToEndOfTasks();
        mShadowMainLooper.runToEndOfTasks();
        verify(mTransportConnectionListener)
                .onTransportConnectionResult(isNull(), eq(mTransportClient));
        verify(mTransportConnectionListener2)
@@ -271,9 +287,9 @@ public class TransportClientTest {
    @Test
    public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() {
        ShadowEventLog.setUp();

        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);

        connection.onServiceConnected(mTransportComponent, mTransportBinder);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
@@ -283,15 +299,75 @@ public class TransportClientTest {
    @Test
    public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() {
        ShadowEventLog.setUp();

        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);

        connection.onBindingDied(mTransportComponent);

        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1);
        assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 0);
    }

    @Test
    public void testConnect_whenConnected_returnsTransport() throws Exception {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mTransportBinder);

        IBackupTransport transportBinder =
                runInWorkerThread(() -> mTransportClient.connect("caller2"));

        assertThat(transportBinder).isNotNull();
    }

    @Test
    public void testConnect_afterOnServiceDisconnected_returnsNull() throws Exception {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onServiceConnected(mTransportComponent, mTransportBinder);
        connection.onServiceDisconnected(mTransportComponent);

        IBackupTransport transportBinder =
                runInWorkerThread(() -> mTransportClient.connect("caller2"));

        assertThat(transportBinder).isNull();
    }

    @Test
    public void testConnect_afterOnBindingDied_returnsNull() throws Exception {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
        ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
        connection.onBindingDied(mTransportComponent);

        IBackupTransport transportBinder =
                runInWorkerThread(() -> mTransportClient.connect("caller2"));

        assertThat(transportBinder).isNull();
    }

    @Test
    public void testConnect_callsThroughToConnectAsync() throws Exception {
        // We can't mock bindServiceAsUser() instead of connectAsync() and call the listener inline
        // because in our code in TransportClient we assume this is NOT run inline, such that the
        // reentrant lock can't be acquired by the listener at the call-site of bindServiceAsUser(),
        // which is what would happened if we mocked bindServiceAsUser() to call the listener
        // inline.
        TransportClient transportClient = spy(mTransportClient);
        doAnswer(
                        invocation -> {
                            TransportConnectionListener listener = invocation.getArgument(0);
                            listener.onTransportConnectionResult(mTransportBinder, transportClient);
                            return null;
                        })
                .when(transportClient)
                .connectAsync(any(), any());

        IBackupTransport transportBinder =
                runInWorkerThread(() -> transportClient.connect("caller"));

        assertThat(transportBinder).isNotNull();
    }

    @Test
    public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() {
        mTransportClient.connectAsync(mTransportConnectionListener, "caller1");
@@ -447,6 +523,19 @@ public class TransportClientTest {
        assertThat(ShadowCloseGuard.hasReported()).isFalse();
    }

    @Nullable
    private <T> T runInWorkerThread(Supplier<T> supplier) throws Exception {
        CompletableFuture<T> future = new CompletableFuture<>();
        mWorkerHandler.post(() -> future.complete(supplier.get()));
        // Although we are using a separate looper, we are still calling runToEndOfTasks() in the
        // main thread (Robolectric only *simulates* multi-thread). The only option left is to fool
        // the caller.
        mShadowMainLooper.setCurrentThread(false);
        mShadowWorkerLooper.runToEndOfTasks();
        mShadowMainLooper.reset();
        return future.get();
    }

    private void assertEventLogged(int tag, Object... values) {
        assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue();
    }
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.shadows;

import android.os.Looper;

import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadows.ShadowLooper;

import java.util.Optional;

@Implements(value = Looper.class, inheritImplementationMethods = true)
public class FrameworkShadowLooper extends ShadowLooper {
    @RealObject private Looper mLooper;
    private Optional<Boolean> mIsCurrentThread = Optional.empty();

    public void setCurrentThread(boolean currentThread) {
        mIsCurrentThread = Optional.of(currentThread);
    }

    public void reset() {
        mIsCurrentThread = Optional.empty();
    }

    @Implementation
    public boolean isCurrentThread() {
        if (mIsCurrentThread.isPresent()) {
            return mIsCurrentThread.get();
        }
        return Thread.currentThread() == mLooper.getThread();
    }
}