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 static org.junit.Assert.fail;
14 import static org.mockito.Mockito.timeout;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.verifyNoMoreInteractions;
17 
18 import org.chromium.testing.local.LocalRobolectricTestRunner;
19 import org.junit.After;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.junit.runner.RunWith;
23 import org.mockito.Mock;
24 import org.mockito.MockitoAnnotations;
25 import org.robolectric.annotation.Config;
26 import org.robolectric.shadows.ShadowLog;
27 
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.TimeUnit;
31 
32 @RunWith(LocalRobolectricTestRunner.class)
33 @Config(manifest = Config.NONE)
34 public class TCPChannelClientTest {
35   private static final int PORT = 8888;
36   /**
37    * How long we wait before trying to connect to the server. Note: was
38    * previously only 10, which was too short (tests were flaky).
39    */
40   private static final int SERVER_WAIT = 300;
41   private static final int CONNECT_TIMEOUT = 1000;
42   private static final int SEND_TIMEOUT = 1000;
43   private static final int DISCONNECT_TIMEOUT = 1000;
44   private static final int TERMINATION_TIMEOUT = 1000;
45   private static final String TEST_MESSAGE_SERVER = "Hello, Server!";
46   private static final String TEST_MESSAGE_CLIENT = "Hello, Client!";
47 
48   @Mock TCPChannelClient.TCPChannelEvents serverEvents;
49   @Mock TCPChannelClient.TCPChannelEvents clientEvents;
50 
51   private ExecutorService executor;
52   private TCPChannelClient server;
53   private TCPChannelClient client;
54 
55   @Before
setUp()56   public void setUp() {
57     ShadowLog.stream = System.out;
58 
59     MockitoAnnotations.initMocks(this);
60 
61     executor = Executors.newSingleThreadExecutor();
62   }
63 
64   @After
tearDown()65   public void tearDown() {
66     verifyNoMoreEvents();
67 
68     executeAndWait(new Runnable() {
69       @Override
70       public void run() {
71         client.disconnect();
72         server.disconnect();
73       }
74     });
75 
76     // Stop the executor thread
77     executor.shutdown();
78     try {
79       executor.awaitTermination(TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
80     } catch (InterruptedException e) {
81       fail(e.getMessage());
82     }
83   }
84 
85   @Test
testConnectIPv4()86   public void testConnectIPv4() {
87     setUpIPv4Server();
88     try {
89       Thread.sleep(SERVER_WAIT);
90     } catch (InterruptedException e) {
91       fail(e.getMessage());
92     }
93     setUpIPv4Client();
94 
95     verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
96     verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
97   }
98 
99   @Test
testConnectIPv6()100   public void testConnectIPv6() {
101     setUpIPv6Server();
102     try {
103       Thread.sleep(SERVER_WAIT);
104     } catch (InterruptedException e) {
105       fail(e.getMessage());
106     }
107     setUpIPv6Client();
108 
109     verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
110     verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
111   }
112 
113   @Test
testSendData()114   public void testSendData() {
115     testConnectIPv4();
116 
117     executeAndWait(new Runnable() {
118       @Override
119       public void run() {
120         client.send(TEST_MESSAGE_SERVER);
121         server.send(TEST_MESSAGE_CLIENT);
122       }
123     });
124 
125     verify(serverEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_SERVER);
126     verify(clientEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_CLIENT);
127   }
128 
129   @Test
testDisconnectServer()130   public void testDisconnectServer() {
131     testConnectIPv4();
132     executeAndWait(new Runnable() {
133       @Override
134       public void run() {
135         server.disconnect();
136       }
137     });
138 
139     verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
140     verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
141   }
142 
143   @Test
testDisconnectClient()144   public void testDisconnectClient() {
145     testConnectIPv4();
146     executeAndWait(new Runnable() {
147       @Override
148       public void run() {
149         client.disconnect();
150       }
151     });
152 
153     verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
154     verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
155   }
156 
setUpIPv4Server()157   private void setUpIPv4Server() {
158     setUpServer("0.0.0.0", PORT);
159   }
160 
setUpIPv4Client()161   private void setUpIPv4Client() {
162     setUpClient("127.0.0.1", PORT);
163   }
164 
setUpIPv6Server()165   private void setUpIPv6Server() {
166     setUpServer("::", PORT);
167   }
168 
setUpIPv6Client()169   private void setUpIPv6Client() {
170     setUpClient("::1", PORT);
171   }
172 
setUpServer(String ip, int port)173   private void setUpServer(String ip, int port) {
174     server = new TCPChannelClient(executor, serverEvents, ip, port);
175   }
176 
setUpClient(String ip, int port)177   private void setUpClient(String ip, int port) {
178     client = new TCPChannelClient(executor, clientEvents, ip, port);
179   }
180 
181   /**
182    * Verifies no more server or client events have been issued
183    */
verifyNoMoreEvents()184   private void verifyNoMoreEvents() {
185     verifyNoMoreInteractions(serverEvents);
186     verifyNoMoreInteractions(clientEvents);
187   }
188 
189   /**
190    * Queues runnable to be run and waits for it to be executed by the executor thread
191    */
executeAndWait(Runnable runnable)192   public void executeAndWait(Runnable runnable) {
193     try {
194       executor.submit(runnable).get();
195     } catch (Exception e) {
196       fail(e.getMessage());
197     }
198   }
199 }
200