mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-22 23:59:54 +01:00
Unit tests for PaddedConnectionWriter. Also broke some shared test
code out into separate classes.
This commit is contained in:
@@ -3,39 +3,19 @@ package net.sf.briar.transport;
|
|||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import net.sf.briar.TestUtils;
|
import net.sf.briar.TestUtils;
|
||||||
import net.sf.briar.api.FormatException;
|
import net.sf.briar.api.FormatException;
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.transport.ConnectionReader;
|
import net.sf.briar.api.transport.ConnectionReader;
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
|
||||||
|
|
||||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
public class ConnectionReaderImplTest extends TransportTest {
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
public class ConnectionReaderImplTest extends TestCase {
|
|
||||||
|
|
||||||
private final Mac mac;
|
|
||||||
private final int headerLength = 8, macLength;
|
|
||||||
|
|
||||||
public ConnectionReaderImplTest() throws Exception {
|
public ConnectionReaderImplTest() throws Exception {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
|
||||||
mac = crypto.getMac();
|
|
||||||
mac.init(crypto.generateSecretKey());
|
|
||||||
macLength = mac.getMacLength();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -75,7 +55,6 @@ public class ConnectionReaderImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaxLength() throws Exception {
|
public void testMaxLength() throws Exception {
|
||||||
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
|
|
||||||
// First frame: max payload length
|
// First frame: max payload length
|
||||||
byte[] frame = new byte[MAX_FRAME_LENGTH];
|
byte[] frame = new byte[MAX_FRAME_LENGTH];
|
||||||
writeHeader(frame, 0L, maxPayloadLength, 0);
|
writeHeader(frame, 0L, maxPayloadLength, 0);
|
||||||
@@ -106,7 +85,6 @@ public class ConnectionReaderImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMaxLengthWithPadding() throws Exception {
|
public void testMaxLengthWithPadding() throws Exception {
|
||||||
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
|
|
||||||
int paddingLength = 10;
|
int paddingLength = 10;
|
||||||
// First frame: max payload length, including padding
|
// First frame: max payload length, including padding
|
||||||
byte[] frame = new byte[MAX_FRAME_LENGTH];
|
byte[] frame = new byte[MAX_FRAME_LENGTH];
|
||||||
@@ -204,35 +182,4 @@ public class ConnectionReaderImplTest extends TestCase {
|
|||||||
fail();
|
fail();
|
||||||
} catch(FormatException expected) {}
|
} catch(FormatException expected) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeHeader(byte[] b, long frame, int payload, int padding) {
|
|
||||||
ByteUtils.writeUint32(frame, b, 0);
|
|
||||||
ByteUtils.writeUint16(payload, b, 4);
|
|
||||||
ByteUtils.writeUint16(padding, b, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A ConnectionDecrypter that performs no decryption. */
|
|
||||||
private static class NullConnectionDecrypter
|
|
||||||
implements ConnectionDecrypter {
|
|
||||||
|
|
||||||
private final InputStream in;
|
|
||||||
|
|
||||||
private NullConnectionDecrypter(InputStream in) {
|
|
||||||
this.in = in;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readMac(byte[] mac) throws IOException {
|
|
||||||
int offset = 0;
|
|
||||||
while(offset < mac.length) {
|
|
||||||
int read = in.read(mac, offset, mac.length - offset);
|
|
||||||
if(read == -1) break;
|
|
||||||
offset += read;
|
|
||||||
}
|
|
||||||
if(offset < mac.length) throw new EOFException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,35 +3,17 @@ package net.sf.briar.transport;
|
|||||||
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import net.sf.briar.api.crypto.CryptoComponent;
|
|
||||||
import net.sf.briar.api.transport.ConnectionWriter;
|
import net.sf.briar.api.transport.ConnectionWriter;
|
||||||
import net.sf.briar.crypto.CryptoModule;
|
|
||||||
import net.sf.briar.util.ByteUtils;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.inject.Guice;
|
public class ConnectionWriterImplTest extends TransportTest {
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
public class ConnectionWriterImplTest extends TestCase {
|
|
||||||
|
|
||||||
private final Mac mac;
|
|
||||||
private final int headerLength = 8, macLength;
|
|
||||||
|
|
||||||
public ConnectionWriterImplTest() throws Exception {
|
public ConnectionWriterImplTest() throws Exception {
|
||||||
super();
|
super();
|
||||||
Injector i = Guice.createInjector(new CryptoModule());
|
|
||||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
|
||||||
mac = crypto.getMac();
|
|
||||||
mac.init(crypto.generateSecretKey());
|
|
||||||
macLength = mac.getMacLength();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -64,7 +46,6 @@ public class ConnectionWriterImplTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFrameIsWrittenAtMaxLength() throws Exception {
|
public void testFrameIsWrittenAtMaxLength() throws Exception {
|
||||||
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
ConnectionWriter w = new ConnectionWriterImpl(e, mac);
|
ConnectionWriter w = new ConnectionWriterImpl(e, mac);
|
||||||
@@ -109,29 +90,4 @@ public class ConnectionWriterImplTest extends TestCase {
|
|||||||
byte[] actual = out.toByteArray();
|
byte[] actual = out.toByteArray();
|
||||||
assertTrue(Arrays.equals(expected, actual));
|
assertTrue(Arrays.equals(expected, actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeHeader(byte[] b, long frame, int payload, int padding) {
|
|
||||||
ByteUtils.writeUint32(frame, b, 0);
|
|
||||||
ByteUtils.writeUint16(payload, b, 4);
|
|
||||||
ByteUtils.writeUint16(padding, b, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A ConnectionEncrypter that performs no encryption. */
|
|
||||||
private static class NullConnectionEncrypter
|
|
||||||
implements ConnectionEncrypter {
|
|
||||||
|
|
||||||
private final OutputStream out;
|
|
||||||
|
|
||||||
private NullConnectionEncrypter(OutputStream out) {
|
|
||||||
this.out = out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeMac(byte[] mac) throws IOException {
|
|
||||||
out.write(mac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
29
test/net/sf/briar/transport/NullConnectionDecrypter.java
Normal file
29
test/net/sf/briar/transport/NullConnectionDecrypter.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/** A ConnectionDecrypter that performs no decryption. */
|
||||||
|
class NullConnectionDecrypter implements ConnectionDecrypter {
|
||||||
|
|
||||||
|
private final InputStream in;
|
||||||
|
|
||||||
|
NullConnectionDecrypter(InputStream in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readMac(byte[] mac) throws IOException {
|
||||||
|
int offset = 0;
|
||||||
|
while(offset < mac.length) {
|
||||||
|
int read = in.read(mac, offset, mac.length - offset);
|
||||||
|
if(read == -1) break;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
if(offset < mac.length) throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
22
test/net/sf/briar/transport/NullConnectionEncrypter.java
Normal file
22
test/net/sf/briar/transport/NullConnectionEncrypter.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/** A ConnectionEncrypter that performs no encryption. */
|
||||||
|
class NullConnectionEncrypter implements ConnectionEncrypter {
|
||||||
|
|
||||||
|
private final OutputStream out;
|
||||||
|
|
||||||
|
NullConnectionEncrypter(OutputStream out) {
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream getOutputStream() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMac(byte[] mac) throws IOException {
|
||||||
|
out.write(mac);
|
||||||
|
}
|
||||||
|
}
|
||||||
164
test/net/sf/briar/transport/PaddedConnectionWriterTest.java
Normal file
164
test/net/sf/briar/transport/PaddedConnectionWriterTest.java
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import net.sf.briar.api.transport.ConnectionWriter;
|
||||||
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PaddedConnectionWriterTest extends TransportTest {
|
||||||
|
|
||||||
|
public PaddedConnectionWriterTest() throws Exception {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteByteDoesNotBlockUntilBufferIsFull() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
|
ConnectionWriter w = new PaddedConnectionWriter(e, mac);
|
||||||
|
final OutputStream out1 = w.getOutputStream();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean finished = new AtomicBoolean(false);
|
||||||
|
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
for(int i = 0; i < maxPayloadLength; i++) out1.write(0);
|
||||||
|
finished.set(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
failed.set(true);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
// The wait should not time out
|
||||||
|
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertTrue(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// Nothing should have been written
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteByteBlocksWhenBufferIsFull() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
|
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
|
||||||
|
final OutputStream out1 = w.getOutputStream();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean finished = new AtomicBoolean(false);
|
||||||
|
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
for(int i = 0; i < maxPayloadLength + 1; i++) out1.write(0);
|
||||||
|
finished.set(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
failed.set(true);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
// The wait should time out
|
||||||
|
assertFalse(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertFalse(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// Calling writeFullFrame() should allow the writer to proceed
|
||||||
|
w.writeFullFrame();
|
||||||
|
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertTrue(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// A full frame should have been written
|
||||||
|
assertEquals(MAX_FRAME_LENGTH, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteArrayDoesNotBlockUntilBufferIsFull() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
|
ConnectionWriter w = new PaddedConnectionWriter(e, mac);
|
||||||
|
final OutputStream out1 = w.getOutputStream();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean finished = new AtomicBoolean(false);
|
||||||
|
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
out1.write(new byte[maxPayloadLength]);
|
||||||
|
finished.set(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
failed.set(true);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
// The wait should not time out
|
||||||
|
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertTrue(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// Nothing should have been written
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteArrayBlocksWhenBufferIsFull() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
|
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
|
||||||
|
final OutputStream out1 = w.getOutputStream();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean finished = new AtomicBoolean(false);
|
||||||
|
final AtomicBoolean failed = new AtomicBoolean(false);
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
out1.write(new byte[maxPayloadLength + 1]);
|
||||||
|
finished.set(true);
|
||||||
|
} catch(IOException e) {
|
||||||
|
failed.set(true);
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
// The wait should time out
|
||||||
|
assertFalse(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertFalse(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// Calling writeFullFrame() should allow the writer to proceed
|
||||||
|
w.writeFullFrame();
|
||||||
|
assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||||
|
assertTrue(finished.get());
|
||||||
|
assertFalse(failed.get());
|
||||||
|
// A full frame should have been written
|
||||||
|
assertEquals(MAX_FRAME_LENGTH, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteFullFrameInsertsPadding() throws Exception {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
ConnectionEncrypter e = new NullConnectionEncrypter(out);
|
||||||
|
PaddedConnectionWriter w = new PaddedConnectionWriter(e, mac);
|
||||||
|
w.getOutputStream().write(0);
|
||||||
|
w.writeFullFrame();
|
||||||
|
// A full frame should have been written
|
||||||
|
assertEquals(MAX_FRAME_LENGTH, out.size());
|
||||||
|
// The frame should have a payload length of 1 and padding for the rest
|
||||||
|
byte[] frame = out.toByteArray();
|
||||||
|
assertEquals(0L, ByteUtils.readUint32(frame, 0)); // Frame number
|
||||||
|
assertEquals(1, ByteUtils.readUint16(frame, 4)); // Payload length
|
||||||
|
assertEquals(maxPayloadLength - 1, ByteUtils.readUint16(frame, 6));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
test/net/sf/briar/transport/TransportTest.java
Normal file
35
test/net/sf/briar/transport/TransportTest.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package net.sf.briar.transport;
|
||||||
|
|
||||||
|
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
|
||||||
|
import net.sf.briar.api.crypto.CryptoComponent;
|
||||||
|
import net.sf.briar.crypto.CryptoModule;
|
||||||
|
import net.sf.briar.util.ByteUtils;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public abstract class TransportTest extends TestCase {
|
||||||
|
|
||||||
|
protected final Mac mac;
|
||||||
|
protected final int headerLength = 8, macLength, maxPayloadLength;
|
||||||
|
|
||||||
|
public TransportTest() throws Exception {
|
||||||
|
super();
|
||||||
|
Injector i = Guice.createInjector(new CryptoModule());
|
||||||
|
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||||
|
mac = crypto.getMac();
|
||||||
|
mac.init(crypto.generateSecretKey());
|
||||||
|
macLength = mac.getMacLength();
|
||||||
|
maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeHeader(byte[] b, long frame, int payload, int padding) {
|
||||||
|
ByteUtils.writeUint32(frame, b, 0);
|
||||||
|
ByteUtils.writeUint16(payload, b, 4);
|
||||||
|
ByteUtils.writeUint16(padding, b, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user