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

Commit 228a6496 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

More unit tests for PerformBackupTask

Around transport and agent failures. Caught a few possible errors, check
TODOs.

Test: m -j RunFrameworksServicesRoboTests
Change-Id: I9d04f4253511032473885ce6c0856c52639f2957
parent b97ea7ea
Loading
Loading
Loading
Loading
+232 −36
Original line number Diff line number Diff line
@@ -17,19 +17,21 @@
package com.android.server.backup;

import static com.android.server.backup.testing.TransportData.backupTransport;
import static com.android.server.backup.testing.TransportTestUtils.setUpTransport;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -44,6 +46,7 @@ import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
import android.app.backup.BackupTransport;
import android.app.backup.FullBackupDataOutput;
import android.app.backup.IBackupManager;
@@ -70,6 +73,7 @@ import com.android.server.backup.internal.BackupRequest;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.PerformBackupTask;
import com.android.server.backup.testing.TransportData;
import com.android.server.backup.testing.TransportTestUtils;
import com.android.server.backup.testing.TransportTestUtils.TransportMock;
import com.android.server.backup.transport.TransportClient;
import com.android.server.testing.FrameworkRobolectricTestRunner;
@@ -132,31 +136,25 @@ public class PerformBackupTaskTest {
    @Mock private IBackupManagerMonitor mMonitor;
    @Mock private OnTaskFinishedListener mListener;
    private TransportData mTransport;
    private IBackupTransport mTransportBinder;
    private TransportClient mTransportClient;
    private ShadowLooper mShadowBackupLooper;
    private BackupHandler mBackupHandler;
    private PowerManager.WakeLock mWakeLock;
    private ShadowPackageManager mShadowPackageManager;
    private FakeIBackupManager mBackupManager;
    private File mBaseStateDir;

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

        mTransport = backupTransport();
        TransportMock transportMock = setUpTransport(mTransportManager, mTransport);
        mTransportBinder = transportMock.transport;
        mTransportClient = transportMock.transportClient;

        Application application = RuntimeEnvironment.application;
        File cacheDir = application.getCacheDir();
        File baseStateDir = new File(cacheDir, "base_state_dir");
        mBaseStateDir = new File(cacheDir, "base_state_dir");
        File dataDir = new File(cacheDir, "data_dir");
        File stateDir = new File(baseStateDir, mTransport.transportDirName);
        assertThat(baseStateDir.mkdir()).isTrue();
        assertThat(mBaseStateDir.mkdir()).isTrue();
        assertThat(dataDir.mkdir()).isTrue();
        assertThat(stateDir.mkdir()).isTrue();

        PackageManager packageManager = application.getPackageManager();
        mShadowPackageManager = Shadow.extract(packageManager);
@@ -182,7 +180,7 @@ public class PerformBackupTaskTest {
        when(mBackupManagerService.getWakelock()).thenReturn(mWakeLock);
        when(mBackupManagerService.getCurrentOpLock()).thenReturn(new Object());
        when(mBackupManagerService.getQueueLock()).thenReturn(new Object());
        when(mBackupManagerService.getBaseStateDir()).thenReturn(baseStateDir);
        when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
        when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
        when(mBackupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
        when(mBackupManagerService.getBackupHandler()).thenReturn(mBackupHandler);
@@ -192,10 +190,16 @@ public class PerformBackupTaskTest {

    @Test
    public void testRunTask_whenTransportProvidesFlags_passesThemToTheAgent() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
        when(mTransportBinder.getTransportFlags()).thenReturn(flags);
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

@@ -205,8 +209,14 @@ public class PerformBackupTaskTest {

    @Test
    public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

@@ -216,13 +226,19 @@ public class PerformBackupTaskTest {
    @Test
    public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
            throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
        BackupAgent agent1 = agentMocks.get(0).agent;
        BackupAgent agent2 = agentMocks.get(1).agent;
        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
        when(mTransportBinder.getTransportFlags()).thenReturn(flags);
        when(transportMock.transport.getTransportFlags()).thenReturn(flags);
        PerformBackupTask task =
                createPerformBackupTask(emptyList(), false, true, PACKAGE_1, PACKAGE_2);
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1,
                        PACKAGE_2);

        runTask(task);

@@ -232,10 +248,16 @@ public class PerformBackupTaskTest {

    @Test
    public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);
        int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
        when(mTransportBinder.getTransportFlags()).thenReturn(flags);
        when(transportMock.transport.getTransportFlags()).thenReturn(flags);

        runTask(task);

@@ -244,17 +266,26 @@ public class PerformBackupTaskTest {
    }

    @Test
    public void testRunTask_callsListenerOnTaskFinished() throws Exception {
    public void testRunTask_callsListenerAndObserver() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        setUpAgent(PACKAGE_1);
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        verify(mListener).onFinished(any());
        verify(mObserver).backupFinished(eq(BackupManager.SUCCESS));
    }

    @Test
    public void testRunTask_callsTransportPerformBackup() throws Exception {
    public void testRunTask_callsTransportPerformBackupWithAgentData() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        IBackupTransport transportBinder = transportMock.transport;
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        agentOnBackupDo(
                agentMock.agent,
@@ -262,15 +293,20 @@ public class PerformBackupTaskTest {
                    writeData(dataOutput, "key1", "foo".getBytes());
                    writeData(dataOutput, "key2", "bar".getBytes());
                });
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);
        // We need to verify at call time because the file is deleted right after
        when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .then(this::mockAndVerifyTransportPerformBackupData);

        runTask(task);

        // Already verified data in mockAndVerifyPerformBackupData
        verify(mTransportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
        verify(transportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
    }

    private int mockAndVerifyTransportPerformBackupData(InvocationOnMock invocation)
@@ -305,18 +341,26 @@ public class PerformBackupTaskTest {
    @Test
    public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
            throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        IBackupTransport transportBinder = transportMock.transport;
        setUpAgent(PACKAGE_1);
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_OK);

        runTask(task);

        verify(mTransportBinder).finishBackup();
        verify(transportBinder).finishBackup();
    }

    @Test
    public void testRunTask_whenProhibitedKey_failsAgent() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        agentOnBackupDo(
                agentMock.agent,
@@ -324,13 +368,149 @@ public class PerformBackupTaskTest {
                    char prohibitedChar = 0xff00;
                    writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
                });
        PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        // TODO: Should it not call mListener.onFinished()? PerformBackupTask:891 return?
        // verify(mListener).onFinished(any());
        verify(mObserver).onResult(eq(PACKAGE_1), eq(BackupManager.ERROR_AGENT_FAILURE));
        verify(agentMock.agentBinder).fail(any());
    }

    @Test
    public void testRunTask_whenTransportUnavailable() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport.unavailable());
        setUpAgent(PACKAGE_1);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        verify(mListener).onFinished(any());
        // TODO: Should it be 2 times? (PBT.beginBackup() and PBT.finalizeBackup())
        verify(mObserver, times(2)).backupFinished(eq(BackupManager.ERROR_TRANSPORT_ABORTED));
    }

    @Test
    public void testRunTask_whenTransportRejectsPackage() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        setUpAgent(PACKAGE_1);
        when(transportMock.transport.performBackup(
                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
    }

    @Test
    public void testRunTask_whenTransportRejectsFirstPackageButLastSucceeds() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        IBackupTransport transportBinder = transportMock.transport;
        setUpAgents(PACKAGE_1, PACKAGE_2);
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_OK);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1,
                        PACKAGE_2);

        runTask(task);

        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
        verify(mObserver).onResult(PACKAGE_2, BackupManager.SUCCESS);
        verify(mObserver).backupFinished(BackupManager.SUCCESS);
    }

    @Test
    public void testRunTask_whenTransportRejectsLastPackageButFirstSucceeds() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        IBackupTransport transportBinder = transportMock.transport;
        setUpAgents(PACKAGE_1, PACKAGE_2);
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_OK);
        when(transportBinder.performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1,
                        PACKAGE_2);

        runTask(task);

        verify(mObserver).onResult(PACKAGE_1, BackupManager.SUCCESS);
        verify(mObserver).onResult(PACKAGE_2, BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED);
        // TODO: Should we return the status of the last?
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
    }

    @Test
    public void testRunTask_whenTransportReturnsQuotaExceeded() throws Exception {
        TransportMock transportMock = setUpTransport(mTransport);
        AgentMock agentMock = setUpAgent(PACKAGE_1);
        when(transportMock.transport.performBackup(
                        argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                .thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
        verify(agentMock.agent).onQuotaExceeded(anyLong(), anyLong());
    }

    // TODO: Giving NPE at PerformBackupTask:524 because mCurrentPackage is null (PackageManager
    // rightfully threw NameNotFoundException). Uncomment @Test when fixed.
    // @Test
    public void testRunTask_whenAgentUnknown() throws Exception {
        // Not calling setUpAgent()
        TransportMock transportMock = setUpTransport(mTransport);
        PerformBackupTask task =
                createPerformBackupTask(
                        transportMock.transportClient,
                        mTransport.transportDirName,
                        emptyList(),
                        PACKAGE_1);

        runTask(task);

        verify(transportMock.transport, never()).performBackup(any(), any(), anyInt());
        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_PACKAGE_NOT_FOUND);
        verify(mObserver).backupFinished(BackupManager.SUCCESS);
    }

    private void runTask(PerformBackupTask task) {
        Message message = mBackupHandler.obtainMessage(BackupHandler.MSG_BACKUP_RESTORE_STEP, task);
        mBackupHandler.sendMessage(message);
@@ -339,6 +519,14 @@ public class PerformBackupTaskTest {
        }
    }

    private TransportMock setUpTransport(TransportData transport) throws Exception {
        TransportMock transportMock =
                TransportTestUtils.setUpTransport(mTransportManager, transport);
        File stateDir = new File(mBaseStateDir, transport.transportDirName);
        assertThat(stateDir.mkdir()).isTrue();
        return transportMock;
    }

    private List<AgentMock> setUpAgents(String... packageNames) {
        return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
    }
@@ -370,9 +558,9 @@ public class PerformBackupTaskTest {
    }

    private PerformBackupTask createPerformBackupTask(
            TransportClient transportClient,
            String transportDirName,
            List<String> pendingFullBackups,
            boolean userInitiated,
            boolean nonIncremental,
            String... packages) {
        ArrayList<BackupRequest> backupRequests =
                Stream.of(packages).map(BackupRequest::new).collect(toCollection(ArrayList::new));
@@ -380,22 +568,30 @@ public class PerformBackupTaskTest {
        PerformBackupTask task =
                new PerformBackupTask(
                        mBackupManagerService,
                        mTransportClient,
                        mTransport.transportDirName,
                        transportClient,
                        transportDirName,
                        backupRequests,
                        mDataChangedJournal,
                        mObserver,
                        mMonitor,
                        mListener,
                        pendingFullBackups,
                        userInitiated,
                        nonIncremental);
                        /* userInitiated */ false,
                        /* nonIncremental */ true);
        mBackupManager.setUp(mBackupHandler, task);
        return task;
    }

    private static ArgumentMatcher<PackageInfo> packageInfo(String packageName) {
        return packageInfo -> packageName.equals(packageInfo.packageName);
        // We have to test for packageInfo nulity because of Mockito's own stubbing with argThat().
        // E.g. if you do:
        //
        //   1. when(object.method(argThat(str -> str.equals("foo")))).thenReturn(0)
        //   2. when(object.method(argThat(str -> str.equals("bar")))).thenReturn(2)
        //
        // The second line will throw NPE because it will call lambda 1 with null, since argThat()
        // returns null. So we guard against that by checking for null.
        return packageInfo -> packageInfo != null && packageName.equals(packageInfo.packageName);
    }

    private static ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
@@ -435,7 +631,7 @@ public class PerformBackupTaskTest {
        private final IBackupAgent agentBinder;
        private final BackupAgent agent;

        public AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
        private AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
            this.agentBinder = agentBinder;
            this.agent = agent;
        }