diff options
Diffstat (limited to '')
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()); + } + } +} |