summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Bernardo Rufino <brufino@google.com> 2018-03-02 09:32:28 +0000
committer Bernardo Rufino <brufino@google.com> 2018-03-02 14:17:21 +0000
commitc3ec538d4ef299bef2b7abd4b99ff84265fd5e8e (patch)
tree0cf172f518931ebcee1678437105be19d22897e6
parent0f0d1ab1d370bb3f99a288a85b03f9b551eb1383 (diff)
Add transport tests
Some tests that were lacking. Test: m -j RunFrameworksServicesRoboTests Change-Id: Ia6800e950e96d7481d5c74f62147ad3b9b1493ba
-rw-r--r--services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java31
-rw-r--r--services/robotests/src/com/android/server/backup/transport/TransportClientTest.java125
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java48
3 files changed, 184 insertions, 20 deletions
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
index 5e3c9741ae70..3d2d8afd4a35 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -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";
@@ -72,15 +73,30 @@ public class TransportClientManagerTest {
}
@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)) {
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
index ff1644cb75ad..5b65473e0783 100644
--- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java
@@ -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,9 +299,9 @@ 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);
@@ -293,6 +309,66 @@ public class TransportClientTest {
}
@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");
ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext);
@@ -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();
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
new file mode 100644
index 000000000000..c0eeb3890a86
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java
@@ -0,0 +1,48 @@
+/*
+ * 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();
+ }
+}