mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Protocol refactoring. Each bundle now consists of a signed header and zero or more signed batches. There is no overall signature on the bundle, since the bundle's contents may need to be processed before the entire bundle has been read. The protocol does not prevent an adversary from removing batches from a bundle, reordering batches, moving them from one bundle to another, etc. However, since each batch is signed and acknowledged independently, no such guarantees are required. Bundle IDs will go away when the retransmission mechanism is changed.
This commit is contained in:
@@ -20,6 +20,8 @@
|
||||
<test name='net.sf.briar.i18n.FontManagerTest'/>
|
||||
<test name='net.sf.briar.i18n.I18nTest'/>
|
||||
<test name='net.sf.briar.invitation.InvitationWorkerTest'/>
|
||||
<test name='net.sf.briar.protocol.BundleReaderTest'/>
|
||||
<test name='net.sf.briar.protocol.BundleWriterTest'/>
|
||||
<test name='net.sf.briar.serial.ReaderImplTest'/>
|
||||
<test name='net.sf.briar.serial.WriterImplTest'/>
|
||||
<test name='net.sf.briar.setup.SetupWorkerTest'/>
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Collections;
|
||||
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.db.DatabaseCleaner.Callback;
|
||||
|
||||
@@ -24,6 +25,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
|
||||
protected abstract <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider);
|
||||
|
||||
@Test
|
||||
@@ -33,14 +35,17 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
@@ -54,8 +59,11 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
@@ -69,7 +77,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
@@ -84,8 +92,11 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
@@ -101,7 +112,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
@@ -116,8 +127,11 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).getFreeSpace();
|
||||
will(returnValue(MIN_FREE_SPACE - 1));
|
||||
@@ -135,7 +149,7 @@ public abstract class DatabaseComponentImplTest extends DatabaseComponentTest {
|
||||
will(returnValue(MIN_FREE_SPACE));
|
||||
}});
|
||||
Callback db = createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.checkFreeSpaceAndClean();
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -20,6 +22,8 @@ import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.BundleBuilder;
|
||||
import net.sf.briar.api.protocol.BundleId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageId;
|
||||
import net.sf.briar.protocol.MessageImpl;
|
||||
@@ -32,8 +36,6 @@ import com.google.inject.Provider;
|
||||
|
||||
public abstract class DatabaseComponentTest extends TestCase {
|
||||
|
||||
private static final int ONE_MEGABYTE = 1024 * 1024;
|
||||
|
||||
protected final Object txn = new Object();
|
||||
protected final AuthorId authorId;
|
||||
protected final BatchId batchId;
|
||||
@@ -45,6 +47,11 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
private final int size;
|
||||
private final byte[] body;
|
||||
private final Message message;
|
||||
private final Set<ContactId> contacts;
|
||||
private final Set<BatchId> acks;
|
||||
private final Set<GroupId> subs;
|
||||
private final Map<String, String> transports;
|
||||
private final Set<MessageId> messages;
|
||||
|
||||
public DatabaseComponentTest() {
|
||||
super();
|
||||
@@ -60,26 +67,32 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
body = new byte[size];
|
||||
message = new MessageImpl(messageId, MessageId.NONE, groupId, authorId,
|
||||
timestamp, body);
|
||||
contacts = Collections.singleton(contactId);
|
||||
acks = Collections.singleton(batchId);
|
||||
subs = Collections.singleton(groupId);
|
||||
transports = Collections.singletonMap("foo", "bar");
|
||||
messages = Collections.singleton(messageId);
|
||||
}
|
||||
|
||||
protected abstract <T> DatabaseComponent createDatabaseComponent(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider);
|
||||
|
||||
@Test
|
||||
public void testSimpleCalls() throws DbException {
|
||||
final Map<String, String> transports =
|
||||
Collections.singletonMap("foo", "bar");
|
||||
final Map<String, String> transports1 =
|
||||
Collections.singletonMap("foo", "bar baz");
|
||||
final Set<GroupId> subs = Collections.singleton(groupId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
@@ -95,7 +108,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
will(returnValue(contactId));
|
||||
// getContacts()
|
||||
oneOf(database).getContacts(txn);
|
||||
will(returnValue(Collections.singleton(contactId)));
|
||||
will(returnValue(contacts));
|
||||
// getTransports(contactId)
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
@@ -119,16 +132,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).close();
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.open(false);
|
||||
assertEquals(Rating.UNRATED, db.getRating(authorId));
|
||||
assertEquals(contactId, db.addContact(transports));
|
||||
assertEquals(Collections.singleton(contactId), db.getContacts());
|
||||
assertEquals(contacts, db.getContacts());
|
||||
assertEquals(transports, db.getTransports(contactId));
|
||||
db.setTransports(contactId, transports1);
|
||||
db.subscribe(groupId);
|
||||
assertEquals(Collections.singleton(groupId), db.getSubscriptions());
|
||||
assertEquals(subs, db.getSubscriptions());
|
||||
db.unsubscribe(groupId);
|
||||
db.removeContact(contactId);
|
||||
db.close();
|
||||
@@ -138,14 +151,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testNoParentStopsBackwardInclusion() throws DbException {
|
||||
final Set<MessageId> messages = Collections.singleton(messageId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// setRating(Rating.GOOD)
|
||||
allowing(database).startTransaction();
|
||||
@@ -163,7 +178,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.setRating(authorId, Rating.GOOD);
|
||||
|
||||
@@ -172,14 +187,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testMissingParentStopsBackwardInclusion() throws DbException {
|
||||
final Set<MessageId> messages = Collections.singleton(messageId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// setRating(Rating.GOOD)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -200,7 +217,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.setRating(authorId, Rating.GOOD);
|
||||
|
||||
@@ -210,14 +227,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
@Test
|
||||
public void testChangingGroupsStopsBackwardInclusion() throws DbException {
|
||||
final GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
final Set<MessageId> messages = Collections.singleton(messageId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// setRating(Rating.GOOD)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -242,7 +261,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.setRating(authorId, Rating.GOOD);
|
||||
|
||||
@@ -252,14 +271,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
@Test
|
||||
public void testUnaffectedParentStopsBackwardInclusion()
|
||||
throws DbException {
|
||||
final Set<MessageId> messages = Collections.singleton(messageId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// setRating(Rating.GOOD)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -287,7 +308,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.setRating(authorId, Rating.GOOD);
|
||||
|
||||
@@ -297,14 +318,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
@Test
|
||||
public void testAffectedParentContinuesBackwardInclusion()
|
||||
throws DbException {
|
||||
final Set<MessageId> messages = Collections.singleton(messageId);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// setRating(Rating.GOOD)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -334,7 +357,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.setRating(authorId, Rating.GOOD);
|
||||
|
||||
@@ -349,8 +372,11 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// addLocallyGeneratedMessage(message)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -360,7 +386,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.addLocallyGeneratedMessage(message);
|
||||
|
||||
@@ -374,8 +400,11 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// addLocallyGeneratedMessage(message)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -387,7 +416,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.addLocallyGeneratedMessage(message);
|
||||
|
||||
@@ -401,8 +430,11 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// addLocallyGeneratedMessage(message)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -412,7 +444,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).addMessage(txn, message);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getContacts(txn);
|
||||
will(returnValue(Collections.singleton(contactId)));
|
||||
will(returnValue(contacts));
|
||||
oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
|
||||
// The author is unrated and there are no sendable children
|
||||
oneOf(database).getRating(txn, authorId);
|
||||
@@ -423,7 +455,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.addLocallyGeneratedMessage(message);
|
||||
|
||||
@@ -438,8 +470,11 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
context.checking(new Expectations() {{
|
||||
// addLocallyGeneratedMessage(message)
|
||||
oneOf(database).startTransaction();
|
||||
@@ -449,7 +484,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).addMessage(txn, message);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getContacts(txn);
|
||||
will(returnValue(Collections.singleton(contactId)));
|
||||
will(returnValue(contacts));
|
||||
oneOf(database).setStatus(txn, contactId, messageId, Status.NEW);
|
||||
// The author is rated GOOD and there are two sendable children
|
||||
oneOf(database).getRating(txn, authorId);
|
||||
@@ -463,7 +498,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.addLocallyGeneratedMessage(message);
|
||||
|
||||
@@ -472,14 +507,17 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testGenerateBundleThrowsExceptionIfContactIsMissing()
|
||||
throws DbException {
|
||||
throws DbException, IOException, SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check that the contact is still in the DB
|
||||
@@ -490,7 +528,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
try {
|
||||
db.generateBundle(contactId, bundleBuilder);
|
||||
@@ -501,15 +539,22 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBundle() throws DbException {
|
||||
public void testGenerateBundle() throws DbException, IOException,
|
||||
SignatureException {
|
||||
final long headerSize = 1234L;
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final BundleBuilder bundleBuilder = context.mock(BundleBuilder.class);
|
||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
final BatchBuilder batchBuilder = context.mock(BatchBuilder.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
final Bundle bundle = context.mock(Bundle.class);
|
||||
@@ -519,24 +564,33 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Add acks to the bundle
|
||||
// Build the header
|
||||
oneOf(headerBuilderProvider).get();
|
||||
will(returnValue(headerBuilder));
|
||||
// Add acks to the header
|
||||
oneOf(database).removeBatchesToAck(txn, contactId);
|
||||
will(returnValue(Collections.singleton(batchId)));
|
||||
oneOf(bundleBuilder).addAck(batchId);
|
||||
// Add subscriptions to the bundle
|
||||
will(returnValue(acks));
|
||||
oneOf(headerBuilder).addAcks(acks);
|
||||
// Add subscriptions to the header
|
||||
oneOf(database).getSubscriptions(txn);
|
||||
will(returnValue(Collections.singleton(groupId)));
|
||||
oneOf(bundleBuilder).addSubscription(groupId);
|
||||
// Add transports to the bundle
|
||||
will(returnValue(subs));
|
||||
oneOf(headerBuilder).addSubscriptions(subs);
|
||||
// Add transports to the header
|
||||
oneOf(database).getTransports(txn);
|
||||
will(returnValue(Collections.singletonMap("foo", "bar")));
|
||||
oneOf(bundleBuilder).addTransport("foo", "bar");
|
||||
// Prepare to add batches to the bundle
|
||||
will(returnValue(transports));
|
||||
oneOf(headerBuilder).addTransports(transports);
|
||||
// Build the header
|
||||
oneOf(headerBuilder).build();
|
||||
will(returnValue(header));
|
||||
oneOf(bundleBuilder).getCapacity();
|
||||
will(returnValue((long) ONE_MEGABYTE));
|
||||
// Add messages to the batch
|
||||
oneOf(database).getSendableMessages(txn, contactId, Batch.CAPACITY);
|
||||
will(returnValue(Collections.singleton(messageId)));
|
||||
will(returnValue(1024L * 1024L));
|
||||
oneOf(header).getSize();
|
||||
will(returnValue(headerSize));
|
||||
oneOf(bundleBuilder).addHeader(header);
|
||||
// Add a batch to the bundle
|
||||
oneOf(database).getSendableMessages(txn, contactId,
|
||||
Batch.MAX_SIZE - headerSize);
|
||||
will(returnValue(messages));
|
||||
oneOf(batchBuilderProvider).get();
|
||||
will(returnValue(batchBuilder));
|
||||
oneOf(database).getMessage(txn, messageId);
|
||||
@@ -547,8 +601,8 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
// Record the batch as outstanding
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addOutstandingBatch(txn, contactId, batchId,
|
||||
Collections.singleton(messageId));
|
||||
oneOf(database).addOutstandingBatch(
|
||||
txn, contactId, batchId, messages);
|
||||
// Add the batch to the bundle
|
||||
oneOf(bundleBuilder).addBatch(batch);
|
||||
// Check whether to add another batch
|
||||
@@ -559,7 +613,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
will(returnValue(bundle));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.generateBundle(contactId, bundleBuilder);
|
||||
|
||||
@@ -568,14 +622,17 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testReceiveBundleThrowsExceptionIfContactIsMissing()
|
||||
throws DbException {
|
||||
throws DbException, IOException, SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final Bundle bundle = context.mock(Bundle.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check that the contact is still in the DB
|
||||
@@ -586,7 +643,7 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
try {
|
||||
db.receiveBundle(contactId, bundle);
|
||||
@@ -597,17 +654,20 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceivedBundle() throws DbException {
|
||||
final Map<String, String> transports =
|
||||
Collections.singletonMap("foo", "bar");
|
||||
public void testReceivedBundle() throws DbException, IOException,
|
||||
SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final Bundle bundle = context.mock(Bundle.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
context.checking(new Expectations() {{
|
||||
allowing(database).startTransaction();
|
||||
@@ -615,22 +675,25 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
allowing(database).commitTransaction(txn);
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// Header
|
||||
oneOf(bundle).getHeader();
|
||||
will(returnValue(header));
|
||||
// Acks
|
||||
oneOf(bundle).getAcks();
|
||||
will(returnValue(Collections.singleton(batchId)));
|
||||
oneOf(header).getAcks();
|
||||
will(returnValue(acks));
|
||||
oneOf(database).removeAckedBatch(txn, contactId, batchId);
|
||||
// Subscriptions
|
||||
oneOf(database).clearSubscriptions(txn, contactId);
|
||||
oneOf(bundle).getSubscriptions();
|
||||
will(returnValue(Collections.singleton(groupId)));
|
||||
oneOf(header).getSubscriptions();
|
||||
will(returnValue(subs));
|
||||
oneOf(database).addSubscription(txn, contactId, groupId);
|
||||
// Transports
|
||||
oneOf(bundle).getTransports();
|
||||
oneOf(header).getTransports();
|
||||
will(returnValue(transports));
|
||||
oneOf(database).setTransports(txn, contactId, transports);
|
||||
// Batches
|
||||
oneOf(bundle).getBatches();
|
||||
will(returnValue(Collections.singleton(batch)));
|
||||
oneOf(bundle).getNextBatch();
|
||||
will(returnValue(batch));
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singleton(message)));
|
||||
oneOf(database).containsSubscription(txn, groupId);
|
||||
@@ -642,15 +705,18 @@ public abstract class DatabaseComponentTest extends TestCase {
|
||||
oneOf(batch).getId();
|
||||
will(returnValue(batchId));
|
||||
oneOf(database).addBatchToAck(txn, contactId, batchId);
|
||||
// Any more batches? Nope
|
||||
oneOf(bundle).getNextBatch();
|
||||
will(returnValue(null));
|
||||
// Lost batches
|
||||
oneOf(bundle).getId();
|
||||
oneOf(header).getId();
|
||||
will(returnValue(bundleId));
|
||||
oneOf(database).addReceivedBundle(txn, contactId, bundleId);
|
||||
will(returnValue(Collections.singleton(batchId)));
|
||||
oneOf(database).removeLostBatch(txn, contactId, batchId);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
db.receiveBundle(contactId, bundle);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.sf.briar.db;
|
||||
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
@@ -11,16 +12,18 @@ extends DatabaseComponentImplTest {
|
||||
@Override
|
||||
protected <T> DatabaseComponent createDatabaseComponent(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
return createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
return new ReadWriteLockDatabaseComponent<T>(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.sf.briar.db;
|
||||
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
@@ -11,16 +12,18 @@ extends DatabaseComponentImplTest {
|
||||
@Override
|
||||
protected <T> DatabaseComponent createDatabaseComponent(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
return createDatabaseComponentImpl(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> DatabaseComponentImpl<T> createDatabaseComponentImpl(
|
||||
Database<T> database, DatabaseCleaner cleaner,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
return new SynchronizedDatabaseComponent<T>(database, cleaner,
|
||||
batchBuilderProvider);
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
}
|
||||
}
|
||||
|
||||
251
test/net/sf/briar/protocol/BundleReaderTest.java
Normal file
251
test/net/sf/briar/protocol/BundleReaderTest.java
Normal file
@@ -0,0 +1,251 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchBuilder;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.HeaderBuilder;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.protocol.MessageParser;
|
||||
import net.sf.briar.api.serial.Raw;
|
||||
import net.sf.briar.api.serial.Reader;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.inject.Provider;
|
||||
|
||||
public class BundleReaderTest extends TestCase {
|
||||
|
||||
private final long size = 1024L * 1024L;
|
||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
||||
private final List<Raw> rawAcks =
|
||||
Collections.<Raw>singletonList(new TestRaw(ack.getBytes()));
|
||||
private final Set<BatchId> acks = Collections.singleton(ack);
|
||||
private final GroupId sub = new GroupId(TestUtils.getRandomId());
|
||||
private final List<Raw> rawSubs =
|
||||
Collections.<Raw>singletonList(new TestRaw(sub.getBytes()));
|
||||
private final Set<GroupId> subs = Collections.singleton(sub);
|
||||
private final Map<String, String> transports =
|
||||
Collections.singletonMap("foo", "bar");
|
||||
private final byte[] headerSig = TestUtils.getRandomId();
|
||||
private final byte[] messageBody = new byte[123];
|
||||
private final List<Raw> rawMessages =
|
||||
Collections.<Raw>singletonList(new TestRaw(messageBody));
|
||||
private final byte[] batchSig = TestUtils.getRandomId();
|
||||
|
||||
@Test
|
||||
public void testGetHeader() throws IOException, SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
final Reader reader = context.mock(Reader.class);
|
||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
||||
oneOf(headerBuilderProvider).get();
|
||||
will(returnValue(headerBuilder));
|
||||
// Acks
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawAcks));
|
||||
oneOf(headerBuilder).addAcks(acks);
|
||||
// Subs
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawSubs));
|
||||
oneOf(headerBuilder).addSubscriptions(subs);
|
||||
// Transports
|
||||
oneOf(reader).readMap(String.class, String.class);
|
||||
will(returnValue(transports));
|
||||
oneOf(headerBuilder).addTransports(transports);
|
||||
// Signature
|
||||
oneOf(reader).readRaw();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(headerBuilder).setSignature(headerSig);
|
||||
// Build the header
|
||||
oneOf(headerBuilder).build();
|
||||
will(returnValue(header));
|
||||
}});
|
||||
BundleReader r = createBundleReader(reader, messageParser,
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
assertEquals(header, r.getHeader());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchBeforeHeaderThrowsException() throws IOException,
|
||||
SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
final Reader reader = context.mock(Reader.class);
|
||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
BundleReader r = createBundleReader(reader, messageParser,
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
try {
|
||||
r.getNextBatch();
|
||||
assertTrue(false);
|
||||
} catch(IllegalStateException expected) {}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeaderNoBatches() throws IOException,
|
||||
SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
final Reader reader = context.mock(Reader.class);
|
||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
||||
oneOf(headerBuilderProvider).get();
|
||||
will(returnValue(headerBuilder));
|
||||
// Acks
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawAcks));
|
||||
oneOf(headerBuilder).addAcks(acks);
|
||||
// Subs
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawSubs));
|
||||
oneOf(headerBuilder).addSubscriptions(subs);
|
||||
// Transports
|
||||
oneOf(reader).readMap(String.class, String.class);
|
||||
will(returnValue(transports));
|
||||
oneOf(headerBuilder).addTransports(transports);
|
||||
// Signature
|
||||
oneOf(reader).readRaw();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(headerBuilder).setSignature(headerSig);
|
||||
// Build the header
|
||||
oneOf(headerBuilder).build();
|
||||
will(returnValue(header));
|
||||
// No batches
|
||||
oneOf(reader).readListStart();
|
||||
oneOf(reader).hasListEnd();
|
||||
will(returnValue(true));
|
||||
oneOf(reader).readListEnd();
|
||||
}});
|
||||
BundleReader r = createBundleReader(reader, messageParser,
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
assertEquals(header, r.getHeader());
|
||||
assertNull(r.getNextBatch());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeaderOneBatch() throws IOException,
|
||||
SignatureException {
|
||||
Mockery context = new Mockery();
|
||||
final Reader reader = context.mock(Reader.class);
|
||||
final MessageParser messageParser = context.mock(MessageParser.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<HeaderBuilder> headerBuilderProvider =
|
||||
context.mock(Provider.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Provider<BatchBuilder> batchBuilderProvider =
|
||||
context.mock(Provider.class, "batchBuilderProvider");
|
||||
final HeaderBuilder headerBuilder = context.mock(HeaderBuilder.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
final BatchBuilder batchBuilder = context.mock(BatchBuilder.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
final Message message = context.mock(Message.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reader).setReadLimit(Header.MAX_SIZE);
|
||||
oneOf(headerBuilderProvider).get();
|
||||
will(returnValue(headerBuilder));
|
||||
// Acks
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawAcks));
|
||||
oneOf(headerBuilder).addAcks(acks);
|
||||
// Subs
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawSubs));
|
||||
oneOf(headerBuilder).addSubscriptions(subs);
|
||||
// Transports
|
||||
oneOf(reader).readMap(String.class, String.class);
|
||||
will(returnValue(transports));
|
||||
oneOf(headerBuilder).addTransports(transports);
|
||||
// Signature
|
||||
oneOf(reader).readRaw();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(headerBuilder).setSignature(headerSig);
|
||||
// Build the header
|
||||
oneOf(headerBuilder).build();
|
||||
will(returnValue(header));
|
||||
// First batch
|
||||
oneOf(reader).readListStart();
|
||||
oneOf(reader).hasListEnd();
|
||||
will(returnValue(false));
|
||||
oneOf(reader).setReadLimit(Batch.MAX_SIZE);
|
||||
oneOf(batchBuilderProvider).get();
|
||||
will(returnValue(batchBuilder));
|
||||
oneOf(reader).readList(Raw.class);
|
||||
will(returnValue(rawMessages));
|
||||
oneOf(messageParser).parseMessage(messageBody);
|
||||
will(returnValue(message));
|
||||
oneOf(batchBuilder).addMessage(message);
|
||||
oneOf(reader).readRaw();
|
||||
will(returnValue(batchSig));
|
||||
oneOf(batchBuilder).setSignature(batchSig);
|
||||
oneOf(batchBuilder).build();
|
||||
will(returnValue(batch));
|
||||
// No more batches
|
||||
oneOf(reader).hasListEnd();
|
||||
will(returnValue(true));
|
||||
oneOf(reader).readListEnd();
|
||||
}});
|
||||
BundleReader r = createBundleReader(reader, messageParser,
|
||||
headerBuilderProvider, batchBuilderProvider);
|
||||
|
||||
assertEquals(header, r.getHeader());
|
||||
assertEquals(batch, r.getNextBatch());
|
||||
assertNull(r.getNextBatch());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private BundleReader createBundleReader(Reader reader,
|
||||
MessageParser messageParser,
|
||||
Provider<HeaderBuilder> headerBuilderProvider,
|
||||
Provider<BatchBuilder> batchBuilderProvider) {
|
||||
return new BundleReader(reader, messageParser, headerBuilderProvider,
|
||||
batchBuilderProvider) {
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
242
test/net/sf/briar/protocol/BundleWriterTest.java
Normal file
242
test/net/sf/briar/protocol/BundleWriterTest.java
Normal file
@@ -0,0 +1,242 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.briar.TestUtils;
|
||||
import net.sf.briar.api.protocol.Batch;
|
||||
import net.sf.briar.api.protocol.BatchId;
|
||||
import net.sf.briar.api.protocol.Bundle;
|
||||
import net.sf.briar.api.protocol.GroupId;
|
||||
import net.sf.briar.api.protocol.Header;
|
||||
import net.sf.briar.api.protocol.Message;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.Mockery;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BundleWriterTest extends TestCase {
|
||||
|
||||
private final BatchId ack = new BatchId(TestUtils.getRandomId());
|
||||
private final Set<BatchId> acks = Collections.singleton(ack);
|
||||
private final GroupId sub = new GroupId(TestUtils.getRandomId());
|
||||
private final Set<GroupId> subs = Collections.singleton(sub);
|
||||
private final Map<String, String> transports =
|
||||
Collections.singletonMap("foo", "bar");
|
||||
private final byte[] headerSig = TestUtils.getRandomId();
|
||||
private final long capacity = 1024L * 1024L;
|
||||
private final byte[] messageBody = new byte[123];
|
||||
private final byte[] batchSig = TestUtils.getRandomId();
|
||||
|
||||
@Test
|
||||
public void testAddHeader() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Acks
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getAcks();
|
||||
will(returnValue(acks));
|
||||
oneOf(writer).writeRaw(ack);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Subs
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getSubscriptions();
|
||||
will(returnValue(subs));
|
||||
oneOf(writer).writeRaw(sub);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Transports
|
||||
oneOf(header).getTransports();
|
||||
will(returnValue(transports));
|
||||
oneOf(writer).writeMap(transports);
|
||||
// Signature
|
||||
oneOf(header).getSignature();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(writer).writeRaw(headerSig);
|
||||
}});
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
w.addHeader(header);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddHeaderEmptyLists() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Acks
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getAcks();
|
||||
will(returnValue(Collections.emptySet()));
|
||||
oneOf(writer).writeListEnd();
|
||||
// Subs
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getSubscriptions();
|
||||
will(returnValue(Collections.emptySet()));
|
||||
oneOf(writer).writeListEnd();
|
||||
// Transports
|
||||
oneOf(header).getTransports();
|
||||
will(returnValue(Collections.emptyMap()));
|
||||
oneOf(writer).writeMap(Collections.emptyMap());
|
||||
// Signature
|
||||
oneOf(header).getSignature();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(writer).writeRaw(headerSig);
|
||||
}});
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
w.addHeader(header);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchBeforeHeaderThrowsException() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
try {
|
||||
w.addBatch(batch);
|
||||
assertTrue(false);
|
||||
} catch(IllegalStateException expected) {}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseBeforeHeaderThrowsException() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
try {
|
||||
w.close();
|
||||
assertTrue(false);
|
||||
} catch(IllegalStateException expected) {}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseWithoutBatchesDoesNotThrowException()
|
||||
throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Acks
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getAcks();
|
||||
will(returnValue(acks));
|
||||
oneOf(writer).writeRaw(ack);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Subs
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getSubscriptions();
|
||||
will(returnValue(subs));
|
||||
oneOf(writer).writeRaw(sub);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Transports
|
||||
oneOf(header).getTransports();
|
||||
will(returnValue(transports));
|
||||
oneOf(writer).writeMap(transports);
|
||||
// Signature
|
||||
oneOf(header).getSignature();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(writer).writeRaw(headerSig);
|
||||
// Close - write an empty list of batches
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(writer).writeListEnd();
|
||||
oneOf(writer).close();
|
||||
}});
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
w.addHeader(header);
|
||||
w.close();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddHeaderAndTwoBatches() throws IOException {
|
||||
Mockery context = new Mockery();
|
||||
final Writer writer = context.mock(Writer.class);
|
||||
final Header header = context.mock(Header.class);
|
||||
final Batch batch = context.mock(Batch.class);
|
||||
final Message message = context.mock(Message.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Acks
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getAcks();
|
||||
will(returnValue(acks));
|
||||
oneOf(writer).writeRaw(ack);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Subs
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(header).getSubscriptions();
|
||||
will(returnValue(subs));
|
||||
oneOf(writer).writeRaw(sub);
|
||||
oneOf(writer).writeListEnd();
|
||||
// Transports
|
||||
oneOf(header).getTransports();
|
||||
will(returnValue(transports));
|
||||
oneOf(writer).writeMap(transports);
|
||||
// Signature
|
||||
oneOf(header).getSignature();
|
||||
will(returnValue(headerSig));
|
||||
oneOf(writer).writeRaw(headerSig);
|
||||
// First batch
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singleton(message)));
|
||||
oneOf(message).getBody();
|
||||
will(returnValue(messageBody));
|
||||
oneOf(writer).writeRaw(messageBody);
|
||||
oneOf(writer).writeListEnd();
|
||||
oneOf(batch).getSignature();
|
||||
will(returnValue(batchSig));
|
||||
oneOf(writer).writeRaw(batchSig);
|
||||
// Second batch
|
||||
oneOf(writer).writeListStart();
|
||||
oneOf(batch).getMessages();
|
||||
will(returnValue(Collections.singleton(message)));
|
||||
oneOf(message).getBody();
|
||||
will(returnValue(messageBody));
|
||||
oneOf(writer).writeRaw(messageBody);
|
||||
oneOf(writer).writeListEnd();
|
||||
oneOf(batch).getSignature();
|
||||
will(returnValue(batchSig));
|
||||
oneOf(writer).writeRaw(batchSig);
|
||||
// Close
|
||||
oneOf(writer).writeListEnd();
|
||||
oneOf(writer).close();
|
||||
}});
|
||||
BundleWriter w = createBundleWriter(writer);
|
||||
|
||||
w.addHeader(header);
|
||||
w.addBatch(batch);
|
||||
w.addBatch(batch);
|
||||
w.close();
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
private BundleWriter createBundleWriter(Writer writer) {
|
||||
return new BundleWriter(writer, capacity) {
|
||||
public Bundle build() throws IOException {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
29
test/net/sf/briar/protocol/TestRaw.java
Normal file
29
test/net/sf/briar/protocol/TestRaw.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package net.sf.briar.protocol;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.sf.briar.api.serial.Raw;
|
||||
|
||||
class TestRaw implements Raw {
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
TestRaw(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(o instanceof Raw) return Arrays.equals(bytes, ((Raw) o).getBytes());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user