OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 package org.appspot.apprtc; | |
12 | |
13 import org.appspot.apprtc.util.LooperExecutor; | |
14 import org.junit.After; | |
15 import org.junit.Before; | |
16 import org.junit.Test; | |
17 import org.junit.runner.RunWith; | |
18 import org.mockito.Mock; | |
19 import org.mockito.MockitoAnnotations; | |
20 import org.mockito.invocation.InvocationOnMock; | |
21 import org.mockito.stubbing.Answer; | |
22 import org.robolectric.RobolectricTestRunner; | |
23 import org.robolectric.annotation.Config; | |
24 import org.robolectric.shadows.ShadowLog; | |
25 | |
26 import java.util.ArrayDeque; | |
27 import java.util.Queue; | |
28 | |
29 import static org.junit.Assert.fail; | |
30 import static org.mockito.Matchers.any; | |
31 import static org.mockito.Mockito.doAnswer; | |
32 import static org.mockito.Mockito.timeout; | |
33 import static org.mockito.Mockito.verify; | |
34 import static org.mockito.Mockito.verifyNoMoreInteractions; | |
35 import static org.mockito.Mockito.when; | |
36 | |
37 @RunWith(RobolectricTestRunner.class) | |
38 @Config(manifest = Config.NONE) | |
39 public class TCPChannelClientTest { | |
40 private static final int PORT = 8888; | |
41 /** | |
42 * How long we wait before trying to connect to the server. Chosen quite arbit rarily and | |
43 * could be made smaller if need be. | |
44 */ | |
45 private static final int SERVER_WAIT = 10; | |
46 private static final int CONNECT_TIMEOUT = 100; | |
47 private static final int SEND_TIMEOUT = 100; | |
48 private static final int DISCONNECT_TIMEOUT = 100; | |
49 private static final String TEST_MESSAGE_SERVER = "Hello, Server!"; | |
50 private static final String TEST_MESSAGE_CLIENT = "Hello, Client!"; | |
51 | |
52 @Mock TCPChannelClient.TCPChannelEvents serverEvents; | |
53 @Mock TCPChannelClient.TCPChannelEvents clientEvents; | |
54 | |
55 private final ExecutorThread executorThread = new ExecutorThread(); | |
56 private final Queue<Runnable> executorQueue = new ArrayDeque<Runnable>(); | |
magjed_webrtc
2016/05/17 12:54:37
If you make |executorQueue| a BlockingQueue instea
sakal
2016/05/17 13:46:26
Done.
| |
57 @Mock LooperExecutor executor; | |
magjed_webrtc
2016/05/17 12:54:38
I don't really understand why you need to mock Loo
sakal
2016/05/17 13:46:26
Mocking LooperExecutor was a selection of multiple
| |
58 | |
59 private TCPChannelClient server; | |
60 private TCPChannelClient client; | |
61 | |
62 /** | |
63 * Runs commands on executor queue until stopped with requestStop. | |
64 */ | |
65 private class ExecutorThread extends Thread { | |
66 private volatile boolean shouldStop = false; | |
67 | |
68 @Override | |
69 public void run() { | |
70 while (true) { | |
magjed_webrtc
2016/05/17 12:54:38
while (!shouldStop) ?
sakal
2016/05/17 13:46:26
Done.
| |
71 Runnable runnable; | |
magjed_webrtc
2016/05/17 12:54:37
nit: Make |runnable| final.
sakal
2016/05/17 13:46:26
Done.
| |
72 | |
73 synchronized (executorQueue) { | |
74 while (executorQueue.isEmpty()) { | |
75 try { | |
76 executorQueue.wait(); | |
77 } catch (InterruptedException e) { | |
78 if (!shouldStop) { | |
79 fail(e.getMessage()); | |
80 } | |
81 } | |
82 | |
83 if (shouldStop) { | |
84 return; | |
85 } | |
86 } | |
87 | |
88 runnable = executorQueue.remove(); | |
89 } | |
90 | |
91 runnable.run(); | |
92 } | |
93 } | |
94 | |
95 public void requestStop() { | |
96 shouldStop = true; | |
97 interrupt(); | |
98 } | |
99 } | |
100 | |
101 | |
102 /** | |
103 * Executes the runnable passed to the constructor and sets isDone flag afterw ards. | |
104 */ | |
105 private static class ExecuteAndWaitRunnable implements Runnable { | |
106 public boolean isDone = false; | |
107 private Runnable runnable; | |
magjed_webrtc
2016/05/17 12:54:38
nit: Make |runnable| final.
sakal
2016/05/17 13:46:26
Done.
| |
108 | |
109 ExecuteAndWaitRunnable(Runnable runnable) { | |
110 this.runnable = runnable; | |
111 } | |
112 | |
113 @Override | |
114 public void run() { | |
115 runnable.run(); | |
116 | |
117 synchronized (this) { | |
118 isDone = true; | |
119 notifyAll(); | |
120 } | |
121 } | |
122 } | |
123 | |
124 @Before | |
125 public void setUp() { | |
126 ShadowLog.stream = System.out; | |
127 | |
128 MockitoAnnotations.initMocks(this); | |
129 | |
130 Answer queueRunnableAnswer = new Answer() { | |
131 @Override | |
132 public Object answer(InvocationOnMock invocation) throws Throwable { | |
133 Runnable runnable = invocation.getArgumentAt(0, Runnable.class); | |
134 | |
135 synchronized (executorQueue) { | |
136 executorQueue.add(runnable); | |
137 executorQueue.notify(); | |
138 } | |
139 | |
140 return null; | |
141 } | |
142 }; | |
143 doAnswer(queueRunnableAnswer).when(executor).execute(any(Runnable.class)); | |
144 when(executor.checkOnLooperThread()).thenAnswer(new Answer<Boolean>() { | |
145 @Override | |
146 public Boolean answer(InvocationOnMock invocation) throws Throwable { | |
147 return Thread.currentThread() == executorThread; | |
148 } | |
149 }); | |
150 | |
151 executorThread.start(); | |
152 } | |
153 | |
154 @After | |
155 public void tearDown() { | |
156 verifyNoMoreEvents(); | |
157 | |
158 executeAndWait(new Runnable() { | |
159 @Override | |
160 public void run() { | |
161 client.disconnect(); | |
162 server.disconnect(); | |
163 } | |
164 }); | |
165 | |
166 // Stop the executor thread | |
167 executorThread.requestStop(); | |
168 executor.requestStop(); | |
169 try { | |
170 executorThread.join(); | |
171 executor.join(); | |
172 } catch (InterruptedException e) { | |
173 fail(e.getMessage()); | |
174 } | |
175 executorQueue.clear(); | |
176 } | |
177 | |
178 @Test | |
179 public void testConnectIPv4() { | |
180 setUpIPv4Server(); | |
181 try { | |
182 Thread.sleep(SERVER_WAIT); | |
183 } catch (InterruptedException e) { | |
184 fail(e.getMessage()); | |
185 } | |
186 setUpIPv4Client(); | |
187 | |
188 verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true); | |
189 verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false); | |
190 } | |
191 | |
192 @Test | |
193 public void testConnectIPv6() { | |
194 setUpIPv6Server(); | |
195 try { | |
196 Thread.sleep(SERVER_WAIT); | |
197 } catch (InterruptedException e) { | |
198 fail(e.getMessage()); | |
199 } | |
200 setUpIPv6Client(); | |
201 | |
202 verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true); | |
203 verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false); | |
204 } | |
205 | |
206 @Test | |
207 public void testSendData() { | |
208 testConnectIPv4(); | |
209 | |
210 executeAndWait(new Runnable() { | |
211 @Override | |
212 public void run() { | |
213 client.send(TEST_MESSAGE_SERVER); | |
214 server.send(TEST_MESSAGE_CLIENT); | |
215 } | |
216 }); | |
217 | |
218 verify(serverEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_SERVER ); | |
219 verify(clientEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_CLIENT ); | |
220 } | |
221 | |
222 @Test | |
223 public void testDisconnectServer() { | |
224 testConnectIPv4(); | |
225 executeAndWait(new Runnable() { | |
226 @Override | |
227 public void run() { | |
228 server.disconnect(); | |
229 } | |
230 }); | |
231 | |
232 verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose(); | |
233 verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose(); | |
234 } | |
235 | |
236 @Test | |
237 public void testDisconnectClient() { | |
238 testConnectIPv4(); | |
239 executeAndWait(new Runnable() { | |
240 @Override | |
241 public void run() { | |
242 client.disconnect(); | |
243 } | |
244 }); | |
245 | |
246 verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose(); | |
247 verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose(); | |
248 } | |
249 | |
250 private void setUpIPv4Server() { | |
251 setUpServer("0.0.0.0", PORT); | |
252 } | |
253 | |
254 private void setUpIPv4Client() { | |
255 setUpClient("127.0.0.1", PORT); | |
256 } | |
257 | |
258 private void setUpIPv6Server() { | |
259 setUpServer("::", PORT); | |
260 } | |
261 | |
262 private void setUpIPv6Client() { | |
263 setUpClient("::1", PORT); | |
264 } | |
265 | |
266 private void setUpServer(String ip, int port) { | |
267 server = new TCPChannelClient(executor, serverEvents, ip, port); | |
268 } | |
269 | |
270 private void setUpClient(String ip, int port) { | |
271 client = new TCPChannelClient(executor, clientEvents, ip, port); | |
272 } | |
273 | |
274 /** | |
275 * Queues runnable to be run and waits for it to be executed by the executor t hread | |
276 */ | |
277 private void executeAndWait(Runnable runnable) { | |
278 ExecuteAndWaitRunnable executeAndWaitRunnable = new ExecuteAndWaitRunnable(r unnable); | |
279 executor.execute(executeAndWaitRunnable); | |
280 executor.requestStart(); | |
281 | |
282 synchronized (executeAndWaitRunnable) { | |
283 while (!executeAndWaitRunnable.isDone) { | |
284 try { | |
285 executeAndWaitRunnable.wait(); | |
286 } catch (InterruptedException e) { | |
287 fail(e.getMessage()); | |
288 } | |
289 } | |
290 } | |
291 } | |
292 | |
293 /** | |
294 * Verifies no more server or client events have been issued | |
295 */ | |
296 private void verifyNoMoreEvents() { | |
297 verifyNoMoreInteractions(serverEvents); | |
298 verifyNoMoreInteractions(clientEvents); | |
299 } | |
300 } | |
OLD | NEW |