blob: f77608f95b3ea6c8b5c30cbc0e9534910dd97d31 [file] [log] [blame]
Hugo Benichidb8adb72017-04-17 15:27:52 +09001/*
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
17package android.net.nsd;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertNotNull;
Hugo Benichi52d9e732017-04-25 15:04:10 +090021import static org.junit.Assert.fail;
Hugo Benichidb8adb72017-04-17 15:27:52 +090022import static org.mockito.Mockito.any;
Hugo Benichi93f45912017-04-28 13:29:21 +090023import static org.mockito.Mockito.mock;
24import static org.mockito.Mockito.never;
Hugo Benichidb8adb72017-04-17 15:27:52 +090025import static org.mockito.Mockito.reset;
26import static org.mockito.Mockito.spy;
Hugo Benichidb8adb72017-04-17 15:27:52 +090027import static org.mockito.Mockito.timeout;
28import static org.mockito.Mockito.times;
Hugo Benichi93f45912017-04-28 13:29:21 +090029import static org.mockito.Mockito.verify;
30import static org.mockito.Mockito.when;
Hugo Benichidb8adb72017-04-17 15:27:52 +090031
32import android.os.HandlerThread;
33import android.os.Handler;
34import android.os.Looper;
35import android.content.Context;
36import android.support.test.filters.SmallTest;
37import android.support.test.runner.AndroidJUnit4;
38import android.os.Message;
39import android.os.Messenger;
40import com.android.internal.util.AsyncChannel;
41import org.junit.Before;
42import org.junit.Test;
43import org.junit.runner.RunWith;
44import org.mockito.Mock;
45import org.mockito.MockitoAnnotations;
46
Hugo Benichi52d9e732017-04-25 15:04:10 +090047import java.util.function.Consumer;
48
Hugo Benichidb8adb72017-04-17 15:27:52 +090049@RunWith(AndroidJUnit4.class)
50@SmallTest
51public class NsdManagerTest {
52
Hugo Benichi8c5eeb02017-04-28 11:01:22 +090053 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
54
Hugo Benichidb8adb72017-04-17 15:27:52 +090055 @Mock Context mContext;
56 @Mock INsdManager mService;
57 MockServiceHandler mServiceHandler;
58
59 long mTimeoutMs = 100; // non-final so that tests can adjust the value.
60
61 @Before
62 public void setUp() throws Exception {
63 MockitoAnnotations.initMocks(this);
64
65 mServiceHandler = spy(MockServiceHandler.create(mContext));
66 when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler));
67 }
68
69 @Test
70 public void testResolveService() {
71 NsdManager manager = makeManager();
72
73 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
74 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
75 NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
76
77 manager.resolveService(request, listener);
78 int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
79 int err = 33;
80 sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null);
81 verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
82
83 manager.resolveService(request, listener);
84 int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
85 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
86 verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
87 }
88
89 @Test
90 public void testParallelResolveService() {
91 NsdManager manager = makeManager();
92
93 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
94 NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
95
96 NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class);
97 NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
98
99 manager.resolveService(request, listener1);
100 int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
101
102 manager.resolveService(request, listener2);
103 int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
104
105 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
106 sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply);
107
108 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
109 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
Hugo Benichi52d9e732017-04-25 15:04:10 +0900110 }
111
112 @Test
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900113 public void testRegisterService() {
114 NsdManager manager = makeManager();
115
116 NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
117 NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type");
118 request1.setPort(2201);
119 request2.setPort(2202);
120 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
121 NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class);
122
123 // Register two services
124 manager.registerService(request1, PROTOCOL, listener1);
125 int key1 = verifyRequest(NsdManager.REGISTER_SERVICE);
126
127 manager.registerService(request2, PROTOCOL, listener2);
128 int key2 = verifyRequest(NsdManager.REGISTER_SERVICE);
129
130 // First reques fails, second request succeeds
131 sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2);
132 verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
133
134 int err = 1;
135 sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1);
136 verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
137
138 // Client retries first request, it succeeds
139 manager.registerService(request1, PROTOCOL, listener1);
140 int key3 = verifyRequest(NsdManager.REGISTER_SERVICE);
141
142 sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1);
143 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
144
145 // First request is unregistered, it succeeds
146 manager.unregisterService(listener1);
147 int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
148 assertEquals(key3, key3again);
149
150 sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null);
151 verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
152
153 // Second request is unregistered, it fails
154 manager.unregisterService(listener2);
155 int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
156 assertEquals(key2, key2again);
157
158 sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null);
159 verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
160
161 // TODO: do not unregister listener until service is unregistered
162 // Client retries unregistration of second request, it succeeds
163 //manager.unregisterService(listener2);
164 //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE);
165 //assertEquals(key2, key2yetAgain);
166
167 //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null);
168 //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
169 }
170
171 @Test
Hugo Benichi93f45912017-04-28 13:29:21 +0900172 public void testDiscoverService() {
173 NsdManager manager = makeManager();
174
175 NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
176 NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type");
177 NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type");
178
179 NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class);
180
181 // Client registers for discovery, request fails
182 manager.discoverServices("a_type", PROTOCOL, listener);
183 int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES);
184
185 int err = 1;
186 sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null);
187 verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
188
189 // Client retries, request succeeds
190 manager.discoverServices("a_type", PROTOCOL, listener);
191 int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES);
192
193 sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1);
194 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
195
196
197 // mdns notifies about services
198 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1);
199 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
200
201 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2);
202 verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
203
204 sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2);
205 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
206
207
208 // Client unregisters its listener
209 manager.stopServiceDiscovery(listener);
210 int key2again = verifyRequest(NsdManager.STOP_DISCOVERY);
211 assertEquals(key2, key2again);
212
213 // TODO: unregister listener immediately and stop notifying it about services
214 // Notifications are still passed to the client's listener
215 sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1);
216 verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
217
218 // Client is notified of complete unregistration
219 sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type");
220 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
221
222 // Notifications are not passed to the client anymore
223 sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3);
224 verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
225
226
227 // Client registers for service discovery
228 reset(listener);
229 manager.discoverServices("a_type", PROTOCOL, listener);
230 int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES);
231
232 sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1);
233 verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
234
235 // Client unregisters immediately, it fails
236 manager.stopServiceDiscovery(listener);
237 int key3again = verifyRequest(NsdManager.STOP_DISCOVERY);
238 assertEquals(key3, key3again);
239
240 err = 2;
241 sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type");
242 verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
243
244 // New notifications are not passed to the client anymore
245 sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1);
246 verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
247 }
248
249 @Test
Hugo Benichi52d9e732017-04-25 15:04:10 +0900250 public void testInvalidCalls() {
251 NsdManager manager = new NsdManager(mContext, mService);
252
253 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
254 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
255 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
256
257 NsdServiceInfo invalidService = new NsdServiceInfo(null, null);
258 NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type");
259 validService.setPort(2222);
260
Hugo Benichi52d9e732017-04-25 15:04:10 +0900261 // Service registration
262 // - invalid arguments
263 mustFail(() -> { manager.unregisterService(null); });
264 mustFail(() -> { manager.registerService(null, -1, null); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900265 mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); });
266 mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); });
Hugo Benichi52d9e732017-04-25 15:04:10 +0900267 mustFail(() -> { manager.registerService(validService, -1, listener1); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900268 mustFail(() -> { manager.registerService(validService, PROTOCOL, null); });
269 manager.registerService(validService, PROTOCOL, listener1);
Hugo Benichi52d9e732017-04-25 15:04:10 +0900270 // - listener already registered
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900271 mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); });
Hugo Benichi52d9e732017-04-25 15:04:10 +0900272 manager.unregisterService(listener1);
273 // TODO: make listener immediately reusable
274 //mustFail(() -> { manager.unregisterService(listener1); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900275 //manager.registerService(validService, PROTOCOL, listener1);
Hugo Benichi52d9e732017-04-25 15:04:10 +0900276
277 // Discover service
278 // - invalid arguments
279 mustFail(() -> { manager.stopServiceDiscovery(null); });
280 mustFail(() -> { manager.discoverServices(null, -1, null); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900281 mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); });
Hugo Benichi52d9e732017-04-25 15:04:10 +0900282 mustFail(() -> { manager.discoverServices("a_service", -1, listener2); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900283 mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); });
284 manager.discoverServices("a_service", PROTOCOL, listener2);
Hugo Benichi52d9e732017-04-25 15:04:10 +0900285 // - listener already registered
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900286 mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); });
Hugo Benichi52d9e732017-04-25 15:04:10 +0900287 manager.stopServiceDiscovery(listener2);
288 // TODO: make listener immediately reusable
289 //mustFail(() -> { manager.stopServiceDiscovery(listener2); });
Hugo Benichi8c5eeb02017-04-28 11:01:22 +0900290 //manager.discoverServices("another_service", PROTOCOL, listener2);
Hugo Benichi52d9e732017-04-25 15:04:10 +0900291
292 // Resolver service
293 // - invalid arguments
294 mustFail(() -> { manager.resolveService(null, null); });
295 mustFail(() -> { manager.resolveService(null, listener3); });
296 mustFail(() -> { manager.resolveService(invalidService, listener3); });
297 mustFail(() -> { manager.resolveService(validService, null); });
298 manager.resolveService(validService, listener3);
299 // - listener already registered:w
300 mustFail(() -> { manager.resolveService(validService, listener3); });
301 }
302
303 public void mustFail(Runnable fn) {
304 try {
305 fn.run();
306 fail();
307 } catch (Exception expected) {
308 }
309 }
Hugo Benichidb8adb72017-04-17 15:27:52 +0900310
311 NsdManager makeManager() {
312 NsdManager manager = new NsdManager(mContext, mService);
313 // Acknowledge first two messages connecting the AsyncChannel.
314 verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any());
315 reset(mServiceHandler);
316 assertNotNull(mServiceHandler.chan);
317 return manager;
318 }
319
320 int verifyRequest(int expectedMessageType) {
321 verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
322 reset(mServiceHandler);
323 Message received = mServiceHandler.lastMessage;
324 assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
325 return received.arg2;
326 }
327
328 void sendResponse(int replyType, int arg, int key, Object obj) {
329 mServiceHandler.chan.sendMessage(replyType, arg, key, obj);
330 }
331
332 // Implements the server side of AsyncChannel connection protocol
333 public static class MockServiceHandler extends Handler {
334 public Context mContext;
335 public AsyncChannel chan;
Hugo Benichi22143952017-05-31 15:31:59 +0900336 public volatile Message lastMessage;
Hugo Benichidb8adb72017-04-17 15:27:52 +0900337
338 MockServiceHandler(Looper looper, Context context) {
339 super(looper);
340 mContext = context;
341 }
342
343 @Override
344 public void handleMessage(Message msg) {
345 lastMessage = obtainMessage();
346 lastMessage.copyFrom(msg);
347 if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
348 chan = new AsyncChannel();
349 chan.connect(mContext, this, msg.replyTo);
350 chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
351 }
Hugo Benichidb8adb72017-04-17 15:27:52 +0900352 }
353
354 public static MockServiceHandler create(Context context) {
355 HandlerThread t = new HandlerThread("mock-service-handler");
356 t.start();
357 return new MockServiceHandler(t.getLooper(), context);
358 }
359 }
360}