Unit tests for PaddedConnectionWriter. Also broke some shared test

code out into separate classes.
This commit is contained in:
akwizgran
2011-08-19 19:47:24 +02:00
parent 4e2a74858b
commit d11f7ef824
6 changed files with 252 additions and 99 deletions

View File

@@ -3,39 +3,19 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import javax.crypto.Mac;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.crypto.CryptoComponent;
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.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class ConnectionReaderImplTest extends TestCase {
private final Mac mac;
private final int headerLength = 8, macLength;
public class ConnectionReaderImplTest extends TransportTest {
public ConnectionReaderImplTest() 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();
}
@Test
@@ -75,7 +55,6 @@ public class ConnectionReaderImplTest extends TestCase {
@Test
public void testMaxLength() throws Exception {
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
// First frame: max payload length
byte[] frame = new byte[MAX_FRAME_LENGTH];
writeHeader(frame, 0L, maxPayloadLength, 0);
@@ -106,7 +85,6 @@ public class ConnectionReaderImplTest extends TestCase {
@Test
public void testMaxLengthWithPadding() throws Exception {
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
int paddingLength = 10;
// First frame: max payload length, including padding
byte[] frame = new byte[MAX_FRAME_LENGTH];
@@ -204,35 +182,4 @@ public class ConnectionReaderImplTest extends TestCase {
fail();
} 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();
}
}
}

View File

@@ -3,35 +3,17 @@ 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.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.crypto.CryptoModule;
import net.sf.briar.util.ByteUtils;
import org.junit.Test;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class ConnectionWriterImplTest extends TestCase {
private final Mac mac;
private final int headerLength = 8, macLength;
public class ConnectionWriterImplTest extends TransportTest {
public ConnectionWriterImplTest() 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();
}
@Test
@@ -64,7 +46,6 @@ public class ConnectionWriterImplTest extends TestCase {
@Test
public void testFrameIsWrittenAtMaxLength() throws Exception {
int maxPayloadLength = MAX_FRAME_LENGTH - headerLength - macLength;
ByteArrayOutputStream out = new ByteArrayOutputStream();
ConnectionEncrypter e = new NullConnectionEncrypter(out);
ConnectionWriter w = new ConnectionWriterImpl(e, mac);
@@ -109,29 +90,4 @@ public class ConnectionWriterImplTest extends TestCase {
byte[] actual = out.toByteArray();
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);
}
}
}

View 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();
}
}

View 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);
}
}

View 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));
}
}

View 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);
}
}