Loading services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java +29 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"; Loading @@ -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)), Loading @@ -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)) { Loading services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +107 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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(); Loading @@ -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 Loading @@ -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)); } Loading @@ -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) Loading @@ -164,7 +181,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); mShadowLooper.runToEndOfTasks(); mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener2) .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient)); } Loading @@ -180,7 +197,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener, "caller"); mShadowLooper.runToEndOfTasks(); mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(isNull(), eq(mTransportClient)); } Loading Loading @@ -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)); } Loading @@ -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) Loading @@ -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); Loading @@ -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"); Loading Loading @@ -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(); } Loading services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java 0 → 100644 +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(); } } Loading
services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java +29 −2 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"; Loading @@ -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)), Loading @@ -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)) { Loading
services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +107 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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(); Loading @@ -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 Loading @@ -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)); } Loading @@ -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) Loading @@ -164,7 +181,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); mShadowLooper.runToEndOfTasks(); mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener2) .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient)); } Loading @@ -180,7 +197,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener, "caller"); mShadowLooper.runToEndOfTasks(); mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(isNull(), eq(mTransportClient)); } Loading Loading @@ -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)); } Loading @@ -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) Loading @@ -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); Loading @@ -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"); Loading Loading @@ -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(); } Loading
services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java 0 → 100644 +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(); } }