Merge "Update NSD tests without AsyncChannel"

This commit is contained in:
Remi NGUYEN VAN
2021-11-22 05:58:15 +00:00
committed by Gerrit Code Review
2 changed files with 162 additions and 177 deletions

View File

@@ -20,38 +20,32 @@ import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChange
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.compat.testing.PlatformCompatChangeRule; import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.internal.util.AsyncChannel;
import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner; import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils; import com.android.testutils.ExceptionUtils;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
@@ -67,9 +61,10 @@ public class NsdManagerTest {
@Mock Context mContext; @Mock Context mContext;
@Mock INsdManager mService; @Mock INsdManager mService;
MockServiceHandler mServiceHandler; @Mock INsdServiceConnector mServiceConn;
NsdManager mManager; NsdManager mManager;
INsdManagerCallback mCallback;
long mTimeoutMs = 200; // non-final so that tests can adjust the value. long mTimeoutMs = 200; // non-final so that tests can adjust the value.
@@ -77,91 +72,85 @@ public class NsdManagerTest {
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mServiceHandler = spy(MockServiceHandler.create(mContext)); doReturn(mServiceConn).when(mService).connect(any());
doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger(); mManager = new NsdManager(mContext, mService);
} final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
INsdManagerCallback.class);
@After verify(mService).connect(cbCaptor.capture());
public void tearDown() throws Exception { mCallback = cbCaptor.getValue();
HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
mServiceHandler.chan.disconnect();
mServiceHandler.stop();
if (mManager != null) {
mManager.disconnect();
}
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testResolveServiceS() { public void testResolveServiceS() throws Exception {
mManager = makeNsdManagerS(); verify(mServiceConn, never()).startDaemon();
doTestResolveService(); doTestResolveService();
} }
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testResolveServicePreS() { public void testResolveServicePreS() throws Exception {
mManager = makeNsdManagerPreS(); verify(mServiceConn).startDaemon();
doTestResolveService(); doTestResolveService();
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testDiscoverServiceS() { public void testDiscoverServiceS() throws Exception {
mManager = makeNsdManagerS(); verify(mServiceConn, never()).startDaemon();
doTestDiscoverService(); doTestDiscoverService();
} }
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testDiscoverServicePreS() { public void testDiscoverServicePreS() throws Exception {
mManager = makeNsdManagerPreS(); verify(mServiceConn).startDaemon();
doTestDiscoverService(); doTestDiscoverService();
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testParallelResolveServiceS() { public void testParallelResolveServiceS() throws Exception {
mManager = makeNsdManagerS(); verify(mServiceConn, never()).startDaemon();
doTestParallelResolveService(); doTestParallelResolveService();
} }
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testParallelResolveServicePreS() { public void testParallelResolveServicePreS() throws Exception {
mManager = makeNsdManagerPreS(); verify(mServiceConn).startDaemon();
doTestParallelResolveService(); doTestParallelResolveService();
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testInvalidCallsS() { public void testInvalidCallsS() throws Exception {
mManager = makeNsdManagerS(); verify(mServiceConn, never()).startDaemon();
doTestInvalidCalls(); doTestInvalidCalls();
} }
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testInvalidCallsPreS() { public void testInvalidCallsPreS() throws Exception {
mManager = makeNsdManagerPreS(); verify(mServiceConn).startDaemon();
doTestInvalidCalls(); doTestInvalidCalls();
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testRegisterServiceS() { public void testRegisterServiceS() throws Exception {
mManager = makeNsdManagerS(); verify(mServiceConn, never()).startDaemon();
doTestRegisterService(); doTestRegisterService();
} }
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testRegisterServicePreS() { public void testRegisterServicePreS() throws Exception {
mManager = makeNsdManagerPreS(); verify(mServiceConn).startDaemon();
doTestRegisterService(); doTestRegisterService();
} }
public void doTestResolveService() { private void doTestResolveService() throws Exception {
NsdManager manager = mManager; NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -169,18 +158,19 @@ public class NsdManagerTest {
NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener); manager.resolveService(request, listener);
int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
int err = 33; int err = 33;
sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null); mCallback.onResolveServiceFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
manager.resolveService(request, listener); manager.resolveService(request, listener);
int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); int key2 = getRequestKey(req ->
sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
mCallback.onResolveServiceSucceeded(key2, reply);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
} }
public void doTestParallelResolveService() { private void doTestParallelResolveService() throws Exception {
NsdManager manager = mManager; NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -190,19 +180,20 @@ public class NsdManagerTest {
NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener1); manager.resolveService(request, listener1);
int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
manager.resolveService(request, listener2); manager.resolveService(request, listener2);
int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); int key2 = getRequestKey(req ->
verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); mCallback.onResolveServiceSucceeded(key2, reply);
sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply); mCallback.onResolveServiceSucceeded(key1, reply);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
} }
public void doTestRegisterService() { private void doTestRegisterService() throws Exception {
NsdManager manager = mManager; NsdManager manager = mManager;
NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
@@ -214,40 +205,43 @@ public class NsdManagerTest {
// Register two services // Register two services
manager.registerService(request1, PROTOCOL, listener1); manager.registerService(request1, PROTOCOL, listener1);
int key1 = verifyRequest(NsdManager.REGISTER_SERVICE); int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
manager.registerService(request2, PROTOCOL, listener2); manager.registerService(request2, PROTOCOL, listener2);
int key2 = verifyRequest(NsdManager.REGISTER_SERVICE); int key2 = getRequestKey(req ->
verify(mServiceConn, times(2)).registerService(req.capture(), any()));
// First reques fails, second request succeeds // First reques fails, second request succeeds
sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2); mCallback.onRegisterServiceSucceeded(key2, request2);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
int err = 1; int err = 1;
sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1); mCallback.onRegisterServiceFailed(key1, err);
verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
// Client retries first request, it succeeds // Client retries first request, it succeeds
manager.registerService(request1, PROTOCOL, listener1); manager.registerService(request1, PROTOCOL, listener1);
int key3 = verifyRequest(NsdManager.REGISTER_SERVICE); int key3 = getRequestKey(req ->
verify(mServiceConn, times(3)).registerService(req.capture(), any()));
sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1); mCallback.onRegisterServiceSucceeded(key3, request1);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
// First request is unregistered, it succeeds // First request is unregistered, it succeeds
manager.unregisterService(listener1); manager.unregisterService(listener1);
int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE); int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture()));
assertEquals(key3, key3again); assertEquals(key3, key3again);
sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null); mCallback.onUnregisterServiceSucceeded(key3again);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
// Second request is unregistered, it fails // Second request is unregistered, it fails
manager.unregisterService(listener2); manager.unregisterService(listener2);
int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE); int key2again = getRequestKey(req ->
verify(mServiceConn, times(2)).unregisterService(req.capture()));
assertEquals(key2, key2again); assertEquals(key2, key2again);
sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null); mCallback.onUnregisterServiceFailed(key2again, err);
verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
// TODO: do not unregister listener until service is unregistered // TODO: do not unregister listener until service is unregistered
@@ -260,7 +254,7 @@ public class NsdManagerTest {
//verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
} }
public void doTestDiscoverService() { private void doTestDiscoverService() throws Exception {
NsdManager manager = mManager; NsdManager manager = mManager;
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
@@ -271,69 +265,73 @@ public class NsdManagerTest {
// Client registers for discovery, request fails // Client registers for discovery, request fails
manager.discoverServices("a_type", PROTOCOL, listener); manager.discoverServices("a_type", PROTOCOL, listener);
int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES); int key1 = getRequestKey(req ->
verify(mServiceConn).discoverServices(req.capture(), any()));
int err = 1; int err = 1;
sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null); mCallback.onDiscoverServicesFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
// Client retries, request succeeds // Client retries, request succeeds
manager.discoverServices("a_type", PROTOCOL, listener); manager.discoverServices("a_type", PROTOCOL, listener);
int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES); int key2 = getRequestKey(req ->
verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1); mCallback.onDiscoverServicesStarted(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// mdns notifies about services // mdns notifies about services
sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1); mCallback.onServiceFound(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2); mCallback.onServiceFound(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2); mCallback.onServiceLost(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
// Client unregisters its listener // Client unregisters its listener
manager.stopServiceDiscovery(listener); manager.stopServiceDiscovery(listener);
int key2again = verifyRequest(NsdManager.STOP_DISCOVERY); int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture()));
assertEquals(key2, key2again); assertEquals(key2, key2again);
// TODO: unregister listener immediately and stop notifying it about services // TODO: unregister listener immediately and stop notifying it about services
// Notifications are still passed to the client's listener // Notifications are still passed to the client's listener
sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1); mCallback.onServiceLost(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
// Client is notified of complete unregistration // Client is notified of complete unregistration
sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type"); mCallback.onStopDiscoverySucceeded(key2again);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
// Notifications are not passed to the client anymore // Notifications are not passed to the client anymore
sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3); mCallback.onServiceFound(key2, reply3);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
// Client registers for service discovery // Client registers for service discovery
reset(listener); reset(listener);
manager.discoverServices("a_type", PROTOCOL, listener); manager.discoverServices("a_type", PROTOCOL, listener);
int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES); int key3 = getRequestKey(req ->
verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1); mCallback.onDiscoverServicesStarted(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// Client unregisters immediately, it fails // Client unregisters immediately, it fails
manager.stopServiceDiscovery(listener); manager.stopServiceDiscovery(listener);
int key3again = verifyRequest(NsdManager.STOP_DISCOVERY); int key3again = getRequestKey(req ->
verify(mServiceConn, times(2)).stopDiscovery(req.capture()));
assertEquals(key3, key3again); assertEquals(key3, key3again);
err = 2; err = 2;
sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type"); mCallback.onStopDiscoveryFailed(key3again, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
// New notifications are not passed to the client anymore // New notifications are not passed to the client anymore
sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1); mCallback.onServiceFound(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
} }
@@ -398,77 +396,10 @@ public class NsdManagerTest {
} }
} }
NsdManager makeNsdManagerS() { int getRequestKey(ExceptionUtils.ThrowingConsumer<ArgumentCaptor<Integer>> verifier)
// Expect we'll get 2 AsyncChannel related msgs. throws Exception {
return makeManager(2); final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
} verifier.accept(captor);
return captor.getValue();
NsdManager makeNsdManagerPreS() {
// Expect we'll get 3 msgs. 2 AsyncChannel related msgs + 1 additional daemon startup msg.
return makeManager(3);
}
NsdManager makeManager(int expectedMsgCount) {
NsdManager manager = new NsdManager(mContext, mService);
// Acknowledge first two messages connecting the AsyncChannel.
verify(mServiceHandler, timeout(mTimeoutMs).times(expectedMsgCount)).handleMessage(any());
reset(mServiceHandler);
assertNotNull(mServiceHandler.chan);
return manager;
}
int verifyRequest(int expectedMessageType) {
HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
reset(mServiceHandler);
Message received = mServiceHandler.getLastMessage();
assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
return received.arg2;
}
void sendResponse(int replyType, int arg, int key, Object obj) {
mServiceHandler.chan.sendMessage(replyType, arg, key, obj);
}
// Implements the server side of AsyncChannel connection protocol
public static class MockServiceHandler extends Handler {
public final Context context;
public AsyncChannel chan;
public Message lastMessage;
MockServiceHandler(Looper l, Context c) {
super(l);
context = c;
}
synchronized Message getLastMessage() {
return lastMessage;
}
synchronized void setLastMessage(Message msg) {
lastMessage = obtainMessage();
lastMessage.copyFrom(msg);
}
@Override
public void handleMessage(Message msg) {
setLastMessage(msg);
if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
chan = new AsyncChannel();
chan.connect(context, this, msg.replyTo);
chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
}
}
void stop() {
getLooper().quitSafely();
}
static MockServiceHandler create(Context context) {
HandlerThread t = new HandlerThread("mock-service-handler");
t.start();
return new MockServiceHandler(t.getLooper(), context);
}
} }
} }

View File

@@ -20,6 +20,7 @@ import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChange
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -33,14 +34,19 @@ import static org.mockito.Mockito.when;
import android.compat.testing.PlatformCompatChangeRule; import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager; import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo; import android.net.nsd.NsdServiceInfo;
import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.server.NsdService.DaemonConnection; import com.android.server.NsdService.DaemonConnection;
@@ -56,11 +62,15 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.Spy; import org.mockito.Spy;
import java.util.LinkedList;
import java.util.Queue;
// TODOs: // TODOs:
// - test client can send requests and receive replies // - test client can send requests and receive replies
// - test NSD_ON ENABLE/DISABLED listening // - test NSD_ON ENABLE/DISABLED listening
@@ -73,6 +83,11 @@ public class NsdServiceTest {
private static final long CLEANUP_DELAY_MS = 500; private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500; private static final long TIMEOUT_MS = 500;
// Records INsdManagerCallback created when NsdService#connect is called.
// Only accessed on the test thread, since NsdService#connect is called by the NsdManager
// constructor called on the test thread.
private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>();
@Rule @Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule(); public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext; @Mock Context mContext;
@@ -83,6 +98,16 @@ public class NsdServiceTest {
HandlerThread mThread; HandlerThread mThread;
TestHandler mHandler; TestHandler mHandler;
private static class LinkToDeathRecorder extends Binder {
IBinder.DeathRecipient mDr;
@Override
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
super.linkToDeath(recipient, flags);
mDr = recipient;
}
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -103,26 +128,30 @@ public class NsdServiceTest {
@Test @Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testPreSClients() { public void testPreSClients() throws Exception {
when(mSettings.isEnabled()).thenReturn(true); when(mSettings.isEnabled()).thenReturn(true);
NsdService service = makeService(); NsdService service = makeService();
// Pre S client connected, the daemon should be started. // Pre S client connected, the daemon should be started.
NsdManager client1 = connectClient(service); connectClient(service);
waitForIdle(); waitForIdle();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
verify(mDaemon, times(1)).maybeStart(); verify(mDaemon, times(1)).maybeStart();
verifyDaemonCommands("start-service"); verifyDaemonCommands("start-service");
NsdManager client2 = connectClient(service); connectClient(service);
waitForIdle(); waitForIdle();
final INsdManagerCallback cb2 = getCallback();
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
verify(mDaemon, times(1)).maybeStart(); verify(mDaemon, times(1)).maybeStart();
client1.disconnect(); deathRecipient1.binderDied();
// Still 1 client remains, daemon shouldn't be stopped. // Still 1 client remains, daemon shouldn't be stopped.
waitForIdle(); waitForIdle();
verify(mDaemon, never()).maybeStop(); verify(mDaemon, never()).maybeStop();
client2.disconnect(); deathRecipient2.binderDied();
// All clients are disconnected, the daemon should be stopped. // All clients are disconnected, the daemon should be stopped.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
verifyDaemonCommands("stop-service"); verifyDaemonCommands("stop-service");
@@ -130,43 +159,51 @@ public class NsdServiceTest {
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testNoDaemonStartedWhenClientsConnect() { public void testNoDaemonStartedWhenClientsConnect() throws Exception {
when(mSettings.isEnabled()).thenReturn(true); when(mSettings.isEnabled()).thenReturn(true);
final NsdService service = makeService();
NsdService service = makeService();
// Creating an NsdManager will not cause any cmds executed, which means // Creating an NsdManager will not cause any cmds executed, which means
// no daemon is started. // no daemon is started.
NsdManager client1 = connectClient(service); connectClient(service);
waitForIdle(); waitForIdle();
verify(mDaemon, never()).execute(any()); verify(mDaemon, never()).execute(any());
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
// Creating another NsdManager will not cause any cmds executed. // Creating another NsdManager will not cause any cmds executed.
NsdManager client2 = connectClient(service); connectClient(service);
waitForIdle(); waitForIdle();
verify(mDaemon, never()).execute(any()); verify(mDaemon, never()).execute(any());
final INsdManagerCallback cb2 = getCallback();
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
// If there is no active request, try to clean up the daemon // If there is no active request, try to clean up the daemon
// every time the client disconnects. // every time the client disconnects.
client1.disconnect(); deathRecipient1.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
reset(mDaemon); reset(mDaemon);
client2.disconnect(); deathRecipient2.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
}
client1.disconnect(); private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
client2.disconnect(); throws Exception {
final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr;
assertNotNull(dr);
return dr;
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testClientRequestsAreGCedAtDisconnection() { public void testClientRequestsAreGCedAtDisconnection() throws Exception {
when(mSettings.isEnabled()).thenReturn(true); when(mSettings.isEnabled()).thenReturn(true);
NsdService service = makeService(); NsdService service = makeService();
NsdManager client = connectClient(service);
NsdManager client = connectClient(service);
waitForIdle(); waitForIdle();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verify(mDaemon, never()).maybeStart(); verify(mDaemon, never()).maybeStart();
verify(mDaemon, never()).execute(any()); verify(mDaemon, never()).execute(any());
@@ -195,18 +232,16 @@ public class NsdServiceTest {
verifyDaemonCommand("resolve 4 a_name a_type local."); verifyDaemonCommand("resolve 4 a_name a_type local.");
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS. // Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
client.disconnect(); deathRecipient.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned // checks that request are cleaned
verifyDaemonCommands("stop-register 2", "stop-discover 3", verifyDaemonCommands("stop-register 2", "stop-discover 3",
"stop-resolve 4", "stop-service"); "stop-resolve 4", "stop-service");
client.disconnect();
} }
@Test @Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS) @EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testCleanupDelayNoRequestActive() { public void testCleanupDelayNoRequestActive() throws Exception {
when(mSettings.isEnabled()).thenReturn(true); when(mSettings.isEnabled()).thenReturn(true);
NsdService service = makeService(); NsdService service = makeService();
@@ -218,6 +253,8 @@ public class NsdServiceTest {
client.registerService(request, PROTOCOL, listener1); client.registerService(request, PROTOCOL, listener1);
waitForIdle(); waitForIdle();
verify(mDaemon, times(1)).maybeStart(); verify(mDaemon, times(1)).maybeStart();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verifyDaemonCommands("start-service", "register 2 a_name a_type 2201"); verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
client.unregisterService(listener1); client.unregisterService(listener1);
@@ -226,7 +263,7 @@ public class NsdServiceTest {
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
verifyDaemonCommand("stop-service"); verifyDaemonCommand("stop-service");
reset(mDaemon); reset(mDaemon);
client.disconnect(); deathRecipient.binderDied();
// Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon. // Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS); verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
} }
@@ -240,12 +277,29 @@ public class NsdServiceTest {
mDaemonCallback = callback; mDaemonCallback = callback;
return mDaemon; return mDaemon;
}; };
NsdService service = new NsdService(mContext, mSettings, final NsdService service = new NsdService(mContext, mSettings,
mHandler, supplier, CLEANUP_DELAY_MS); mHandler, supplier, CLEANUP_DELAY_MS) {
@Override
public INsdServiceConnector connect(INsdManagerCallback baseCb) {
// Wrap the callback in a transparent mock, to mock asBinder returning a
// LinkToDeathRecorder. This will allow recording the binder death recipient
// registered on the callback. Use a transparent mock and not a spy as the actual
// implementation class is not public and cannot be spied on by Mockito.
final INsdManagerCallback cb = mock(INsdManagerCallback.class,
AdditionalAnswers.delegatesTo(baseCb));
doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
mCreatedCallbacks.add(cb);
return super.connect(cb);
}
};
verify(mDaemon, never()).execute(any(String.class)); verify(mDaemon, never()).execute(any(String.class));
return service; return service;
} }
private INsdManagerCallback getCallback() {
return mCreatedCallbacks.remove();
}
NsdManager connectClient(NsdService service) { NsdManager connectClient(NsdService service) {
return new NsdManager(mContext, service); return new NsdManager(mContext, service);
} }