Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab5bdbf8 authored by Hugo Benichi's avatar Hugo Benichi
Browse files

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
parent ff3e6ccc
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -273,6 +273,14 @@ public final class NsdManager {
        init();
    }

    /**
     * @hide
     */
    @VisibleForTesting
    public void disconnect() {
        mAsyncChannel.disconnect();
    }

    /**
     * Failures are passed with {@link RegistrationListener#onRegistrationFailed},
     * {@link RegistrationListener#onUnregistrationFailed},
+10 −25
Original line number Diff line number Diff line
@@ -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");
        public void stop() {
            execute("stop-service");
        }

    private boolean stopMDnsDaemon() {
        return mDaemon.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) {
+84 −13
Original line number Diff line number Diff line
@@ -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<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 {