• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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