OSDN Git Service

NsdService: test coverage for client requests.
authorHugo Benichi <hugobenichi@google.com>
Fri, 28 Apr 2017 06:31:10 +0000 (15:31 +0900)
committerHugo Benichi <hugobenichi@google.com>
Tue, 9 May 2017 01:49:33 +0000 (10:49 +0900)
Adding coverage for:
  - NsdManager client disconnection
  - in-flight request GC

Test: new test passes
Bug: 3701336933298084
Change-Id: I92039f297cf99352bbf4196797933d89c0b819ff

core/java/android/net/nsd/NsdManager.java
services/core/java/com/android/server/NsdService.java
tests/net/java/android/net/nsd/NsdServiceTest.java

index 13648d2..27ca6e2 100644 (file)
@@ -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},
index 55bca64..efbad45 100644 (file)
@@ -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) {
index acc390c..68cb251 100644 (file)
 
 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<Object> 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 {