Index: components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java |
diff --git a/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a250009a885ce14223690b9f5faf2cb368ea0907 |
--- /dev/null |
+++ b/components/devtools_bridge/android/javatests/src/org/chromium/components/devtools_bridge/SocketTunnelServerTest.java |
@@ -0,0 +1,305 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.components.devtools_bridge; |
+ |
+import android.net.LocalServerSocket; |
+import android.net.LocalSocket; |
+import android.test.InstrumentationTestCase; |
+import android.test.suitebuilder.annotation.MediumTest; |
+import android.test.suitebuilder.annotation.SmallTest; |
+ |
+import junit.framework.Assert; |
+ |
+import java.io.IOException; |
+import java.io.OutputStream; |
+import java.nio.ByteBuffer; |
+ |
+/** |
+ * Tests for {@link SocketTunnelServer} |
+ */ |
+public class SocketTunnelServerTest extends InstrumentationTestCase { |
+ private static final int CONNECTION_ID = 30; |
+ private static final String SOCKET_NAME = "ksdjhflksjhdflk"; |
+ |
+ private DataChannelMock mDataChannelMock; |
+ private SocketTunnelServer mServer; |
+ private LocalServerSocket mSocket; |
+ |
+ @Override |
+ public void setUp() throws Exception { |
+ super.setUp(); |
+ mSocket = new LocalServerSocket(SOCKET_NAME); |
+ } |
+ |
+ @Override |
+ public void tearDown() throws Exception { |
+ mSocket.close(); |
+ if (mServer != null) destroyServer(); |
+ super.tearDown(); |
+ } |
+ |
+ private void createServer() { |
+ createServer(new DataChannelMock()); |
+ } |
+ |
+ private void createServer(DataChannelMock dataChannel) { |
+ mDataChannelMock = dataChannel; |
+ mServer = new SocketTunnelServer(SOCKET_NAME); |
+ mServer.bind(mDataChannelMock); |
+ } |
+ |
+ private void destroyServer() { |
+ mServer.unbind().dispose(); |
+ mServer = null; |
+ } |
+ |
+ @SmallTest |
+ public void testOpenDataChannel() { |
+ createServer(); |
+ mDataChannelMock.open(); |
+ } |
+ |
+ @SmallTest |
+ public void testDecodeControlPacket() { |
+ createServer(); |
+ ByteBuffer packet = buildControlPacket(CONNECTION_ID, SocketTunnelBase.SERVER_OPEN_ACK); |
+ |
+ PacketDecoder decoder = PacketDecoder.decode(packet); |
+ Assert.assertTrue(decoder.isControlPacket()); |
+ Assert.assertEquals(CONNECTION_ID, decoder.connectionId()); |
+ Assert.assertEquals(SocketTunnelBase.SERVER_OPEN_ACK, decoder.opCode()); |
+ } |
+ |
+ @SmallTest |
+ public void testConnectToSocket() throws IOException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(1); |
+ Assert.assertTrue(mServer.hasConnections()); |
+ socket.close(); |
+ } |
+ |
+ private LocalSocket connectToSocket(int connectionId) throws IOException { |
+ mDataChannelMock.notifyMessage( |
+ buildControlPacket(connectionId, SocketTunnelBase.CLIENT_OPEN)); |
+ return mSocket.accept(); |
+ } |
+ |
+ private void sendClose(int connectionId) { |
+ mDataChannelMock.notifyMessage( |
+ buildControlPacket(connectionId, SocketTunnelBase.CLIENT_CLOSE)); |
+ } |
+ |
+ private ByteBuffer buildControlPacket(int connectionId, byte opCode) { |
+ ByteBuffer packet = SocketTunnelBase.buildControlPacket(connectionId, opCode); |
+ packet.limit(packet.position()); |
+ packet.position(0); |
+ Assert.assertTrue(packet.remaining() > 0); |
+ return packet; |
+ } |
+ |
+ private ByteBuffer buildDataPacket(int connectionId, byte[] data) { |
+ ByteBuffer packet = SocketTunnelBase.buildDataPacket(connectionId, data, data.length); |
+ packet.limit(packet.position()); |
+ packet.position(0); |
+ Assert.assertTrue(packet.remaining() > 0); |
+ return packet; |
+ } |
+ |
+ @SmallTest |
+ public void testReceiveOpenAcknowledgement() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ socket.close(); |
+ } |
+ |
+ private PacketDecoder receiveControlPacket(int connectionId) throws InterruptedException { |
+ PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive()); |
+ Assert.assertTrue(decoder.isControlPacket()); |
+ Assert.assertEquals(connectionId, decoder.connectionId()); |
+ return decoder; |
+ } |
+ |
+ private void receiveOpenAck(int connectionId) throws InterruptedException { |
+ PacketDecoder decoder = receiveControlPacket(connectionId); |
+ Assert.assertEquals(SocketTunnelBase.SERVER_OPEN_ACK, decoder.opCode()); |
+ } |
+ |
+ private void receiveClose(int connectionId) throws InterruptedException { |
+ PacketDecoder decoder = receiveControlPacket(connectionId); |
+ Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode()); |
+ } |
+ |
+ @SmallTest |
+ public void testClosingSocket() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ socket.shutdownOutput(); |
+ |
+ PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive()); |
+ |
+ Assert.assertTrue(decoder.isControlPacket()); |
+ Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode()); |
+ Assert.assertEquals(CONNECTION_ID, decoder.connectionId()); |
+ |
+ socket.close(); |
+ } |
+ |
+ @SmallTest |
+ public void testReadData() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ byte[] sample = "Sample".getBytes(); |
+ |
+ socket.getOutputStream().write(sample); |
+ socket.getOutputStream().flush(); |
+ socket.shutdownOutput(); |
+ |
+ ByteBuffer result = receiveData(CONNECTION_ID, sample.length); |
+ Assert.assertEquals(ByteBuffer.wrap(sample), result); |
+ } |
+ |
+ private ByteBuffer receiveData(int connectionId, int length) throws InterruptedException { |
+ ByteBuffer result = ByteBuffer.allocate(length); |
+ |
+ while (true) { |
+ PacketDecoder decoder = PacketDecoder.decode(mDataChannelMock.receive()); |
+ if (decoder.isDataPacket()) { |
+ Assert.assertEquals(connectionId, decoder.connectionId()); |
+ result.put(decoder.data()); |
+ } else if (decoder.isControlPacket()) { |
+ Assert.assertEquals(SocketTunnelBase.SERVER_CLOSE, decoder.opCode()); |
+ Assert.assertEquals(connectionId, decoder.connectionId()); |
+ break; |
+ } |
+ } |
+ result.limit(result.position()); |
+ result.position(0); |
+ return result; |
+ } |
+ |
+ private int sum(int[] values) { |
+ int result = 0; |
+ for (int v : values) |
+ result += v; |
+ return result; |
+ } |
+ |
+ private static final int[] CHUNK_SIZES = |
+ new int[] { 0, 1, 5, 100, 1000, SocketTunnelBase.READING_BUFFER_SIZE * 2 }; |
+ |
+ @SmallTest |
+ public void testReadLongDataChunk() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ byte[] buffer = new byte[CHUNK_SIZES[CHUNK_SIZES.length - 1]]; |
+ ByteBuffer sentData = ByteBuffer.allocate(sum(CHUNK_SIZES)); |
+ OutputStream stream = socket.getOutputStream(); |
+ byte next = 0; |
+ int prevSize = 0; |
+ for (int size : CHUNK_SIZES) { |
+ while (prevSize < size) |
+ buffer[prevSize++] = next++; |
+ |
+ stream.write(buffer, 0, size); |
+ sentData.put(buffer, 0, size); |
+ } |
+ |
+ socket.shutdownOutput(); |
+ |
+ sentData.limit(sentData.position()); |
+ sentData.position(0); |
+ ByteBuffer readData = receiveData(CONNECTION_ID, sentData.limit()); |
+ |
+ Assert.assertEquals(sentData, readData); |
+ } |
+ |
+ @SmallTest |
+ public void testReuseConnectionId() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ socket.shutdownOutput(); |
+ socket.close(); |
+ receiveClose(CONNECTION_ID); |
+ sendClose(CONNECTION_ID); |
+ |
+ // Open connection with the same ID |
+ socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ } |
+ |
+ private static final byte[] SAMPLE = "Sample".getBytes(); |
+ |
+ @SmallTest |
+ public void testWriteData() throws IOException, InterruptedException { |
+ createServer(); |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ receiveOpenAck(CONNECTION_ID); |
+ |
+ mDataChannelMock.notifyMessage(buildDataPacket(CONNECTION_ID, SAMPLE)); |
+ |
+ byte[] result = new byte[SAMPLE.length]; |
+ int read = 0; |
+ while (read < SAMPLE.length) { |
+ int count = socket.getInputStream().read(result, 0, SAMPLE.length - read); |
+ Assert.assertTrue(count > 0); |
+ read += count; |
+ } |
+ |
+ Assert.assertEquals(ByteBuffer.wrap(SAMPLE), ByteBuffer.wrap(result)); |
+ |
+ socket.close(); |
+ } |
+ |
+ private enum TestStates { |
+ INITIAL, SENDING, CLOSING, MAY_FINISH_SENDING, SENT, DONE |
+ } |
+ |
+ @MediumTest |
+ public void testStopWhileSendingData() throws IOException { |
+ |
+ final TestUtils.StateBarrier<TestStates> barrier = |
+ new TestUtils.StateBarrier<TestStates>(TestStates.INITIAL); |
+ |
+ createServer(new DataChannelMock() { |
+ @Override |
+ public void send(ByteBuffer message, AbstractDataChannel.MessageType type) { |
+ barrier.advance(TestStates.INITIAL, TestStates.SENDING); |
+ barrier.advance(TestStates.MAY_FINISH_SENDING, TestStates.SENT); |
+ } |
+ }); |
+ |
+ LocalSocket socket = connectToSocket(CONNECTION_ID); |
+ barrier.advance(TestStates.SENDING, TestStates.CLOSING); |
+ socket.close(); |
+ |
+ new Thread() { |
+ @Override |
+ public void run() { |
+ try { |
+ Thread.sleep(100); |
+ } catch (InterruptedException e) { |
+ throw new RuntimeException(e); |
+ } |
+ |
+ barrier.advance(TestStates.CLOSING, TestStates.MAY_FINISH_SENDING); |
+ } |
+ }.start(); |
+ |
+ destroyServer(); |
+ |
+ barrier.advance(TestStates.SENT, TestStates.DONE); |
+ } |
+} |