1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.mockito.Mockito.any; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.reset; 24 import static org.mockito.Mockito.timeout; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.os.Handler; 30 import android.os.HandlerThread; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.content.Context; 34 import android.content.ContentResolver; 35 import android.net.nsd.NsdManager; 36 import android.net.nsd.NsdServiceInfo; 37 import com.android.server.NsdService.DaemonConnection; 38 import com.android.server.NsdService.DaemonConnectionSupplier; 39 import com.android.server.NsdService.NativeCallbackReceiver; 40 import android.support.test.filters.SmallTest; 41 import android.support.test.runner.AndroidJUnit4; 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.ArgumentCaptor; 47 import org.mockito.Mock; 48 import org.mockito.MockitoAnnotations; 49 50 // TODOs: 51 // - test client can send requests and receive replies 52 // - test NSD_ON ENABLE/DISABLED listening 53 @RunWith(AndroidJUnit4.class) 54 @SmallTest 55 public class NsdServiceTest { 56 57 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; 58 59 long mTimeoutMs = 100; // non-final so that tests can adjust the value. 60 61 @Mock Context mContext; 62 @Mock ContentResolver mResolver; 63 @Mock NsdService.NsdSettings mSettings; 64 @Mock DaemonConnection mDaemon; 65 NativeCallbackReceiver mDaemonCallback; 66 HandlerThread mThread; 67 TestHandler mHandler; 68 69 @Before 70 public void setUp() throws Exception { 71 MockitoAnnotations.initMocks(this); 72 mThread = new HandlerThread("mock-service-handler"); 73 mThread.start(); 74 mHandler = new TestHandler(mThread.getLooper()); 75 when(mContext.getContentResolver()).thenReturn(mResolver); 76 } 77 78 @After 79 public void tearDown() throws Exception { 80 mThread.quit(); 81 } 82 83 @Test 84 public void testClientsCanConnectAndDisconnect() { 85 when(mSettings.isEnabled()).thenReturn(true); 86 87 NsdService service = makeService(); 88 89 NsdManager client1 = connectClient(service); 90 verify(mDaemon, timeout(100).times(1)).start(); 91 92 NsdManager client2 = connectClient(service); 93 94 client1.disconnect(); 95 client2.disconnect(); 96 97 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 98 } 99 100 @Test 101 public void testClientRequestsAreGCedAtDisconnection() { 102 when(mSettings.isEnabled()).thenReturn(true); 103 when(mDaemon.execute(any())).thenReturn(true); 104 105 NsdService service = makeService(); 106 NsdManager client = connectClient(service); 107 108 verify(mDaemon, timeout(100).times(1)).start(); 109 110 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 111 request.setPort(2201); 112 113 // Client registration request 114 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 115 client.registerService(request, PROTOCOL, listener1); 116 verifyDaemonCommand("register 2 a_name a_type 2201"); 117 118 // Client discovery request 119 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); 120 client.discoverServices("a_type", PROTOCOL, listener2); 121 verifyDaemonCommand("discover 3 a_type"); 122 123 // Client resolve request 124 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); 125 client.resolveService(request, listener3); 126 verifyDaemonCommand("resolve 4 a_name a_type local."); 127 128 // Client disconnects 129 client.disconnect(); 130 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 131 132 // checks that request are cleaned 133 verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); 134 } 135 136 NsdService makeService() { 137 DaemonConnectionSupplier supplier = (callback) -> { 138 mDaemonCallback = callback; 139 return mDaemon; 140 }; 141 NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); 142 verify(mDaemon, never()).execute(any(String.class)); 143 return service; 144 } 145 146 NsdManager connectClient(NsdService service) { 147 return new NsdManager(mContext, service); 148 } 149 150 void verifyDaemonCommands(String... wants) { 151 verifyDaemonCommand(String.join(" ", wants), wants.length); 152 } 153 154 void verifyDaemonCommand(String want) { 155 verifyDaemonCommand(want, 1); 156 } 157 158 void verifyDaemonCommand(String want, int n) { 159 ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class); 160 verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); 161 String got = ""; 162 for (Object o : argumentsCaptor.getAllValues()) { 163 got += o + " "; 164 } 165 assertEquals(want, got.trim()); 166 // rearm deamon for next command verification 167 reset(mDaemon); 168 when(mDaemon.execute(any())).thenReturn(true); 169 } 170 171 public static class TestHandler extends Handler { 172 public Message lastMessage; 173 174 TestHandler(Looper looper) { 175 super(looper); 176 } 177 178 @Override 179 public void handleMessage(Message msg) { 180 lastMessage = obtainMessage(); 181 lastMessage.copyFrom(msg); 182 } 183 } 184 } 185