mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Merged prototype-test repo into prototype repo, as a separate Eclipse project.
This commit is contained in:
12
briar-tests/.classpath
Normal file
12
briar-tests/.classpath
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
|
||||
<classpathentry kind="lib" path="libs/hamcrest-core-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="libs/hamcrest-library-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="libs/jmock-2.5.1.jar"/>
|
||||
<classpathentry kind="lib" path="libs/junit-4.9b3.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/briar-core"/>
|
||||
<classpathentry kind="lib" path="/briar-core/android.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
1
briar-tests/.gitignore
vendored
Normal file
1
briar-tests/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bin
|
||||
17
briar-tests/.project
Normal file
17
briar-tests/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>briar-tests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
BIN
briar-tests/libs/hamcrest-core-1.1.jar
Normal file
BIN
briar-tests/libs/hamcrest-core-1.1.jar
Normal file
Binary file not shown.
BIN
briar-tests/libs/hamcrest-library-1.1.jar
Normal file
BIN
briar-tests/libs/hamcrest-library-1.1.jar
Normal file
Binary file not shown.
BIN
briar-tests/libs/jmock-2.5.1.jar
Normal file
BIN
briar-tests/libs/jmock-2.5.1.jar
Normal file
Binary file not shown.
BIN
briar-tests/libs/junit-4.9b3.jar
Normal file
BIN
briar-tests/libs/junit-4.9b3.jar
Normal file
Binary file not shown.
2
briar-tests/src/.gitignore
vendored
Normal file
2
briar-tests/src/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
build
|
||||
test.tmp
|
||||
118
briar-tests/src/build.xml
Normal file
118
briar-tests/src/build.xml
Normal file
@@ -0,0 +1,118 @@
|
||||
<project name='test' default='test'>
|
||||
<fileset id='core-jars' dir='../../briar-core/libs'>
|
||||
<include name='*.jar'/>
|
||||
</fileset>
|
||||
<fileset id='test-jars' dir='../libs'>
|
||||
<include name='*.jar'/>
|
||||
</fileset>
|
||||
<path id='android-jar'>
|
||||
<pathelement location='../../briar-core/android.jar'/>
|
||||
</path>
|
||||
<path id='core-classes'>
|
||||
<pathelement location='../../briar-core/build'/>
|
||||
</path>
|
||||
<path id='test-classes'>
|
||||
<pathelement location='../build'/>
|
||||
</path>
|
||||
<target name='clean'>
|
||||
<delete dir='../../briar-core/build'/>
|
||||
<delete dir='../build'/>
|
||||
<delete dir='test.tmp'/>
|
||||
</target>
|
||||
<target name='compile'>
|
||||
<mkdir dir='../../briar-core/build'/>
|
||||
<javac srcdir='../../briar-core/src'
|
||||
destdir='../../briar-core/build' source='1.5'
|
||||
includeantruntime='false' debug='off'>
|
||||
<classpath>
|
||||
<fileset refid='core-jars'/>
|
||||
<path refid='android-jar'/>
|
||||
<path refid='core-classes'/>
|
||||
</classpath>
|
||||
</javac>
|
||||
<mkdir dir='../build'/>
|
||||
<javac srcdir='.' destdir='../build' source='1.5'
|
||||
includeantruntime='false' debug='off'>
|
||||
<classpath>
|
||||
<fileset refid='core-jars'/>
|
||||
<fileset refid='test-jars'/>
|
||||
<path refid='android-jar'/>
|
||||
<path refid='core-classes'/>
|
||||
<path refid='test-classes'/>
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
<target name='test' depends='compile'>
|
||||
<junit printsummary='on' fork='yes' forkmode='once'>
|
||||
<assertions>
|
||||
<enable/>
|
||||
</assertions>
|
||||
<classpath>
|
||||
<fileset refid='core-jars'/>
|
||||
<fileset refid='test-jars'/>
|
||||
<path refid='core-classes'/>
|
||||
<path refid='test-classes'/>
|
||||
</classpath>
|
||||
<jvmarg value='-Djava.library.path=../../briar-core/libs'/>
|
||||
<test name='net.sf.briar.LockFairnessTest'/>
|
||||
<test name='net.sf.briar.ProtocolIntegrationTest'/>
|
||||
<test name='net.sf.briar.crypto.CounterModeTest'/>
|
||||
<test name='net.sf.briar.crypto.ErasableKeyTest'/>
|
||||
<test name='net.sf.briar.crypto.KeyAgreementTest'/>
|
||||
<test name='net.sf.briar.crypto.KeyDerivationTest'/>
|
||||
<test name='net.sf.briar.db.BasicH2Test'/>
|
||||
<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
|
||||
<test name='net.sf.briar.db.DatabaseComponentImplTest'/>
|
||||
<test name='net.sf.briar.lifecycle.ShutdownManagerImplTest'/>
|
||||
<test name='net.sf.briar.lifecycle.WindowsShutdownManagerImplTest'/>
|
||||
<test name='net.sf.briar.plugins.PluginManagerImplTest'/>
|
||||
<test name='net.sf.briar.plugins.file.LinuxRemovableDriveFinderTest'/>
|
||||
<test name='net.sf.briar.plugins.file.MacRemovableDriveFinderTest'/>
|
||||
<test name='net.sf.briar.plugins.file.PollingRemovableDriveMonitorTest'/>
|
||||
<test name='net.sf.briar.plugins.file.RemovableDrivePluginTest'/>
|
||||
<test name='net.sf.briar.plugins.file.UnixRemovableDriveMonitorTest'/>
|
||||
<test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/>
|
||||
<test name='net.sf.briar.protocol.AckReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.BatchReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.ConstantsTest'/>
|
||||
<test name='net.sf.briar.protocol.ConsumersTest'/>
|
||||
<test name='net.sf.briar.protocol.OfferReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.ProtocolIntegrationTest'/>
|
||||
<test name='net.sf.briar.protocol.ProtocolWriterImplTest'/>
|
||||
<test name='net.sf.briar.protocol.RequestReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.UnverifiedBatchImplTest'/>
|
||||
<test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/>
|
||||
<test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/>
|
||||
<test name='net.sf.briar.serial.ReaderImplTest'/>
|
||||
<test name='net.sf.briar.serial.WriterImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionReaderImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionRegistryImplTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionWindowTest'/>
|
||||
<test name='net.sf.briar.transport.ConnectionWriterImplTest'/>
|
||||
<test name='net.sf.briar.transport.IncomingEncryptionLayerTest'/>
|
||||
<test name='net.sf.briar.transport.OutgoingEncryptionLayerTest'/>
|
||||
<test name='net.sf.briar.transport.TransportIntegrationTest'/>
|
||||
<test name='net.sf.briar.transport.TransportConnectionRecogniserTest'/>
|
||||
<test name='net.sf.briar.util.ByteUtilsTest'/>
|
||||
<test name='net.sf.briar.util.FileUtilsTest'/>
|
||||
<test name='net.sf.briar.util.StringUtilsTest'/>
|
||||
<test name='net.sf.briar.util.ZipUtilsTest'/>
|
||||
</junit>
|
||||
</target>
|
||||
<target name='test-slow' depends='compile'>
|
||||
<junit printsummary='withOutAndErr' fork='yes' forkmode='once'>
|
||||
<assertions>
|
||||
<enable/>
|
||||
</assertions>
|
||||
<classpath>
|
||||
<fileset refid='core-jars'/>
|
||||
<fileset refid='test-jars'/>
|
||||
<path refid='core-classes'/>
|
||||
<path refid='test-classes'/>
|
||||
</classpath>
|
||||
<jvmarg value='-Djava.library.path=../../briar-core/libs'/>
|
||||
<test name='net.sf.briar.db.H2DatabaseTest'/>
|
||||
<test name='net.sf.briar.plugins.tor.TorPluginTest'/>
|
||||
</junit>
|
||||
</target>
|
||||
</project>
|
||||
20
briar-tests/src/net/sf/briar/BriarTestCase.java
Normal file
20
briar-tests/src/net/sf/briar/BriarTestCase.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.sf.briar;
|
||||
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public abstract class BriarTestCase extends TestCase {
|
||||
|
||||
public BriarTestCase() {
|
||||
super();
|
||||
// Ensure exceptions thrown on worker threads cause tests to fail
|
||||
UncaughtExceptionHandler fail = new UncaughtExceptionHandler() {
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
fail();
|
||||
}
|
||||
};
|
||||
Thread.setDefaultUncaughtExceptionHandler(fail);
|
||||
}
|
||||
}
|
||||
161
briar-tests/src/net/sf/briar/LockFairnessTest.java
Normal file
161
briar-tests/src/net/sf/briar/LockFairnessTest.java
Normal file
@@ -0,0 +1,161 @@
|
||||
package net.sf.briar;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class LockFairnessTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadersCanShareTheLock() throws Exception {
|
||||
// Use a fair lock
|
||||
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
|
||||
final CountDownLatch firstReaderHasLock = new CountDownLatch(1);
|
||||
final CountDownLatch firstReaderHasFinished = new CountDownLatch(1);
|
||||
final CountDownLatch secondReaderHasLock = new CountDownLatch(1);
|
||||
final CountDownLatch secondReaderHasFinished = new CountDownLatch(1);
|
||||
// First reader
|
||||
Thread first = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Acquire the lock
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Allow the second reader to acquire the lock
|
||||
firstReaderHasLock.countDown();
|
||||
// Wait for the second reader to acquire the lock
|
||||
assertTrue(secondReaderHasLock.await(10, SECONDS));
|
||||
} finally {
|
||||
// Release the lock
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
firstReaderHasFinished.countDown();
|
||||
}
|
||||
};
|
||||
first.start();
|
||||
// Second reader
|
||||
Thread second = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the first reader to acquire the lock
|
||||
assertTrue(firstReaderHasLock.await(10, SECONDS));
|
||||
// Acquire the lock
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Allow the first reader to release the lock
|
||||
secondReaderHasLock.countDown();
|
||||
} finally {
|
||||
// Release the lock
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
secondReaderHasFinished.countDown();
|
||||
}
|
||||
};
|
||||
second.start();
|
||||
// Wait for both readers to finish
|
||||
assertTrue(firstReaderHasFinished.await(10, SECONDS));
|
||||
assertTrue(secondReaderHasFinished.await(10, SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritersDoNotStarve() throws Exception {
|
||||
// Use a fair lock
|
||||
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
|
||||
final CountDownLatch firstReaderHasLock = new CountDownLatch(1);
|
||||
final CountDownLatch firstReaderHasFinished = new CountDownLatch(1);
|
||||
final CountDownLatch secondReaderHasFinished = new CountDownLatch(1);
|
||||
final CountDownLatch writerHasFinished = new CountDownLatch(1);
|
||||
final AtomicBoolean secondReaderHasHeldLock = new AtomicBoolean(false);
|
||||
final AtomicBoolean writerHasHeldLock = new AtomicBoolean(false);
|
||||
// First reader
|
||||
Thread first = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Acquire the lock
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
// Allow the other threads to acquire the lock
|
||||
firstReaderHasLock.countDown();
|
||||
// Wait for both other threads to wait for the lock
|
||||
while(lock.getQueueLength() < 2) Thread.sleep(10);
|
||||
// No other thread should have acquired the lock
|
||||
assertFalse(secondReaderHasHeldLock.get());
|
||||
assertFalse(writerHasHeldLock.get());
|
||||
} finally {
|
||||
// Release the lock
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
firstReaderHasFinished.countDown();
|
||||
}
|
||||
};
|
||||
first.start();
|
||||
// Writer
|
||||
Thread writer = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the first reader to acquire the lock
|
||||
assertTrue(firstReaderHasLock.await(10, SECONDS));
|
||||
// Acquire the lock
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
writerHasHeldLock.set(true);
|
||||
// The second reader should not overtake the writer
|
||||
assertFalse(secondReaderHasHeldLock.get());
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
writerHasFinished.countDown();
|
||||
}
|
||||
};
|
||||
writer.start();
|
||||
// Second reader
|
||||
Thread second = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Wait for the first reader to acquire the lock
|
||||
assertTrue(firstReaderHasLock.await(10, SECONDS));
|
||||
// Wait for the writer to wait for the lock
|
||||
while(lock.getQueueLength() < 1) Thread.sleep(10);
|
||||
// Acquire the lock
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
secondReaderHasHeldLock.set(true);
|
||||
// The second reader should not overtake the writer
|
||||
assertTrue(writerHasHeldLock.get());
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
secondReaderHasFinished.countDown();
|
||||
}
|
||||
};
|
||||
second.start();
|
||||
// Wait for all the threads to finish
|
||||
assertTrue(firstReaderHasFinished.await(10, SECONDS));
|
||||
assertTrue(secondReaderHasFinished.await(10, SECONDS));
|
||||
assertTrue(writerHasFinished.await(10, SECONDS));
|
||||
}
|
||||
}
|
||||
264
briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
Normal file
264
briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
Normal file
@@ -0,0 +1,264 @@
|
||||
package net.sf.briar;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.Author;
|
||||
import net.sf.briar.api.protocol.AuthorFactory;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageFactory;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolReader;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriter;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.clock.ClockModule;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.db.DatabaseModule;
|
||||
import net.sf.briar.lifecycle.LifecycleModule;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
|
||||
import net.sf.briar.protocol.simplex.SimplexProtocolModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.transport.TransportModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
||||
private final long timestamp = System.currentTimeMillis();
|
||||
|
||||
private final ConnectionReaderFactory connectionReaderFactory;
|
||||
private final ConnectionWriterFactory connectionWriterFactory;
|
||||
private final ProtocolReaderFactory protocolReaderFactory;
|
||||
private final ProtocolWriterFactory protocolWriterFactory;
|
||||
private final PacketFactory packetFactory;
|
||||
private final CryptoComponent crypto;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final byte[] secret;
|
||||
private final Author author;
|
||||
private final Group group, group1;
|
||||
private final Message message, message1, message2, message3;
|
||||
private final String authorName = "Alice";
|
||||
private final String subject = "Hello";
|
||||
private final String messageBody = "Hello world";
|
||||
private final Collection<Transport> transports;
|
||||
|
||||
public ProtocolIntegrationTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(),
|
||||
new DatabaseModule(), new LifecycleModule(),
|
||||
new ProtocolModule(), new SerialModule(),
|
||||
new TestDatabaseModule(), new SimplexProtocolModule(),
|
||||
new TransportModule(), new DuplexProtocolModule());
|
||||
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
|
||||
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
|
||||
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
contactId = new ContactId(234);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
// Create a shared secret
|
||||
Random r = new Random();
|
||||
secret = new byte[32];
|
||||
r.nextBytes(secret);
|
||||
// Create two groups: one restricted, one unrestricted
|
||||
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
|
||||
group = groupFactory.createGroup("Unrestricted group", null);
|
||||
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
|
||||
group1 = groupFactory.createGroup("Restricted group",
|
||||
groupKeyPair.getPublic().getEncoded());
|
||||
// Create an author
|
||||
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
|
||||
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
|
||||
author = authorFactory.createAuthor(authorName,
|
||||
authorKeyPair.getPublic().getEncoded());
|
||||
// Create two messages to each group: one anonymous, one pseudonymous
|
||||
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
||||
message = messageFactory.createMessage(null, group, subject,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message1 = messageFactory.createMessage(null, group1,
|
||||
groupKeyPair.getPrivate(), subject,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message2 = messageFactory.createMessage(null, group, author,
|
||||
authorKeyPair.getPrivate(), subject,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message3 = messageFactory.createMessage(null, group1,
|
||||
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
|
||||
subject, messageBody.getBytes("UTF-8"));
|
||||
// Create some transports
|
||||
TransportId transportId = new TransportId(TestUtils.getRandomId());
|
||||
Transport transport = new Transport(transportId,
|
||||
Collections.singletonMap("bar", "baz"));
|
||||
transports = Collections.singletonList(transport);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() throws Exception {
|
||||
read(write());
|
||||
}
|
||||
|
||||
private byte[] write() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret.clone(), 0L, true);
|
||||
ConnectionWriter conn = connectionWriterFactory.createConnectionWriter(
|
||||
out, Long.MAX_VALUE, ctx, false, true);
|
||||
OutputStream out1 = conn.getOutputStream();
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1,
|
||||
false);
|
||||
|
||||
Ack a = packetFactory.createAck(Collections.singletonList(ack));
|
||||
writer.writeAck(a);
|
||||
|
||||
Collection<byte[]> batch = Arrays.asList(message.getSerialised(),
|
||||
message1.getSerialised(), message2.getSerialised(),
|
||||
message3.getSerialised());
|
||||
RawBatch b = packetFactory.createBatch(batch);
|
||||
writer.writeBatch(b);
|
||||
|
||||
Collection<MessageId> offer = Arrays.asList(message.getId(),
|
||||
message1.getId(), message2.getId(), message3.getId());
|
||||
Offer o = packetFactory.createOffer(offer);
|
||||
writer.writeOffer(o);
|
||||
|
||||
BitSet requested = new BitSet(4);
|
||||
requested.set(1);
|
||||
requested.set(3);
|
||||
Request r = packetFactory.createRequest(requested, 4);
|
||||
writer.writeRequest(r);
|
||||
|
||||
// Use a LinkedHashMap for predictable iteration order
|
||||
Map<Group, Long> subs = new LinkedHashMap<Group, Long>();
|
||||
subs.put(group, 0L);
|
||||
subs.put(group1, 0L);
|
||||
SubscriptionUpdate s = packetFactory.createSubscriptionUpdate(
|
||||
Collections.<GroupId, GroupId>emptyMap(), subs, 0L, timestamp);
|
||||
writer.writeSubscriptionUpdate(s);
|
||||
|
||||
TransportUpdate t = packetFactory.createTransportUpdate(transports,
|
||||
timestamp);
|
||||
writer.writeTransportUpdate(t);
|
||||
|
||||
writer.flush();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private void read(byte[] connectionData) throws Exception {
|
||||
InputStream in = new ByteArrayInputStream(connectionData);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH));
|
||||
// FIXME: Check that the expected tag was received
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret.clone(), 0L, false);
|
||||
ConnectionReader conn = connectionReaderFactory.createConnectionReader(
|
||||
in, ctx, true, true);
|
||||
InputStream in1 = conn.getInputStream();
|
||||
ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1);
|
||||
|
||||
// Read the ack
|
||||
assertTrue(reader.hasAck());
|
||||
Ack a = reader.readAck();
|
||||
assertEquals(Collections.singletonList(ack), a.getBatchIds());
|
||||
|
||||
// Read and verify the batch
|
||||
assertTrue(reader.hasBatch());
|
||||
Batch b = reader.readBatch().verify();
|
||||
Collection<Message> messages = b.getMessages();
|
||||
assertEquals(4, messages.size());
|
||||
Iterator<Message> it = messages.iterator();
|
||||
checkMessageEquality(message, it.next());
|
||||
checkMessageEquality(message1, it.next());
|
||||
checkMessageEquality(message2, it.next());
|
||||
checkMessageEquality(message3, it.next());
|
||||
|
||||
// Read the offer
|
||||
assertTrue(reader.hasOffer());
|
||||
Offer o = reader.readOffer();
|
||||
Collection<MessageId> offered = o.getMessageIds();
|
||||
assertEquals(4, offered.size());
|
||||
Iterator<MessageId> it1 = offered.iterator();
|
||||
assertEquals(message.getId(), it1.next());
|
||||
assertEquals(message1.getId(), it1.next());
|
||||
assertEquals(message2.getId(), it1.next());
|
||||
assertEquals(message3.getId(), it1.next());
|
||||
|
||||
// Read the request
|
||||
assertTrue(reader.hasRequest());
|
||||
Request req = reader.readRequest();
|
||||
BitSet requested = req.getBitmap();
|
||||
assertFalse(requested.get(0));
|
||||
assertTrue(requested.get(1));
|
||||
assertFalse(requested.get(2));
|
||||
assertTrue(requested.get(3));
|
||||
// If there are any padding bits, they should all be zero
|
||||
assertEquals(2, requested.cardinality());
|
||||
|
||||
// Read the subscription update
|
||||
assertTrue(reader.hasSubscriptionUpdate());
|
||||
SubscriptionUpdate s = reader.readSubscriptionUpdate();
|
||||
Map<Group, Long> subs = s.getSubscriptions();
|
||||
assertEquals(2, subs.size());
|
||||
assertEquals(Long.valueOf(0L), subs.get(group));
|
||||
assertEquals(Long.valueOf(0L), subs.get(group1));
|
||||
assertTrue(s.getTimestamp() == timestamp);
|
||||
|
||||
// Read the transport update
|
||||
assertTrue(reader.hasTransportUpdate());
|
||||
TransportUpdate t = reader.readTransportUpdate();
|
||||
assertEquals(transports, t.getTransports());
|
||||
assertTrue(t.getTimestamp() == timestamp);
|
||||
|
||||
in.close();
|
||||
}
|
||||
|
||||
private void checkMessageEquality(Message m1, Message m2) {
|
||||
assertEquals(m1.getId(), m2.getId());
|
||||
assertEquals(m1.getParent(), m2.getParent());
|
||||
assertEquals(m1.getGroup(), m2.getGroup());
|
||||
assertEquals(m1.getAuthor(), m2.getAuthor());
|
||||
assertEquals(m1.getTimestamp(), m2.getTimestamp());
|
||||
assertArrayEquals(m1.getSerialised(), m2.getSerialised());
|
||||
}
|
||||
}
|
||||
33
briar-tests/src/net/sf/briar/TestDatabaseConfig.java
Normal file
33
briar-tests/src/net/sf/briar/TestDatabaseConfig.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.sf.briar;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.sf.briar.api.crypto.Password;
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
|
||||
public class TestDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
private final File dir;
|
||||
private final long maxSize;
|
||||
|
||||
public TestDatabaseConfig(File dir, long maxSize) {
|
||||
this.dir = dir;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public File getDataDirectory() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public Password getPassword() {
|
||||
return new Password() {
|
||||
public char[] getPassword() {
|
||||
return "foo bar".toCharArray();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public long getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
}
|
||||
29
briar-tests/src/net/sf/briar/TestDatabaseModule.java
Normal file
29
briar-tests/src/net/sf/briar/TestDatabaseModule.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package net.sf.briar;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.sf.briar.api.db.DatabaseConfig;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class TestDatabaseModule extends AbstractModule {
|
||||
|
||||
private final DatabaseConfig config;
|
||||
|
||||
public TestDatabaseModule() {
|
||||
this(new File("."), Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
public TestDatabaseModule(File dir) {
|
||||
this(dir, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
public TestDatabaseModule(File dir, long maxSize) {
|
||||
this.config = new TestDatabaseConfig(dir, maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(DatabaseConfig.class).toInstance(config);
|
||||
}
|
||||
}
|
||||
76
briar-tests/src/net/sf/briar/TestUtils.java
Normal file
76
briar-tests/src/net/sf/briar/TestUtils.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package net.sf.briar;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
|
||||
public class TestUtils {
|
||||
|
||||
private static final AtomicInteger nextTestDir =
|
||||
new AtomicInteger((int) (Math.random() * 1000 * 1000));
|
||||
private static final Random random = new Random();
|
||||
|
||||
public static void delete(File f) {
|
||||
if(f.isDirectory()) for(File child : f.listFiles()) delete(child);
|
||||
f.delete();
|
||||
}
|
||||
|
||||
public static void createFile(File f, String s) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
PrintStream out = new PrintStream(new FileOutputStream(f));
|
||||
out.print(s);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
public static File getTestDirectory() {
|
||||
int name = nextTestDir.getAndIncrement();
|
||||
File testDir = new File("test.tmp/" + name);
|
||||
return testDir;
|
||||
}
|
||||
|
||||
public static void deleteTestDirectory(File testDir) {
|
||||
delete(testDir);
|
||||
testDir.getParentFile().delete(); // Delete if empty
|
||||
}
|
||||
|
||||
public static File getBuildDirectory() {
|
||||
File build = new File("build"); // Ant
|
||||
if(build.exists() && build.isDirectory()) return build;
|
||||
File bin = new File("bin"); // Eclipse
|
||||
if(bin.exists() && bin.isDirectory()) return bin;
|
||||
throw new RuntimeException("Could not find build directory");
|
||||
}
|
||||
|
||||
public static File getFontDirectory() {
|
||||
File f = new File("i18n");
|
||||
if(f.exists() && f.isDirectory()) return f;
|
||||
f = new File("../i18n");
|
||||
if(f.exists() && f.isDirectory()) return f;
|
||||
throw new RuntimeException("Could not find font directory");
|
||||
}
|
||||
|
||||
public static byte[] getRandomId() {
|
||||
byte[] b = new byte[UniqueId.LENGTH];
|
||||
random.nextBytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public static void readFully(InputStream in, byte[] b) throws IOException {
|
||||
int offset = 0;
|
||||
while(offset < b.length) {
|
||||
int read = in.read(b, offset, b.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
TestCase.assertEquals(b.length, offset);
|
||||
}
|
||||
}
|
||||
156
briar-tests/src/net/sf/briar/crypto/CounterModeTest.java
Normal file
156
briar-tests/src/net/sf/briar/crypto/CounterModeTest.java
Normal file
@@ -0,0 +1,156 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.Bytes;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
public class CounterModeTest extends BriarTestCase {
|
||||
|
||||
private static final String CIPHER_ALGO = "AES";
|
||||
private static final String CIPHER_MODE = "AES/CTR/NoPadding";
|
||||
private static final String PROVIDER = "SC";
|
||||
private static final int KEY_SIZE_BYTES = 32; // AES-256
|
||||
private static final int BLOCK_SIZE_BYTES = 16;
|
||||
|
||||
private final SecureRandom random;
|
||||
private final byte[] keyBytes;
|
||||
private final SecretKeySpec key;
|
||||
|
||||
public CounterModeTest() {
|
||||
super();
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
random = new SecureRandom();
|
||||
keyBytes = new byte[KEY_SIZE_BYTES];
|
||||
random.nextBytes(keyBytes);
|
||||
key = new SecretKeySpec(keyBytes, CIPHER_ALGO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEveryBitOfIvIsSignificant()
|
||||
throws GeneralSecurityException {
|
||||
// Set each bit of the IV in turn, encrypt the same plaintext and check
|
||||
// that all the resulting ciphertexts are distinct
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(plaintext);
|
||||
Set<Bytes> ciphertexts = new HashSet<Bytes>();
|
||||
for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
|
||||
// Set the i^th bit of the IV
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
ivBytes[i / 8] |= (byte) (128 >> i % 8);
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
// Encrypt the plaintext
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
||||
ciphertexts.add(new Bytes(ciphertext));
|
||||
}
|
||||
// All the ciphertexts should be distinct using Arrays.equals()
|
||||
assertEquals(BLOCK_SIZE_BYTES * 8, ciphertexts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRepeatedIvsProduceRepeatedCiphertexts()
|
||||
throws GeneralSecurityException {
|
||||
// This is the inverse of the previous test, to check that the
|
||||
// distinct ciphertexts were due to using distinct IVs
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(plaintext);
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(ivBytes);
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
Set<Bytes> ciphertexts = new HashSet<Bytes>();
|
||||
for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext =
|
||||
new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
||||
ciphertexts.add(new Bytes(ciphertext));
|
||||
}
|
||||
assertEquals(1, ciphertexts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLeastSignificantBitsUsedAsCounter()
|
||||
throws GeneralSecurityException {
|
||||
// Initialise the least significant 16 bits of the IV to zero and
|
||||
// encrypt ten blocks of zeroes
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(ivBytes);
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
||||
// Make sure the IV array hasn't been modified
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 2]);
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 1]);
|
||||
// Initialise the least significant 16 bits of the IV to one and
|
||||
// encrypt another ten blocks of zeroes
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 1;
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1);
|
||||
// The last nine blocks of the first ciphertext should be identical to
|
||||
// the first nine blocks of the second ciphertext
|
||||
for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
|
||||
assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCounterUsesMoreThan16Bits()
|
||||
throws GeneralSecurityException {
|
||||
// Initialise the least significant bits of the IV to 2^16-1 and
|
||||
// encrypt ten blocks of zeroes
|
||||
byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
|
||||
byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
|
||||
random.nextBytes(ivBytes);
|
||||
ivBytes[BLOCK_SIZE_BYTES - 3] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = (byte) 255;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = (byte) 255;
|
||||
IvParameterSpec iv = new IvParameterSpec(ivBytes);
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
|
||||
// Make sure the IV array hasn't been modified
|
||||
assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 3]);
|
||||
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 2]);
|
||||
assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 1]);
|
||||
// Initialise the least significant bits of the IV to 2^16 and
|
||||
// encrypt another ten blocks of zeroes
|
||||
ivBytes[BLOCK_SIZE_BYTES - 3] = 1;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
|
||||
ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
|
||||
iv = new IvParameterSpec(ivBytes);
|
||||
cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||
byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
|
||||
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1);
|
||||
// The last nine blocks of the first ciphertext should be identical to
|
||||
// the first nine blocks of the second ciphertext
|
||||
for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
|
||||
assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java
Normal file
79
briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ErasableKeyTest extends BriarTestCase {
|
||||
|
||||
private static final String CIPHER = "AES";
|
||||
private static final String CIPHER_MODE = "AES/CTR/NoPadding";
|
||||
private static final int IV_BYTES = 16; // 128 bits
|
||||
private static final int KEY_BYTES = 32; // 256 bits
|
||||
private static final String MAC = "HMacSHA384";
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
@Test
|
||||
public void testCopiesAreErased() {
|
||||
byte[] master = new byte[KEY_BYTES];
|
||||
random.nextBytes(master);
|
||||
ErasableKey k = new ErasableKeyImpl(master, CIPHER);
|
||||
byte[] copy = k.getEncoded();
|
||||
assertArrayEquals(master, copy);
|
||||
k.erase();
|
||||
byte[] blank = new byte[KEY_BYTES];
|
||||
assertArrayEquals(blank, master);
|
||||
assertArrayEquals(blank, copy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErasureDoesNotAffectCipher() throws Exception {
|
||||
byte[] key = new byte[KEY_BYTES];
|
||||
random.nextBytes(key);
|
||||
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
|
||||
Cipher c = Cipher.getInstance(CIPHER_MODE);
|
||||
IvParameterSpec iv = new IvParameterSpec(new byte[IV_BYTES]);
|
||||
c.init(Cipher.ENCRYPT_MODE, k, iv);
|
||||
// Encrypt a blank plaintext
|
||||
byte[] plaintext = new byte[123];
|
||||
byte[] ciphertext = c.doFinal(plaintext);
|
||||
// Erase the key and encrypt again - erase() was called after doFinal()
|
||||
k.erase();
|
||||
byte[] ciphertext1 = c.doFinal(plaintext);
|
||||
// Encrypt again - this time erase() was called before doFinal()
|
||||
byte[] ciphertext2 = c.doFinal(plaintext);
|
||||
// The ciphertexts should match
|
||||
assertArrayEquals(ciphertext, ciphertext1);
|
||||
assertArrayEquals(ciphertext, ciphertext2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErasureDoesNotAffectMac() throws Exception {
|
||||
byte[] key = new byte[KEY_BYTES];
|
||||
random.nextBytes(key);
|
||||
ErasableKey k = new ErasableKeyImpl(key, CIPHER);
|
||||
Mac m = Mac.getInstance(MAC);
|
||||
m.init(k);
|
||||
// Authenticate a blank plaintext
|
||||
byte[] plaintext = new byte[123];
|
||||
byte[] mac = m.doFinal(plaintext);
|
||||
// Erase the key and authenticate again
|
||||
k.erase();
|
||||
byte[] mac1 = m.doFinal(plaintext);
|
||||
// Authenticate again
|
||||
byte[] mac2 = m.doFinal(plaintext);
|
||||
// The MACs should match
|
||||
assertArrayEquals(mac, mac1);
|
||||
assertArrayEquals(mac, mac2);
|
||||
}
|
||||
}
|
||||
25
briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java
Normal file
25
briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class KeyAgreementTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testKeyAgreement() throws Exception {
|
||||
CryptoComponent crypto = new CryptoComponentImpl();
|
||||
KeyPair a = crypto.generateAgreementKeyPair();
|
||||
byte[] aPub = a.getPublic().getEncoded();
|
||||
KeyPair b = crypto.generateAgreementKeyPair();
|
||||
byte[] bPub = b.getPublic().getEncoded();
|
||||
byte[] aSecret = crypto.deriveInitialSecret(aPub, b, true);
|
||||
byte[] bSecret = crypto.deriveInitialSecret(bPub, a, false);
|
||||
assertArrayEquals(aSecret, bSecret);
|
||||
}
|
||||
}
|
||||
76
briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java
Normal file
76
briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package net.sf.briar.crypto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class KeyDerivationTest extends BriarTestCase {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final byte[] secret;
|
||||
|
||||
public KeyDerivationTest() {
|
||||
super();
|
||||
crypto = new CryptoComponentImpl();
|
||||
secret = new byte[32];
|
||||
new Random().nextBytes(secret);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeysAreDistinct() {
|
||||
List<ErasableKey> keys = new ArrayList<ErasableKey>();
|
||||
keys.add(crypto.deriveFrameKey(secret, 0, false, false));
|
||||
keys.add(crypto.deriveFrameKey(secret, 0, false, true));
|
||||
keys.add(crypto.deriveFrameKey(secret, 0, true, false));
|
||||
keys.add(crypto.deriveFrameKey(secret, 0, true, true));
|
||||
keys.add(crypto.deriveTagKey(secret, true));
|
||||
keys.add(crypto.deriveTagKey(secret, false));
|
||||
for(int i = 0; i < 4; i++) {
|
||||
byte[] keyI = keys.get(i).getEncoded();
|
||||
for(int j = 0; j < 4; j++) {
|
||||
byte[] keyJ = keys.get(j).getEncoded();
|
||||
assertEquals(i == j, Arrays.equals(keyI, keyJ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecretAffectsDerivation() {
|
||||
Random r = new Random();
|
||||
List<byte[]> secrets = new ArrayList<byte[]>();
|
||||
for(int i = 0; i < 20; i++) {
|
||||
byte[] b = new byte[32];
|
||||
r.nextBytes(b);
|
||||
secrets.add(crypto.deriveNextSecret(b, 0));
|
||||
}
|
||||
for(int i = 0; i < 20; i++) {
|
||||
byte[] secretI = secrets.get(i);
|
||||
for(int j = 0; j < 20; j++) {
|
||||
byte[] secretJ = secrets.get(j);
|
||||
assertEquals(i == j, Arrays.equals(secretI, secretJ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionNumberAffectsDerivation() {
|
||||
List<byte[]> secrets = new ArrayList<byte[]>();
|
||||
for(int i = 0; i < 20; i++) {
|
||||
secrets.add(crypto.deriveNextSecret(secret.clone(), i));
|
||||
}
|
||||
for(int i = 0; i < 20; i++) {
|
||||
byte[] secretI = secrets.get(i);
|
||||
for(int j = 0; j < 20; j++) {
|
||||
byte[] secretJ = secrets.get(j);
|
||||
assertEquals(i == j, Arrays.equals(secretI, secretJ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
192
briar-tests/src/net/sf/briar/db/BasicH2Test.java
Normal file
192
briar-tests/src/net/sf/briar/db/BasicH2Test.java
Normal file
@@ -0,0 +1,192 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BasicH2Test extends BriarTestCase {
|
||||
|
||||
private static final String CREATE_TABLE =
|
||||
"CREATE TABLE foo"
|
||||
+ " (uniqueId BINARY(32),"
|
||||
+ " name VARCHAR NOT NULL)";
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File db = new File(testDir, "db");
|
||||
private final String url = "jdbc:h2:" + db.getPath();
|
||||
|
||||
private Connection connection = null;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
testDir.mkdirs();
|
||||
Class.forName("org.h2.Driver");
|
||||
connection = DriverManager.getConnection(url);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTableAndAddRow() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID
|
||||
byte[] id = new byte[32];
|
||||
new Random().nextBytes(id);
|
||||
// Insert the ID and name into the table
|
||||
addRow(id, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTableAddAndRetrieveRow() throws Exception {
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Generate an ID
|
||||
byte[] id = new byte[32];
|
||||
new Random().nextBytes(id);
|
||||
// Insert the ID and name into the table
|
||||
addRow(id, "foo");
|
||||
// Check that the name can be retrieved using the ID
|
||||
assertEquals("foo", getName(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortOrder() throws Exception {
|
||||
byte[] first = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, -128
|
||||
};
|
||||
byte[] second = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
byte[] third = new byte[] {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 127
|
||||
};
|
||||
// Create the table
|
||||
createTable(connection);
|
||||
// Insert the rows
|
||||
addRow(first, "first");
|
||||
addRow(second, "second");
|
||||
addRow(third, "third");
|
||||
addRow(null, "null");
|
||||
// Check the ordering of the < operator: the null ID is not comparable
|
||||
assertNull(getPredecessor(first));
|
||||
assertEquals("first", getPredecessor(second));
|
||||
assertEquals("second", getPredecessor(third));
|
||||
assertNull(getPredecessor(null));
|
||||
// Check the ordering of ORDER BY: nulls come first
|
||||
List<String> names = getNames();
|
||||
assertEquals(4, names.size());
|
||||
assertEquals("null", names.get(0));
|
||||
assertEquals("first", names.get(1));
|
||||
assertEquals("second", names.get(2));
|
||||
assertEquals("third", names.get(3));
|
||||
}
|
||||
|
||||
private void createTable(Connection connection) throws SQLException {
|
||||
try {
|
||||
Statement s = connection.createStatement();
|
||||
s.executeUpdate(CREATE_TABLE);
|
||||
s.close();
|
||||
} catch(SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void addRow(byte[] id, String name) throws SQLException {
|
||||
String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if(id == null) ps.setNull(1, Types.BINARY);
|
||||
else ps.setBytes(1, id);
|
||||
ps.setString(2, name);
|
||||
int rowsAffected = ps.executeUpdate();
|
||||
ps.close();
|
||||
assertEquals(1, rowsAffected);
|
||||
} catch(SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getName(byte[] id) throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueID = ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
if(id != null) ps.setBytes(1, id);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
assertTrue(rs.next());
|
||||
String name = rs.getString(1);
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch(SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private String getPredecessor(byte[] id) throws SQLException {
|
||||
String sql = "SELECT name FROM foo WHERE uniqueId < ?"
|
||||
+ " ORDER BY uniqueId DESC LIMIT ?";
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ps.setBytes(1, id);
|
||||
ps.setInt(2, 1);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
String name = rs.next() ? rs.getString(1) : null;
|
||||
assertFalse(rs.next());
|
||||
rs.close();
|
||||
ps.close();
|
||||
return name;
|
||||
} catch(SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getNames() throws SQLException {
|
||||
String sql = "SELECT name FROM foo ORDER BY uniqueId";
|
||||
List<String> names = new ArrayList<String>();
|
||||
try {
|
||||
PreparedStatement ps = connection.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) names.add(rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return names;
|
||||
} catch(SQLException e) {
|
||||
connection.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
if(connection != null) connection.close();
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
67
briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java
Normal file
67
briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.clock.SystemTimer;
|
||||
import net.sf.briar.api.clock.Timer;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.db.DatabaseCleaner.Callback;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
// FIXME: Use a mock timer
|
||||
public class DatabaseCleanerImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testCleanerRunsPeriodically() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(5);
|
||||
Callback callback = new Callback() {
|
||||
|
||||
public void checkFreeSpaceAndClean() throws DbException {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public boolean shouldCheckFreeSpace() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Timer timer = new SystemTimer();
|
||||
DatabaseCleanerImpl cleaner = new DatabaseCleanerImpl(timer);
|
||||
// Start the cleaner
|
||||
cleaner.startCleaning(callback, 10L);
|
||||
// The database should be cleaned five times (allow 5s for system load)
|
||||
assertTrue(latch.await(5, SECONDS));
|
||||
// Stop the cleaner
|
||||
cleaner.stopCleaning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoppingCleanerWakesItUp() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Callback callback = new Callback() {
|
||||
|
||||
public void checkFreeSpaceAndClean() throws DbException {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public boolean shouldCheckFreeSpace() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Timer timer = new SystemTimer();
|
||||
DatabaseCleanerImpl cleaner = new DatabaseCleanerImpl(timer);
|
||||
long start = System.currentTimeMillis();
|
||||
// Start the cleaner
|
||||
cleaner.startCleaning(callback, 10L * 1000L);
|
||||
// The database should be cleaned once at startup
|
||||
assertTrue(latch.await(5, SECONDS));
|
||||
// Stop the cleaner (it should be waiting between sweeps)
|
||||
cleaner.stopCleaning();
|
||||
long end = System.currentTimeMillis();
|
||||
// Check that much less than 10 seconds expired
|
||||
assertTrue(end - start < 10L * 1000L);
|
||||
}
|
||||
}
|
||||
151
briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
Normal file
151
briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP;
|
||||
import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sf.briar.api.clock.SystemClock;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.db.DatabaseCleaner.Callback;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests that use the DatabaseCleaner.Callback interface of
|
||||
* DatabaseComponentImpl.
|
||||
*/
|
||||
public class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
|
||||
@Test
|
||||
public void testNotCleanedIfEnoughFreeSpace() throws DbException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown,
|
||||
packetFactory);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanedIfNotEnoughFreeSpace() throws DbException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
|
||||
will(returnValue(Collections.emptyList()));
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// As if by magic, some free space has appeared
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown,
|
||||
packetFactory);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpiringUnsendableMessageDoesNotTriggerBackwardInclusion()
|
||||
throws DbException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
|
||||
will(returnValue(Collections.singletonList(messageId)));
|
||||
oneOf(database).getSendability(txn, messageId);
|
||||
will(returnValue(0));
|
||||
oneOf(database).removeMessage(txn, messageId);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown,
|
||||
packetFactory);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpiringSendableMessageTriggersBackwardInclusion()
|
||||
throws DbException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
|
||||
will(returnValue(Collections.singletonList(messageId)));
|
||||
oneOf(database).getSendability(txn, messageId);
|
||||
will(returnValue(1));
|
||||
oneOf(database).getGroupMessageParent(txn, messageId);
|
||||
will(returnValue(null));
|
||||
oneOf(database).removeMessage(txn, messageId);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown,
|
||||
packetFactory);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> DatabaseComponent createDatabaseComponent(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
ShutdownManager shutdown, PacketFactory packetFactory) {
|
||||
return createDatabaseComponentImpl(database, cleaner, shutdown,
|
||||
packetFactory);
|
||||
}
|
||||
|
||||
private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
ShutdownManager shutdown, PacketFactory packetFactory) {
|
||||
return new DatabaseComponentImpl<T>(database, cleaner, shutdown,
|
||||
packetFactory, new SystemClock());
|
||||
}
|
||||
}
|
||||
1606
briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
Normal file
1606
briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
Normal file
File diff suppressed because it is too large
Load Diff
2044
briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
Normal file
2044
briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
Normal file
File diff suppressed because it is too large
Load Diff
29
briar-tests/src/net/sf/briar/db/TestGroup.java
Normal file
29
briar-tests/src/net/sf/briar/db/TestGroup.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
|
||||
class TestGroup implements Group {
|
||||
|
||||
private final GroupId id;
|
||||
private final String name;
|
||||
private final byte[] publicKey;
|
||||
|
||||
public TestGroup(GroupId id, String name, byte[] publicKey) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
public GroupId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
}
|
||||
20
briar-tests/src/net/sf/briar/db/TestGroupFactory.java
Normal file
20
briar-tests/src/net/sf/briar/db/TestGroupFactory.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
|
||||
class TestGroupFactory implements GroupFactory {
|
||||
|
||||
public Group createGroup(String name, byte[] publicKey) throws IOException {
|
||||
GroupId id = new GroupId(TestUtils.getRandomId());
|
||||
return new TestGroup(id, name, publicKey);
|
||||
}
|
||||
|
||||
public Group createGroup(GroupId id, String name, byte[] publicKey) {
|
||||
return new TestGroup(id, name, publicKey);
|
||||
}
|
||||
}
|
||||
89
briar-tests/src/net/sf/briar/db/TestMessage.java
Normal file
89
briar-tests/src/net/sf/briar/db/TestMessage.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
|
||||
class TestMessage implements Message {
|
||||
|
||||
private final MessageId id, parent;
|
||||
private final GroupId group;
|
||||
private final AuthorId author;
|
||||
private final String subject;
|
||||
private final long timestamp;
|
||||
private final byte[] raw;
|
||||
private final int bodyStart, bodyLength;
|
||||
|
||||
public TestMessage(MessageId id, MessageId parent, GroupId group,
|
||||
AuthorId author, String subject, long timestamp, byte[] raw) {
|
||||
this(id, parent, group, author, subject, timestamp, raw, 0, raw.length);
|
||||
}
|
||||
|
||||
public TestMessage(MessageId id, MessageId parent, GroupId group,
|
||||
AuthorId author, String subject, long timestamp, byte[] raw,
|
||||
int bodyStart, int bodyLength) {
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
this.group = group;
|
||||
this.author = author;
|
||||
this.subject = subject;
|
||||
this.timestamp = timestamp;
|
||||
this.raw = raw;
|
||||
this.bodyStart = bodyStart;
|
||||
this.bodyLength = bodyLength;
|
||||
}
|
||||
|
||||
public MessageId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public MessageId getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public GroupId getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public AuthorId getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public byte[] getSerialised() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
public int getBodyStart() {
|
||||
return bodyStart;
|
||||
}
|
||||
|
||||
public int getBodyLength() {
|
||||
return bodyLength;
|
||||
}
|
||||
|
||||
public InputStream getSerialisedStream() {
|
||||
return new ByteArrayInputStream(raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Message && id.equals(((Message)o).getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.sf.briar.lifecycle;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ShutdownManagerImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testAddAndRemove() {
|
||||
ShutdownManager s = createShutdownManager();
|
||||
Set<Integer> handles = new HashSet<Integer>();
|
||||
for(int i = 0; i < 100; i++) {
|
||||
int handle = s.addShutdownHook(new Runnable() {
|
||||
public void run() {}
|
||||
});
|
||||
// The handles should all be distinct
|
||||
assertTrue(handles.add(handle));
|
||||
}
|
||||
// The hooks should be removable
|
||||
for(int handle : handles) assertTrue(s.removeShutdownHook(handle));
|
||||
// The hooks should no longer be removable
|
||||
for(int handle : handles) assertFalse(s.removeShutdownHook(handle));
|
||||
}
|
||||
|
||||
protected ShutdownManager createShutdownManager() {
|
||||
return new ShutdownManagerImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.sf.briar.lifecycle;
|
||||
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class WindowsShutdownManagerImplTest extends ShutdownManagerImplTest {
|
||||
|
||||
@Override
|
||||
protected ShutdownManager createShutdownManager() {
|
||||
return new WindowsShutdownManagerImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManagerWaitsForHooksToRun() {
|
||||
WindowsShutdownManagerImpl s = new WindowsShutdownManagerImpl();
|
||||
SlowHook[] hooks = new SlowHook[10];
|
||||
for(int i = 0; i < hooks.length; i++) {
|
||||
hooks[i] = new SlowHook();
|
||||
s.addShutdownHook(hooks[i]);
|
||||
}
|
||||
s.runShutdownHooks();
|
||||
for(int i = 0; i < hooks.length; i++) assertTrue(hooks[i].finished);
|
||||
}
|
||||
|
||||
private static class SlowHook implements Runnable {
|
||||
|
||||
private volatile boolean finished = false;
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
finished = true;
|
||||
} catch(InterruptedException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
Normal file
101
briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
|
||||
public abstract class DuplexClientTest extends DuplexTest {
|
||||
|
||||
protected ClientCallback callback = null;
|
||||
|
||||
protected void run() throws IOException {
|
||||
assert plugin != null;
|
||||
// Start the plugin
|
||||
System.out.println("Starting plugin");
|
||||
plugin.start();
|
||||
// Try to connect to the server
|
||||
System.out.println("Creating connection");
|
||||
DuplexTransportConnection d = plugin.createConnection(contactId);
|
||||
if(d == null) {
|
||||
System.out.println("Connection failed");
|
||||
} else {
|
||||
System.out.println("Connection created");
|
||||
receiveChallengeSendResponse(d);
|
||||
}
|
||||
// Try to send an invitation
|
||||
System.out.println("Sending invitation");
|
||||
d = plugin.sendInvitation(getPseudoRandom(123), INVITATION_TIMEOUT);
|
||||
if(d == null) {
|
||||
System.out.println("Connection failed");
|
||||
} else {
|
||||
System.out.println("Connection created");
|
||||
receiveChallengeSendResponse(d);
|
||||
}
|
||||
// Try to accept an invitation
|
||||
System.out.println("Accepting invitation");
|
||||
d = plugin.acceptInvitation(getPseudoRandom(456), INVITATION_TIMEOUT);
|
||||
if(d == null) {
|
||||
System.out.println("Connection failed");
|
||||
} else {
|
||||
System.out.println("Connection created");
|
||||
sendChallengeReceiveResponse(d);
|
||||
}
|
||||
// Stop the plugin
|
||||
System.out.println("Stopping plugin");
|
||||
plugin.stop();
|
||||
}
|
||||
|
||||
protected static class ClientCallback implements DuplexPluginCallback {
|
||||
|
||||
private TransportConfig config = null;
|
||||
private TransportProperties local = null;
|
||||
private Map<ContactId, TransportProperties> remote = null;
|
||||
|
||||
public ClientCallback(TransportConfig config, TransportProperties local,
|
||||
Map<ContactId, TransportProperties> remote) {
|
||||
this.config = config;
|
||||
this.local = local;
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
public TransportConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public TransportProperties getLocalProperties() {
|
||||
return local;
|
||||
}
|
||||
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void mergeConfig(TransportConfig c) {
|
||||
config = c;
|
||||
}
|
||||
|
||||
public void mergeLocalProperties(TransportProperties p) {
|
||||
local = p;
|
||||
}
|
||||
|
||||
public int showChoice(String[] options, String... message) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean showConfirmationMessage(String... message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showMessage(String... message) {}
|
||||
|
||||
public void incomingConnectionCreated(DuplexTransportConnection d) {}
|
||||
|
||||
public void outgoingConnectionCreated(ContactId contactId,
|
||||
DuplexTransportConnection d) {}
|
||||
}
|
||||
}
|
||||
103
briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
Normal file
103
briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
|
||||
public abstract class DuplexServerTest extends DuplexTest {
|
||||
|
||||
protected ServerCallback callback = null;
|
||||
|
||||
protected void run() throws Exception {
|
||||
assert callback != null;
|
||||
assert plugin != null;
|
||||
// Start the plugin
|
||||
System.out.println("Starting plugin");
|
||||
plugin.start();
|
||||
// Wait for a connection
|
||||
System.out.println("Waiting for connection");
|
||||
callback.latch.await();
|
||||
// Try to accept an invitation
|
||||
System.out.println("Accepting invitation");
|
||||
DuplexTransportConnection d = plugin.acceptInvitation(
|
||||
getPseudoRandom(123), INVITATION_TIMEOUT);
|
||||
if(d == null) {
|
||||
System.out.println("Connection failed");
|
||||
} else {
|
||||
System.out.println("Connection created");
|
||||
sendChallengeReceiveResponse(d);
|
||||
}
|
||||
// Try to send an invitation
|
||||
System.out.println("Sending invitation");
|
||||
d = plugin.sendInvitation(getPseudoRandom(456), INVITATION_TIMEOUT);
|
||||
if(d == null) {
|
||||
System.out.println("Connection failed");
|
||||
} else {
|
||||
System.out.println("Connection created");
|
||||
receiveChallengeSendResponse(d);
|
||||
}
|
||||
// Stop the plugin
|
||||
System.out.println("Stopping plugin");
|
||||
plugin.stop();
|
||||
}
|
||||
|
||||
protected class ServerCallback implements DuplexPluginCallback {
|
||||
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
private TransportConfig config;
|
||||
private TransportProperties local;
|
||||
private Map<ContactId, TransportProperties> remote;
|
||||
|
||||
public ServerCallback(TransportConfig config, TransportProperties local,
|
||||
Map<ContactId, TransportProperties> remote) {
|
||||
this.config = config;
|
||||
this.local = local;
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
public TransportConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public TransportProperties getLocalProperties() {
|
||||
return local;
|
||||
}
|
||||
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void mergeConfig(TransportConfig c) {
|
||||
config = c;
|
||||
}
|
||||
|
||||
public void mergeLocalProperties(TransportProperties p) {
|
||||
local = p;
|
||||
}
|
||||
|
||||
public int showChoice(String[] options, String... message) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean showConfirmationMessage(String... message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showMessage(String... message) {}
|
||||
|
||||
public void incomingConnectionCreated(DuplexTransportConnection d) {
|
||||
System.out.println("Connection received");
|
||||
sendChallengeReceiveResponse(d);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void outgoingConnectionCreated(ContactId c,
|
||||
DuplexTransportConnection d) {}
|
||||
}
|
||||
}
|
||||
98
briar-tests/src/net/sf/briar/plugins/DuplexTest.java
Normal file
98
briar-tests/src/net/sf/briar/plugins/DuplexTest.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.PseudoRandom;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
|
||||
abstract class DuplexTest {
|
||||
|
||||
protected static final String CHALLENGE = "Carrots!";
|
||||
protected static final String RESPONSE = "Potatoes!";
|
||||
protected static final long INVITATION_TIMEOUT = 30 * 1000;
|
||||
|
||||
protected final ContactId contactId = new ContactId(234);
|
||||
|
||||
protected DuplexPlugin plugin = null;
|
||||
|
||||
protected void sendChallengeReceiveResponse(DuplexTransportConnection d) {
|
||||
assert plugin != null;
|
||||
try {
|
||||
PrintStream out = new PrintStream(d.getOutputStream());
|
||||
out.println(CHALLENGE);
|
||||
System.out.println("Sent challenge: " + CHALLENGE);
|
||||
Scanner in = new Scanner(d.getInputStream());
|
||||
if(in.hasNextLine()) {
|
||||
String response = in.nextLine();
|
||||
System.out.println("Received response: " + response);
|
||||
if(RESPONSE.equals(response)) {
|
||||
System.out.println("Correct response");
|
||||
} else {
|
||||
System.out.println("Incorrect response");
|
||||
}
|
||||
} else {
|
||||
System.out.println("No response");
|
||||
}
|
||||
d.dispose(false, true);
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
d.dispose(true, true);
|
||||
} catch(IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void receiveChallengeSendResponse(DuplexTransportConnection d) {
|
||||
assert plugin != null;
|
||||
try {
|
||||
Scanner in = new Scanner(d.getInputStream());
|
||||
if(in.hasNextLine()) {
|
||||
String challenge = in.nextLine();
|
||||
System.out.println("Received challenge: " + challenge);
|
||||
if(CHALLENGE.equals(challenge)) {
|
||||
PrintStream out = new PrintStream(d.getOutputStream());
|
||||
out.println(RESPONSE);
|
||||
System.out.println("Sent response: " + RESPONSE);
|
||||
} else {
|
||||
System.out.println("Incorrect challenge");
|
||||
}
|
||||
} else {
|
||||
System.out.println("No challenge");
|
||||
}
|
||||
d.dispose(false, true);
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
d.dispose(true, true);
|
||||
} catch(IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected PseudoRandom getPseudoRandom(int seed) {
|
||||
return new TestPseudoRandom(seed);
|
||||
}
|
||||
|
||||
private static class TestPseudoRandom implements PseudoRandom {
|
||||
|
||||
private final Random r;
|
||||
|
||||
private TestPseudoRandom(int seed) {
|
||||
r = new Random(seed);
|
||||
}
|
||||
|
||||
public byte[] nextBytes(int bytes) {
|
||||
byte[] b = new byte[bytes];
|
||||
r.nextBytes(b);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java
Normal file
10
briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class ImmediateExecutor implements Executor {
|
||||
|
||||
public void execute(Runnable r) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package net.sf.briar.plugins;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.android.AndroidExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.lifecycle.ShutdownManager;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.transport.ConnectionDispatcher;
|
||||
import net.sf.briar.api.ui.UiCallback;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PluginManagerImplTest extends BriarTestCase {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testStartAndStop() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final AndroidExecutor androidExecutor =
|
||||
context.mock(AndroidExecutor.class);
|
||||
final ShutdownManager shutdownManager =
|
||||
context.mock(ShutdownManager.class);
|
||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
final Poller poller = context.mock(Poller.class);
|
||||
final ConnectionDispatcher dispatcher =
|
||||
context.mock(ConnectionDispatcher.class);
|
||||
final UiCallback uiCallback = context.mock(UiCallback.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Start
|
||||
oneOf(poller).start(with(any(Collection.class)));
|
||||
allowing(db).getConfig(with(any(TransportId.class)));
|
||||
will(returnValue(new TransportConfig()));
|
||||
allowing(db).getLocalProperties(with(any(TransportId.class)));
|
||||
will(returnValue(new TransportProperties()));
|
||||
allowing(db).getRemoteProperties(with(any(TransportId.class)));
|
||||
will(returnValue(new TransportProperties()));
|
||||
allowing(db).mergeLocalProperties(with(any(TransportId.class)),
|
||||
with(any(TransportProperties.class)));
|
||||
// Stop
|
||||
oneOf(poller).stop();
|
||||
oneOf(androidExecutor).shutdown();
|
||||
}});
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
PluginManagerImpl p = new PluginManagerImpl(executor, androidExecutor,
|
||||
shutdownManager, db, poller, dispatcher, uiCallback);
|
||||
// We expect either 3 or 4 plugins to be started, depending on whether
|
||||
// the test machine has a Bluetooth device
|
||||
int started = p.start(null);
|
||||
int stopped = p.stop();
|
||||
assertEquals(started, stopped);
|
||||
assertTrue(started >= 3);
|
||||
assertTrue(started <= 4);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.sf.briar.plugins.bluetooth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.clock.SystemClock;
|
||||
import net.sf.briar.plugins.DuplexClientTest;
|
||||
|
||||
// This is not a JUnit test - it has to be run manually while the server test
|
||||
// is running on another machine
|
||||
public class BluetoothClientTest extends DuplexClientTest {
|
||||
|
||||
private BluetoothClientTest(Executor executor, String serverAddress) {
|
||||
// Store the server's Bluetooth address and UUID
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("address", serverAddress);
|
||||
p.put("uuid", BluetoothTest.getUuid());
|
||||
Map<ContactId, TransportProperties> remote =
|
||||
Collections.singletonMap(contactId, p);
|
||||
// Create the plugin
|
||||
callback = new ClientCallback(new TransportConfig(),
|
||||
new TransportProperties(), remote);
|
||||
plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if(args.length != 1) {
|
||||
System.err.println("Please specify the server's Bluetooth address");
|
||||
System.exit(1);
|
||||
}
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
try {
|
||||
new BluetoothClientTest(executor, args[0]).run();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.sf.briar.plugins.bluetooth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.clock.SystemClock;
|
||||
import net.sf.briar.plugins.DuplexServerTest;
|
||||
|
||||
// This is not a JUnit test - it has to be run manually while the client test
|
||||
// is running on another machine
|
||||
public class BluetoothServerTest extends DuplexServerTest {
|
||||
|
||||
private BluetoothServerTest(Executor executor) {
|
||||
// Store the UUID
|
||||
TransportProperties local = new TransportProperties();
|
||||
local.put("uuid", BluetoothTest.getUuid());
|
||||
// Create the plugin
|
||||
callback = new ServerCallback(new TransportConfig(), local,
|
||||
Collections.singletonMap(contactId, new TransportProperties()));
|
||||
plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
try {
|
||||
new BluetoothServerTest(executor).run();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.sf.briar.plugins.bluetooth;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
class BluetoothTest {
|
||||
|
||||
private static final String EMPTY_UUID =
|
||||
UUID.nameUUIDFromBytes(new byte[0]).toString().replaceAll("-", "");
|
||||
|
||||
static String getUuid() {
|
||||
return EMPTY_UUID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.sf.briar.plugins.file;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class LinuxRemovableDriveFinderTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testParseMountPoint() {
|
||||
LinuxRemovableDriveFinder f = new LinuxRemovableDriveFinder();
|
||||
String line = "/dev/sda3 on / type ext3"
|
||||
+ " (rw,errors=remount-ro,commit=0)";
|
||||
assertEquals("/", f.parseMountPoint(line));
|
||||
line = "gvfs-fuse-daemon on /home/alice/.gvfs"
|
||||
+ " type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=alice)";
|
||||
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
|
||||
line = "fusectl on /sys/fs/fuse/connections type fusectl (rw)";
|
||||
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
|
||||
line = "/dev/sdd1 on /media/HAZ SPACE(!) type vfat"
|
||||
+ " (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000,"
|
||||
+ "shortname=mixed,dmask=0077,utf8=1,showexec,flush)";
|
||||
assertEquals("/media/HAZ SPACE(!)", f.parseMountPoint(line));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.sf.briar.plugins.file;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MacRemovableDriveFinderTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testParseMountPoint() {
|
||||
MacRemovableDriveFinder f = new MacRemovableDriveFinder();
|
||||
String line = "/dev/disk0s3 on / (local, journaled)";
|
||||
assertEquals("/", f.parseMountPoint(line));
|
||||
line = "devfs on /dev (local)";
|
||||
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
|
||||
line = "<volfs> on /.vol";
|
||||
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
|
||||
line = "automount -nsl [117] on /Network (automounted)";
|
||||
assertEquals(null, f.parseMountPoint(line)); // Can't be parsed
|
||||
line = "/dev/disk1s1 on /Volumes/HAZ SPACE(!) (local, nodev, nosuid)";
|
||||
assertEquals("/Volumes/HAZ SPACE(!)", f.parseMountPoint(line));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package net.sf.briar.plugins.file;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class PollingRemovableDriveMonitorTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testOneCallbackPerFile() throws Exception {
|
||||
// Create a finder that returns no files the first time, then two files
|
||||
final File file1 = new File("foo");
|
||||
final File file2 = new File("bar");
|
||||
final RemovableDriveFinder finder = new RemovableDriveFinder() {
|
||||
|
||||
private AtomicBoolean firstCall = new AtomicBoolean(true);
|
||||
|
||||
public Collection<File> findRemovableDrives() throws IOException {
|
||||
if(firstCall.getAndSet(false)) return Collections.emptyList();
|
||||
else return Arrays.asList(file1, file2);
|
||||
}
|
||||
};
|
||||
// Create a callback that waits for two files
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
final List<File> detected = new ArrayList<File>();
|
||||
Callback callback = new Callback() {
|
||||
|
||||
public void driveInserted(File f) {
|
||||
detected.add(f);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void exceptionThrown(IOException e) {
|
||||
fail();
|
||||
}
|
||||
};
|
||||
// Create the monitor and start it
|
||||
final RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor(
|
||||
Executors.newCachedThreadPool(), finder, 1);
|
||||
monitor.start(callback);
|
||||
// Wait for the monitor to detect the files
|
||||
assertTrue(latch.await(10, SECONDS));
|
||||
monitor.stop();
|
||||
// Check that both files were detected
|
||||
assertEquals(2, detected.size());
|
||||
assertTrue(detected.contains(file1));
|
||||
assertTrue(detected.contains(file2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionCallback() throws Exception {
|
||||
// Create a finder that throws an exception the second time it's polled
|
||||
final RemovableDriveFinder finder = new RemovableDriveFinder() {
|
||||
|
||||
private AtomicBoolean firstCall = new AtomicBoolean(true);
|
||||
|
||||
public Collection<File> findRemovableDrives() throws IOException {
|
||||
if(firstCall.getAndSet(false)) return Collections.emptyList();
|
||||
else throw new IOException();
|
||||
}
|
||||
};
|
||||
// Create a callback that waits for an exception
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Callback callback = new Callback() {
|
||||
|
||||
public void driveInserted(File root) {
|
||||
fail();
|
||||
}
|
||||
|
||||
public void exceptionThrown(IOException e) {
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
// Create the monitor and start it
|
||||
final RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor(
|
||||
Executors.newCachedThreadPool(), finder, 1);
|
||||
monitor.start(callback);
|
||||
assertTrue(latch.await(10, SECONDS));
|
||||
monitor.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
package net.sf.briar.plugins.file;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexPluginCallback;
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
|
||||
import net.sf.briar.plugins.ImmediateExecutor;
|
||||
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RemovableDrivePluginTest extends BriarTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final ContactId contactId = new ContactId(234);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriterIsNullIfNoDrivesAreFound() throws Exception {
|
||||
final List<File> drives = Collections.emptyList();
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
assertNull(plugin.createWriter(contactId));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriterIsNullIfNoDriveIsChosen() throws Exception {
|
||||
final File drive1 = new File(testDir, "1");
|
||||
final File drive2 = new File(testDir, "2");
|
||||
final List<File> drives = new ArrayList<File>();
|
||||
drives.add(drive1);
|
||||
drives.add(drive2);
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
oneOf(callback).showChoice(with(any(String[].class)),
|
||||
with(any(String.class)));
|
||||
will(returnValue(-1)); // The user cancelled the choice
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
assertNull(plugin.createWriter(contactId));
|
||||
File[] files = drive1.listFiles();
|
||||
assertTrue(files == null || files.length == 0);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriterIsNullIfOutputDirDoesNotExist() throws Exception {
|
||||
final File drive1 = new File(testDir, "1");
|
||||
final File drive2 = new File(testDir, "2");
|
||||
final List<File> drives = new ArrayList<File>();
|
||||
drives.add(drive1);
|
||||
drives.add(drive2);
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
oneOf(callback).showChoice(with(any(String[].class)),
|
||||
with(any(String.class)));
|
||||
will(returnValue(0)); // The user chose drive1 but it doesn't exist
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
assertNull(plugin.createWriter(contactId));
|
||||
File[] files = drive1.listFiles();
|
||||
assertTrue(files == null || files.length == 0);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriterIsNullIfOutputDirIsAFile() throws Exception {
|
||||
final File drive1 = new File(testDir, "1");
|
||||
final File drive2 = new File(testDir, "2");
|
||||
final List<File> drives = new ArrayList<File>();
|
||||
drives.add(drive1);
|
||||
drives.add(drive2);
|
||||
// Create drive1 as a file rather than a directory
|
||||
assertTrue(drive1.createNewFile());
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
oneOf(callback).showChoice(with(any(String[].class)),
|
||||
with(any(String.class)));
|
||||
will(returnValue(0)); // The user chose drive1 but it's not a dir
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
assertNull(plugin.createWriter(contactId));
|
||||
File[] files = drive1.listFiles();
|
||||
assertTrue(files == null || files.length == 0);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriterIsNotNullIfOutputDirIsADir() throws Exception {
|
||||
final File drive1 = new File(testDir, "1");
|
||||
final File drive2 = new File(testDir, "2");
|
||||
final List<File> drives = new ArrayList<File>();
|
||||
drives.add(drive1);
|
||||
drives.add(drive2);
|
||||
// Create drive1 as a directory
|
||||
assertTrue(drive1.mkdir());
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
oneOf(callback).showChoice(with(any(String[].class)),
|
||||
with(any(String.class)));
|
||||
will(returnValue(0)); // The user chose drive1
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
assertNotNull(plugin.createWriter(contactId));
|
||||
// The output file should exist and should be empty
|
||||
File[] files = drive1.listFiles();
|
||||
assertNotNull(files);
|
||||
assertEquals(1, files.length);
|
||||
assertEquals(0L, files[0].length());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWritingToWriter() throws Exception {
|
||||
final File drive1 = new File(testDir, "1");
|
||||
final File drive2 = new File(testDir, "2");
|
||||
final List<File> drives = new ArrayList<File>();
|
||||
drives.add(drive1);
|
||||
drives.add(drive2);
|
||||
// Create drive1 as a directory
|
||||
assertTrue(drive1.mkdir());
|
||||
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(finder).findRemovableDrives();
|
||||
will(returnValue(drives));
|
||||
oneOf(callback).showChoice(with(any(String[].class)),
|
||||
with(any(String.class)));
|
||||
will(returnValue(0)); // The user chose drive1
|
||||
oneOf(callback).showMessage(with(any(String.class)));
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
SimplexTransportWriter writer = plugin.createWriter(contactId);
|
||||
assertNotNull(writer);
|
||||
// The output file should exist and should be empty
|
||||
File[] files = drive1.listFiles();
|
||||
assertNotNull(files);
|
||||
assertEquals(1, files.length);
|
||||
assertEquals(0L, files[0].length());
|
||||
// Writing to the output stream should increase the size of the file
|
||||
OutputStream out = writer.getOutputStream();
|
||||
out.write(new byte[123]);
|
||||
out.flush();
|
||||
out.close();
|
||||
// Disposing of the writer should not delete the file
|
||||
writer.dispose(false);
|
||||
assertTrue(files[0].exists());
|
||||
assertEquals(123L, files[0].length());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyDriveIsIgnored() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
plugin.driveInserted(testDir);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilenames() {
|
||||
Mockery context = new Mockery();
|
||||
final Executor executor = context.mock(Executor.class);
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor,
|
||||
callback, finder, monitor);
|
||||
|
||||
assertFalse(plugin.isPossibleConnectionFilename("abcdefg.dat"));
|
||||
assertFalse(plugin.isPossibleConnectionFilename("abcdefghi.dat"));
|
||||
assertFalse(plugin.isPossibleConnectionFilename("abcdefgh_dat"));
|
||||
assertFalse(plugin.isPossibleConnectionFilename("abcdefgh.rat"));
|
||||
assertTrue(plugin.isPossibleConnectionFilename("abcdefgh.dat"));
|
||||
assertTrue(plugin.isPossibleConnectionFilename("ABCDEFGH.DAT"));
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReaderIsCreated() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final SimplexPluginCallback callback =
|
||||
context.mock(SimplexPluginCallback.class);
|
||||
final RemovableDriveFinder finder =
|
||||
context.mock(RemovableDriveFinder.class);
|
||||
final RemovableDriveMonitor monitor =
|
||||
context.mock(RemovableDriveMonitor.class);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(monitor).start(with(any(Callback.class)));
|
||||
oneOf(callback).readerCreated(with(any(FileTransportReader.class)));
|
||||
}});
|
||||
|
||||
RemovableDrivePlugin plugin = new RemovableDrivePlugin(
|
||||
new ImmediateExecutor(), callback, finder, monitor);
|
||||
plugin.start();
|
||||
|
||||
File f = new File(testDir, "abcdefgh.dat");
|
||||
OutputStream out = new FileOutputStream(f);
|
||||
out.write(new byte[MIN_CONNECTION_LENGTH]);
|
||||
out.flush();
|
||||
out.close();
|
||||
assertEquals(MIN_CONNECTION_LENGTH, f.length());
|
||||
plugin.driveInserted(testDir);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package net.sf.briar.plugins.file;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
|
||||
import net.sf.briar.util.OsUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnixRemovableDriveMonitorTest extends BriarTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonexistentDir() throws Exception {
|
||||
if(!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) {
|
||||
System.err.println("Warning: Skipping test");
|
||||
return;
|
||||
}
|
||||
File doesNotExist = new File(testDir, "doesNotExist");
|
||||
RemovableDriveMonitor monitor = createMonitor(doesNotExist);
|
||||
monitor.start(new Callback() {
|
||||
|
||||
public void driveInserted(File root) {
|
||||
fail();
|
||||
}
|
||||
|
||||
public void exceptionThrown(IOException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
monitor.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneCallbackPerFile() throws Exception {
|
||||
if(!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) {
|
||||
System.err.println("Warning: Skipping test");
|
||||
return;
|
||||
}
|
||||
// Create a callback that will wait for two files before stopping
|
||||
final List<File> detected = new ArrayList<File>();
|
||||
final CountDownLatch latch = new CountDownLatch(2);
|
||||
final Callback callback = new Callback() {
|
||||
|
||||
public void driveInserted(File f) {
|
||||
detected.add(f);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public void exceptionThrown(IOException e) {
|
||||
fail();
|
||||
}
|
||||
};
|
||||
// Create the monitor and start it
|
||||
RemovableDriveMonitor monitor = createMonitor(testDir);
|
||||
monitor.start(callback);
|
||||
// Create two files in the test directory
|
||||
File file1 = new File(testDir, "1");
|
||||
File file2 = new File(testDir, "2");
|
||||
assertTrue(file1.createNewFile());
|
||||
assertTrue(file2.createNewFile());
|
||||
// Wait for the monitor to detect the files
|
||||
assertTrue(latch.await(5, SECONDS));
|
||||
monitor.stop();
|
||||
// Check that both files were detected
|
||||
assertEquals(2, detected.size());
|
||||
assertTrue(detected.contains(file1));
|
||||
assertTrue(detected.contains(file2));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private RemovableDriveMonitor createMonitor(final File dir) {
|
||||
return new UnixRemovableDriveMonitor() {
|
||||
@Override
|
||||
protected String[] getPathsToWatch() {
|
||||
return new String[] { dir.getPath() };
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.sf.briar.plugins.tcp;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.plugins.DuplexClientTest;
|
||||
import net.sf.briar.plugins.tcp.LanTcpPlugin;
|
||||
|
||||
// This is not a JUnit test - it has to be run manually while the server test
|
||||
// is running on another machine
|
||||
public class LanTcpClientTest extends DuplexClientTest {
|
||||
|
||||
private LanTcpClientTest(Executor executor, String serverAddress,
|
||||
String serverPort) {
|
||||
// Store the server's internal address and port
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("internal", serverAddress);
|
||||
p.put("port", serverPort);
|
||||
Map<ContactId, TransportProperties> remote =
|
||||
Collections.singletonMap(contactId, p);
|
||||
// Create the plugin
|
||||
callback = new ClientCallback(new TransportConfig(),
|
||||
new TransportProperties(), remote);
|
||||
plugin = new LanTcpPlugin(executor, callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if(args.length != 2) {
|
||||
System.err.println("Please specify the server's address and port");
|
||||
System.exit(1);
|
||||
}
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
try {
|
||||
new LanTcpClientTest(executor, args[0], args[1]).run();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
142
briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java
Normal file
142
briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package net.sf.briar.plugins.tcp;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
import net.sf.briar.plugins.tcp.LanTcpPlugin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class LanTcpPluginTest extends BriarTestCase {
|
||||
|
||||
private final ContactId contactId = new ContactId(234);
|
||||
|
||||
@Test
|
||||
public void testIncomingConnection() throws Exception {
|
||||
Callback callback = new Callback();
|
||||
callback.local.put("address", "127.0.0.1");
|
||||
callback.local.put("port", "0");
|
||||
Executor e = Executors.newCachedThreadPool();
|
||||
DuplexPlugin plugin = new LanTcpPlugin(e, callback, 0L);
|
||||
plugin.start();
|
||||
// The plugin should have bound a socket and stored the port number
|
||||
assertTrue(callback.propertiesLatch.await(5, SECONDS));
|
||||
String host = callback.local.get("address");
|
||||
assertNotNull(host);
|
||||
assertEquals("127.0.0.1", host);
|
||||
String portString = callback.local.get("port");
|
||||
assertNotNull(portString);
|
||||
int port = Integer.valueOf(portString);
|
||||
assertTrue(port > 0 && port < 65536);
|
||||
// The plugin should be listening on the port
|
||||
InetSocketAddress addr = new InetSocketAddress(host, port);
|
||||
Socket s = new Socket();
|
||||
s.connect(addr, 100);
|
||||
assertTrue(callback.connectionsLatch.await(5, SECONDS));
|
||||
s.close();
|
||||
// Stop the plugin
|
||||
plugin.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutgoingConnection() throws Exception {
|
||||
Callback callback = new Callback();
|
||||
Executor e = Executors.newCachedThreadPool();
|
||||
DuplexPlugin plugin = new LanTcpPlugin(e, callback, 0L);
|
||||
plugin.start();
|
||||
// Listen on a local port
|
||||
final ServerSocket ss = new ServerSocket();
|
||||
ss.bind(new InetSocketAddress("127.0.0.1", 0), 10);
|
||||
int port = ss.getLocalPort();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicBoolean error = new AtomicBoolean(false);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ss.accept();
|
||||
latch.countDown();
|
||||
} catch(IOException e) {
|
||||
error.set(true);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
// Tell the plugin about the port
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("address", "127.0.0.1");
|
||||
p.put("port", String.valueOf(port));
|
||||
callback.remote.put(contactId, p);
|
||||
// Connect to the port
|
||||
DuplexTransportConnection d = plugin.createConnection(contactId);
|
||||
assertNotNull(d);
|
||||
// Check that the connection was accepted
|
||||
assertTrue(latch.await(5, SECONDS));
|
||||
assertFalse(error.get());
|
||||
// Clean up
|
||||
d.dispose(false, true);
|
||||
ss.close();
|
||||
plugin.stop();
|
||||
}
|
||||
|
||||
private static class Callback implements DuplexPluginCallback {
|
||||
|
||||
private final Map<ContactId, TransportProperties> remote =
|
||||
new Hashtable<ContactId, TransportProperties>();
|
||||
private final CountDownLatch propertiesLatch = new CountDownLatch(1);
|
||||
private final CountDownLatch connectionsLatch = new CountDownLatch(1);
|
||||
private final TransportProperties local = new TransportProperties();
|
||||
|
||||
public TransportConfig getConfig() {
|
||||
return new TransportConfig();
|
||||
}
|
||||
|
||||
public TransportProperties getLocalProperties() {
|
||||
return local;
|
||||
}
|
||||
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void mergeConfig(TransportConfig c) {}
|
||||
|
||||
public void mergeLocalProperties(TransportProperties p) {
|
||||
local.putAll(p);
|
||||
propertiesLatch.countDown();
|
||||
}
|
||||
|
||||
public int showChoice(String[] options, String... message) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean showConfirmationMessage(String... message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showMessage(String... message) {}
|
||||
|
||||
public void incomingConnectionCreated(DuplexTransportConnection d) {
|
||||
connectionsLatch.countDown();
|
||||
}
|
||||
|
||||
public void outgoingConnectionCreated(ContactId c,
|
||||
DuplexTransportConnection d) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.sf.briar.plugins.tcp;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.plugins.DuplexServerTest;
|
||||
import net.sf.briar.plugins.tcp.LanTcpPlugin;
|
||||
|
||||
// This is not a JUnit test - it has to be run manually while the client test
|
||||
// is running on another machine
|
||||
public class LanTcpServerTest extends DuplexServerTest {
|
||||
|
||||
private LanTcpServerTest(Executor executor) {
|
||||
callback = new ServerCallback(new TransportConfig(),
|
||||
new TransportProperties(),
|
||||
Collections.singletonMap(contactId, new TransportProperties()));
|
||||
plugin = new LanTcpPlugin(executor, callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
try {
|
||||
new LanTcpServerTest(executor).run();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
175
briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java
Normal file
175
briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java
Normal file
@@ -0,0 +1,175 @@
|
||||
package net.sf.briar.plugins.tor;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.TransportConfig;
|
||||
import net.sf.briar.api.TransportProperties;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TorPluginTest extends BriarTestCase {
|
||||
|
||||
private final ContactId contactId = new ContactId(234);
|
||||
|
||||
@Test
|
||||
public void testHiddenService() throws Exception {
|
||||
System.err.println("======== testHiddenService ========");
|
||||
Executor e = Executors.newCachedThreadPool();
|
||||
TorPlugin serverPlugin = null, clientPlugin = null;
|
||||
try {
|
||||
// Create a plugin instance for the server
|
||||
Callback serverCallback = new Callback();
|
||||
serverPlugin = new TorPlugin(e, serverCallback, 0L);
|
||||
System.out.println("Starting server plugin");
|
||||
serverPlugin.start();
|
||||
// The plugin should create a hidden service... eventually
|
||||
assertTrue(serverCallback.latch.await(600, SECONDS));
|
||||
System.out.println("Started server plugin");
|
||||
String onion = serverCallback.local.get("onion");
|
||||
assertNotNull(onion);
|
||||
assertTrue(onion.endsWith(".onion"));
|
||||
// Create another plugin instance for the client
|
||||
Callback clientCallback = new Callback();
|
||||
clientCallback.config.put("noHiddenService", "");
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put("onion", onion);
|
||||
clientCallback.remote.put(contactId, p);
|
||||
clientPlugin = new TorPlugin(e, clientCallback, 0L);
|
||||
System.out.println("Starting client plugin");
|
||||
clientPlugin.start();
|
||||
// The plugin should start without creating a hidden service
|
||||
assertTrue(clientCallback.latch.await(600, SECONDS));
|
||||
System.out.println("Started client plugin");
|
||||
// Connect to the server's hidden service
|
||||
System.out.println("Connecting to hidden service");
|
||||
DuplexTransportConnection clientEnd =
|
||||
clientPlugin.createConnection(contactId);
|
||||
assertNotNull(clientEnd);
|
||||
DuplexTransportConnection serverEnd =
|
||||
serverCallback.incomingConnection;
|
||||
assertNotNull(serverEnd);
|
||||
System.out.println("Connected to hidden service");
|
||||
// Send some data through the Tor connection
|
||||
PrintStream out = new PrintStream(clientEnd.getOutputStream());
|
||||
out.println("Hello world");
|
||||
out.flush();
|
||||
Scanner in = new Scanner(serverEnd.getInputStream());
|
||||
assertTrue(in.hasNextLine());
|
||||
assertEquals("Hello world", in.nextLine());
|
||||
serverEnd.dispose(false, false);
|
||||
clientEnd.dispose(false, false);
|
||||
} finally {
|
||||
// Stop the plugins
|
||||
System.out.println("Stopping plugins");
|
||||
if(serverPlugin != null) serverPlugin.stop();
|
||||
if(clientPlugin != null) clientPlugin.stop();
|
||||
System.out.println("Stopped plugins");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreAndRetrievePrivateKey() throws Exception {
|
||||
System.err.println("======== testStoreAndRetrievePrivateKey ========");
|
||||
Executor e = Executors.newCachedThreadPool();
|
||||
TorPlugin plugin = null;
|
||||
try {
|
||||
// Start a plugin instance with no private key
|
||||
Callback callback = new Callback();
|
||||
plugin = new TorPlugin(e, callback, 0L);
|
||||
System.out.println("Starting plugin without private key");
|
||||
plugin.start();
|
||||
// The plugin should create a hidden service... eventually
|
||||
assertTrue(callback.latch.await(600, SECONDS));
|
||||
System.out.println("Started plugin");
|
||||
String onion = callback.local.get("onion");
|
||||
assertNotNull(onion);
|
||||
assertTrue(onion.endsWith(".onion"));
|
||||
// Get the PEM-encoded private key
|
||||
String privateKey = callback.config.get("privateKey");
|
||||
assertNotNull(privateKey);
|
||||
// Stop the plugin
|
||||
System.out.println("Stopping plugin");
|
||||
plugin.stop();
|
||||
System.out.println("Stopped plugin");
|
||||
// Start another instance, reusing the private key
|
||||
callback = new Callback();
|
||||
callback.config.put("privateKey", privateKey);
|
||||
plugin = new TorPlugin(e, callback, 0L);
|
||||
System.out.println("Starting plugin with private key");
|
||||
plugin.start();
|
||||
// The plugin should create a hidden service... eventually
|
||||
assertTrue(callback.latch.await(600, SECONDS));
|
||||
System.out.println("Started plugin");
|
||||
// The onion URL should be the same
|
||||
assertEquals(onion, callback.local.get("onion"));
|
||||
// The private key should be the same
|
||||
assertEquals(privateKey, callback.config.get("privateKey"));
|
||||
} finally {
|
||||
// Stop the plugin
|
||||
System.out.println("Stopping plugin");
|
||||
if(plugin != null) plugin.stop();
|
||||
System.out.println("Stopped plugin");
|
||||
}
|
||||
}
|
||||
|
||||
private static class Callback implements DuplexPluginCallback {
|
||||
|
||||
private final Map<ContactId, TransportProperties> remote =
|
||||
new Hashtable<ContactId, TransportProperties>();
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
private final TransportConfig config = new TransportConfig();
|
||||
private final TransportProperties local = new TransportProperties();
|
||||
|
||||
private volatile DuplexTransportConnection incomingConnection = null;
|
||||
|
||||
public TransportConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public TransportProperties getLocalProperties() {
|
||||
return local;
|
||||
}
|
||||
|
||||
public Map<ContactId, TransportProperties> getRemoteProperties() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void mergeConfig(TransportConfig c) {
|
||||
config.putAll(c);
|
||||
}
|
||||
|
||||
public void mergeLocalProperties(TransportProperties p) {
|
||||
local.putAll(p);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public int showChoice(String[] options, String... message) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean showConfirmationMessage(String... message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void showMessage(String... message) {}
|
||||
|
||||
public void incomingConnectionCreated(DuplexTransportConnection d) {
|
||||
incomingConnection = d;
|
||||
}
|
||||
|
||||
public void outgoingConnectionCreated(ContactId c,
|
||||
DuplexTransportConnection d) {}
|
||||
}
|
||||
}
|
||||
124
briar-tests/src/net/sf/briar/protocol/AckReaderTest.java
Normal file
124
briar-tests/src/net/sf/briar/protocol/AckReaderTest.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.SerialComponent;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class AckReaderTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final SerialComponent serial;
|
||||
private final ReaderFactory readerFactory;
|
||||
private final WriterFactory writerFactory;
|
||||
private final Mockery context;
|
||||
|
||||
public AckReaderTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new SerialModule());
|
||||
serial = i.getInstance(SerialComponent.class);
|
||||
readerFactory = i.getInstance(ReaderFactory.class);
|
||||
writerFactory = i.getInstance(WriterFactory.class);
|
||||
context = new Mockery();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatExceptionIfAckIsTooLarge() throws Exception {
|
||||
PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
AckReader ackReader = new AckReader(packetFactory);
|
||||
|
||||
byte[] b = createAck(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.ACK, ackReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.ACK, Ack.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
AckReader ackReader = new AckReader(packetFactory);
|
||||
final Ack ack = context.mock(Ack.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(packetFactory).createAck(with(any(Collection.class)));
|
||||
will(returnValue(ack));
|
||||
}});
|
||||
|
||||
byte[] b = createAck(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.ACK, ackReader);
|
||||
|
||||
assertEquals(ack, reader.readStruct(Types.ACK, Ack.class));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyAck() throws Exception {
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
AckReader ackReader = new AckReader(packetFactory);
|
||||
|
||||
byte[] b = createEmptyAck();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.ACK, ackReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.ACK, Ack.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private byte[] createAck(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.ACK);
|
||||
w.writeListStart();
|
||||
while(out.size() + serial.getSerialisedUniqueIdLength()
|
||||
< MAX_PACKET_LENGTH) {
|
||||
w.writeBytes(TestUtils.getRandomId());
|
||||
}
|
||||
if(tooBig) w.writeBytes(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createEmptyAck() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.ACK);
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
137
briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java
Normal file
137
briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.protocol.UnverifiedBatch;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class BatchReaderTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final ReaderFactory readerFactory;
|
||||
private final WriterFactory writerFactory;
|
||||
private final Mockery context;
|
||||
private final UnverifiedMessage message;
|
||||
private final StructReader<UnverifiedMessage> messageReader;
|
||||
|
||||
public BatchReaderTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new SerialModule());
|
||||
readerFactory = i.getInstance(ReaderFactory.class);
|
||||
writerFactory = i.getInstance(WriterFactory.class);
|
||||
context = new Mockery();
|
||||
message = context.mock(UnverifiedMessage.class);
|
||||
messageReader = new TestMessageReader();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatExceptionIfBatchIsTooLarge() throws Exception {
|
||||
UnverifiedBatchFactory batchFactory =
|
||||
context.mock(UnverifiedBatchFactory.class);
|
||||
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
|
||||
|
||||
byte[] b = createBatch(MAX_PACKET_LENGTH + 1);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.BATCH, batchReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.BATCH, UnverifiedBatch.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoFormatExceptionIfBatchIsMaximumSize() throws Exception {
|
||||
final UnverifiedBatchFactory batchFactory =
|
||||
context.mock(UnverifiedBatchFactory.class);
|
||||
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
|
||||
final UnverifiedBatch batch = context.mock(UnverifiedBatch.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(batchFactory).createUnverifiedBatch(
|
||||
Collections.singletonList(message));
|
||||
will(returnValue(batch));
|
||||
}});
|
||||
|
||||
byte[] b = createBatch(MAX_PACKET_LENGTH);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.BATCH, batchReader);
|
||||
|
||||
assertEquals(batch, reader.readStruct(Types.BATCH,
|
||||
UnverifiedBatch.class));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyBatch() throws Exception {
|
||||
final UnverifiedBatchFactory batchFactory =
|
||||
context.mock(UnverifiedBatchFactory.class);
|
||||
BatchReader batchReader = new BatchReader(messageReader, batchFactory);
|
||||
|
||||
byte[] b = createEmptyBatch();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.BATCH, batchReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.BATCH, UnverifiedBatch.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private byte[] createBatch(int size) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(size);
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.BATCH);
|
||||
w.writeListStart();
|
||||
// We're using a fake message reader, so it's OK to use a fake message
|
||||
w.writeStructId(Types.MESSAGE);
|
||||
w.writeBytes(new byte[size - 10]);
|
||||
w.writeListEnd();
|
||||
byte[] b = out.toByteArray();
|
||||
assertEquals(size, b.length);
|
||||
return b;
|
||||
}
|
||||
|
||||
private byte[] createEmptyBatch() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.BATCH);
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private class TestMessageReader implements StructReader<UnverifiedMessage> {
|
||||
|
||||
public UnverifiedMessage readStruct(Reader r) throws IOException {
|
||||
r.readStructId(Types.MESSAGE);
|
||||
r.readBytes();
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
193
briar-tests/src/net/sf/briar/protocol/ConstantsTest.java
Normal file
193
briar-tests/src/net/sf/briar/protocol/ConstantsTest.java
Normal file
@@ -0,0 +1,193 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_BODY_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_GROUP_NAME_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_SUBJECT_LENGTH;
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.Author;
|
||||
import net.sf.briar.api.protocol.AuthorFactory;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageFactory;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriter;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ConstantsTest extends BriarTestCase {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final GroupFactory groupFactory;
|
||||
private final AuthorFactory authorFactory;
|
||||
private final MessageFactory messageFactory;
|
||||
private final PacketFactory packetFactory;
|
||||
private final ProtocolWriterFactory protocolWriterFactory;
|
||||
|
||||
public ConstantsTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule(),
|
||||
new ProtocolModule(), new SerialModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
groupFactory = i.getInstance(GroupFactory.class);
|
||||
authorFactory = i.getInstance(AuthorFactory.class);
|
||||
messageFactory = i.getInstance(MessageFactory.class);
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchesFitIntoLargeAck() throws Exception {
|
||||
testBatchesFitIntoAck(MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchesFitIntoSmallAck() throws Exception {
|
||||
testBatchesFitIntoAck(1000);
|
||||
}
|
||||
|
||||
private void testBatchesFitIntoAck(int length) throws Exception {
|
||||
// Create an ack with as many batch IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
int maxBatches = writer.getMaxBatchesForAck(length);
|
||||
Collection<BatchId> acked = new ArrayList<BatchId>();
|
||||
for(int i = 0; i < maxBatches; i++) {
|
||||
acked.add(new BatchId(TestUtils.getRandomId()));
|
||||
}
|
||||
Ack a = packetFactory.createAck(acked);
|
||||
writer.writeAck(a);
|
||||
// Check the size of the serialised ack
|
||||
assertTrue(out.size() <= length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageFitsIntoBatch() throws Exception {
|
||||
// Create a maximum-length group
|
||||
String groupName = createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Group group = groupFactory.createGroup(groupName, groupPublic);
|
||||
// Create a maximum-length author
|
||||
String authorName = createRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Author author = authorFactory.createAuthor(authorName, authorPublic);
|
||||
// Create a maximum-length message
|
||||
PrivateKey groupPrivate = crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey authorPrivate = crypto.generateSignatureKeyPair().getPrivate();
|
||||
String subject = createRandomString(MAX_SUBJECT_LENGTH);
|
||||
byte[] body = new byte[MAX_BODY_LENGTH];
|
||||
Message message = messageFactory.createMessage(null, group,
|
||||
groupPrivate, author, authorPrivate, subject, body);
|
||||
// Add the message to a batch
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MAX_PACKET_LENGTH);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
RawBatch b = packetFactory.createBatch(Collections.singletonList(
|
||||
message.getSerialised()));
|
||||
writer.writeBatch(b);
|
||||
// Check the size of the serialised batch
|
||||
assertTrue(out.size() > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH);
|
||||
assertTrue(out.size() <= MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagesFitIntoLargeOffer() throws Exception {
|
||||
testMessagesFitIntoOffer(MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessagesFitIntoSmallOffer() throws Exception {
|
||||
testMessagesFitIntoOffer(1000);
|
||||
}
|
||||
|
||||
private void testMessagesFitIntoOffer(int length) throws Exception {
|
||||
// Create an offer with as many message IDs as possible
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(length);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
int maxMessages = writer.getMaxMessagesForOffer(length);
|
||||
Collection<MessageId> offered = new ArrayList<MessageId>();
|
||||
for(int i = 0; i < maxMessages; i++) {
|
||||
offered.add(new MessageId(TestUtils.getRandomId()));
|
||||
}
|
||||
Offer o = packetFactory.createOffer(offered);
|
||||
writer.writeOffer(o);
|
||||
// Check the size of the serialised offer
|
||||
assertTrue(out.size() <= length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransportsFitIntoUpdate() throws Exception {
|
||||
// Create the maximum number of plugins, each with the maximum number
|
||||
// of maximum-length properties
|
||||
Collection<Transport> transports = new ArrayList<Transport>();
|
||||
for(int i = 0; i < MAX_TRANSPORTS; i++) {
|
||||
TransportId id = new TransportId(TestUtils.getRandomId());
|
||||
Transport t = new Transport(id);
|
||||
for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) {
|
||||
String key = createRandomString(MAX_PROPERTY_LENGTH);
|
||||
String value = createRandomString(MAX_PROPERTY_LENGTH);
|
||||
t.put(key, value);
|
||||
}
|
||||
transports.add(t);
|
||||
}
|
||||
// Add the transports to an update
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MAX_PACKET_LENGTH);
|
||||
ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out,
|
||||
true);
|
||||
TransportUpdate t = packetFactory.createTransportUpdate(transports,
|
||||
Long.MAX_VALUE);
|
||||
writer.writeTransportUpdate(t);
|
||||
// Check the size of the serialised update
|
||||
assertTrue(out.size() > MAX_TRANSPORTS * (UniqueId.LENGTH + 4
|
||||
+ (MAX_PROPERTIES_PER_TRANSPORT * MAX_PROPERTY_LENGTH * 2))
|
||||
+ 8);
|
||||
assertTrue(out.size() <= MAX_PACKET_LENGTH);
|
||||
}
|
||||
|
||||
private static String createRandomString(int length) throws Exception {
|
||||
StringBuilder s = new StringBuilder(length);
|
||||
for(int i = 0; i < length; i++) {
|
||||
int digit = (int) (Math.random() * 10);
|
||||
s.append((char) ('0' + digit));
|
||||
}
|
||||
String string = s.toString();
|
||||
assertEquals(length, string.getBytes("UTF-8").length);
|
||||
return string;
|
||||
}
|
||||
}
|
||||
105
briar-tests/src/net/sf/briar/protocol/ConsumersTest.java
Normal file
105
briar-tests/src/net/sf/briar/protocol/ConsumersTest.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.crypto.MessageDigest;
|
||||
import net.sf.briar.api.serial.CopyingConsumer;
|
||||
import net.sf.briar.api.serial.CountingConsumer;
|
||||
import net.sf.briar.api.serial.DigestingConsumer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConsumersTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testDigestingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
// Generate some random data and digest it
|
||||
new Random().nextBytes(data);
|
||||
MessageDigest messageDigest = new TestMessageDigest();
|
||||
messageDigest.update(data);
|
||||
byte[] dig = messageDigest.digest();
|
||||
// Check that feeding a DigestingConsumer generates the same digest
|
||||
DigestingConsumer dc = new DigestingConsumer(messageDigest);
|
||||
dc.write(data[0]);
|
||||
dc.write(data, 1, data.length - 2);
|
||||
dc.write(data[data.length - 1]);
|
||||
byte[] dig1 = messageDigest.digest();
|
||||
assertArrayEquals(dig, dig1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCountingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
CountingConsumer cc = new CountingConsumer(data.length);
|
||||
cc.write(data[0]);
|
||||
cc.write(data, 1, data.length - 2);
|
||||
cc.write(data[data.length - 1]);
|
||||
assertEquals(data.length, cc.getCount());
|
||||
try {
|
||||
cc.write((byte) 0);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
new Random().nextBytes(data);
|
||||
// Check that a CopyingConsumer creates a faithful copy
|
||||
CopyingConsumer cc = new CopyingConsumer();
|
||||
cc.write(data[0]);
|
||||
cc.write(data, 1, data.length - 2);
|
||||
cc.write(data[data.length - 1]);
|
||||
assertArrayEquals(data, cc.getCopy());
|
||||
}
|
||||
|
||||
private static class TestMessageDigest implements MessageDigest {
|
||||
|
||||
private final java.security.MessageDigest delegate;
|
||||
|
||||
private TestMessageDigest() throws GeneralSecurityException {
|
||||
delegate = java.security.MessageDigest.getInstance("SHA-256");
|
||||
}
|
||||
|
||||
public byte[] digest() {
|
||||
return delegate.digest();
|
||||
}
|
||||
|
||||
public byte[] digest(byte[] input) {
|
||||
return delegate.digest(input);
|
||||
}
|
||||
|
||||
public int digest(byte[] buf, int offset, int len) {
|
||||
byte[] digest = digest();
|
||||
len = Math.min(len, digest.length);
|
||||
System.arraycopy(digest, 0, buf, offset, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
public int getDigestLength() {
|
||||
return delegate.getDigestLength();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
delegate.reset();
|
||||
}
|
||||
|
||||
public void update(byte input) {
|
||||
delegate.update(input);
|
||||
}
|
||||
|
||||
public void update(byte[] input) {
|
||||
delegate.update(input);
|
||||
}
|
||||
|
||||
public void update(byte[] input, int offset, int len) {
|
||||
delegate.update(input, offset, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java
Normal file
124
briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java
Normal file
@@ -0,0 +1,124 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.SerialComponent;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class OfferReaderTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final SerialComponent serial;
|
||||
private final ReaderFactory readerFactory;
|
||||
private final WriterFactory writerFactory;
|
||||
private final Mockery context;
|
||||
|
||||
public OfferReaderTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new SerialModule());
|
||||
serial = i.getInstance(SerialComponent.class);
|
||||
readerFactory = i.getInstance(ReaderFactory.class);
|
||||
writerFactory = i.getInstance(WriterFactory.class);
|
||||
context = new Mockery();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatExceptionIfOfferIsTooLarge() throws Exception {
|
||||
PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
OfferReader offerReader = new OfferReader(packetFactory);
|
||||
|
||||
byte[] b = createOffer(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.OFFER, offerReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.OFFER, Offer.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception {
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
OfferReader offerReader = new OfferReader(packetFactory);
|
||||
final Offer offer = context.mock(Offer.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(packetFactory).createOffer(with(any(Collection.class)));
|
||||
will(returnValue(offer));
|
||||
}});
|
||||
|
||||
byte[] b = createOffer(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.OFFER, offerReader);
|
||||
|
||||
assertEquals(offer, reader.readStruct(Types.OFFER, Offer.class));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyOffer() throws Exception {
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
OfferReader offerReader = new OfferReader(packetFactory);
|
||||
|
||||
byte[] b = createEmptyOffer();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.OFFER, offerReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.OFFER, Offer.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private byte[] createOffer(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.OFFER);
|
||||
w.writeListStart();
|
||||
while(out.size() + serial.getSerialisedUniqueIdLength()
|
||||
< MAX_PACKET_LENGTH) {
|
||||
w.writeBytes(TestUtils.getRandomId());
|
||||
}
|
||||
if(tooBig) w.writeBytes(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createEmptyOffer() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.OFFER);
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupFactory;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageFactory;
|
||||
import net.sf.briar.api.protocol.Offer;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolReader;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriter;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.SubscriptionUpdate;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
private final ProtocolReaderFactory readerFactory;
|
||||
private final ProtocolWriterFactory writerFactory;
|
||||
private final PacketFactory packetFactory;
|
||||
private final BatchId batchId;
|
||||
private final Group group;
|
||||
private final Message message;
|
||||
private final String subject = "Hello";
|
||||
private final String messageBody = "Hello world";
|
||||
private final BitSet bitSet;
|
||||
private final Map<Group, Long> subscriptions;
|
||||
private final Collection<Transport> transports;
|
||||
private final long timestamp = System.currentTimeMillis();
|
||||
|
||||
public ProtocolIntegrationTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule(),
|
||||
new ProtocolModule(), new SerialModule());
|
||||
readerFactory = i.getInstance(ProtocolReaderFactory.class);
|
||||
writerFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
batchId = new BatchId(TestUtils.getRandomId());
|
||||
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
|
||||
group = groupFactory.createGroup("Unrestricted group", null);
|
||||
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
||||
message = messageFactory.createMessage(null, group, subject,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
bitSet = new BitSet();
|
||||
bitSet.set(3);
|
||||
bitSet.set(7);
|
||||
subscriptions = Collections.singletonMap(group, 123L);
|
||||
TransportId transportId = new TransportId(TestUtils.getRandomId());
|
||||
Transport transport = new Transport(transportId,
|
||||
Collections.singletonMap("bar", "baz"));
|
||||
transports = Collections.singletonList(transport);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() throws Exception {
|
||||
// Write
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ProtocolWriter writer = writerFactory.createProtocolWriter(out, true);
|
||||
|
||||
Ack a = packetFactory.createAck(Collections.singletonList(batchId));
|
||||
writer.writeAck(a);
|
||||
|
||||
RawBatch b = packetFactory.createBatch(Collections.singletonList(
|
||||
message.getSerialised()));
|
||||
writer.writeBatch(b);
|
||||
|
||||
Offer o = packetFactory.createOffer(Collections.singletonList(
|
||||
message.getId()));
|
||||
writer.writeOffer(o);
|
||||
|
||||
Request r = packetFactory.createRequest(bitSet, 10);
|
||||
writer.writeRequest(r);
|
||||
|
||||
SubscriptionUpdate s = packetFactory.createSubscriptionUpdate(
|
||||
Collections.<GroupId, GroupId>emptyMap(), subscriptions, 0L,
|
||||
timestamp);
|
||||
writer.writeSubscriptionUpdate(s);
|
||||
|
||||
TransportUpdate t = packetFactory.createTransportUpdate(transports,
|
||||
timestamp);
|
||||
writer.writeTransportUpdate(t);
|
||||
|
||||
// Read
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
ProtocolReader reader = readerFactory.createProtocolReader(in);
|
||||
|
||||
a = reader.readAck();
|
||||
assertEquals(Collections.singletonList(batchId), a.getBatchIds());
|
||||
|
||||
Batch b1 = reader.readBatch().verify();
|
||||
assertEquals(Collections.singletonList(message), b1.getMessages());
|
||||
|
||||
o = reader.readOffer();
|
||||
assertEquals(Collections.singletonList(message.getId()),
|
||||
o.getMessageIds());
|
||||
|
||||
r = reader.readRequest();
|
||||
assertEquals(bitSet, r.getBitmap());
|
||||
assertEquals(10, r.getLength());
|
||||
|
||||
s = reader.readSubscriptionUpdate();
|
||||
assertEquals(subscriptions, s.getSubscriptions());
|
||||
assertEquals(timestamp, s.getTimestamp());
|
||||
|
||||
t = reader.readTransportUpdate();
|
||||
assertEquals(transports, t.getTransports());
|
||||
assertEquals(timestamp, t.getTimestamp());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriter;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.serial.SerialComponent;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class ProtocolWriterImplTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final PacketFactory packetFactory;
|
||||
private final SerialComponent serial;
|
||||
private final WriterFactory writerFactory;
|
||||
|
||||
public ProtocolWriterImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule(),
|
||||
new ProtocolModule(), new SerialModule());
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
serial = i.getInstance(SerialComponent.class);
|
||||
writerFactory = i.getInstance(WriterFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteBitmapNoPadding() throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ProtocolWriter w = new ProtocolWriterImpl(serial, writerFactory, out,
|
||||
true);
|
||||
BitSet b = new BitSet();
|
||||
// 11011001 = 0xD9
|
||||
b.set(0);
|
||||
b.set(1);
|
||||
b.set(3);
|
||||
b.set(4);
|
||||
b.set(7);
|
||||
// 01011001 = 0x59
|
||||
b.set(9);
|
||||
b.set(11);
|
||||
b.set(12);
|
||||
b.set(15);
|
||||
Request r = packetFactory.createRequest(b, 16);
|
||||
w.writeRequest(r);
|
||||
// Short user tag 6, 0 as uint7, short bytes with length 2, 0xD959
|
||||
byte[] output = out.toByteArray();
|
||||
assertEquals("C6" + "00" + "92" + "D959",
|
||||
StringUtils.toHexString(output));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteBitmapWithPadding() throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ProtocolWriter w = new ProtocolWriterImpl(serial, writerFactory, out,
|
||||
true);
|
||||
BitSet b = new BitSet();
|
||||
// 01011001 = 0x59
|
||||
b.set(1);
|
||||
b.set(3);
|
||||
b.set(4);
|
||||
b.set(7);
|
||||
// 11011xxx = 0xD8, after padding
|
||||
b.set(8);
|
||||
b.set(9);
|
||||
b.set(11);
|
||||
b.set(12);
|
||||
Request r = packetFactory.createRequest(b, 13);
|
||||
w.writeRequest(r);
|
||||
// Short user tag 6, 3 as uint7, short bytes with length 2, 0x59D8
|
||||
byte[] output = out.toByteArray();
|
||||
assertEquals("C6" + "03" + "92" + "59D8",
|
||||
StringUtils.toHexString(output));
|
||||
}
|
||||
}
|
||||
146
briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java
Normal file
146
briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.BitSet;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.protocol.PacketFactory;
|
||||
import net.sf.briar.api.protocol.Request;
|
||||
import net.sf.briar.api.protocol.Types;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.api.serial.ReaderFactory;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class RequestReaderTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final ReaderFactory readerFactory;
|
||||
private final WriterFactory writerFactory;
|
||||
private final PacketFactory packetFactory;
|
||||
private final Mockery context;
|
||||
|
||||
public RequestReaderTest() throws Exception {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule(),
|
||||
new ProtocolModule(), new SerialModule());
|
||||
readerFactory = i.getInstance(ReaderFactory.class);
|
||||
writerFactory = i.getInstance(WriterFactory.class);
|
||||
packetFactory = i.getInstance(PacketFactory.class);
|
||||
context = new Mockery();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatExceptionIfRequestIsTooLarge() throws Exception {
|
||||
PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
RequestReader requestReader = new RequestReader(packetFactory);
|
||||
|
||||
byte[] b = createRequest(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.REQUEST, requestReader);
|
||||
|
||||
try {
|
||||
reader.readStruct(Types.REQUEST, Request.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception {
|
||||
final PacketFactory packetFactory = context.mock(PacketFactory.class);
|
||||
RequestReader requestReader = new RequestReader(packetFactory);
|
||||
final Request request = context.mock(Request.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(packetFactory).createRequest(with(any(BitSet.class)),
|
||||
with(any(int.class)));
|
||||
will(returnValue(request));
|
||||
}});
|
||||
|
||||
byte[] b = createRequest(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
reader.addStructReader(Types.REQUEST, requestReader);
|
||||
|
||||
assertEquals(request, reader.readStruct(Types.REQUEST,
|
||||
Request.class));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitmapDecoding() throws Exception {
|
||||
// Test sizes from 0 to 1000 bits
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
// Create a BitSet of size i with one in ten bits set (on average)
|
||||
BitSet requested = new BitSet(i);
|
||||
for(int j = 0; j < i; j++) if(Math.random() < 0.1) requested.set(j);
|
||||
// Encode the BitSet as a bitmap
|
||||
int bytes = i % 8 == 0 ? i / 8 : i / 8 + 1;
|
||||
byte[] bitmap = new byte[bytes];
|
||||
for(int j = 0; j < i; j++) {
|
||||
if(requested.get(j)) {
|
||||
int offset = j / 8;
|
||||
byte bit = (byte) (128 >> j % 8);
|
||||
bitmap[offset] |= bit;
|
||||
}
|
||||
}
|
||||
// Create a serialised request containing the bitmap
|
||||
byte[] b = createRequest(bitmap);
|
||||
// Deserialise the request
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
Reader reader = readerFactory.createReader(in);
|
||||
RequestReader requestReader = new RequestReader(packetFactory);
|
||||
reader.addStructReader(Types.REQUEST, requestReader);
|
||||
Request r = reader.readStruct(Types.REQUEST, Request.class);
|
||||
BitSet decoded = r.getBitmap();
|
||||
// Check that the decoded BitSet matches the original - we can't
|
||||
// use equals() because of padding, but the first i bits should
|
||||
// match and the cardinalities should be equal, indicating that no
|
||||
// padding bits are set
|
||||
for(int j = 0; j < i; j++) {
|
||||
assertEquals(requested.get(j), decoded.get(j));
|
||||
}
|
||||
assertEquals(requested.cardinality(), decoded.cardinality());
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] createRequest(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.REQUEST);
|
||||
// Allow one byte for the REQUEST tag, one byte for the padding length
|
||||
// as a uint7, one byte for the BYTES tag, and five bytes for the
|
||||
// length of the byte array as an int32
|
||||
int size = MAX_PACKET_LENGTH - 8;
|
||||
if(tooBig) size++;
|
||||
assertTrue(size > Short.MAX_VALUE);
|
||||
w.writeUint7((byte) 0);
|
||||
w.writeBytes(new byte[size]);
|
||||
assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private byte[] createRequest(byte[] bitmap) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(Types.REQUEST);
|
||||
w.writeUint7((byte) 0);
|
||||
w.writeBytes(bitmap);
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.Signature;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.MessageDigest;
|
||||
import net.sf.briar.api.protocol.Author;
|
||||
import net.sf.briar.api.protocol.AuthorId;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Group;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.api.protocol.UnverifiedBatch;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class UnverifiedBatchImplTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final byte[] raw, raw1;
|
||||
private final String subject;
|
||||
private final long timestamp;
|
||||
|
||||
public UnverifiedBatchImplTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
Random r = new Random();
|
||||
raw = new byte[123];
|
||||
r.nextBytes(raw);
|
||||
raw1 = new byte[1234];
|
||||
r.nextBytes(raw1);
|
||||
subject = "Unit tests are exciting";
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIds() throws Exception {
|
||||
// Calculate the expected batch and message IDs
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
messageDigest.update(raw);
|
||||
messageDigest.update(raw1);
|
||||
BatchId batchId = new BatchId(messageDigest.digest());
|
||||
messageDigest.update(raw);
|
||||
MessageId messageId = new MessageId(messageDigest.digest());
|
||||
messageDigest.update(raw1);
|
||||
MessageId messageId1 = new MessageId(messageDigest.digest());
|
||||
// Verify the batch
|
||||
Mockery context = new Mockery();
|
||||
final UnverifiedMessage message =
|
||||
context.mock(UnverifiedMessage.class, "message");
|
||||
final UnverifiedMessage message1 =
|
||||
context.mock(UnverifiedMessage.class, "message1");
|
||||
context.checking(new Expectations() {{
|
||||
// First message
|
||||
oneOf(message).getRaw();
|
||||
will(returnValue(raw));
|
||||
oneOf(message).getAuthor();
|
||||
will(returnValue(null));
|
||||
oneOf(message).getGroup();
|
||||
will(returnValue(null));
|
||||
oneOf(message).getParent();
|
||||
will(returnValue(null));
|
||||
oneOf(message).getSubject();
|
||||
will(returnValue(subject));
|
||||
oneOf(message).getTimestamp();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(message).getBodyStart();
|
||||
will(returnValue(10));
|
||||
oneOf(message).getBodyLength();
|
||||
will(returnValue(100));
|
||||
// Second message
|
||||
oneOf(message1).getRaw();
|
||||
will(returnValue(raw1));
|
||||
oneOf(message1).getAuthor();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getGroup();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getParent();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getSubject();
|
||||
will(returnValue(subject));
|
||||
oneOf(message1).getTimestamp();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(message1).getBodyStart();
|
||||
will(returnValue(10));
|
||||
oneOf(message1).getBodyLength();
|
||||
will(returnValue(1000));
|
||||
}});
|
||||
Collection<UnverifiedMessage> messages = Arrays.asList(message,
|
||||
message1);
|
||||
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
|
||||
Batch verifiedBatch = batch.verify();
|
||||
// Check that the batch and message IDs match
|
||||
assertEquals(batchId, verifiedBatch.getId());
|
||||
Collection<Message> verifiedMessages = verifiedBatch.getMessages();
|
||||
assertEquals(2, verifiedMessages.size());
|
||||
Iterator<Message> it = verifiedMessages.iterator();
|
||||
Message verifiedMessage = it.next();
|
||||
assertEquals(messageId, verifiedMessage.getId());
|
||||
Message verifiedMessage1 = it.next();
|
||||
assertEquals(messageId1, verifiedMessage1.getId());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignatures() throws Exception {
|
||||
final int signedByAuthor = 100, signedByGroup = 110;
|
||||
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
|
||||
final KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
|
||||
Signature signature = crypto.getSignature();
|
||||
// Calculate the expected author and group signatures
|
||||
signature.initSign(authorKeyPair.getPrivate());
|
||||
signature.update(raw, 0, signedByAuthor);
|
||||
final byte[] authorSignature = signature.sign();
|
||||
signature.initSign(groupKeyPair.getPrivate());
|
||||
signature.update(raw, 0, signedByGroup);
|
||||
final byte[] groupSignature = signature.sign();
|
||||
// Verify the batch
|
||||
Mockery context = new Mockery();
|
||||
final UnverifiedMessage message =
|
||||
context.mock(UnverifiedMessage.class, "message");
|
||||
final Author author = context.mock(Author.class);
|
||||
final Group group = context.mock(Group.class);
|
||||
final UnverifiedMessage message1 =
|
||||
context.mock(UnverifiedMessage.class, "message1");
|
||||
context.checking(new Expectations() {{
|
||||
// First message
|
||||
oneOf(message).getRaw();
|
||||
will(returnValue(raw));
|
||||
oneOf(message).getAuthor();
|
||||
will(returnValue(author));
|
||||
oneOf(author).getPublicKey();
|
||||
will(returnValue(authorKeyPair.getPublic().getEncoded()));
|
||||
oneOf(message).getLengthSignedByAuthor();
|
||||
will(returnValue(signedByAuthor));
|
||||
oneOf(message).getAuthorSignature();
|
||||
will(returnValue(authorSignature));
|
||||
oneOf(message).getGroup();
|
||||
will(returnValue(group));
|
||||
exactly(2).of(group).getPublicKey();
|
||||
will(returnValue(groupKeyPair.getPublic().getEncoded()));
|
||||
oneOf(message).getLengthSignedByGroup();
|
||||
will(returnValue(signedByGroup));
|
||||
oneOf(message).getGroupSignature();
|
||||
will(returnValue(groupSignature));
|
||||
oneOf(author).getId();
|
||||
will(returnValue(new AuthorId(TestUtils.getRandomId())));
|
||||
oneOf(group).getId();
|
||||
will(returnValue(new GroupId(TestUtils.getRandomId())));
|
||||
oneOf(message).getParent();
|
||||
will(returnValue(null));
|
||||
oneOf(message).getSubject();
|
||||
will(returnValue(subject));
|
||||
oneOf(message).getTimestamp();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(message).getBodyStart();
|
||||
will(returnValue(10));
|
||||
oneOf(message).getBodyLength();
|
||||
will(returnValue(100));
|
||||
// Second message
|
||||
oneOf(message1).getRaw();
|
||||
will(returnValue(raw1));
|
||||
oneOf(message1).getAuthor();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getGroup();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getParent();
|
||||
will(returnValue(null));
|
||||
oneOf(message1).getSubject();
|
||||
will(returnValue(subject));
|
||||
oneOf(message1).getTimestamp();
|
||||
will(returnValue(timestamp));
|
||||
oneOf(message1).getBodyStart();
|
||||
will(returnValue(10));
|
||||
oneOf(message1).getBodyLength();
|
||||
will(returnValue(1000));
|
||||
}});
|
||||
Collection<UnverifiedMessage> messages = Arrays.asList(message,
|
||||
message1);
|
||||
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
|
||||
batch.verify();
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionThrownIfMessageIsModified() throws Exception {
|
||||
final int signedByAuthor = 100;
|
||||
final KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
|
||||
Signature signature = crypto.getSignature();
|
||||
// Calculate the expected author signature
|
||||
signature.initSign(authorKeyPair.getPrivate());
|
||||
signature.update(raw, 0, signedByAuthor);
|
||||
final byte[] authorSignature = signature.sign();
|
||||
// Modify the message
|
||||
raw[signedByAuthor / 2] ^= 0xff;
|
||||
// Verify the batch
|
||||
Mockery context = new Mockery();
|
||||
final UnverifiedMessage message =
|
||||
context.mock(UnverifiedMessage.class, "message");
|
||||
final Author author = context.mock(Author.class);
|
||||
final UnverifiedMessage message1 =
|
||||
context.mock(UnverifiedMessage.class, "message1");
|
||||
context.checking(new Expectations() {{
|
||||
// First message - verification will fail at the author's signature
|
||||
oneOf(message).getRaw();
|
||||
will(returnValue(raw));
|
||||
oneOf(message).getAuthor();
|
||||
will(returnValue(author));
|
||||
oneOf(author).getPublicKey();
|
||||
will(returnValue(authorKeyPair.getPublic().getEncoded()));
|
||||
oneOf(message).getLengthSignedByAuthor();
|
||||
will(returnValue(signedByAuthor));
|
||||
oneOf(message).getAuthorSignature();
|
||||
will(returnValue(authorSignature));
|
||||
}});
|
||||
Collection<UnverifiedMessage> messages = Arrays.asList(message,
|
||||
message1);
|
||||
UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages);
|
||||
try {
|
||||
batch.verify();
|
||||
fail();
|
||||
} catch(GeneralSecurityException expected) {}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DatabaseExecutor;
|
||||
import net.sf.briar.api.protocol.Ack;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.RawBatch;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.UniqueId;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.clock.ClockModule;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.transport.TransportModule;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
|
||||
public class OutgoingSimplexConnectionTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final Mockery context;
|
||||
private final DatabaseComponent db;
|
||||
private final ConnectionRegistry connRegistry;
|
||||
private final ConnectionWriterFactory connFactory;
|
||||
private final ProtocolWriterFactory protoFactory;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final byte[] secret;
|
||||
|
||||
public OutgoingSimplexConnectionTest() {
|
||||
super();
|
||||
context = new Mockery();
|
||||
db = context.mock(DatabaseComponent.class);
|
||||
Module testModule = new AbstractModule() {
|
||||
@Override
|
||||
public void configure() {
|
||||
bind(DatabaseComponent.class).toInstance(db);
|
||||
bind(Executor.class).annotatedWith(
|
||||
DatabaseExecutor.class).toInstance(
|
||||
Executors.newCachedThreadPool());
|
||||
}
|
||||
};
|
||||
Injector i = Guice.createInjector(testModule, new ClockModule(),
|
||||
new CryptoModule(), new SerialModule(), new TransportModule(),
|
||||
new SimplexProtocolModule(), new ProtocolModule(),
|
||||
new DuplexProtocolModule());
|
||||
connRegistry = i.getInstance(ConnectionRegistry.class);
|
||||
connFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
protoFactory = i.getInstance(ProtocolWriterFactory.class);
|
||||
contactId = new ContactId(234);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
secret = new byte[32];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionTooShort() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, MAX_PACKET_LENGTH, true);
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret, 0L, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
connection.write();
|
||||
// Nothing should have been written
|
||||
assertEquals(0, out.size());
|
||||
// The transport should have been disposed with exception == true
|
||||
assertTrue(transport.getDisposed());
|
||||
assertTrue(transport.getException());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNothingToSend() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, MIN_CONNECTION_LENGTH, true);
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret, 0L, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
context.checking(new Expectations() {{
|
||||
// No transports to send
|
||||
oneOf(db).generateTransportUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No subscriptions to send
|
||||
oneOf(db).generateSubscriptionUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No acks to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// No batches to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
connection.write();
|
||||
// Nothing should have been written
|
||||
assertEquals(0, out.size());
|
||||
// The transport should have been disposed with exception == false
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSomethingToSend() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, MIN_CONNECTION_LENGTH, true);
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret, 0L, true);
|
||||
OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
final Ack ack = context.mock(Ack.class);
|
||||
final BatchId batchId = new BatchId(TestUtils.getRandomId());
|
||||
final RawBatch batch = context.mock(RawBatch.class);
|
||||
final byte[] message = new byte[1234];
|
||||
context.checking(new Expectations() {{
|
||||
// No transports to send
|
||||
oneOf(db).generateTransportUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// No subscriptions to send
|
||||
oneOf(db).generateSubscriptionUpdate(contactId);
|
||||
will(returnValue(null));
|
||||
// One ack to send
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(ack));
|
||||
oneOf(ack).getBatchIds();
|
||||
will(returnValue(Collections.singletonList(batchId)));
|
||||
// No more acks
|
||||
oneOf(db).generateAck(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
// One batch to send
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(batch));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singletonList(message)));
|
||||
// No more batches
|
||||
oneOf(db).generateBatch(with(contactId), with(any(int.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
connection.write();
|
||||
// Something should have been written
|
||||
int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH;
|
||||
assertTrue(out.size() > overhead + UniqueId.LENGTH + message.length);
|
||||
// The transport should have been disposed with exception == false
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestDatabaseModule;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.KeyManager;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.MessagesAddedEvent;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolReaderFactory;
|
||||
import net.sf.briar.api.protocol.ProtocolWriterFactory;
|
||||
import net.sf.briar.api.protocol.Transport;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.protocol.TransportUpdate;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReaderFactory;
|
||||
import net.sf.briar.api.transport.ConnectionRecogniser;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.api.transport.ContactTransport;
|
||||
import net.sf.briar.clock.ClockModule;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.db.DatabaseModule;
|
||||
import net.sf.briar.lifecycle.LifecycleModule;
|
||||
import net.sf.briar.plugins.ImmediateExecutor;
|
||||
import net.sf.briar.protocol.ProtocolModule;
|
||||
import net.sf.briar.protocol.duplex.DuplexProtocolModule;
|
||||
import net.sf.briar.serial.SerialModule;
|
||||
import net.sf.briar.transport.TransportModule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class SimplexProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
private static final long CLOCK_DIFFERENCE = 60 * 1000L;
|
||||
private static final long LATENCY = 60 * 1000L;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File aliceDir = new File(testDir, "alice");
|
||||
private final File bobDir = new File(testDir, "bob");
|
||||
private final TransportId transportId;
|
||||
private final byte[] initialSecret;
|
||||
private final long epoch;
|
||||
|
||||
private Injector alice, bob;
|
||||
|
||||
public SimplexProtocolIntegrationTest() throws Exception {
|
||||
super();
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
// Create matching secrets for Alice and Bob
|
||||
initialSecret = new byte[32];
|
||||
new Random().nextBytes(initialSecret);
|
||||
long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY;
|
||||
epoch = System.currentTimeMillis() - 2 * rotationPeriod;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
alice = createInjector(aliceDir);
|
||||
bob = createInjector(bobDir);
|
||||
}
|
||||
|
||||
private Injector createInjector(File dir) {
|
||||
return Guice.createInjector(new ClockModule(), new CryptoModule(),
|
||||
new DatabaseModule(), new LifecycleModule(),
|
||||
new ProtocolModule(), new SerialModule(),
|
||||
new TestDatabaseModule(dir), new SimplexProtocolModule(),
|
||||
new TransportModule(), new DuplexProtocolModule());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjection() {
|
||||
DatabaseComponent aliceDb = alice.getInstance(DatabaseComponent.class);
|
||||
DatabaseComponent bobDb = bob.getInstance(DatabaseComponent.class);
|
||||
assertFalse(aliceDb == bobDb);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAndRead() throws Exception {
|
||||
read(write());
|
||||
}
|
||||
|
||||
private byte[] write() throws Exception {
|
||||
// Open Alice's database
|
||||
DatabaseComponent db = alice.getInstance(DatabaseComponent.class);
|
||||
db.open(false);
|
||||
// Start Alice's key manager
|
||||
KeyManager km = alice.getInstance(KeyManager.class);
|
||||
km.start();
|
||||
// Add Bob as a contact
|
||||
ContactId contactId = db.addContact();
|
||||
ContactTransport ct = new ContactTransport(contactId, transportId,
|
||||
epoch, CLOCK_DIFFERENCE, LATENCY, true);
|
||||
db.addContactTransport(ct);
|
||||
km.contactTransportAdded(ct, initialSecret.clone());
|
||||
// Send Bob a message
|
||||
String subject = "Hello";
|
||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||
MessageFactory messageFactory = alice.getInstance(MessageFactory.class);
|
||||
Message message = messageFactory.createMessage(null, subject, body);
|
||||
db.addLocalPrivateMessage(message, contactId);
|
||||
// Create an outgoing simplex connection
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ConnectionRegistry connRegistry =
|
||||
alice.getInstance(ConnectionRegistry.class);
|
||||
ConnectionWriterFactory connFactory =
|
||||
alice.getInstance(ConnectionWriterFactory.class);
|
||||
ProtocolWriterFactory protoFactory =
|
||||
alice.getInstance(ProtocolWriterFactory.class);
|
||||
TestSimplexTransportWriter transport = new TestSimplexTransportWriter(
|
||||
out, Long.MAX_VALUE, false);
|
||||
ConnectionContext ctx = km.getConnectionContext(contactId, transportId);
|
||||
assertNotNull(ctx);
|
||||
OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
// Write whatever needs to be written
|
||||
simplex.write();
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
// Clean up
|
||||
km.stop();
|
||||
db.close();
|
||||
// Return the contents of the simplex connection
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private void read(byte[] b) throws Exception {
|
||||
// Open Bob's database
|
||||
DatabaseComponent db = bob.getInstance(DatabaseComponent.class);
|
||||
db.open(false);
|
||||
// Start Bob's key manager
|
||||
KeyManager km = bob.getInstance(KeyManager.class);
|
||||
km.start();
|
||||
// Add Alice as a contact
|
||||
ContactId contactId = db.addContact();
|
||||
ContactTransport ct = new ContactTransport(contactId, transportId,
|
||||
epoch, CLOCK_DIFFERENCE, LATENCY, false);
|
||||
db.addContactTransport(ct);
|
||||
km.contactTransportAdded(ct, initialSecret.clone());
|
||||
// Set up a database listener
|
||||
MessageListener listener = new MessageListener();
|
||||
db.addListener(listener);
|
||||
// Fake a transport update from Alice
|
||||
TransportUpdate transportUpdate = new TransportUpdate() {
|
||||
|
||||
public Collection<Transport> getTransports() {
|
||||
Transport t = new Transport(transportId);
|
||||
return Collections.singletonList(t);
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
};
|
||||
db.receiveTransportUpdate(contactId, transportUpdate);
|
||||
// Create a connection recogniser and recognise the connection
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
ConnectionRecogniser rec = bob.getInstance(ConnectionRecogniser.class);
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
int read = in.read(tag);
|
||||
assertEquals(tag.length, read);
|
||||
ConnectionContext ctx = rec.acceptConnection(transportId, tag);
|
||||
assertNotNull(ctx);
|
||||
// Create an incoming simplex connection
|
||||
ConnectionRegistry connRegistry =
|
||||
bob.getInstance(ConnectionRegistry.class);
|
||||
ConnectionReaderFactory connFactory =
|
||||
bob.getInstance(ConnectionReaderFactory.class);
|
||||
ProtocolReaderFactory protoFactory =
|
||||
bob.getInstance(ProtocolReaderFactory.class);
|
||||
TestSimplexTransportReader transport =
|
||||
new TestSimplexTransportReader(in);
|
||||
IncomingSimplexConnection simplex = new IncomingSimplexConnection(
|
||||
new ImmediateExecutor(), new ImmediateExecutor(), db,
|
||||
connRegistry, connFactory, protoFactory, ctx, transport);
|
||||
// No messages should have been added yet
|
||||
assertFalse(listener.messagesAdded);
|
||||
// Read whatever needs to be read
|
||||
simplex.read();
|
||||
assertTrue(transport.getDisposed());
|
||||
assertFalse(transport.getException());
|
||||
assertTrue(transport.getRecognised());
|
||||
// The private message from Alice should have been added
|
||||
assertTrue(listener.messagesAdded);
|
||||
// Clean up
|
||||
km.stop();
|
||||
db.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private static class MessageListener implements DatabaseListener {
|
||||
|
||||
private boolean messagesAdded = false;
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof MessagesAddedEvent)
|
||||
messagesAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportReader;
|
||||
|
||||
class TestSimplexTransportReader implements SimplexTransportReader {
|
||||
|
||||
private final InputStream in;
|
||||
|
||||
private boolean disposed = false, exception = false, recognised = false;
|
||||
|
||||
TestSimplexTransportReader(InputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public void dispose(boolean exception, boolean recognised) {
|
||||
assert !disposed;
|
||||
disposed = true;
|
||||
this.exception = exception;
|
||||
this.recognised = recognised;
|
||||
}
|
||||
|
||||
boolean getDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
boolean getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
boolean getRecognised() {
|
||||
return recognised;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.sf.briar.protocol.simplex;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.sf.briar.api.plugins.simplex.SimplexTransportWriter;
|
||||
|
||||
class TestSimplexTransportWriter implements SimplexTransportWriter {
|
||||
|
||||
private final ByteArrayOutputStream out;
|
||||
private final long capacity;
|
||||
private final boolean flush;
|
||||
|
||||
private boolean disposed = false, exception = false;
|
||||
|
||||
TestSimplexTransportWriter(ByteArrayOutputStream out, long capacity,
|
||||
boolean flush) {
|
||||
this.out = out;
|
||||
this.capacity = capacity;
|
||||
this.flush = flush;
|
||||
}
|
||||
|
||||
public long getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public boolean shouldFlush() {
|
||||
return flush;
|
||||
}
|
||||
|
||||
public void dispose(boolean exception) {
|
||||
assert !disposed;
|
||||
disposed = true;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
boolean getDisposed() {
|
||||
return disposed;
|
||||
}
|
||||
|
||||
boolean getException() {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
556
briar-tests/src/net/sf/briar/serial/ReaderImplTest.java
Normal file
556
briar-tests/src/net/sf/briar/serial/ReaderImplTest.java
Normal file
@@ -0,0 +1,556 @@
|
||||
package net.sf.briar.serial;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.Bytes;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.serial.Consumer;
|
||||
import net.sf.briar.api.serial.StructReader;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReaderImplTest extends BriarTestCase {
|
||||
|
||||
private ByteArrayInputStream in = null;
|
||||
private ReaderImpl r = null;
|
||||
|
||||
@Test
|
||||
public void testReadBoolean() throws Exception {
|
||||
setContents("FFFE");
|
||||
assertFalse(r.readBoolean());
|
||||
assertTrue(r.readBoolean());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt8() throws Exception {
|
||||
setContents("FD00" + "FDFF" + "FD7F" + "FD80");
|
||||
assertEquals((byte) 0, r.readInt8());
|
||||
assertEquals((byte) -1, r.readInt8());
|
||||
assertEquals(Byte.MAX_VALUE, r.readInt8());
|
||||
assertEquals(Byte.MIN_VALUE, r.readInt8());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt16() throws Exception {
|
||||
setContents("FC0000" + "FCFFFF" + "FC7FFF" + "FC8000");
|
||||
assertEquals((short) 0, r.readInt16());
|
||||
assertEquals((short) -1, r.readInt16());
|
||||
assertEquals(Short.MAX_VALUE, r.readInt16());
|
||||
assertEquals(Short.MIN_VALUE, r.readInt16());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt32() throws Exception {
|
||||
setContents("FB00000000" + "FBFFFFFFFF" + "FB7FFFFFFF" + "FB80000000");
|
||||
assertEquals(0, r.readInt32());
|
||||
assertEquals(-1, r.readInt32());
|
||||
assertEquals(Integer.MAX_VALUE, r.readInt32());
|
||||
assertEquals(Integer.MIN_VALUE, r.readInt32());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadInt64() throws Exception {
|
||||
setContents("FA0000000000000000" + "FAFFFFFFFFFFFFFFFF"
|
||||
+ "FA7FFFFFFFFFFFFFFF" + "FA8000000000000000");
|
||||
assertEquals(0L, r.readInt64());
|
||||
assertEquals(-1L, r.readInt64());
|
||||
assertEquals(Long.MAX_VALUE, r.readInt64());
|
||||
assertEquals(Long.MIN_VALUE, r.readInt64());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadIntAny() throws Exception {
|
||||
setContents("00" + "7F" + "FD80" + "FDFF" + "FC0080" + "FC7FFF"
|
||||
+ "FB00008000" + "FB7FFFFFFF" + "FA0000000080000000");
|
||||
assertEquals(0L, r.readIntAny());
|
||||
assertEquals(127L, r.readIntAny());
|
||||
assertEquals(-128L, r.readIntAny());
|
||||
assertEquals(-1L, r.readIntAny());
|
||||
assertEquals(128L, r.readIntAny());
|
||||
assertEquals(32767L, r.readIntAny());
|
||||
assertEquals(32768L, r.readIntAny());
|
||||
assertEquals(2147483647L, r.readIntAny());
|
||||
assertEquals(2147483648L, r.readIntAny());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFloat32() throws Exception {
|
||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||
// http://steve.hollasch.net/cgindex/coding/ieeefloat.html
|
||||
setContents("F900000000" + "F93F800000" + "F940000000" + "F9BF800000"
|
||||
+ "F980000000" + "F9FF800000" + "F97F800000" + "F97FC00000");
|
||||
assertEquals(0F, r.readFloat32());
|
||||
assertEquals(1F, r.readFloat32());
|
||||
assertEquals(2F, r.readFloat32());
|
||||
assertEquals(-1F, r.readFloat32());
|
||||
assertEquals(-0F, r.readFloat32());
|
||||
assertEquals(Float.NEGATIVE_INFINITY, r.readFloat32());
|
||||
assertEquals(Float.POSITIVE_INFINITY, r.readFloat32());
|
||||
assertTrue(Float.isNaN(r.readFloat32()));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFloat64() throws Exception {
|
||||
setContents("F80000000000000000" + "F83FF0000000000000"
|
||||
+ "F84000000000000000" + "F8BFF0000000000000"
|
||||
+ "F88000000000000000" + "F8FFF0000000000000"
|
||||
+ "F87FF0000000000000" + "F87FF8000000000000");
|
||||
assertEquals(0.0, r.readFloat64());
|
||||
assertEquals(1.0, r.readFloat64());
|
||||
assertEquals(2.0, r.readFloat64());
|
||||
assertEquals(-1.0, r.readFloat64());
|
||||
assertEquals(-0.0, r.readFloat64());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, r.readFloat64());
|
||||
assertEquals(Double.POSITIVE_INFINITY, r.readFloat64());
|
||||
assertTrue(Double.isNaN(r.readFloat64()));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadString() throws Exception {
|
||||
setContents("F703666F6F" + "83666F6F" + "F700" + "80");
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("foo", r.readString());
|
||||
assertEquals("", r.readString());
|
||||
assertEquals("", r.readString());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStringMaxLength() throws Exception {
|
||||
setContents("83666F6F" + "83666F6F");
|
||||
assertEquals("foo", r.readString(3));
|
||||
try {
|
||||
r.readString(2);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBytes() throws Exception {
|
||||
setContents("F603010203" + "93010203" + "F600" + "90");
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes());
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBytesMaxLength() throws Exception {
|
||||
setContents("93010203" + "93010203");
|
||||
assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(3));
|
||||
try {
|
||||
r.readBytes(2);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShortList() throws Exception {
|
||||
setContents("A" + "3" + "01" + "83666F6F" + "FC0080");
|
||||
List<Object> l = r.readList(Object.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertEquals("foo", l.get(1));
|
||||
assertEquals((short) 128, l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadList() throws Exception {
|
||||
setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3");
|
||||
List<Object> l = r.readList(Object.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertEquals("foo", l.get(1));
|
||||
assertEquals((short) 128, l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafe() throws Exception {
|
||||
setContents("A" + "3" + "01" + "02" + "03");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), l.get(0));
|
||||
assertEquals(Byte.valueOf((byte) 2), l.get(1));
|
||||
assertEquals(Byte.valueOf((byte) 3), l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListTypeSafeThrowsFormatException() throws Exception {
|
||||
setContents("A" + "3" + "01" + "83666F6F" + "03");
|
||||
// Trying to read a mixed list as a list of bytes should throw a
|
||||
// FormatException
|
||||
try {
|
||||
r.readList(Byte.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadShortMap() throws Exception {
|
||||
setContents("B" + "2" + "83666F6F" + "7B" + "90" + "F2");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals((byte) 123, m.get("foo"));
|
||||
Bytes b = new Bytes(new byte[] {});
|
||||
assertTrue(m.containsKey(b));
|
||||
assertNull(m.get(b));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadMap() throws Exception {
|
||||
setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals((byte) 123, m.get("foo"));
|
||||
Bytes b = new Bytes(new byte[] {});
|
||||
assertTrue(m.containsKey(b));
|
||||
assertNull(m.get(b));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadMapTypeSafe() throws Exception {
|
||||
setContents("B" + "2" + "83666F6F" + "7B" + "80" + "F2");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
|
||||
assertTrue(m.containsKey(""));
|
||||
assertNull(m.get(""));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapKeysMustBeUnique() throws Exception {
|
||||
setContents("B" + "2" + "83666F6F" + "01" + "83626172" + "02"
|
||||
+ "B" + "2" + "83666F6F" + "01" + "83666F6F" + "02");
|
||||
// The first map has unique keys
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), m.get("foo"));
|
||||
assertEquals(Byte.valueOf((byte) 2), m.get("bar"));
|
||||
// The second map has a duplicate key
|
||||
try {
|
||||
r.readMap(String.class, Byte.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedList() throws Exception {
|
||||
setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3");
|
||||
List<Object> l = r.readList(Object.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertEquals("foo", l.get(1));
|
||||
assertEquals((short) 128, l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListElements() throws Exception {
|
||||
setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3");
|
||||
assertTrue(r.hasListStart());
|
||||
r.readListStart();
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals((byte) 1, r.readIntAny());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasListEnd());
|
||||
assertEquals((short) 128, r.readIntAny());
|
||||
assertTrue(r.hasListEnd());
|
||||
r.readListEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedListTypeSafe() throws Exception {
|
||||
setContents("F5" + "01" + "02" + "03" + "F3");
|
||||
List<Byte> l = r.readList(Byte.class);
|
||||
assertNotNull(l);
|
||||
assertEquals(3, l.size());
|
||||
assertEquals(Byte.valueOf((byte) 1), l.get(0));
|
||||
assertEquals(Byte.valueOf((byte) 2), l.get(1));
|
||||
assertEquals(Byte.valueOf((byte) 3), l.get(2));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMap() throws Exception {
|
||||
setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals((byte) 123, m.get("foo"));
|
||||
Bytes b = new Bytes(new byte[] {});
|
||||
assertTrue(m.containsKey(b));
|
||||
assertNull(m.get(b));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapEntries() throws Exception {
|
||||
setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3");
|
||||
assertTrue(r.hasMapStart());
|
||||
r.readMapStart();
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertEquals("foo", r.readString());
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertEquals((byte) 123, r.readIntAny());
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertArrayEquals(new byte[] {}, r.readBytes());
|
||||
assertFalse(r.hasMapEnd());
|
||||
assertTrue(r.hasNull());
|
||||
r.readNull();
|
||||
assertTrue(r.hasMapEnd());
|
||||
r.readMapEnd();
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadDelimitedMapTypeSafe() throws Exception {
|
||||
setContents("F4" + "83666F6F" + "7B" + "80" + "F2" + "F3");
|
||||
Map<String, Byte> m = r.readMap(String.class, Byte.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(2, m.size());
|
||||
assertEquals(Byte.valueOf((byte) 123), m.get("foo"));
|
||||
assertTrue(m.containsKey(""));
|
||||
assertNull(m.get(""));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testReadNestedMapsAndLists() throws Exception {
|
||||
setContents("B" + "1" + "B" + "1" + "83666F6F" + "7B"
|
||||
+ "A" + "1" + "01");
|
||||
Map<Object, Object> m = r.readMap(Object.class, Object.class);
|
||||
assertNotNull(m);
|
||||
assertEquals(1, m.size());
|
||||
Entry<Object, Object> e = m.entrySet().iterator().next();
|
||||
Map<Object, Object> m1 = (Map<Object, Object>) e.getKey();
|
||||
assertNotNull(m1);
|
||||
assertEquals(1, m1.size());
|
||||
assertEquals((byte) 123, m1.get("foo"));
|
||||
List<Object> l = (List<Object>) e.getValue();
|
||||
assertNotNull(l);
|
||||
assertEquals(1, l.size());
|
||||
assertEquals((byte) 1, l.get(0));
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStruct() throws Exception {
|
||||
setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F");
|
||||
// Add readers for two structs
|
||||
r.addStructReader(0, new StructReader<Foo>() {
|
||||
public Foo readStruct(Reader r) throws IOException {
|
||||
r.readStructId(0);
|
||||
return new Foo(r.readString());
|
||||
}
|
||||
});
|
||||
r.addStructReader(255, new StructReader<Bar>() {
|
||||
public Bar readStruct(Reader r) throws IOException {
|
||||
r.readStructId(255);
|
||||
return new Bar(r.readString());
|
||||
}
|
||||
});
|
||||
// Test both ID formats, short and long
|
||||
assertTrue(r.hasStruct(0));
|
||||
assertEquals("foo", r.readStruct(0, Foo.class).s);
|
||||
assertTrue(r.hasStruct(255));
|
||||
assertEquals("foo", r.readStruct(255, Bar.class).s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadStructWithConsumer() throws Exception {
|
||||
setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F");
|
||||
// Add readers for two structs
|
||||
r.addStructReader(0, new StructReader<Foo>() {
|
||||
public Foo readStruct(Reader r) throws IOException {
|
||||
r.readStructId(0);
|
||||
return new Foo(r.readString());
|
||||
}
|
||||
});
|
||||
r.addStructReader(255, new StructReader<Bar>() {
|
||||
public Bar readStruct(Reader r) throws IOException {
|
||||
r.readStructId(255);
|
||||
return new Bar(r.readString());
|
||||
}
|
||||
});
|
||||
// Add a consumer
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
r.addConsumer(new Consumer() {
|
||||
|
||||
public void write(byte b) throws IOException {
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
out.write(b, off, len);
|
||||
}
|
||||
});
|
||||
// Test both ID formats, short and long
|
||||
assertTrue(r.hasStruct(0));
|
||||
assertEquals("foo", r.readStruct(0, Foo.class).s);
|
||||
assertTrue(r.hasStruct(255));
|
||||
assertEquals("foo", r.readStruct(255, Bar.class).s);
|
||||
// Check that everything was passed to the consumer
|
||||
assertEquals("C0" + "83666F6F" + "F1" + "FF" + "83666F6F",
|
||||
StringUtils.toHexString(out.toByteArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownStructIdThrowsFormatException() throws Exception {
|
||||
setContents("C0" + "83666F6F");
|
||||
assertTrue(r.hasStruct(0));
|
||||
// No reader has been added for struct ID 0
|
||||
try {
|
||||
r.readStruct(0, Foo.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongClassThrowsFormatException() throws Exception {
|
||||
setContents("C0" + "83666F6F");
|
||||
// Add a reader for struct ID 0, class Foo
|
||||
r.addStructReader(0, new StructReader<Foo>() {
|
||||
public Foo readStruct(Reader r) throws IOException {
|
||||
r.readStructId(0);
|
||||
return new Foo(r.readString());
|
||||
}
|
||||
});
|
||||
assertTrue(r.hasStruct(0));
|
||||
// Trying to read the struct as class Bar should throw a FormatException
|
||||
try {
|
||||
r.readStruct(0, Bar.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadListUsingStructReader() throws Exception {
|
||||
setContents("A" + "1" + "C0" + "83666F6F");
|
||||
// Add a reader for a struct
|
||||
r.addStructReader(0, new StructReader<Foo>() {
|
||||
public Foo readStruct(Reader r) throws IOException {
|
||||
r.readStructId(0);
|
||||
return new Foo(r.readString());
|
||||
}
|
||||
});
|
||||
// Check that the reader is used for lists
|
||||
List<Foo> l = r.readList(Foo.class);
|
||||
assertEquals(1, l.size());
|
||||
assertEquals("foo", l.get(0).s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadMapUsingStructReader() throws Exception {
|
||||
setContents("B" + "1" + "C0" + "83666F6F" + "C1" + "83626172");
|
||||
// Add readers for two structs
|
||||
r.addStructReader(0, new StructReader<Foo>() {
|
||||
public Foo readStruct(Reader r) throws IOException {
|
||||
r.readStructId(0);
|
||||
return new Foo(r.readString());
|
||||
}
|
||||
});
|
||||
r.addStructReader(1, new StructReader<Bar>() {
|
||||
public Bar readStruct(Reader r) throws IOException {
|
||||
r.readStructId(1);
|
||||
return new Bar(r.readString());
|
||||
}
|
||||
});
|
||||
// Check that the readers are used for maps
|
||||
Map<Foo, Bar> m = r.readMap(Foo.class, Bar.class);
|
||||
assertEquals(1, m.size());
|
||||
Entry<Foo, Bar> e = m.entrySet().iterator().next();
|
||||
assertEquals("foo", e.getKey().s);
|
||||
assertEquals("bar", e.getValue().s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxLengthAppliesInsideMap() throws Exception {
|
||||
setContents("B" + "1" + "83666F6F" + "93010203");
|
||||
r.setMaxStringLength(3);
|
||||
r.setMaxBytesLength(3);
|
||||
Map<String, Bytes> m = r.readMap(String.class, Bytes.class);
|
||||
String key = "foo";
|
||||
Bytes value = new Bytes(new byte[] {1, 2, 3});
|
||||
assertEquals(Collections.singletonMap(key, value), m);
|
||||
// The max string length should be applied inside the map
|
||||
setContents("B" + "1" + "83666F6F" + "93010203");
|
||||
r.setMaxStringLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
// The max bytes length should be applied inside the map
|
||||
setContents("B" + "1" + "83666F6F" + "93010203");
|
||||
r.setMaxBytesLength(2);
|
||||
try {
|
||||
r.readMap(String.class, Bytes.class);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEmptyInput() throws Exception {
|
||||
setContents("");
|
||||
assertTrue(r.eof());
|
||||
}
|
||||
|
||||
private void setContents(String hex) {
|
||||
in = new ByteArrayInputStream(StringUtils.fromHexString(hex));
|
||||
r = new ReaderImpl(in);
|
||||
}
|
||||
|
||||
private static class Foo {
|
||||
|
||||
private final String s;
|
||||
|
||||
private Foo(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Bar {
|
||||
|
||||
private final String s;
|
||||
|
||||
private Bar(String s) {
|
||||
this.s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
291
briar-tests/src/net/sf/briar/serial/WriterImplTest.java
Normal file
291
briar-tests/src/net/sf/briar/serial/WriterImplTest.java
Normal file
@@ -0,0 +1,291 @@
|
||||
package net.sf.briar.serial;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.util.StringUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class WriterImplTest extends BriarTestCase {
|
||||
|
||||
private ByteArrayOutputStream out = null;
|
||||
private WriterImpl w = null;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
out = new ByteArrayOutputStream();
|
||||
w = new WriterImpl(out);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteBoolean() throws IOException {
|
||||
w.writeBoolean(true);
|
||||
w.writeBoolean(false);
|
||||
// TRUE tag, FALSE tag
|
||||
checkContents("FE" + "FF");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteUint7() throws IOException {
|
||||
w.writeUint7((byte) 0);
|
||||
w.writeUint7(Byte.MAX_VALUE);
|
||||
// 0, 127
|
||||
checkContents("00" + "7F");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInt8() throws IOException {
|
||||
w.writeInt8((byte) 0);
|
||||
w.writeInt8((byte) -1);
|
||||
w.writeInt8(Byte.MIN_VALUE);
|
||||
w.writeInt8(Byte.MAX_VALUE);
|
||||
// INT8 tag, 0, INT8 tag, -1, INT8 tag, -128, INT8 tag, 127
|
||||
checkContents("FD" + "00" + "FD" + "FF" + "FD" + "80" + "FD" + "7F");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInt16() throws IOException {
|
||||
w.writeInt16((short) 0);
|
||||
w.writeInt16((short) -1);
|
||||
w.writeInt16(Short.MIN_VALUE);
|
||||
w.writeInt16(Short.MAX_VALUE);
|
||||
// INT16 tag, 0, INT16 tag, -1, INT16 tag, -32768, INT16 tag, 32767
|
||||
checkContents("FC" + "0000" + "FC" + "FFFF" + "FC" + "8000"
|
||||
+ "FC" + "7FFF");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInt32() throws IOException {
|
||||
w.writeInt32(0);
|
||||
w.writeInt32(-1);
|
||||
w.writeInt32(Integer.MIN_VALUE);
|
||||
w.writeInt32(Integer.MAX_VALUE);
|
||||
// INT32 tag, 0, INT32 tag, -1, etc
|
||||
checkContents("FB" + "00000000" + "FB" + "FFFFFFFF" + "FB" + "80000000"
|
||||
+ "FB" + "7FFFFFFF");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteInt64() throws IOException {
|
||||
w.writeInt64(0L);
|
||||
w.writeInt64(-1L);
|
||||
w.writeInt64(Long.MIN_VALUE);
|
||||
w.writeInt64(Long.MAX_VALUE);
|
||||
// INT64 tag, 0, INT64 tag, -1, etc
|
||||
checkContents("FA" + "0000000000000000" + "FA" + "FFFFFFFFFFFFFFFF"
|
||||
+ "FA" + "8000000000000000" + "FA" + "7FFFFFFFFFFFFFFF");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteIntAny() throws IOException {
|
||||
w.writeIntAny(0); // uint7
|
||||
w.writeIntAny(-1); // int8
|
||||
w.writeIntAny(Byte.MAX_VALUE); // uint7
|
||||
w.writeIntAny(Byte.MAX_VALUE + 1); // int16
|
||||
w.writeIntAny(Short.MAX_VALUE); // int16
|
||||
w.writeIntAny(Short.MAX_VALUE + 1); // int32
|
||||
w.writeIntAny(Integer.MAX_VALUE); // int32
|
||||
w.writeIntAny(Integer.MAX_VALUE + 1L); // int64
|
||||
checkContents("00" + "FDFF" + "7F" + "FC0080" + "FC7FFF"
|
||||
+ "FB00008000" + "FB7FFFFFFF" + "FA0000000080000000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFloat32() throws IOException {
|
||||
// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
|
||||
// 1 bit for sign, 8 for exponent, 23 for significand
|
||||
w.writeFloat32(0F); // 0 0 0 -> 0x00000000
|
||||
w.writeFloat32(1F); // 0 127 1 -> 0x3F800000
|
||||
w.writeFloat32(2F); // 0 128 1 -> 0x40000000
|
||||
w.writeFloat32(-1F); // 1 127 1 -> 0xBF800000
|
||||
w.writeFloat32(-0F); // 1 0 0 -> 0x80000000
|
||||
// http://steve.hollasch.net/cgindex/coding/ieeefloat.html
|
||||
w.writeFloat32(Float.NEGATIVE_INFINITY); // 1 255 0 -> 0xFF800000
|
||||
w.writeFloat32(Float.POSITIVE_INFINITY); // 0 255 0 -> 0x7F800000
|
||||
w.writeFloat32(Float.NaN); // 0 255 1 -> 0x7FC00000
|
||||
checkContents("F9" + "00000000" + "F9" + "3F800000" + "F9" + "40000000"
|
||||
+ "F9" + "BF800000" + "F9" + "80000000" + "F9" + "FF800000"
|
||||
+ "F9" + "7F800000" + "F9" + "7FC00000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFloat64() throws IOException {
|
||||
// 1 bit for sign, 11 for exponent, 52 for significand
|
||||
w.writeFloat64(0.0); // 0 0 0 -> 0x0000000000000000
|
||||
w.writeFloat64(1.0); // 0 1023 1 -> 0x3FF0000000000000
|
||||
w.writeFloat64(2.0); // 0 1024 1 -> 0x4000000000000000
|
||||
w.writeFloat64(-1.0); // 1 1023 1 -> 0xBFF0000000000000
|
||||
w.writeFloat64(-0.0); // 1 0 0 -> 0x8000000000000000
|
||||
w.writeFloat64(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000...
|
||||
w.writeFloat64(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000...
|
||||
w.writeFloat64(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000
|
||||
checkContents("F8" + "0000000000000000" + "F8" + "3FF0000000000000"
|
||||
+ "F8" + "4000000000000000" + "F8" + "BFF0000000000000"
|
||||
+ "F8" + "8000000000000000" + "F8" + "FFF0000000000000"
|
||||
+ "F8" + "7FF0000000000000" + "F8" + "7FF8000000000000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortString() throws IOException {
|
||||
w.writeString("foo bar baz bam");
|
||||
// SHORT_STRING tag, length 15, UTF-8 bytes
|
||||
checkContents("8" + "F" + "666F6F206261722062617A2062616D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteString() throws IOException {
|
||||
w.writeString("foo bar baz bam ");
|
||||
// STRING tag, length 16 as uint7, UTF-8 bytes
|
||||
checkContents("F7" + "10" + "666F6F206261722062617A2062616D20");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortBytes() throws IOException {
|
||||
w.writeBytes(new byte[] {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
|
||||
});
|
||||
// SHORT_BYTES tag, length 15, bytes
|
||||
checkContents("9" + "F" + "000102030405060708090A0B0C0D0E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteBytes() throws IOException {
|
||||
w.writeBytes(new byte[] {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||||
});
|
||||
// BYTES tag, length 16 as uint7, bytes
|
||||
checkContents("F6" + "10" + "000102030405060708090A0B0C0D0E0F");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortList() throws IOException {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
for(int i = 0; i < 15; i++) l.add(i);
|
||||
w.writeList(l);
|
||||
// SHORT_LIST tag, length, elements as uint7
|
||||
checkContents("A" + "F" + "000102030405060708090A0B0C0D0E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteList() throws IOException {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
for(int i = 0; i < 16; i++) l.add(i);
|
||||
w.writeList(l);
|
||||
// LIST tag, elements as uint7, END tag
|
||||
checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCanContainNull() throws IOException {
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
l.add(1);
|
||||
l.add(null);
|
||||
l.add(2);
|
||||
w.writeList(l);
|
||||
// SHORT_LIST tag, length, 1 as uint7, null, 2 as uint7
|
||||
checkContents("A" + "3" + "01" + "F2" + "02");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortMap() throws IOException {
|
||||
// Use LinkedHashMap to get predictable iteration order
|
||||
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
||||
for(int i = 0; i < 15; i++) m.put(i, i + 1);
|
||||
w.writeMap(m);
|
||||
// SHORT_MAP tag, size, entries as uint7
|
||||
checkContents("B" + "F" + "0001" + "0102" + "0203" + "0304" + "0405"
|
||||
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
|
||||
+ "0C0D" + "0D0E" + "0E0F");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMap() throws IOException {
|
||||
// Use LinkedHashMap to get predictable iteration order
|
||||
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
||||
for(int i = 0; i < 16; i++) m.put(i, i + 1);
|
||||
w.writeMap(m);
|
||||
// MAP tag, entries as uint7, END tag
|
||||
checkContents("F4" + "0001" + "0102" + "0203" + "0304" + "0405"
|
||||
+ "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C"
|
||||
+ "0C0D" + "0D0E" + "0E0F" + "0F10" + "F3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDelimitedList() throws IOException {
|
||||
w.writeListStart();
|
||||
w.writeIntAny((byte) 1); // Written as uint7
|
||||
w.writeString("foo"); // Written as short string
|
||||
w.writeIntAny(128L); // Written as an int16
|
||||
w.writeListEnd();
|
||||
// LIST tag, 1 as uint7, "foo" as short string, 128 as int16,
|
||||
// END tag
|
||||
checkContents("F5" + "01" + "83666F6F" + "FC0080" + "F3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteDelimitedMap() throws IOException {
|
||||
w.writeMapStart();
|
||||
w.writeString("foo"); // Written as short string
|
||||
w.writeIntAny(123); // Written as a uint7
|
||||
w.writeBytes(new byte[] {}); // Written as short bytes
|
||||
w.writeNull();
|
||||
w.writeMapEnd();
|
||||
// MAP tag, "foo" as short string, 123 as uint7,
|
||||
// byte[] {} as short bytes, NULL tag, END tag
|
||||
checkContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteNestedMapsAndLists() throws IOException {
|
||||
Map<Object, Object> m = new LinkedHashMap<Object, Object>();
|
||||
m.put("foo", Integer.valueOf(123));
|
||||
List<Object> l = new ArrayList<Object>();
|
||||
l.add(Byte.valueOf((byte) 1));
|
||||
Map<Object, Object> m1 = new LinkedHashMap<Object, Object>();
|
||||
m1.put(m, l);
|
||||
w.writeMap(m1);
|
||||
// SHORT_MAP tag, length 1, SHORT_MAP tag, length 1,
|
||||
// "foo" as short string, 123 as uint7, SHORT_LIST tag, length 1,
|
||||
// 1 as uint7
|
||||
checkContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" + "A1" + "01");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteNull() throws IOException {
|
||||
w.writeNull();
|
||||
checkContents("F2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortStructId() throws IOException {
|
||||
w.writeStructId(0);
|
||||
w.writeStructId(31);
|
||||
// SHORT_STRUCT tag (3 bits), 0 (5 bits), SHORT_STRUCT tag (3 bits),
|
||||
// 31 (5 bits)
|
||||
checkContents("C0" + "DF");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteStructId() throws IOException {
|
||||
w.writeStructId(32);
|
||||
w.writeStructId(255);
|
||||
// STRUCT tag, 32 as uint8, STRUCT tag, 255 as uint8
|
||||
checkContents("F1" + "20" + "F1" + "FF");
|
||||
}
|
||||
|
||||
private void checkContents(String hex) throws IOException {
|
||||
out.flush();
|
||||
out.close();
|
||||
byte[] expected = StringUtils.fromHexString(hex);
|
||||
assertTrue(StringUtils.toHexString(out.toByteArray()),
|
||||
Arrays.equals(expected, out.toByteArray()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConnectionReaderImplTest extends BriarTestCase {
|
||||
|
||||
private static final int FRAME_LENGTH = 1024;
|
||||
private static final int MAX_PAYLOAD_LENGTH =
|
||||
FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
||||
|
||||
@Test
|
||||
public void testEmptyFramesAreSkipped() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameReader reader = context.mock(FrameReader.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(0)); // Empty frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(2)); // Non-empty frame with two payload bytes
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(0)); // Empty frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(-1)); // No more frames
|
||||
}});
|
||||
ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH);
|
||||
assertEquals(0, c.read()); // Skip the first empty frame, read a byte
|
||||
assertEquals(0, c.read()); // Read another byte
|
||||
assertEquals(-1, c.read()); // Skip the second empty frame, reach EOF
|
||||
assertEquals(-1, c.read()); // Still at EOF
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyFramesAreSkippedWithBuffer() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameReader reader = context.mock(FrameReader.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(0)); // Empty frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(2)); // Non-empty frame with two payload bytes
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(0)); // Empty frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(-1)); // No more frames
|
||||
}});
|
||||
ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH);
|
||||
byte[] buf = new byte[MAX_PAYLOAD_LENGTH];
|
||||
// Skip the first empty frame, read the two payload bytes
|
||||
assertEquals(2, c.read(buf));
|
||||
// Skip the second empty frame, reach EOF
|
||||
assertEquals(-1, c.read(buf));
|
||||
// Still at EOF
|
||||
assertEquals(-1, c.read(buf));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleReadsPerFrame() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameReader reader = context.mock(FrameReader.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(-1)); // No more frames
|
||||
}});
|
||||
ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH);
|
||||
byte[] buf = new byte[MAX_PAYLOAD_LENGTH / 2];
|
||||
// Read the first half of the payload
|
||||
assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf));
|
||||
// Read the second half of the payload
|
||||
assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf));
|
||||
// Reach EOF
|
||||
assertEquals(-1, c.read(buf, 0, buf.length));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleReadsPerFrameWithOffsets() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameReader reader = context.mock(FrameReader.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame
|
||||
oneOf(reader).readFrame(with(any(byte[].class)));
|
||||
will(returnValue(-1)); // No more frames
|
||||
}});
|
||||
ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH);
|
||||
byte[] buf = new byte[MAX_PAYLOAD_LENGTH];
|
||||
// Read the first half of the payload
|
||||
assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf, MAX_PAYLOAD_LENGTH / 2,
|
||||
MAX_PAYLOAD_LENGTH / 2));
|
||||
// Read the second half of the payload
|
||||
assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf, 123,
|
||||
MAX_PAYLOAD_LENGTH / 2));
|
||||
// Reach EOF
|
||||
assertEquals(-1, c.read(buf, 0, buf.length));
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConnectionRegistryImplTest extends BriarTestCase {
|
||||
|
||||
private final ContactId contactId, contactId1;
|
||||
private final TransportId transportId, transportId1;
|
||||
|
||||
public ConnectionRegistryImplTest() {
|
||||
super();
|
||||
contactId = new ContactId(1);
|
||||
contactId1 = new ContactId(2);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
transportId1 = new TransportId(TestUtils.getRandomId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterAndUnregister() {
|
||||
ConnectionRegistry c = new ConnectionRegistryImpl();
|
||||
// The registry should be empty
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId1));
|
||||
// Check that a registered connection shows up
|
||||
c.registerConnection(contactId, transportId);
|
||||
assertEquals(Collections.singletonList(contactId),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId1));
|
||||
// Register an identical connection - lookup should be unaffected
|
||||
c.registerConnection(contactId, transportId);
|
||||
assertEquals(Collections.singletonList(contactId),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId1));
|
||||
// Unregister one of the connections - lookup should be unaffected
|
||||
c.unregisterConnection(contactId, transportId);
|
||||
assertEquals(Collections.singletonList(contactId),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId1));
|
||||
// Unregister the other connection - lookup should be affected
|
||||
c.unregisterConnection(contactId, transportId);
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.emptyList(),
|
||||
c.getConnectedContacts(transportId1));
|
||||
// Try to unregister the connection again - exception should be thrown
|
||||
try {
|
||||
c.unregisterConnection(contactId, transportId);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Register both contacts with one transport, one contact with both
|
||||
c.registerConnection(contactId, transportId);
|
||||
c.registerConnection(contactId1, transportId);
|
||||
c.registerConnection(contactId1, transportId1);
|
||||
assertEquals(Arrays.asList(contactId, contactId1),
|
||||
c.getConnectedContacts(transportId));
|
||||
assertEquals(Collections.singletonList(contactId1),
|
||||
c.getConnectedContacts(transportId1));
|
||||
}
|
||||
}
|
||||
157
briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java
Normal file
157
briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.CONNECTION_WINDOW_SIZE;
|
||||
import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ConnectionWindowTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testWindowSliding() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
for(int i = 0; i < 100; i++) {
|
||||
assertFalse(w.isSeen(i));
|
||||
w.setSeen(i);
|
||||
assertTrue(w.isSeen(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowJumping() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
for(int i = 0; i < 100; i += 13) {
|
||||
assertFalse(w.isSeen(i));
|
||||
w.setSeen(i);
|
||||
assertTrue(w.isSeen(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowUpperLimit() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
// Centre is 0, highest value in window is 15
|
||||
w.setSeen(15);
|
||||
// Centre is 16, highest value in window is 31
|
||||
w.setSeen(31);
|
||||
try {
|
||||
// Centre is 32, highest value in window is 47
|
||||
w.setSeen(48);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Centre is max - 1, highest value in window is max
|
||||
byte[] bitmap = new byte[CONNECTION_WINDOW_SIZE / 8];
|
||||
w = new ConnectionWindow(MAX_32_BIT_UNSIGNED - 1, bitmap);
|
||||
assertFalse(w.isSeen(MAX_32_BIT_UNSIGNED - 1));
|
||||
assertFalse(w.isSeen(MAX_32_BIT_UNSIGNED));
|
||||
// Values greater than max should never be allowed
|
||||
try {
|
||||
w.setSeen(MAX_32_BIT_UNSIGNED + 1);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
w.setSeen(MAX_32_BIT_UNSIGNED);
|
||||
assertTrue(w.isSeen(MAX_32_BIT_UNSIGNED));
|
||||
// Centre should have moved to max + 1
|
||||
assertEquals(MAX_32_BIT_UNSIGNED + 1, w.getCentre());
|
||||
// The bit corresponding to max should be set
|
||||
byte[] expectedBitmap = new byte[CONNECTION_WINDOW_SIZE / 8];
|
||||
expectedBitmap[expectedBitmap.length / 2 - 1] = 1; // 00000001
|
||||
assertArrayEquals(expectedBitmap, w.getBitmap());
|
||||
// Values greater than max should never be allowed even if centre > max
|
||||
try {
|
||||
w.setSeen(MAX_32_BIT_UNSIGNED + 1);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowLowerLimit() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
// Centre is 0, negative values should never be allowed
|
||||
try {
|
||||
w.setSeen(-1);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Slide the window
|
||||
w.setSeen(15);
|
||||
// Centre is 16, lowest value in window is 0
|
||||
w.setSeen(0);
|
||||
// Slide the window
|
||||
w.setSeen(16);
|
||||
// Centre is 17, lowest value in window is 1
|
||||
w.setSeen(1);
|
||||
try {
|
||||
w.setSeen(0);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Slide the window
|
||||
w.setSeen(25);
|
||||
// Centre is 26, lowest value in window is 10
|
||||
w.setSeen(10);
|
||||
try {
|
||||
w.setSeen(9);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
// Centre should still be 26
|
||||
assertEquals(26, w.getCentre());
|
||||
// The bits corresponding to 10, 15, 16 and 25 should be set
|
||||
byte[] expectedBitmap = new byte[CONNECTION_WINDOW_SIZE / 8];
|
||||
expectedBitmap[0] = (byte) 134; // 10000110
|
||||
expectedBitmap[1] = 1; // 00000001
|
||||
assertArrayEquals(expectedBitmap, w.getBitmap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotSetSeenTwice() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
w.setSeen(15);
|
||||
try {
|
||||
w.setSeen(15);
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnseenConnectionNumbers() {
|
||||
ConnectionWindow w = new ConnectionWindow();
|
||||
// Centre is 0; window should cover 0 to 15, inclusive, with none seen
|
||||
Collection<Long> unseen = w.getUnseen();
|
||||
assertEquals(16, unseen.size());
|
||||
for(int i = 0; i < 16; i++) {
|
||||
assertTrue(unseen.contains(Long.valueOf(i)));
|
||||
assertFalse(w.isSeen(i));
|
||||
}
|
||||
w.setSeen(3);
|
||||
w.setSeen(4);
|
||||
// Centre is 5; window should cover 0 to 20, inclusive, with two seen
|
||||
unseen = w.getUnseen();
|
||||
assertEquals(19, unseen.size());
|
||||
for(int i = 0; i < 21; i++) {
|
||||
if(i == 3 || i == 4) {
|
||||
assertFalse(unseen.contains(Long.valueOf(i)));
|
||||
assertTrue(w.isSeen(i));
|
||||
} else {
|
||||
assertTrue(unseen.contains(Long.valueOf(i)));
|
||||
assertFalse(w.isSeen(i));
|
||||
}
|
||||
}
|
||||
w.setSeen(19);
|
||||
// Centre is 20; window should cover 4 to 35, inclusive, with two seen
|
||||
unseen = w.getUnseen();
|
||||
assertEquals(30, unseen.size());
|
||||
for(int i = 4; i < 36; i++) {
|
||||
if(i == 4 || i == 19) {
|
||||
assertFalse(unseen.contains(Long.valueOf(i)));
|
||||
assertTrue(w.isSeen(i));
|
||||
} else {
|
||||
assertTrue(unseen.contains(Long.valueOf(i)));
|
||||
assertFalse(w.isSeen(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class ConnectionWriterImplTest extends BriarTestCase {
|
||||
|
||||
private static final int FRAME_LENGTH = 1024;
|
||||
private static final int MAX_PAYLOAD_LENGTH =
|
||||
FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
||||
|
||||
@Test
|
||||
public void testCloseWithoutWritingWritesFinalFrame() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Write an empty final frame
|
||||
oneOf(writer).writeFrame(with(any(byte[].class)), with(0),
|
||||
with(true));
|
||||
// Flush the stream
|
||||
oneOf(writer).flush();
|
||||
}});
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
c.close();
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlushWithoutBufferedDataWritesFrame() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
context.checking(new Expectations() {{
|
||||
// Flush the stream
|
||||
oneOf(writer).flush();
|
||||
}});
|
||||
c.flush();
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlushWithBufferedDataWritesFrameAndFlushes()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
context.checking(new Expectations() {{
|
||||
// Write a non-final frame with one payload byte
|
||||
oneOf(writer).writeFrame(with(any(byte[].class)), with(1),
|
||||
with(false));
|
||||
// Flush the stream
|
||||
oneOf(writer).flush();
|
||||
}});
|
||||
c.write(0);
|
||||
c.flush();
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleByteWritesWriteFullFrame() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
context.checking(new Expectations() {{
|
||||
// Write a full non-final frame
|
||||
oneOf(writer).writeFrame(with(any(byte[].class)),
|
||||
with(MAX_PAYLOAD_LENGTH), with(false));
|
||||
}});
|
||||
for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) {
|
||||
c.write(0);
|
||||
}
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiByteWritesWriteFullFrames() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
context.checking(new Expectations() {{
|
||||
// Write two full non-final frames
|
||||
exactly(2).of(writer).writeFrame(with(any(byte[].class)),
|
||||
with(MAX_PAYLOAD_LENGTH), with(false));
|
||||
}});
|
||||
// Sanity check
|
||||
assertEquals(0, MAX_PAYLOAD_LENGTH % 2);
|
||||
// Write two full payloads using four multi-byte writes
|
||||
byte[] b = new byte[MAX_PAYLOAD_LENGTH / 2];
|
||||
c.write(b);
|
||||
c.write(b);
|
||||
c.write(b);
|
||||
c.write(b);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeMultiByteWriteWritesFullFrames() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final FrameWriter writer = context.mock(FrameWriter.class);
|
||||
ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH);
|
||||
context.checking(new Expectations() {{
|
||||
// Write two full non-final frames
|
||||
exactly(2).of(writer).writeFrame(with(any(byte[].class)),
|
||||
with(MAX_PAYLOAD_LENGTH), with(false));
|
||||
// Write a final frame with a one-byte payload
|
||||
oneOf(writer).writeFrame(with(any(byte[].class)), with(1),
|
||||
with(true));
|
||||
// Flush the stream
|
||||
oneOf(writer).flush();
|
||||
}});
|
||||
// Write two full payloads using one large multi-byte write
|
||||
byte[] b = new byte[MAX_PAYLOAD_LENGTH * 2 + 1];
|
||||
c.write(b);
|
||||
// There should be one byte left in the buffer
|
||||
c.close();
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static javax.crypto.Cipher.ENCRYPT_MODE;
|
||||
import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.FormatException;
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class IncomingEncryptionLayerTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private static final int FRAME_LENGTH = 1024;
|
||||
private static final int MAX_PAYLOAD_LENGTH =
|
||||
FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final AuthenticatedCipher frameCipher;
|
||||
private final ErasableKey frameKey;
|
||||
|
||||
public IncomingEncryptionLayerTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
frameKey = crypto.generateTestKey();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadValidFrames() throws Exception {
|
||||
// Generate two valid frames
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false);
|
||||
byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, false, false);
|
||||
// Concatenate the frames
|
||||
byte[] valid = new byte[FRAME_LENGTH * 2];
|
||||
System.arraycopy(frame, 0, valid, 0, FRAME_LENGTH);
|
||||
System.arraycopy(frame1, 0, valid, FRAME_LENGTH, FRAME_LENGTH);
|
||||
// Read the frames
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(valid);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
assertEquals(123, i.readFrame(buf));
|
||||
assertEquals(123, i.readFrame(buf));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncatedFrameThrowsException() throws Exception {
|
||||
// Generate a valid frame
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false);
|
||||
// Chop off the last byte
|
||||
byte[] truncated = new byte[FRAME_LENGTH - 1];
|
||||
System.arraycopy(frame, 0, truncated, 0, FRAME_LENGTH - 1);
|
||||
// Try to read the frame, which should fail due to truncation
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(truncated);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
try {
|
||||
i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifiedFrameThrowsException() throws Exception {
|
||||
// Generate a valid frame
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false);
|
||||
// Modify a randomly chosen byte of the frame
|
||||
frame[(int) (Math.random() * FRAME_LENGTH)] ^= 1;
|
||||
// Try to read the frame, which should fail due to modification
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
try {
|
||||
i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortNonFinalFrameThrowsException() throws Exception {
|
||||
// Generate a short non-final frame
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, false, false);
|
||||
// Try to read the frame, which should fail due to invalid length
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
try {
|
||||
i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortFinalFrameDoesNotThrowException() throws Exception {
|
||||
// Generate a short final frame
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, true, false);
|
||||
// Read the frame
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
int length = i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
assertEquals(123, length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidPayloadLengthThrowsException() throws Exception {
|
||||
// Generate a frame with an invalid payload length
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH + 1,
|
||||
false, false);
|
||||
// Try to read the frame, which should fail due to invalid length
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
try {
|
||||
i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonZeroPaddingThrowsException() throws Exception {
|
||||
// Generate a frame with bad padding
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, true);
|
||||
// Try to read the frame, which should fail due to bad padding
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(frame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
try {
|
||||
i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]);
|
||||
fail();
|
||||
} catch(FormatException expected) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCannotReadBeyondFinalFrame() throws Exception {
|
||||
// Generate a valid final frame and another valid final frame after it
|
||||
byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH, true,
|
||||
false);
|
||||
byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, true, false);
|
||||
// Concatenate the frames
|
||||
byte[] extraFrame = new byte[FRAME_LENGTH * 2];
|
||||
System.arraycopy(frame, 0, extraFrame, 0, FRAME_LENGTH);
|
||||
System.arraycopy(frame1, 0, extraFrame, FRAME_LENGTH, FRAME_LENGTH);
|
||||
// Read the final frame, which should first read the tag
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(extraFrame);
|
||||
IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(buf));
|
||||
// The frame after the final frame should not be read
|
||||
assertEquals(-1, i.readFrame(buf));
|
||||
}
|
||||
|
||||
private byte[] generateFrame(long frameNumber, int frameLength,
|
||||
int payloadLength, boolean finalFrame, boolean badPadding)
|
||||
throws Exception {
|
||||
byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH];
|
||||
byte[] plaintext = new byte[frameLength - MAC_LENGTH];
|
||||
byte[] ciphertext = new byte[frameLength];
|
||||
FrameEncoder.encodeIv(iv, frameNumber);
|
||||
FrameEncoder.encodeAad(aad, frameNumber, plaintext.length);
|
||||
frameCipher.init(ENCRYPT_MODE, frameKey, iv, aad);
|
||||
FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength);
|
||||
if(badPadding) plaintext[HEADER_LENGTH + payloadLength] = 1;
|
||||
frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
|
||||
return ciphertext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static javax.crypto.Cipher.ENCRYPT_MODE;
|
||||
import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
public class OutgoingEncryptionLayerTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private static final int FRAME_LENGTH = 1024;
|
||||
private static final int MAX_PAYLOAD_LENGTH =
|
||||
FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final AuthenticatedCipher frameCipher;
|
||||
private final byte[] tag;
|
||||
|
||||
public OutgoingEncryptionLayerTest() {
|
||||
super();
|
||||
Injector i = Guice.createInjector(new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
tag = new byte[TAG_LENGTH];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryption() throws Exception {
|
||||
int payloadLength = 123;
|
||||
byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH];
|
||||
byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
byte[] ciphertext = new byte[FRAME_LENGTH];
|
||||
ErasableKey frameKey = crypto.generateTestKey();
|
||||
// Calculate the expected ciphertext
|
||||
FrameEncoder.encodeIv(iv, 0);
|
||||
FrameEncoder.encodeAad(aad, 0, plaintext.length);
|
||||
frameCipher.init(ENCRYPT_MODE, frameKey, iv, aad);
|
||||
FrameEncoder.encodeHeader(plaintext, false, payloadLength);
|
||||
frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0);
|
||||
// Check that the actual tag and ciphertext match what's expected
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
10 * FRAME_LENGTH, frameCipher, frameKey, FRAME_LENGTH, tag);
|
||||
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false);
|
||||
byte[] actual = out.toByteArray();
|
||||
assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length);
|
||||
for(int i = 0; i < TAG_LENGTH; i++) assertEquals(tag[i], actual[i]);
|
||||
for(int i = 0; i < FRAME_LENGTH; i++) {
|
||||
assertEquals("" + i, ciphertext[i], actual[TAG_LENGTH + i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitiatorClosesConnectionWithoutWriting() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Initiator's constructor
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(),
|
||||
FRAME_LENGTH, tag);
|
||||
// Write an empty final frame without having written any other frames
|
||||
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
|
||||
// Nothing should be written to the output stream
|
||||
assertEquals(0, out.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponderClosesConnectionWithoutWriting() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Responder's constructor
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(),
|
||||
FRAME_LENGTH);
|
||||
// Write an empty final frame without having written any other frames
|
||||
o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true);
|
||||
// An empty final frame should be written to the output stream
|
||||
assertEquals(HEADER_LENGTH + MAC_LENGTH, out.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemainingCapacityWithTag() throws Exception {
|
||||
int MAX_PAYLOAD_LENGTH = FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Initiator's constructor
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(),
|
||||
FRAME_LENGTH, tag);
|
||||
// There should be space for nine full frames and one partial frame
|
||||
byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
assertEquals(10 * MAX_PAYLOAD_LENGTH - TAG_LENGTH,
|
||||
o.getRemainingCapacity());
|
||||
// Write nine frames, each containing a partial payload
|
||||
for(int i = 0; i < 9; i++) {
|
||||
o.writeFrame(frame, 123, false);
|
||||
assertEquals((9 - i) * MAX_PAYLOAD_LENGTH - TAG_LENGTH,
|
||||
o.getRemainingCapacity());
|
||||
}
|
||||
// Write the final frame, which will not be padded
|
||||
o.writeFrame(frame, 123, true);
|
||||
int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH;
|
||||
assertEquals(MAX_PAYLOAD_LENGTH - TAG_LENGTH - finalFrameLength,
|
||||
o.getRemainingCapacity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemainingCapacityWithoutTag() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Responder's constructor
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(),
|
||||
FRAME_LENGTH);
|
||||
// There should be space for ten full frames
|
||||
assertEquals(10 * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity());
|
||||
// Write nine frames, each containing a partial payload
|
||||
byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
for(int i = 0; i < 9; i++) {
|
||||
o.writeFrame(frame, 123, false);
|
||||
assertEquals((9 - i) * MAX_PAYLOAD_LENGTH,
|
||||
o.getRemainingCapacity());
|
||||
}
|
||||
// Write the final frame, which will not be padded
|
||||
o.writeFrame(frame, 123, true);
|
||||
int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH;
|
||||
assertEquals(MAX_PAYLOAD_LENGTH - finalFrameLength,
|
||||
o.getRemainingCapacity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemainingCapacityLimitedByFrameNumbers() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// The connection has plenty of space so we're limited by frame numbers
|
||||
OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out,
|
||||
Long.MAX_VALUE, frameCipher, crypto.generateTestKey(),
|
||||
FRAME_LENGTH);
|
||||
// There should be enough frame numbers for 2^32 frames
|
||||
assertEquals((1L << 32) * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity());
|
||||
// Write a frame containing a partial payload
|
||||
byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH];
|
||||
o.writeFrame(frame, 123, false);
|
||||
// There should be enough frame numbers for 2^32 - 1 frames
|
||||
assertEquals(((1L << 32) - 1) * MAX_PAYLOAD_LENGTH,
|
||||
o.getRemainingCapacity());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NullCipher;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.TemporarySecret;
|
||||
import net.sf.briar.util.ByteUtils;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.jmock.api.Action;
|
||||
import org.jmock.api.Invocation;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TransportConnectionRecogniserTest extends BriarTestCase {
|
||||
|
||||
private final ContactId contactId = new ContactId(234);
|
||||
private final TransportId transportId =
|
||||
new TransportId(TestUtils.getRandomId());
|
||||
|
||||
@Test
|
||||
public void testAddAndRemoveSecret() {
|
||||
Mockery context = new Mockery();
|
||||
final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
final Cipher tagCipher = new NullCipher();
|
||||
final byte[] secret = new byte[32];
|
||||
new Random().nextBytes(secret);
|
||||
final boolean alice = false;
|
||||
final ErasableKey tagKey = context.mock(ErasableKey.class);
|
||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Add secret
|
||||
oneOf(crypto).getTagCipher();
|
||||
will(returnValue(tagCipher));
|
||||
oneOf(crypto).deriveTagKey(secret, !alice);
|
||||
will(returnValue(tagKey));
|
||||
exactly(16).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagCipher), with(tagKey), with(any(long.class)));
|
||||
will(new EncodeTagAction());
|
||||
oneOf(tagKey).erase();
|
||||
// Remove secret
|
||||
oneOf(crypto).getTagCipher();
|
||||
will(returnValue(tagCipher));
|
||||
oneOf(crypto).deriveTagKey(secret, !alice);
|
||||
will(returnValue(tagKey));
|
||||
exactly(16).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagCipher), with(tagKey), with(any(long.class)));
|
||||
will(new EncodeTagAction());
|
||||
oneOf(tagKey).erase();
|
||||
}});
|
||||
TemporarySecret s = new TemporarySecret(contactId, transportId, 0L,
|
||||
0L, 0L, alice, 0L, secret, 0L, 0L, new byte[4]);
|
||||
TransportConnectionRecogniser recogniser =
|
||||
new TransportConnectionRecogniser(crypto, db, transportId);
|
||||
recogniser.addSecret(s);
|
||||
recogniser.removeSecret(contactId, 0L);
|
||||
// The secret should have been erased
|
||||
assertArrayEquals(new byte[32], secret);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptConnection() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
final Cipher tagCipher = new NullCipher();
|
||||
final byte[] secret = new byte[32];
|
||||
new Random().nextBytes(secret);
|
||||
final boolean alice = false;
|
||||
final ErasableKey tagKey = context.mock(ErasableKey.class);
|
||||
final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Add secret
|
||||
oneOf(crypto).getTagCipher();
|
||||
will(returnValue(tagCipher));
|
||||
oneOf(crypto).deriveTagKey(secret, !alice);
|
||||
will(returnValue(tagKey));
|
||||
exactly(16).of(crypto).encodeTag(with(any(byte[].class)),
|
||||
with(tagCipher), with(tagKey), with(any(long.class)));
|
||||
will(new EncodeTagAction());
|
||||
oneOf(tagKey).erase();
|
||||
// Accept connection
|
||||
oneOf(crypto).getTagCipher();
|
||||
will(returnValue(tagCipher));
|
||||
oneOf(crypto).deriveTagKey(secret, !alice);
|
||||
will(returnValue(tagKey));
|
||||
// The window should slide to include connection 16
|
||||
oneOf(crypto).encodeTag(with(any(byte[].class)), with(tagCipher),
|
||||
with(tagKey), with(16L));
|
||||
will(new EncodeTagAction());
|
||||
// The updated window should be stored
|
||||
oneOf(db).setConnectionWindow(contactId, transportId, 0L, 1L,
|
||||
new byte[] {0, 1, 0, 0});
|
||||
oneOf(tagKey).erase();
|
||||
// Accept connection again - no expectations
|
||||
}});
|
||||
TemporarySecret s = new TemporarySecret(contactId, transportId, 0L,
|
||||
0L, 0L, alice, 0L, secret, 0L, 0L, new byte[4]);
|
||||
TransportConnectionRecogniser recogniser =
|
||||
new TransportConnectionRecogniser(crypto, db, transportId);
|
||||
recogniser.addSecret(s);
|
||||
// Connection 0 should be expected
|
||||
byte[] tag = new byte[TAG_LENGTH];
|
||||
ConnectionContext ctx = recogniser.acceptConnection(tag);
|
||||
assertNotNull(ctx);
|
||||
assertEquals(contactId, ctx.getContactId());
|
||||
assertEquals(transportId, ctx.getTransportId());
|
||||
assertArrayEquals(secret, ctx.getSecret());
|
||||
assertEquals(0L, ctx.getConnectionNumber());
|
||||
assertEquals(alice, ctx.getAlice());
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private static class EncodeTagAction implements Action {
|
||||
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Encodes a tag");
|
||||
}
|
||||
|
||||
public Object invoke(Invocation invocation) throws Throwable {
|
||||
byte[] tag = (byte[]) invocation.getParameter(0);
|
||||
long connection = (Long) invocation.getParameter(3);
|
||||
ByteUtils.writeUint32(connection, tag, 0);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package net.sf.briar.transport;
|
||||
|
||||
import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.crypto.AuthenticatedCipher;
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.ErasableKey;
|
||||
import net.sf.briar.api.protocol.TransportId;
|
||||
import net.sf.briar.api.transport.ConnectionContext;
|
||||
import net.sf.briar.api.transport.ConnectionReader;
|
||||
import net.sf.briar.api.transport.ConnectionWriter;
|
||||
import net.sf.briar.api.transport.ConnectionWriterFactory;
|
||||
import net.sf.briar.crypto.CryptoModule;
|
||||
import net.sf.briar.transport.ConnectionReaderImpl;
|
||||
import net.sf.briar.transport.ConnectionWriterFactoryImpl;
|
||||
import net.sf.briar.transport.ConnectionWriterImpl;
|
||||
import net.sf.briar.transport.IncomingEncryptionLayer;
|
||||
import net.sf.briar.transport.OutgoingEncryptionLayer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
|
||||
public class TransportIntegrationTest extends BriarTestCase {
|
||||
|
||||
private final int FRAME_LENGTH = 2048;
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final ConnectionWriterFactory connectionWriterFactory;
|
||||
private final ContactId contactId;
|
||||
private final TransportId transportId;
|
||||
private final AuthenticatedCipher frameCipher;
|
||||
private final Random random;
|
||||
private final byte[] secret;
|
||||
private final ErasableKey frameKey;
|
||||
|
||||
public TransportIntegrationTest() {
|
||||
super();
|
||||
Module testModule = new AbstractModule() {
|
||||
@Override
|
||||
public void configure() {
|
||||
bind(ConnectionWriterFactory.class).to(
|
||||
ConnectionWriterFactoryImpl.class);
|
||||
}
|
||||
};
|
||||
Injector i = Guice.createInjector(testModule, new CryptoModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
|
||||
contactId = new ContactId(234);
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
frameCipher = crypto.getFrameCipher();
|
||||
random = new Random();
|
||||
// Since we're sending frames to ourselves, we only need outgoing keys
|
||||
secret = new byte[32];
|
||||
random.nextBytes(secret);
|
||||
frameKey = crypto.deriveFrameKey(secret, 0L, true, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitiatorWriteAndRead() throws Exception {
|
||||
testWriteAndRead(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponderWriteAndRead() throws Exception {
|
||||
testWriteAndRead(false);
|
||||
}
|
||||
|
||||
private void testWriteAndRead(boolean initiator) throws Exception {
|
||||
// Generate two random frames
|
||||
byte[] frame = new byte[1234];
|
||||
random.nextBytes(frame);
|
||||
byte[] frame1 = new byte[321];
|
||||
random.nextBytes(frame1);
|
||||
// Copy the frame key - the copy will be erased
|
||||
ErasableKey frameCopy = frameKey.copy();
|
||||
// Write the frames
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,
|
||||
Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH);
|
||||
ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut,
|
||||
FRAME_LENGTH);
|
||||
OutputStream out1 = writer.getOutputStream();
|
||||
out1.write(frame);
|
||||
out1.flush();
|
||||
out1.write(frame1);
|
||||
out1.flush();
|
||||
byte[] output = out.toByteArray();
|
||||
assertEquals(FRAME_LENGTH * 2, output.length);
|
||||
// Read the tag and the frames back
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(output);
|
||||
FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher,
|
||||
frameKey, FRAME_LENGTH);
|
||||
ConnectionReader reader = new ConnectionReaderImpl(encryptionIn,
|
||||
FRAME_LENGTH);
|
||||
InputStream in1 = reader.getInputStream();
|
||||
byte[] recovered = new byte[frame.length];
|
||||
int offset = 0;
|
||||
while(offset < recovered.length) {
|
||||
int read = in1.read(recovered, offset, recovered.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered.length, offset);
|
||||
assertArrayEquals(frame, recovered);
|
||||
byte[] recovered1 = new byte[frame1.length];
|
||||
offset = 0;
|
||||
while(offset < recovered1.length) {
|
||||
int read = in1.read(recovered1, offset, recovered1.length - offset);
|
||||
if(read == -1) break;
|
||||
offset += read;
|
||||
}
|
||||
assertEquals(recovered1.length, offset);
|
||||
assertArrayEquals(frame1, recovered1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheadWithTag() throws Exception {
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MIN_CONNECTION_LENGTH);
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret, 0L, true);
|
||||
ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
|
||||
MIN_CONNECTION_LENGTH, ctx, false, true);
|
||||
// Check that the connection writer thinks there's room for a packet
|
||||
long capacity = w.getRemainingCapacity();
|
||||
assertTrue(capacity > MAX_PACKET_LENGTH);
|
||||
assertTrue(capacity < MIN_CONNECTION_LENGTH);
|
||||
// Check that there really is room for a packet
|
||||
byte[] payload = new byte[MAX_PACKET_LENGTH];
|
||||
w.getOutputStream().write(payload);
|
||||
w.getOutputStream().close();
|
||||
long used = out.size();
|
||||
assertTrue(used > MAX_PACKET_LENGTH);
|
||||
assertTrue(used <= MIN_CONNECTION_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverheadWithoutTag() throws Exception {
|
||||
ByteArrayOutputStream out =
|
||||
new ByteArrayOutputStream(MIN_CONNECTION_LENGTH);
|
||||
ConnectionContext ctx = new ConnectionContext(contactId, transportId,
|
||||
secret, 0L, true);
|
||||
ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
|
||||
MIN_CONNECTION_LENGTH, ctx, false, false);
|
||||
// Check that the connection writer thinks there's room for a packet
|
||||
long capacity = w.getRemainingCapacity();
|
||||
assertTrue(capacity > MAX_PACKET_LENGTH);
|
||||
assertTrue(capacity < MIN_CONNECTION_LENGTH);
|
||||
// Check that there really is room for a packet
|
||||
byte[] payload = new byte[MAX_PACKET_LENGTH];
|
||||
w.getOutputStream().write(payload);
|
||||
w.getOutputStream().close();
|
||||
long used = out.size();
|
||||
assertTrue(used > MAX_PACKET_LENGTH);
|
||||
assertTrue(used <= MIN_CONNECTION_LENGTH);
|
||||
}
|
||||
}
|
||||
66
briar-tests/src/net/sf/briar/util/ByteUtilsTest.java
Normal file
66
briar-tests/src/net/sf/briar/util/ByteUtilsTest.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package net.sf.briar.util;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ByteUtilsTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint16() {
|
||||
byte[] b = StringUtils.fromHexString("000000");
|
||||
assertEquals(0, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("000001");
|
||||
assertEquals(1, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFF");
|
||||
assertEquals(65535, ByteUtils.readUint16(b, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint32() {
|
||||
byte[] b = StringUtils.fromHexString("0000000000");
|
||||
assertEquals(0L, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("0000000001");
|
||||
assertEquals(1L, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFF");
|
||||
assertEquals(4294967295L, ByteUtils.readUint32(b, 1));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteUint16() {
|
||||
byte[] b = new byte[3];
|
||||
ByteUtils.writeUint16(0, b, 1);
|
||||
assertEquals("000000", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint16(1, b, 1);
|
||||
assertEquals("000001", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint16(65535, b, 1);
|
||||
assertEquals("00FFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteUint32() {
|
||||
byte[] b = new byte[5];
|
||||
ByteUtils.writeUint32(0L, b, 1);
|
||||
assertEquals("0000000000", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint32(1L, b, 1);
|
||||
assertEquals("0000000001", StringUtils.toHexString(b));
|
||||
ByteUtils.writeUint32(4294967295L, b, 1);
|
||||
assertEquals("00FFFFFFFF", StringUtils.toHexString(b));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint() {
|
||||
byte[] b = new byte[1];
|
||||
b[0] = (byte) 128;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
assertEquals(1 << i, ByteUtils.readUint(b, i + 1));
|
||||
}
|
||||
b = new byte[2];
|
||||
for(int i = 0; i < 65535; i++) {
|
||||
ByteUtils.writeUint16(i, b, 0);
|
||||
assertEquals(i, ByteUtils.readUint(b, 16));
|
||||
assertEquals(i >> 1, ByteUtils.readUint(b, 15));
|
||||
}
|
||||
}
|
||||
}
|
||||
165
briar-tests/src/net/sf/briar/util/FileUtilsTest.java
Normal file
165
briar-tests/src/net/sf/briar/util/FileUtilsTest.java
Normal file
@@ -0,0 +1,165 @@
|
||||
package net.sf.briar.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.util.FileUtils.Callback;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FileUtilsTest extends BriarTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTempFile() throws IOException {
|
||||
File temp = FileUtils.createTempFile();
|
||||
assertTrue(temp.exists());
|
||||
assertTrue(temp.isFile());
|
||||
assertEquals(0L, temp.length());
|
||||
temp.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopy() throws IOException {
|
||||
File src = new File(testDir, "src");
|
||||
File dest = new File(testDir, "dest");
|
||||
TestUtils.createFile(src, "Foo bar\r\nBar foo\r\n");
|
||||
long length = src.length();
|
||||
|
||||
FileUtils.copy(src, dest);
|
||||
|
||||
assertEquals(length, dest.length());
|
||||
Scanner in = new Scanner(dest);
|
||||
assertTrue(in.hasNextLine());
|
||||
assertEquals("Foo bar", in.nextLine());
|
||||
assertTrue(in.hasNextLine());
|
||||
assertEquals("Bar foo", in.nextLine());
|
||||
assertFalse(in.hasNext());
|
||||
in.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyFromStream() throws IOException {
|
||||
File src = new File(testDir, "src");
|
||||
File dest = new File(testDir, "dest");
|
||||
TestUtils.createFile(src, "Foo bar\r\nBar foo\r\n");
|
||||
long length = src.length();
|
||||
InputStream is = new FileInputStream(src);
|
||||
is.skip(4);
|
||||
|
||||
FileUtils.copy(is, dest);
|
||||
|
||||
assertEquals(length - 4, dest.length());
|
||||
Scanner in = new Scanner(dest);
|
||||
assertTrue(in.hasNextLine());
|
||||
assertEquals("bar", in.nextLine());
|
||||
assertTrue(in.hasNextLine());
|
||||
assertEquals("Bar foo", in.nextLine());
|
||||
assertFalse(in.hasNext());
|
||||
in.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyRecursively() throws IOException {
|
||||
final File dest1 = new File(testDir, "dest/abc/def/1");
|
||||
final File dest2 = new File(testDir, "dest/abc/def/2");
|
||||
final File dest3 = new File(testDir, "dest/abc/3");
|
||||
Mockery context = new Mockery();
|
||||
final Callback callback = context.mock(Callback.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(callback).processingFile(dest1);
|
||||
oneOf(callback).processingFile(dest2);
|
||||
oneOf(callback).processingFile(dest3);
|
||||
}});
|
||||
|
||||
copyRecursively(callback);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyRecursivelyNoCallback() throws IOException {
|
||||
copyRecursively(null);
|
||||
}
|
||||
|
||||
private void copyRecursively(Callback callback) throws IOException {
|
||||
TestUtils.createFile(new File(testDir, "abc/def/1"), "one one one");
|
||||
TestUtils.createFile(new File(testDir, "abc/def/2"), "two two two");
|
||||
TestUtils.createFile(new File(testDir, "abc/3"), "three three three");
|
||||
|
||||
File dest = new File(testDir, "dest");
|
||||
dest.mkdir();
|
||||
|
||||
FileUtils.copyRecursively(new File(testDir, "abc"), dest, callback);
|
||||
|
||||
File dest1 = new File(testDir, "dest/abc/def/1");
|
||||
assertTrue(dest1.exists());
|
||||
assertTrue(dest1.isFile());
|
||||
assertEquals("one one one".length(), dest1.length());
|
||||
File dest2 = new File(testDir, "dest/abc/def/2");
|
||||
assertTrue(dest2.exists());
|
||||
assertTrue(dest2.isFile());
|
||||
assertEquals("two two two".length(), dest2.length());
|
||||
File dest3 = new File(testDir, "dest/abc/3");
|
||||
assertTrue(dest3.exists());
|
||||
assertTrue(dest3.isFile());
|
||||
assertEquals("three three three".length(), dest3.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteFile() throws IOException {
|
||||
File foo = new File(testDir, "foo");
|
||||
foo.createNewFile();
|
||||
assertTrue(foo.exists());
|
||||
|
||||
FileUtils.delete(foo);
|
||||
|
||||
assertFalse(foo.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDirectory() throws IOException {
|
||||
File f1 = new File(testDir, "abc/def/1");
|
||||
File f2 = new File(testDir, "abc/def/2");
|
||||
File f3 = new File(testDir, "abc/3");
|
||||
File abc = new File(testDir, "abc");
|
||||
File def = new File(testDir, "abc/def");
|
||||
TestUtils.createFile(f1, "one one one");
|
||||
TestUtils.createFile(f2, "two two two");
|
||||
TestUtils.createFile(f3, "three three three");
|
||||
|
||||
assertTrue(f1.exists());
|
||||
assertTrue(f2.exists());
|
||||
assertTrue(f3.exists());
|
||||
assertTrue(abc.exists());
|
||||
assertTrue(def.exists());
|
||||
|
||||
FileUtils.delete(def);
|
||||
|
||||
assertFalse(f1.exists());
|
||||
assertFalse(f2.exists());
|
||||
assertTrue(f3.exists());
|
||||
assertTrue(abc.exists());
|
||||
assertFalse(def.exists());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
44
briar-tests/src/net/sf/briar/util/StringUtilsTest.java
Normal file
44
briar-tests/src/net/sf/briar/util/StringUtilsTest.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package net.sf.briar.util;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import net.sf.briar.BriarTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class StringUtilsTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testHead() {
|
||||
String head = StringUtils.head("123456789", 5);
|
||||
assertEquals("12345...", head);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTail() {
|
||||
String tail = StringUtils.tail("987654321", 5);
|
||||
assertEquals("...54321", tail);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexString() {
|
||||
byte[] b = new byte[] {1, 2, 3, 127, -128};
|
||||
String s = StringUtils.toHexString(b);
|
||||
assertEquals("0102037F80", s);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromHexString() {
|
||||
try {
|
||||
StringUtils.fromHexString("12345");
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
try {
|
||||
StringUtils.fromHexString("ABCDEFGH");
|
||||
fail();
|
||||
} catch(IllegalArgumentException expected) {}
|
||||
byte[] b = StringUtils.fromHexString("0102037F80");
|
||||
assertArrayEquals(new byte[] {1, 2, 3, 127, -128}, b);
|
||||
b = StringUtils.fromHexString("0a0b0c0d0e0f");
|
||||
assertArrayEquals(new byte[] {10, 11, 12, 13, 14, 15}, b);
|
||||
}
|
||||
}
|
||||
202
briar-tests/src/net/sf/briar/util/ZipUtilsTest.java
Normal file
202
briar-tests/src/net/sf/briar/util/ZipUtilsTest.java
Normal file
@@ -0,0 +1,202 @@
|
||||
package net.sf.briar.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import net.sf.briar.BriarTestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.util.ZipUtils.Callback;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ZipUtilsTest extends BriarTestCase {
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
|
||||
private final File f1 = new File(testDir, "abc/def/1");
|
||||
private final File f2 = new File(testDir, "abc/def/2");
|
||||
private final File f3 = new File(testDir, "abc/3");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir.mkdirs();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToZip() throws IOException {
|
||||
File src = new File(testDir, "src");
|
||||
File dest = new File(testDir, "dest");
|
||||
TestUtils.createFile(src, "foo bar baz");
|
||||
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest));
|
||||
|
||||
ZipUtils.copyToZip("abc/def", src, zip);
|
||||
zip.flush();
|
||||
zip.close();
|
||||
|
||||
Map<String, String> expected = Collections.singletonMap("abc/def",
|
||||
"foo bar baz");
|
||||
checkZipEntries(dest, expected);
|
||||
}
|
||||
|
||||
private void checkZipEntries(File f, Map<String, String> expected)
|
||||
throws IOException {
|
||||
Map<String, String> found = new HashMap<String, String>();
|
||||
assertTrue(f.exists());
|
||||
assertTrue(f.isFile());
|
||||
ZipInputStream unzip = new ZipInputStream(new FileInputStream(f));
|
||||
ZipEntry entry;
|
||||
while((entry = unzip.getNextEntry()) != null) {
|
||||
String name = entry.getName();
|
||||
Scanner s = new Scanner(unzip);
|
||||
assertTrue(s.hasNextLine());
|
||||
String contents = s.nextLine();
|
||||
assertFalse(s.hasNextLine());
|
||||
unzip.closeEntry();
|
||||
found.put(name, contents);
|
||||
}
|
||||
unzip.close();
|
||||
assertEquals(expected.size(), found.size());
|
||||
for(String name : expected.keySet()) {
|
||||
String contents = found.get(name);
|
||||
assertNotNull(contents);
|
||||
assertEquals(expected.get(name), contents);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToZipRecursively() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Callback callback = context.mock(Callback.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(callback).processingFile(f1);
|
||||
oneOf(callback).processingFile(f2);
|
||||
oneOf(callback).processingFile(f3);
|
||||
}});
|
||||
|
||||
copyRecursively(callback);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyToZipRecursivelyNoCallback() throws IOException {
|
||||
copyRecursively(null);
|
||||
}
|
||||
|
||||
private void copyRecursively(Callback callback) throws IOException {
|
||||
TestUtils.createFile(f1, "one one one");
|
||||
TestUtils.createFile(f2, "two two two");
|
||||
TestUtils.createFile(f3, "three three three");
|
||||
File src = new File(testDir, "abc");
|
||||
File dest = new File(testDir, "dest");
|
||||
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest));
|
||||
|
||||
ZipUtils.copyToZipRecursively("ghi", src, zip, callback);
|
||||
zip.flush();
|
||||
zip.close();
|
||||
|
||||
Map<String, String> expected = new HashMap<String, String>();
|
||||
expected.put("ghi/def/1", "one one one");
|
||||
expected.put("ghi/def/2", "two two two");
|
||||
expected.put("ghi/3", "three three three");
|
||||
checkZipEntries(dest, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnzipStream() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Callback callback = context.mock(Callback.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(callback).processingFile(f1);
|
||||
oneOf(callback).processingFile(f2);
|
||||
oneOf(callback).processingFile(f3);
|
||||
}});
|
||||
|
||||
unzipStream(null, callback);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
|
||||
assertTrue(f1.exists());
|
||||
assertTrue(f1.isFile());
|
||||
assertEquals("one one one".length(), f1.length());
|
||||
assertTrue(f2.exists());
|
||||
assertTrue(f2.isFile());
|
||||
assertEquals("two two two".length(), f2.length());
|
||||
assertTrue(f3.exists());
|
||||
assertTrue(f3.isFile());
|
||||
assertEquals("three three three".length(), f3.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnzipStreamWithRegex() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Callback callback = context.mock(Callback.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(callback).processingFile(f1);
|
||||
oneOf(callback).processingFile(f2);
|
||||
}});
|
||||
|
||||
unzipStream("^abc/def/.*", callback);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
|
||||
assertTrue(f1.exists());
|
||||
assertTrue(f1.isFile());
|
||||
assertEquals("one one one".length(), f1.length());
|
||||
assertTrue(f2.exists());
|
||||
assertTrue(f2.isFile());
|
||||
assertEquals("two two two".length(), f2.length());
|
||||
assertFalse(f3.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnzipStreamNoCallback() throws IOException {
|
||||
unzipStream(null, null);
|
||||
|
||||
assertTrue(f1.exists());
|
||||
assertTrue(f1.isFile());
|
||||
assertEquals("one one one".length(), f1.length());
|
||||
assertTrue(f2.exists());
|
||||
assertTrue(f2.isFile());
|
||||
assertEquals("two two two".length(), f2.length());
|
||||
assertTrue(f3.exists());
|
||||
assertTrue(f3.isFile());
|
||||
assertEquals("three three three".length(), f3.length());
|
||||
}
|
||||
|
||||
private void unzipStream(String regex, Callback callback)
|
||||
throws IOException {
|
||||
TestUtils.createFile(f1, "one one one");
|
||||
TestUtils.createFile(f2, "two two two");
|
||||
TestUtils.createFile(f3, "three three three");
|
||||
File src = new File(testDir, "abc");
|
||||
File dest = new File(testDir, "dest");
|
||||
ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest));
|
||||
ZipUtils.copyToZipRecursively(src.getName(), src, zip, null);
|
||||
zip.flush();
|
||||
zip.close();
|
||||
TestUtils.delete(src);
|
||||
|
||||
InputStream in = new FileInputStream(dest);
|
||||
ZipUtils.unzipStream(in, testDir, regex, callback);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user