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 setUp()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 tearDown()79 public void tearDown() throws Exception { 80 if (mThread != null) { 81 mThread.quit(); 82 mThread = null; 83 } 84 } 85 86 @Test testClientsCanConnectAndDisconnect()87 public void testClientsCanConnectAndDisconnect() { 88 when(mSettings.isEnabled()).thenReturn(true); 89 90 NsdService service = makeService(); 91 92 NsdManager client1 = connectClient(service); 93 verify(mDaemon, timeout(100).times(1)).start(); 94 95 NsdManager client2 = connectClient(service); 96 97 client1.disconnect(); 98 client2.disconnect(); 99 100 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 101 102 client1.disconnect(); 103 client2.disconnect(); 104 } 105 106 @Test testClientRequestsAreGCedAtDisconnection()107 public void testClientRequestsAreGCedAtDisconnection() { 108 when(mSettings.isEnabled()).thenReturn(true); 109 when(mDaemon.execute(any())).thenReturn(true); 110 111 NsdService service = makeService(); 112 NsdManager client = connectClient(service); 113 114 verify(mDaemon, timeout(100).times(1)).start(); 115 116 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type"); 117 request.setPort(2201); 118 119 // Client registration request 120 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class); 121 client.registerService(request, PROTOCOL, listener1); 122 verifyDaemonCommand("register 2 a_name a_type 2201"); 123 124 // Client discovery request 125 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class); 126 client.discoverServices("a_type", PROTOCOL, listener2); 127 verifyDaemonCommand("discover 3 a_type"); 128 129 // Client resolve request 130 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class); 131 client.resolveService(request, listener3); 132 verifyDaemonCommand("resolve 4 a_name a_type local."); 133 134 // Client disconnects 135 client.disconnect(); 136 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop(); 137 138 // checks that request are cleaned 139 verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4"); 140 141 client.disconnect(); 142 } 143 makeService()144 NsdService makeService() { 145 DaemonConnectionSupplier supplier = (callback) -> { 146 mDaemonCallback = callback; 147 return mDaemon; 148 }; 149 NsdService service = new NsdService(mContext, mSettings, mHandler, supplier); 150 verify(mDaemon, never()).execute(any(String.class)); 151 return service; 152 } 153 connectClient(NsdService service)154 NsdManager connectClient(NsdService service) { 155 return new NsdManager(mContext, service); 156 } 157 verifyDaemonCommands(String... wants)158 void verifyDaemonCommands(String... wants) { 159 verifyDaemonCommand(String.join(" ", wants), wants.length); 160 } 161 verifyDaemonCommand(String want)162 void verifyDaemonCommand(String want) { 163 verifyDaemonCommand(want, 1); 164 } 165 verifyDaemonCommand(String want, int n)166 void verifyDaemonCommand(String want, int n) { 167 ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class); 168 verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture()); 169 String got = ""; 170 for (Object o : argumentsCaptor.getAllValues()) { 171 got += o + " "; 172 } 173 assertEquals(want, got.trim()); 174 // rearm deamon for next command verification 175 reset(mDaemon); 176 when(mDaemon.execute(any())).thenReturn(true); 177 } 178 179 public static class TestHandler extends Handler { 180 public Message lastMessage; 181 TestHandler(Looper looper)182 TestHandler(Looper looper) { 183 super(looper); 184 } 185 186 @Override handleMessage(Message msg)187 public void handleMessage(Message msg) { 188 lastMessage = obtainMessage(); 189 lastMessage.copyFrom(msg); 190 } 191 } 192 } 193