summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/examples/androidjunit
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/libwebrtc/examples/androidjunit/OWNERS1
-rw-r--r--third_party/libwebrtc/examples/androidjunit/README8
-rw-r--r--third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java268
-rw-r--r--third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/DirectRTCClientTest.java155
-rw-r--r--third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/TCPChannelClientTest.java199
5 files changed, 631 insertions, 0 deletions
diff --git a/third_party/libwebrtc/examples/androidjunit/OWNERS b/third_party/libwebrtc/examples/androidjunit/OWNERS
new file mode 100644
index 0000000000..cf092a316a
--- /dev/null
+++ b/third_party/libwebrtc/examples/androidjunit/OWNERS
@@ -0,0 +1 @@
+xalep@webrtc.org
diff --git a/third_party/libwebrtc/examples/androidjunit/README b/third_party/libwebrtc/examples/androidjunit/README
new file mode 100644
index 0000000000..03902a779c
--- /dev/null
+++ b/third_party/libwebrtc/examples/androidjunit/README
@@ -0,0 +1,8 @@
+This directory contains example JUnit tests for Android AppRTCMobile.
+Many of these test utilize Robolectric to mock Android classes.
+
+To compile:
+ninja -C out/Debug android_examples_junit_tests
+
+To run:
+out/Debug/bin/run_android_examples_junit_tests
diff --git a/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java
new file mode 100644
index 0000000000..3060bd7a56
--- /dev/null
+++ b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.appspot.apprtc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+import androidx.test.core.app.ApplicationProvider;
+import java.util.ArrayList;
+import java.util.List;
+import org.appspot.apprtc.AppRTCBluetoothManager.State;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+
+/**
+ * Verifies basic behavior of the AppRTCBluetoothManager class.
+ * Note that the test object uses an AppRTCAudioManager (injected in ctor),
+ * but a mocked version is used instead. Hence, the parts "driven" by the AppRTC
+ * audio manager are not included in this test.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class BluetoothManagerTest {
+ private static final String TAG = "BluetoothManagerTest";
+ private static final String BLUETOOTH_TEST_DEVICE_NAME = "BluetoothTestDevice";
+
+ private BroadcastReceiver bluetoothHeadsetStateReceiver;
+ private BluetoothProfile.ServiceListener bluetoothServiceListener;
+ private BluetoothHeadset mockedBluetoothHeadset;
+ private BluetoothDevice mockedBluetoothDevice;
+ private List<BluetoothDevice> mockedBluetoothDeviceList;
+ private AppRTCBluetoothManager bluetoothManager;
+ private AppRTCAudioManager mockedAppRtcAudioManager;
+ private AudioManager mockedAudioManager;
+ private Context context;
+
+ @Before
+ public void setUp() {
+ ShadowLog.stream = System.out;
+ context = ApplicationProvider.getApplicationContext();
+ mockedAppRtcAudioManager = mock(AppRTCAudioManager.class);
+ mockedAudioManager = mock(AudioManager.class);
+ mockedBluetoothHeadset = mock(BluetoothHeadset.class);
+ mockedBluetoothDevice = mock(BluetoothDevice.class);
+ mockedBluetoothDeviceList = new ArrayList<BluetoothDevice>();
+
+ // Simulate that bluetooth SCO audio is available by default.
+ when(mockedAudioManager.isBluetoothScoAvailableOffCall()).thenReturn(true);
+
+ // Create the test object and override protected methods for this test.
+ bluetoothManager = new AppRTCBluetoothManager(context, mockedAppRtcAudioManager) {
+ @Override
+ protected AudioManager getAudioManager(Context context) {
+ Log.d(TAG, "getAudioManager");
+ return mockedAudioManager;
+ }
+
+ @Override
+ protected void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ Log.d(TAG, "registerReceiver");
+ if (filter.hasAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
+ && filter.hasAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+ // Gives access to the real broadcast receiver so the test can use it.
+ bluetoothHeadsetStateReceiver = receiver;
+ }
+ }
+
+ @Override
+ protected void unregisterReceiver(BroadcastReceiver receiver) {
+ Log.d(TAG, "unregisterReceiver");
+ if (receiver == bluetoothHeadsetStateReceiver) {
+ bluetoothHeadsetStateReceiver = null;
+ }
+ }
+
+ @Override
+ protected boolean getBluetoothProfileProxy(
+ Context context, BluetoothProfile.ServiceListener listener, int profile) {
+ Log.d(TAG, "getBluetoothProfileProxy");
+ if (profile == BluetoothProfile.HEADSET) {
+ // Allows the test to access the real Bluetooth service listener object.
+ bluetoothServiceListener = listener;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean hasPermission(Context context, String permission) {
+ Log.d(TAG, "hasPermission(" + permission + ")");
+ // Ensure that the client asks for Bluetooth permission.
+ return android.Manifest.permission.BLUETOOTH.equals(permission);
+ }
+
+ @Override
+ protected void logBluetoothAdapterInfo(BluetoothAdapter localAdapter) {
+ // Do nothing in tests. No need to mock BluetoothAdapter.
+ }
+ };
+ }
+
+ // Verify that Bluetooth service listener for headset profile is properly initialized.
+ @Test
+ public void testBluetoothServiceListenerInitialized() {
+ bluetoothManager.start();
+ assertNotNull(bluetoothServiceListener);
+ verify(mockedAppRtcAudioManager, never()).updateAudioDeviceState();
+ }
+
+ // Verify that broadcast receivers for Bluetooth SCO audio state and Bluetooth headset state
+ // are properly registered and unregistered.
+ @Test
+ public void testBluetoothBroadcastReceiversAreRegistered() {
+ bluetoothManager.start();
+ assertNotNull(bluetoothHeadsetStateReceiver);
+ bluetoothManager.stop();
+ assertNull(bluetoothHeadsetStateReceiver);
+ }
+
+ // Verify that the Bluetooth manager starts and stops with correct states.
+ @Test
+ public void testBluetoothDefaultStartStopStates() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ bluetoothManager.stop();
+ assertEquals(bluetoothManager.getState(), State.UNINITIALIZED);
+ }
+
+ // Verify correct state after receiving BluetoothServiceListener.onServiceConnected()
+ // when no BT device is enabled.
+ @Test
+ public void testBluetoothServiceListenerConnectedWithNoHeadset() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ simulateBluetoothServiceConnectedWithNoConnectedHeadset();
+ verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ }
+
+ // Verify correct state after receiving BluetoothServiceListener.onServiceConnected()
+ // when one emulated (test) BT device is enabled. Android does not support more than
+ // one connected BT headset.
+ @Test
+ public void testBluetoothServiceListenerConnectedWithHeadset() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ simulateBluetoothServiceConnectedWithConnectedHeadset();
+ verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE);
+ }
+
+ // Verify correct state after receiving BluetoothProfile.ServiceListener.onServiceDisconnected().
+ @Test
+ public void testBluetoothServiceListenerDisconnected() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ simulateBluetoothServiceDisconnected();
+ verify(mockedAppRtcAudioManager, times(1)).updateAudioDeviceState();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ }
+
+ // Verify correct state after BluetoothServiceListener.onServiceConnected() and
+ // the intent indicating that the headset is actually connected. Both these callbacks
+ // results in calls to updateAudioDeviceState() on the AppRTC audio manager.
+ // No BT SCO is enabled here to keep the test limited.
+ @Test
+ public void testBluetoothHeadsetConnected() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ simulateBluetoothServiceConnectedWithConnectedHeadset();
+ simulateBluetoothHeadsetConnected();
+ verify(mockedAppRtcAudioManager, times(2)).updateAudioDeviceState();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE);
+ }
+
+ // Verify correct state sequence for a case when a BT headset is available,
+ // followed by BT SCO audio being enabled and then stopped.
+ @Test
+ public void testBluetoothScoAudioStartAndStop() {
+ bluetoothManager.start();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_UNAVAILABLE);
+ simulateBluetoothServiceConnectedWithConnectedHeadset();
+ assertEquals(bluetoothManager.getState(), State.HEADSET_AVAILABLE);
+ bluetoothManager.startScoAudio();
+ assertEquals(bluetoothManager.getState(), State.SCO_CONNECTING);
+ simulateBluetoothScoConnectionConnected();
+ assertEquals(bluetoothManager.getState(), State.SCO_CONNECTED);
+ bluetoothManager.stopScoAudio();
+ simulateBluetoothScoConnectionDisconnected();
+ assertEquals(bluetoothManager.getState(), State.SCO_DISCONNECTING);
+ bluetoothManager.stop();
+ assertEquals(bluetoothManager.getState(), State.UNINITIALIZED);
+ verify(mockedAppRtcAudioManager, times(3)).updateAudioDeviceState();
+ }
+
+ /**
+ * Private helper methods.
+ */
+ private void simulateBluetoothServiceConnectedWithNoConnectedHeadset() {
+ mockedBluetoothDeviceList.clear();
+ when(mockedBluetoothHeadset.getConnectedDevices()).thenReturn(mockedBluetoothDeviceList);
+ bluetoothServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mockedBluetoothHeadset);
+ // In real life, the AppRTC audio manager makes this call.
+ bluetoothManager.updateDevice();
+ }
+
+ private void simulateBluetoothServiceConnectedWithConnectedHeadset() {
+ mockedBluetoothDeviceList.clear();
+ mockedBluetoothDeviceList.add(mockedBluetoothDevice);
+ when(mockedBluetoothHeadset.getConnectedDevices()).thenReturn(mockedBluetoothDeviceList);
+ when(mockedBluetoothDevice.getName()).thenReturn(BLUETOOTH_TEST_DEVICE_NAME);
+ bluetoothServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mockedBluetoothHeadset);
+ // In real life, the AppRTC audio manager makes this call.
+ bluetoothManager.updateDevice();
+ }
+
+ private void simulateBluetoothServiceDisconnected() {
+ bluetoothServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET);
+ }
+
+ private void simulateBluetoothHeadsetConnected() {
+ Intent intent = new Intent();
+ intent.setAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_CONNECTED);
+ bluetoothHeadsetStateReceiver.onReceive(context, intent);
+ }
+
+ private void simulateBluetoothScoConnectionConnected() {
+ Intent intent = new Intent();
+ intent.setAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+ intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_CONNECTED);
+ bluetoothHeadsetStateReceiver.onReceive(context, intent);
+ }
+
+ private void simulateBluetoothScoConnectionDisconnected() {
+ Intent intent = new Intent();
+ intent.setAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+ intent.putExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+ bluetoothHeadsetStateReceiver.onReceive(context, intent);
+ }
+}
diff --git a/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/DirectRTCClientTest.java b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/DirectRTCClientTest.java
new file mode 100644
index 0000000000..2da8164ec7
--- /dev/null
+++ b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/DirectRTCClientTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.appspot.apprtc;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+import org.webrtc.IceCandidate;
+import org.webrtc.SessionDescription;
+
+/**
+ * Test for DirectRTCClient. Test is very simple and only tests the overall sanity of the class
+ * behaviour.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class DirectRTCClientTest {
+ private static final String ROOM_URL = "";
+ private static final boolean LOOPBACK = false;
+
+ private static final String DUMMY_SDP_MID = "sdpMid";
+ private static final String DUMMY_SDP = "sdp";
+
+ public static final int SERVER_WAIT = 100;
+ public static final int NETWORK_TIMEOUT = 1000;
+
+ private DirectRTCClient client;
+ private DirectRTCClient server;
+
+ AppRTCClient.SignalingEvents clientEvents;
+ AppRTCClient.SignalingEvents serverEvents;
+
+ @Before
+ public void setUp() {
+ ShadowLog.stream = System.out;
+
+ clientEvents = mock(AppRTCClient.SignalingEvents.class);
+ serverEvents = mock(AppRTCClient.SignalingEvents.class);
+
+ client = new DirectRTCClient(clientEvents);
+ server = new DirectRTCClient(serverEvents);
+ }
+
+ @Test
+ public void testValidIpPattern() {
+ // Strings that should match the pattern.
+ // clang-format off
+ final String[] ipAddresses = new String[] {
+ "0.0.0.0",
+ "127.0.0.1",
+ "192.168.0.1",
+ "0.0.0.0:8888",
+ "127.0.0.1:8888",
+ "192.168.0.1:8888",
+ "::",
+ "::1",
+ "2001:0db8:85a3:0000:0000:8a2e:0370:7946",
+ "[::]",
+ "[::1]",
+ "[2001:0db8:85a3:0000:0000:8a2e:0370:7946]",
+ "[::]:8888",
+ "[::1]:8888",
+ "[2001:0db8:85a3:0000:0000:8a2e:0370:7946]:8888"
+ };
+ // clang-format on
+
+ for (String ip : ipAddresses) {
+ assertTrue(ip + " didn't match IP_PATTERN even though it should.",
+ DirectRTCClient.IP_PATTERN.matcher(ip).matches());
+ }
+ }
+
+ @Test
+ public void testInvalidIpPattern() {
+ // Strings that shouldn't match the pattern.
+ // clang-format off
+ final String[] invalidIpAddresses = new String[] {
+ "Hello, World!",
+ "aaaa",
+ "1111",
+ "[hello world]",
+ "hello:world"
+ };
+ // clang-format on
+
+ for (String invalidIp : invalidIpAddresses) {
+ assertFalse(invalidIp + " matched IP_PATTERN even though it shouldn't.",
+ DirectRTCClient.IP_PATTERN.matcher(invalidIp).matches());
+ }
+ }
+
+ // TODO(sakal): Replace isNotNull(class) with isNotNull() once Java 8 is used.
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testDirectRTCClient() {
+ server.connectToRoom(new AppRTCClient.RoomConnectionParameters(ROOM_URL, "0.0.0.0", LOOPBACK));
+ try {
+ Thread.sleep(SERVER_WAIT);
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ client.connectToRoom(
+ new AppRTCClient.RoomConnectionParameters(ROOM_URL, "127.0.0.1", LOOPBACK));
+ verify(serverEvents, timeout(NETWORK_TIMEOUT))
+ .onConnectedToRoom(any(AppRTCClient.SignalingParameters.class));
+
+ SessionDescription offerSdp = new SessionDescription(SessionDescription.Type.OFFER, DUMMY_SDP);
+ server.sendOfferSdp(offerSdp);
+ verify(clientEvents, timeout(NETWORK_TIMEOUT))
+ .onConnectedToRoom(any(AppRTCClient.SignalingParameters.class));
+
+ SessionDescription answerSdp =
+ new SessionDescription(SessionDescription.Type.ANSWER, DUMMY_SDP);
+ client.sendAnswerSdp(answerSdp);
+ verify(serverEvents, timeout(NETWORK_TIMEOUT))
+ .onRemoteDescription(isNotNull(SessionDescription.class));
+
+ IceCandidate candidate = new IceCandidate(DUMMY_SDP_MID, 0, DUMMY_SDP);
+ server.sendLocalIceCandidate(candidate);
+ verify(clientEvents, timeout(NETWORK_TIMEOUT))
+ .onRemoteIceCandidate(isNotNull(IceCandidate.class));
+
+ client.sendLocalIceCandidate(candidate);
+ verify(serverEvents, timeout(NETWORK_TIMEOUT))
+ .onRemoteIceCandidate(isNotNull(IceCandidate.class));
+
+ client.disconnectFromRoom();
+ verify(clientEvents, timeout(NETWORK_TIMEOUT)).onChannelClose();
+ verify(serverEvents, timeout(NETWORK_TIMEOUT)).onChannelClose();
+
+ verifyNoMoreInteractions(clientEvents);
+ verifyNoMoreInteractions(serverEvents);
+ }
+}
diff --git a/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/TCPChannelClientTest.java b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/TCPChannelClientTest.java
new file mode 100644
index 0000000000..b301d6317c
--- /dev/null
+++ b/third_party/libwebrtc/examples/androidjunit/src/org/appspot/apprtc/TCPChannelClientTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2016 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.appspot.apprtc;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+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;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLog;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TCPChannelClientTest {
+ private static final int PORT = 8888;
+ /**
+ * How long we wait before trying to connect to the server. Note: was
+ * previously only 10, which was too short (tests were flaky).
+ */
+ private static final int SERVER_WAIT = 300;
+ private static final int CONNECT_TIMEOUT = 1000;
+ private static final int SEND_TIMEOUT = 1000;
+ private static final int DISCONNECT_TIMEOUT = 1000;
+ private static final int TERMINATION_TIMEOUT = 1000;
+ private static final String TEST_MESSAGE_SERVER = "Hello, Server!";
+ private static final String TEST_MESSAGE_CLIENT = "Hello, Client!";
+
+ @Mock TCPChannelClient.TCPChannelEvents serverEvents;
+ @Mock TCPChannelClient.TCPChannelEvents clientEvents;
+
+ private ExecutorService executor;
+ private TCPChannelClient server;
+ private TCPChannelClient client;
+
+ @Before
+ public void setUp() {
+ ShadowLog.stream = System.out;
+
+ MockitoAnnotations.initMocks(this);
+
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ @After
+ public void tearDown() {
+ verifyNoMoreEvents();
+
+ executeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ client.disconnect();
+ server.disconnect();
+ }
+ });
+
+ // Stop the executor thread
+ executor.shutdown();
+ try {
+ executor.awaitTermination(TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testConnectIPv4() {
+ setUpIPv4Server();
+ try {
+ Thread.sleep(SERVER_WAIT);
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ setUpIPv4Client();
+
+ verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
+ verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
+ }
+
+ @Test
+ public void testConnectIPv6() {
+ setUpIPv6Server();
+ try {
+ Thread.sleep(SERVER_WAIT);
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ setUpIPv6Client();
+
+ verify(serverEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(true);
+ verify(clientEvents, timeout(CONNECT_TIMEOUT)).onTCPConnected(false);
+ }
+
+ @Test
+ public void testSendData() {
+ testConnectIPv4();
+
+ executeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ client.send(TEST_MESSAGE_SERVER);
+ server.send(TEST_MESSAGE_CLIENT);
+ }
+ });
+
+ verify(serverEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_SERVER);
+ verify(clientEvents, timeout(SEND_TIMEOUT)).onTCPMessage(TEST_MESSAGE_CLIENT);
+ }
+
+ @Test
+ public void testDisconnectServer() {
+ testConnectIPv4();
+ executeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ server.disconnect();
+ }
+ });
+
+ verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
+ verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
+ }
+
+ @Test
+ public void testDisconnectClient() {
+ testConnectIPv4();
+ executeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ client.disconnect();
+ }
+ });
+
+ verify(serverEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
+ verify(clientEvents, timeout(DISCONNECT_TIMEOUT)).onTCPClose();
+ }
+
+ private void setUpIPv4Server() {
+ setUpServer("0.0.0.0", PORT);
+ }
+
+ private void setUpIPv4Client() {
+ setUpClient("127.0.0.1", PORT);
+ }
+
+ private void setUpIPv6Server() {
+ setUpServer("::", PORT);
+ }
+
+ private void setUpIPv6Client() {
+ setUpClient("::1", PORT);
+ }
+
+ private void setUpServer(String ip, int port) {
+ server = new TCPChannelClient(executor, serverEvents, ip, port);
+ }
+
+ private void setUpClient(String ip, int port) {
+ client = new TCPChannelClient(executor, clientEvents, ip, port);
+ }
+
+ /**
+ * Verifies no more server or client events have been issued
+ */
+ private void verifyNoMoreEvents() {
+ verifyNoMoreInteractions(serverEvents);
+ verifyNoMoreInteractions(clientEvents);
+ }
+
+ /**
+ * Queues runnable to be run and waits for it to be executed by the executor thread
+ */
+ public void executeAndWait(Runnable runnable) {
+ try {
+ executor.submit(runnable).get();
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+}