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 android.net.nsd; 18 19 import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; 20 import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.fail; 24 import static org.mockito.ArgumentMatchers.anyBoolean; 25 import static org.mockito.Mockito.any; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.reset; 30 import static org.mockito.Mockito.timeout; 31 import static org.mockito.Mockito.times; 32 import static org.mockito.Mockito.verify; 33 34 import android.compat.testing.PlatformCompatChangeRule; 35 import android.content.Context; 36 import android.net.connectivity.ConnectivityCompatChanges; 37 import android.os.Build; 38 39 import androidx.test.filters.SmallTest; 40 41 import com.android.testutils.DevSdkIgnoreRule; 42 import com.android.testutils.DevSdkIgnoreRunner; 43 import com.android.testutils.FunctionalUtils.ThrowingConsumer; 44 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.rules.TestRule; 49 import org.junit.runner.RunWith; 50 import org.mockito.ArgumentCaptor; 51 import org.mockito.Mock; 52 import org.mockito.MockitoAnnotations; 53 54 @RunWith(DevSdkIgnoreRunner.class) 55 @SmallTest 56 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 57 public class NsdManagerTest { 58 59 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; 60 61 @Rule 62 public TestRule compatChangeRule = new PlatformCompatChangeRule(); 63 64 @Mock Context mContext; 65 @Mock INsdManager mService; 66 @Mock INsdServiceConnector mServiceConn; 67 68 NsdManager mManager; 69 INsdManagerCallback mCallback; 70 71 long mTimeoutMs = 200; // non-final so that tests can adjust the value. 72 73 @Before setUp()74 public void setUp() throws Exception { 75 MockitoAnnotations.initMocks(this); 76 77 doReturn(mServiceConn).when(mService).connect(any(), anyBoolean()); 78 mManager = new NsdManager(mContext, mService); 79 final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass( 80 INsdManagerCallback.class); 81 verify(mService).connect(cbCaptor.capture(), anyBoolean()); 82 mCallback = cbCaptor.getValue(); 83 } 84 85 @Test 86 @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testResolveServiceS()87 public void testResolveServiceS() throws Exception { 88 verify(mServiceConn, never()).startDaemon(); 89 doTestResolveService(); 90 } 91 92 @Test 93 @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testResolveServicePreS()94 public void testResolveServicePreS() throws Exception { 95 verify(mServiceConn).startDaemon(); 96 doTestResolveService(); 97 } 98 99 @Test 100 @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testDiscoverServiceS()101 public void testDiscoverServiceS() throws Exception { 102 verify(mServiceConn, never()).startDaemon(); 103 doTestDiscoverService(); 104 } 105 106 @Test 107 @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testDiscoverServicePreS()108 public void testDiscoverServicePreS() throws Exception { 109 verify(mServiceConn).startDaemon(); 110 doTestDiscoverService(); 111 } 112 113 @Test 114 @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testParallelResolveServiceS()115 public void testParallelResolveServiceS() throws Exception { 116 verify(mServiceConn, never()).startDaemon(); 117 doTestParallelResolveService(); 118 } 119 120 @Test 121 @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testParallelResolveServicePreS()122 public void testParallelResolveServicePreS() throws Exception { 123 verify(mServiceConn).startDaemon(); 124 doTestParallelResolveService(); 125 } 126 127 @Test 128 @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testInvalidCallsS()129 public void testInvalidCallsS() throws Exception { 130 verify(mServiceConn, never()).startDaemon(); 131 doTestInvalidCalls(); 132 } 133 134 @Test 135 @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testInvalidCallsPreS()136 public void testInvalidCallsPreS() throws Exception { 137 verify(mServiceConn).startDaemon(); 138 doTestInvalidCalls(); 139 } 140 141 @Test 142 @EnableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testRegisterServiceS()143 public void testRegisterServiceS() throws Exception { 144 verify(mServiceConn, never()).startDaemon(); 145 doTestRegisterService(); 146 } 147 148 @Test 149 @DisableCompatChanges(ConnectivityCompatChanges.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS_T_AND_LATER) testRegisterServicePreS()150 public void testRegisterServicePreS() throws Exception { 151 verify(mServiceConn).startDaemon(); 152 doTestRegisterService(); 153 } 154 doTestResolveService()155 private void doTestResolveService() throws Exception { 156 NsdManager manager = mManager; 157 158 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 159 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); 160 NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); 161 162 manager.resolveService(request, listener); 163 int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any())); 164 int err = 33; 165 mCallback.onResolveServiceFailed(key1, err); 166 verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); 167 168 manager.resolveService(request, listener); 169 int key2 = getRequestKey(req -> 170 verify(mServiceConn, times(2)).resolveService(req.capture(), any())); 171 mCallback.onResolveServiceSucceeded(key2, reply); 172 verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 173 } 174 doTestParallelResolveService()175 private void doTestParallelResolveService() throws Exception { 176 NsdManager manager = mManager; 177 178 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 179 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); 180 181 NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class); 182 NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); 183 184 manager.resolveService(request, listener1); 185 int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any())); 186 187 manager.resolveService(request, listener2); 188 int key2 = getRequestKey(req -> 189 verify(mServiceConn, times(2)).resolveService(req.capture(), any())); 190 191 mCallback.onResolveServiceSucceeded(key2, reply); 192 mCallback.onResolveServiceSucceeded(key1, reply); 193 194 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 195 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); 196 } 197 doTestRegisterService()198 private void doTestRegisterService() throws Exception { 199 NsdManager manager = mManager; 200 201 NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); 202 NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); 203 request1.setPort(2201); 204 request2.setPort(2202); 205 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 206 NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class); 207 208 // Register two services 209 manager.registerService(request1, PROTOCOL, listener1); 210 int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any())); 211 212 manager.registerService(request2, PROTOCOL, listener2); 213 int key2 = getRequestKey(req -> 214 verify(mServiceConn, times(2)).registerService(req.capture(), any())); 215 216 // First reques fails, second request succeeds 217 mCallback.onRegisterServiceSucceeded(key2, request2); 218 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); 219 220 int err = 1; 221 mCallback.onRegisterServiceFailed(key1, err); 222 verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); 223 224 // Client retries first request, it succeeds 225 manager.registerService(request1, PROTOCOL, listener1); 226 int key3 = getRequestKey(req -> 227 verify(mServiceConn, times(3)).registerService(req.capture(), any())); 228 229 mCallback.onRegisterServiceSucceeded(key3, request1); 230 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); 231 232 // First request is unregistered, it succeeds 233 manager.unregisterService(listener1); 234 int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture())); 235 assertEquals(key3, key3again); 236 237 mCallback.onUnregisterServiceSucceeded(key3again); 238 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); 239 240 // Second request is unregistered, it fails 241 manager.unregisterService(listener2); 242 int key2again = getRequestKey(req -> 243 verify(mServiceConn, times(2)).unregisterService(req.capture())); 244 assertEquals(key2, key2again); 245 246 mCallback.onUnregisterServiceFailed(key2again, err); 247 verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); 248 249 // TODO: do not unregister listener until service is unregistered 250 // Client retries unregistration of second request, it succeeds 251 //manager.unregisterService(listener2); 252 //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE); 253 //assertEquals(key2, key2yetAgain); 254 255 //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null); 256 //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); 257 } 258 doTestDiscoverService()259 private void doTestDiscoverService() throws Exception { 260 NsdManager manager = mManager; 261 262 NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); 263 NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); 264 NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type"); 265 266 NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class); 267 268 // Client registers for discovery, request fails 269 manager.discoverServices("a_type", PROTOCOL, listener); 270 int key1 = getRequestKey(req -> 271 verify(mServiceConn).discoverServices(req.capture(), any())); 272 273 int err = 1; 274 mCallback.onDiscoverServicesFailed(key1, err); 275 verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); 276 277 // Client retries, request succeeds 278 manager.discoverServices("a_type", PROTOCOL, listener); 279 int key2 = getRequestKey(req -> 280 verify(mServiceConn, times(2)).discoverServices(req.capture(), any())); 281 282 mCallback.onDiscoverServicesStarted(key2, reply1); 283 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); 284 285 286 // mdns notifies about services 287 mCallback.onServiceFound(key2, reply1); 288 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); 289 290 mCallback.onServiceFound(key2, reply2); 291 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); 292 293 mCallback.onServiceLost(key2, reply2); 294 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); 295 296 297 // Client unregisters its listener 298 manager.stopServiceDiscovery(listener); 299 int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture())); 300 assertEquals(key2, key2again); 301 302 // TODO: unregister listener immediately and stop notifying it about services 303 // Notifications are still passed to the client's listener 304 mCallback.onServiceLost(key2, reply1); 305 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); 306 307 // Client is notified of complete unregistration 308 mCallback.onStopDiscoverySucceeded(key2again); 309 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); 310 311 // Notifications are not passed to the client anymore 312 mCallback.onServiceFound(key2, reply3); 313 verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); 314 315 316 // Client registers for service discovery 317 reset(listener); 318 manager.discoverServices("a_type", PROTOCOL, listener); 319 int key3 = getRequestKey(req -> 320 verify(mServiceConn, times(3)).discoverServices(req.capture(), any())); 321 322 mCallback.onDiscoverServicesStarted(key3, reply1); 323 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); 324 325 // Client unregisters immediately, it fails 326 manager.stopServiceDiscovery(listener); 327 int key3again = getRequestKey(req -> 328 verify(mServiceConn, times(2)).stopDiscovery(req.capture())); 329 assertEquals(key3, key3again); 330 331 err = 2; 332 mCallback.onStopDiscoveryFailed(key3again, err); 333 verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); 334 335 // New notifications are not passed to the client anymore 336 mCallback.onServiceFound(key3, reply1); 337 verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); 338 } 339 doTestInvalidCalls()340 public void doTestInvalidCalls() { 341 NsdManager manager = mManager; 342 343 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 344 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); 345 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); 346 347 NsdServiceInfo invalidService = new NsdServiceInfo(null, null); 348 NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type"); 349 validService.setPort(2222); 350 351 // Service registration 352 // - invalid arguments 353 mustFail(() -> { manager.unregisterService(null); }); 354 mustFail(() -> { manager.registerService(null, -1, null); }); 355 mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); }); 356 mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); }); 357 mustFail(() -> { manager.registerService(validService, -1, listener1); }); 358 mustFail(() -> { manager.registerService(validService, PROTOCOL, null); }); 359 manager.registerService(validService, PROTOCOL, listener1); 360 // - listener already registered 361 mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); }); 362 manager.unregisterService(listener1); 363 // TODO: make listener immediately reusable 364 //mustFail(() -> { manager.unregisterService(listener1); }); 365 //manager.registerService(validService, PROTOCOL, listener1); 366 367 // Discover service 368 // - invalid arguments 369 mustFail(() -> { manager.stopServiceDiscovery(null); }); 370 mustFail(() -> { manager.discoverServices(null, -1, null); }); 371 mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); }); 372 mustFail(() -> { manager.discoverServices("a_service", -1, listener2); }); 373 mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); }); 374 manager.discoverServices("a_service", PROTOCOL, listener2); 375 // - listener already registered 376 mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); }); 377 manager.stopServiceDiscovery(listener2); 378 // TODO: make listener immediately reusable 379 //mustFail(() -> { manager.stopServiceDiscovery(listener2); }); 380 //manager.discoverServices("another_service", PROTOCOL, listener2); 381 382 // Resolver service 383 // - invalid arguments 384 mustFail(() -> { manager.resolveService(null, null); }); 385 mustFail(() -> { manager.resolveService(null, listener3); }); 386 mustFail(() -> { manager.resolveService(invalidService, listener3); }); 387 mustFail(() -> { manager.resolveService(validService, null); }); 388 manager.resolveService(validService, listener3); 389 // - listener already registered:w 390 mustFail(() -> { manager.resolveService(validService, listener3); }); 391 } 392 mustFail(Runnable fn)393 public void mustFail(Runnable fn) { 394 try { 395 fn.run(); 396 fail(); 397 } catch (Exception expected) { 398 } 399 } 400 getRequestKey(ThrowingConsumer<ArgumentCaptor<Integer>> verifier)401 int getRequestKey(ThrowingConsumer<ArgumentCaptor<Integer>> verifier) 402 throws Exception { 403 final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class); 404 verifier.accept(captor); 405 return captor.getValue(); 406 } 407 } 408