diff options
Diffstat (limited to 'tests/unit/java/android/net/nsd/NsdManagerTest.java')
| -rw-r--r-- | tests/unit/java/android/net/nsd/NsdManagerTest.java | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java new file mode 100644 index 0000000000..b0a9b8a553 --- /dev/null +++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.AsyncChannel; +import com.android.testutils.HandlerUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NsdManagerTest { + + static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD; + + @Mock Context mContext; + @Mock INsdManager mService; + MockServiceHandler mServiceHandler; + + NsdManager mManager; + + long mTimeoutMs = 200; // non-final so that tests can adjust the value. + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mServiceHandler = spy(MockServiceHandler.create(mContext)); + when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler)); + + mManager = makeManager(); + } + + @After + public void tearDown() throws Exception { + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); + mServiceHandler.chan.disconnect(); + mServiceHandler.stop(); + if (mManager != null) { + mManager.disconnect(); + } + } + + @Test + public void testResolveService() { + NsdManager manager = mManager; + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); + NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class); + + manager.resolveService(request, listener); + int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); + int err = 33; + sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null); + verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err); + + manager.resolveService(request, listener); + int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + } + + @Test + public void testParallelResolveService() { + NsdManager manager = mManager; + + NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type"); + + NsdManager.ResolveListener listener1 = mock(NsdManager.ResolveListener.class); + NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class); + + manager.resolveService(request, listener1); + int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE); + + manager.resolveService(request, listener2); + int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE); + + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply); + sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply); + + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply); + } + + @Test + public void testRegisterService() { + NsdManager manager = mManager; + + NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type"); + request1.setPort(2201); + request2.setPort(2202); + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + NsdManager.RegistrationListener listener2 = mock(NsdManager.RegistrationListener.class); + + // Register two services + manager.registerService(request1, PROTOCOL, listener1); + int key1 = verifyRequest(NsdManager.REGISTER_SERVICE); + + manager.registerService(request2, PROTOCOL, listener2); + int key2 = verifyRequest(NsdManager.REGISTER_SERVICE); + + // First reques fails, second request succeeds + sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2); + verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2); + + int err = 1; + sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1); + verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err); + + // Client retries first request, it succeeds + manager.registerService(request1, PROTOCOL, listener1); + int key3 = verifyRequest(NsdManager.REGISTER_SERVICE); + + sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1); + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1); + + // First request is unregistered, it succeeds + manager.unregisterService(listener1); + int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE); + assertEquals(key3, key3again); + + sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null); + verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1); + + // Second request is unregistered, it fails + manager.unregisterService(listener2); + int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE); + assertEquals(key2, key2again); + + sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null); + verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err); + + // TODO: do not unregister listener until service is unregistered + // Client retries unregistration of second request, it succeeds + //manager.unregisterService(listener2); + //int key2yetAgain = verifyRequest(NsdManager.UNREGISTER_SERVICE); + //assertEquals(key2, key2yetAgain); + + //sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key2yetAgain, null); + //verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2); + } + + @Test + public void testDiscoverService() { + NsdManager manager = mManager; + + NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type"); + NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type"); + NsdServiceInfo reply3 = new NsdServiceInfo("a_third_name", "a_type"); + + NsdManager.DiscoveryListener listener = mock(NsdManager.DiscoveryListener.class); + + // Client registers for discovery, request fails + manager.discoverServices("a_type", PROTOCOL, listener); + int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + int err = 1; + sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null); + verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err); + + // Client retries, request succeeds + manager.discoverServices("a_type", PROTOCOL, listener); + int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); + + + // mdns notifies about services + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1); + + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2); + + sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2); + + + // Client unregisters its listener + manager.stopServiceDiscovery(listener); + int key2again = verifyRequest(NsdManager.STOP_DISCOVERY); + assertEquals(key2, key2again); + + // TODO: unregister listener immediately and stop notifying it about services + // Notifications are still passed to the client's listener + sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1); + + // Client is notified of complete unregistration + sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type"); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type"); + + // Notifications are not passed to the client anymore + sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3); + verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3); + + + // Client registers for service discovery + reset(listener); + manager.discoverServices("a_type", PROTOCOL, listener); + int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES); + + sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1); + verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type"); + + // Client unregisters immediately, it fails + manager.stopServiceDiscovery(listener); + int key3again = verifyRequest(NsdManager.STOP_DISCOVERY); + assertEquals(key3, key3again); + + err = 2; + sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type"); + verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err); + + // New notifications are not passed to the client anymore + sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1); + verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1); + } + + @Test + public void testInvalidCalls() { + NsdManager manager = mManager; + + NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); + NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); + NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); + + NsdServiceInfo invalidService = new NsdServiceInfo(null, null); + NsdServiceInfo validService = new NsdServiceInfo("a_name", "a_type"); + validService.setPort(2222); + + // Service registration + // - invalid arguments + mustFail(() -> { manager.unregisterService(null); }); + mustFail(() -> { manager.registerService(null, -1, null); }); + mustFail(() -> { manager.registerService(null, PROTOCOL, listener1); }); + mustFail(() -> { manager.registerService(invalidService, PROTOCOL, listener1); }); + mustFail(() -> { manager.registerService(validService, -1, listener1); }); + mustFail(() -> { manager.registerService(validService, PROTOCOL, null); }); + manager.registerService(validService, PROTOCOL, listener1); + // - listener already registered + mustFail(() -> { manager.registerService(validService, PROTOCOL, listener1); }); + manager.unregisterService(listener1); + // TODO: make listener immediately reusable + //mustFail(() -> { manager.unregisterService(listener1); }); + //manager.registerService(validService, PROTOCOL, listener1); + + // Discover service + // - invalid arguments + mustFail(() -> { manager.stopServiceDiscovery(null); }); + mustFail(() -> { manager.discoverServices(null, -1, null); }); + mustFail(() -> { manager.discoverServices(null, PROTOCOL, listener2); }); + mustFail(() -> { manager.discoverServices("a_service", -1, listener2); }); + mustFail(() -> { manager.discoverServices("a_service", PROTOCOL, null); }); + manager.discoverServices("a_service", PROTOCOL, listener2); + // - listener already registered + mustFail(() -> { manager.discoverServices("another_service", PROTOCOL, listener2); }); + manager.stopServiceDiscovery(listener2); + // TODO: make listener immediately reusable + //mustFail(() -> { manager.stopServiceDiscovery(listener2); }); + //manager.discoverServices("another_service", PROTOCOL, listener2); + + // Resolver service + // - invalid arguments + mustFail(() -> { manager.resolveService(null, null); }); + mustFail(() -> { manager.resolveService(null, listener3); }); + mustFail(() -> { manager.resolveService(invalidService, listener3); }); + mustFail(() -> { manager.resolveService(validService, null); }); + manager.resolveService(validService, listener3); + // - listener already registered:w + mustFail(() -> { manager.resolveService(validService, listener3); }); + } + + public void mustFail(Runnable fn) { + try { + fn.run(); + fail(); + } catch (Exception expected) { + } + } + + NsdManager makeManager() { + NsdManager manager = new NsdManager(mContext, mService); + // Acknowledge first two messages connecting the AsyncChannel. + verify(mServiceHandler, timeout(mTimeoutMs).times(2)).handleMessage(any()); + reset(mServiceHandler); + assertNotNull(mServiceHandler.chan); + return manager; + } + + int verifyRequest(int expectedMessageType) { + HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs); + verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); + reset(mServiceHandler); + Message received = mServiceHandler.getLastMessage(); + assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what)); + return received.arg2; + } + + void sendResponse(int replyType, int arg, int key, Object obj) { + mServiceHandler.chan.sendMessage(replyType, arg, key, obj); + } + + // Implements the server side of AsyncChannel connection protocol + public static class MockServiceHandler extends Handler { + public final Context context; + public AsyncChannel chan; + public Message lastMessage; + + MockServiceHandler(Looper l, Context c) { + super(l); + context = c; + } + + synchronized Message getLastMessage() { + return lastMessage; + } + + synchronized void setLastMessage(Message msg) { + lastMessage = obtainMessage(); + lastMessage.copyFrom(msg); + } + + @Override + public void handleMessage(Message msg) { + setLastMessage(msg); + if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) { + chan = new AsyncChannel(); + chan.connect(context, this, msg.replyTo); + chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); + } + } + + void stop() { + getLooper().quitSafely(); + } + + static MockServiceHandler create(Context context) { + HandlerThread t = new HandlerThread("mock-service-handler"); + t.start(); + return new MockServiceHandler(t.getLooper(), context); + } + } +} |
