blob: 2e49c98ca9b66a6e1739439fbd269f68d60a9cbc [file] [log] [blame]
Hugo Benichicbb13672017-04-24 11:35:06 +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 com.android.server;
18
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090019import static org.junit.Assert.assertEquals;
Hugo Benichi1fac3192017-04-24 16:19:58 +090020import static org.mockito.Mockito.any;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090021import static org.mockito.Mockito.mock;
Hugo Benichi1fac3192017-04-24 16:19:58 +090022import static org.mockito.Mockito.never;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090023import static org.mockito.Mockito.reset;
Hugo Benichi1fac3192017-04-24 16:19:58 +090024import static org.mockito.Mockito.timeout;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.verify;
Hugo Benichicbb13672017-04-24 11:35:06 +090027import static org.mockito.Mockito.when;
28
29import android.os.Handler;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090030import android.os.HandlerThread;
Hugo Benichicbb13672017-04-24 11:35:06 +090031import android.os.Looper;
32import android.os.Message;
Hugo Benichicbb13672017-04-24 11:35:06 +090033import android.content.Context;
34import android.content.ContentResolver;
35import android.net.nsd.NsdManager;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090036import android.net.nsd.NsdServiceInfo;
Hugo Benichi1fac3192017-04-24 16:19:58 +090037import com.android.server.NsdService.DaemonConnection;
38import com.android.server.NsdService.DaemonConnectionSupplier;
39import com.android.server.NsdService.NativeCallbackReceiver;
Hugo Benichicbb13672017-04-24 11:35:06 +090040import android.support.test.filters.SmallTest;
41import android.support.test.runner.AndroidJUnit4;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090042import org.junit.After;
Hugo Benichicbb13672017-04-24 11:35:06 +090043import org.junit.Before;
44import org.junit.Test;
45import org.junit.runner.RunWith;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090046import org.mockito.ArgumentCaptor;
Hugo Benichicbb13672017-04-24 11:35:06 +090047import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49
50// TODOs:
Hugo Benichicbb13672017-04-24 11:35:06 +090051// - test client can send requests and receive replies
52// - test NSD_ON ENABLE/DISABLED listening
53@RunWith(AndroidJUnit4.class)
54@SmallTest
55public class NsdServiceTest {
56
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090057 static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
58
59 long mTimeoutMs = 100; // non-final so that tests can adjust the value.
60
Hugo Benichicbb13672017-04-24 11:35:06 +090061 @Mock Context mContext;
62 @Mock ContentResolver mResolver;
63 @Mock NsdService.NsdSettings mSettings;
Hugo Benichi1fac3192017-04-24 16:19:58 +090064 @Mock DaemonConnection mDaemon;
65 NativeCallbackReceiver mDaemonCallback;
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090066 HandlerThread mThread;
Hugo Benichicbb13672017-04-24 11:35:06 +090067 TestHandler mHandler;
68
69 @Before
70 public void setUp() throws Exception {
71 MockitoAnnotations.initMocks(this);
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090072 mThread = new HandlerThread("mock-service-handler");
73 mThread.start();
74 mHandler = new TestHandler(mThread.getLooper());
Hugo Benichicbb13672017-04-24 11:35:06 +090075 when(mContext.getContentResolver()).thenReturn(mResolver);
76 }
77
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090078 @After
79 public void tearDown() throws Exception {
80 mThread.quit();
81 }
82
Hugo Benichicbb13672017-04-24 11:35:06 +090083 @Test
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090084 public void testClientsCanConnectAndDisconnect() {
Hugo Benichi1fac3192017-04-24 16:19:58 +090085 when(mSettings.isEnabled()).thenReturn(true);
86
Hugo Benichicbb13672017-04-24 11:35:06 +090087 NsdService service = makeService();
Hugo Benichi1fac3192017-04-24 16:19:58 +090088
Hugo Benichicbb13672017-04-24 11:35:06 +090089 NsdManager client1 = connectClient(service);
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090090 verify(mDaemon, timeout(100).times(1)).start();
Hugo Benichi1fac3192017-04-24 16:19:58 +090091
Hugo Benichicbb13672017-04-24 11:35:06 +090092 NsdManager client2 = connectClient(service);
93
Hugo Benichiab5bdbf2017-04-28 15:31:10 +090094 client1.disconnect();
95 client2.disconnect();
96
97 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
Hugo Benichie062ae02017-07-31 20:35:58 +090098
99 client1.disconnect();
100 client2.disconnect();
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900101 }
102
103 @Test
104 public void testClientRequestsAreGCedAtDisconnection() {
105 when(mSettings.isEnabled()).thenReturn(true);
106 when(mDaemon.execute(any())).thenReturn(true);
107
108 NsdService service = makeService();
109 NsdManager client = connectClient(service);
110
111 verify(mDaemon, timeout(100).times(1)).start();
112
113 NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
114 request.setPort(2201);
115
116 // Client registration request
117 NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
118 client.registerService(request, PROTOCOL, listener1);
119 verifyDaemonCommand("register 2 a_name a_type 2201");
120
121 // Client discovery request
122 NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
123 client.discoverServices("a_type", PROTOCOL, listener2);
124 verifyDaemonCommand("discover 3 a_type");
125
126 // Client resolve request
127 NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
128 client.resolveService(request, listener3);
129 verifyDaemonCommand("resolve 4 a_name a_type local.");
130
131 // Client disconnects
132 client.disconnect();
133 verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
134
135 // checks that request are cleaned
136 verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
Hugo Benichie062ae02017-07-31 20:35:58 +0900137
138 client.disconnect();
Hugo Benichicbb13672017-04-24 11:35:06 +0900139 }
140
141 NsdService makeService() {
Hugo Benichi1fac3192017-04-24 16:19:58 +0900142 DaemonConnectionSupplier supplier = (callback) -> {
143 mDaemonCallback = callback;
144 return mDaemon;
145 };
146 NsdService service = new NsdService(mContext, mSettings, mHandler, supplier);
147 verify(mDaemon, never()).execute(any(String.class));
148 return service;
Hugo Benichicbb13672017-04-24 11:35:06 +0900149 }
150
151 NsdManager connectClient(NsdService service) {
Hugo Benichiab5bdbf2017-04-28 15:31:10 +0900152 return new NsdManager(mContext, service);
153 }
154
155 void verifyDaemonCommands(String... wants) {
156 verifyDaemonCommand(String.join(" ", wants), wants.length);
157 }
158
159 void verifyDaemonCommand(String want) {
160 verifyDaemonCommand(want, 1);
161 }
162
163 void verifyDaemonCommand(String want, int n) {
164 ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
165 verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture());
166 String got = "";
167 for (Object o : argumentsCaptor.getAllValues()) {
168 got += o + " ";
169 }
170 assertEquals(want, got.trim());
171 // rearm deamon for next command verification
172 reset(mDaemon);
173 when(mDaemon.execute(any())).thenReturn(true);
Hugo Benichicbb13672017-04-24 11:35:06 +0900174 }
175
176 public static class TestHandler extends Handler {
177 public Message lastMessage;
178
179 TestHandler(Looper looper) {
180 super(looper);
181 }
182
183 @Override
184 public void handleMessage(Message msg) {
185 lastMessage = obtainMessage();
186 lastMessage.copyFrom(msg);
187 }
188 }
189}