From ab5bdbf84e89d48ad9daa015cae436a9d27cfdd3 Mon Sep 17 00:00:00 2001 From: Hugo Benichi Date: Fri, 28 Apr 2017 15:31:10 +0900 Subject: [PATCH] NsdService: test coverage for client requests. Adding coverage for: - NsdManager client disconnection - in-flight request GC Test: new test passes Bug: 37013369, 33298084 Change-Id: I92039f297cf99352bbf4196797933d89c0b819ff --- core/java/android/net/nsd/NsdManager.java | 8 ++ .../core/java/com/android/server/NsdService.java | 35 +++----- tests/net/java/android/net/nsd/NsdServiceTest.java | 97 +++++++++++++++++++--- 3 files changed, 102 insertions(+), 38 deletions(-) diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 13648d2c7654..27ca6e2e513b 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -274,6 +274,14 @@ public final class NsdManager { } /** + * @hide + */ + @VisibleForTesting + public void disconnect() { + mAsyncChannel.disconnect(); + } + + /** * Failures are passed with {@link RegistrationListener#onRegistrationFailed}, * {@link RegistrationListener#onUnregistrationFailed}, * {@link DiscoveryListener#onStartDiscoveryFailed}, diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index 55bca648d50d..efbad450afee 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -48,7 +48,6 @@ import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; -import com.android.server.NativeDaemonConnector.Command; /** * Network Service Discovery Service handles remote service discovery operation requests by @@ -161,7 +160,7 @@ public class NsdService extends INsdManager.Stub { } //Last client if (mClients.size() == 0) { - stopMDnsDaemon(); + mDaemon.stop(); } break; case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: @@ -221,14 +220,14 @@ public class NsdService extends INsdManager.Stub { public void enter() { sendNsdStateChangeBroadcast(true); if (mClients.size() > 0) { - startMDnsDaemon(); + mDaemon.start(); } } @Override public void exit() { if (mClients.size() > 0) { - stopMDnsDaemon(); + mDaemon.stop(); } } @@ -262,7 +261,7 @@ public class NsdService extends INsdManager.Stub { //First client if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL && mClients.size() == 0) { - startMDnsDaemon(); + mDaemon.start(); } return NOT_HANDLED; case AsyncChannel.CMD_CHANNEL_DISCONNECTED: @@ -712,26 +711,13 @@ public class NsdService extends INsdManager.Stub { return true; } - public boolean execute(Command cmd) { - if (DBG) { - Slog.d(TAG, cmd.toString()); - } - try { - mNativeConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - Slog.e(TAG, "Failed to execute " + cmd, e); - return false; - } - return true; + public void start() { + execute("start-service"); } - } - private boolean startMDnsDaemon() { - return mDaemon.execute("start-service"); - } - - private boolean stopMDnsDaemon() { - return mDaemon.execute("stop-service"); + public void stop() { + execute("stop-service"); + } } private boolean registerService(int regId, NsdServiceInfo service) { @@ -743,8 +729,7 @@ public class NsdService extends INsdManager.Stub { int port = service.getPort(); byte[] textRecord = service.getTxtRecord(); String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", ""); - Command cmd = new Command("mdnssd", "register", regId, name, type, port, record); - return mDaemon.execute(cmd); + return mDaemon.execute("register", regId, name, type, port, record); } private boolean unregisterService(int regId) { diff --git a/tests/net/java/android/net/nsd/NsdServiceTest.java b/tests/net/java/android/net/nsd/NsdServiceTest.java index acc390c8d447..68cb251cf87e 100644 --- a/tests/net/java/android/net/nsd/NsdServiceTest.java +++ b/tests/net/java/android/net/nsd/NsdServiceTest.java @@ -16,68 +16,121 @@ package com.android.server; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.test.TestLooper; import android.content.Context; import android.content.ContentResolver; import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; import com.android.server.NsdService.DaemonConnection; import com.android.server.NsdService.DaemonConnectionSupplier; import com.android.server.NsdService.NativeCallbackReceiver; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; // TODOs: -// - test client disconnects // - test client can send requests and receive replies // - test NSD_ON ENABLE/DISABLED listening @RunWith(AndroidJUnit4.class) @SmallTest public class NsdServiceTest { + static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; + + long mTimeoutMs = 100; // non-final so that tests can adjust the value. + @Mock Context mContext; @Mock ContentResolver mResolver; @Mock NsdService.NsdSettings mSettings; @Mock DaemonConnection mDaemon; NativeCallbackReceiver mDaemonCallback; - TestLooper mLooper; + HandlerThread mThread; TestHandler mHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mLooper = new TestLooper(); - mHandler = new TestHandler(mLooper.getLooper()); + mThread = new HandlerThread("mock-service-handler"); + mThread.start(); + mHandler = new TestHandler(mThread.getLooper()); when(mContext.getContentResolver()).thenReturn(mResolver); } + @After + public void tearDown() throws Exception { + mThread.quit(); + } + @Test - public void testClientsCanConnect() { + public void testClientsCanConnectAndDisconnect() { when(mSettings.isEnabled()).thenReturn(true); NsdService service = makeService(); NsdManager client1 = connectClient(service); - verify(mDaemon, timeout(100).times(1)).execute("start-service"); + verify(mDaemon, timeout(100).times(1)).start(); NsdManager client2 = connectClient(service); - // TODO: disconnect client1 - // TODO: disconnect client2 + client1.disconnect(); + client2.disconnect(); + + verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); + } + + @Test + public void testClientRequestsAreGCedAtDisconnection() { + when(mSettings.isEnabled()).thenReturn(true); + when(mDaemon.execute(any())).thenReturn(true); + + NsdService service = makeService(); + NsdManager client = connectClient(service); + + verify(mDaemon, timeout(100).times(1)).start(); + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + request.setPort(2201); + + // Client registration request + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + client.registerService(request, PROTOCOL, listener1); + verifyDaemonCommand("register 2 a_name a_type 2201"); + + // Client discovery request + NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); + client.discoverServices("a_type", PROTOCOL, listener2); + verifyDaemonCommand("discover 3 a_type"); + + // Client resolve request + NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); + client.resolveService(request, listener3); + verifyDaemonCommand("resolve 4 a_name a_type local."); + + // Client disconnects + client.disconnect(); + verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); + + // checks that request are cleaned + verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); } NsdService makeService() { @@ -91,10 +144,28 @@ public class NsdServiceTest { } NsdManager connectClient(NsdService service) { - mLooper.startAutoDispatch(); - NsdManager client = new NsdManager(mContext, service); - mLooper.stopAutoDispatch(); - return client; + return new NsdManager(mContext, service); + } + + void verifyDaemonCommands(String... wants) { + verifyDaemonCommand(String.join(" ", wants), wants.length); + } + + void verifyDaemonCommand(String want) { + verifyDaemonCommand(want, 1); + } + + void verifyDaemonCommand(String want, int n) { + ArgumentCaptor argumentsCaptor = ArgumentCaptor.forClass(Object.class); + verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); + String got = ""; + for (Object o : argumentsCaptor.getAllValues()) { + got += o + " "; + } + assertEquals(want, got.trim()); + // rearm deamon for next command verification + reset(mDaemon); + when(mDaemon.execute(any())).thenReturn(true); } public static class TestHandler extends Handler { -- 2.11.0