diff --git a/api/net/sf/briar/api/protocol/AuthorId.java b/api/net/sf/briar/api/protocol/AuthorId.java index 8b32851a8b766e6e1198e1466decb730393c9444..578c89c2accf4374de24d59bfcd7541808d79f24 100644 --- a/api/net/sf/briar/api/protocol/AuthorId.java +++ b/api/net/sf/briar/api/protocol/AuthorId.java @@ -3,26 +3,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; /** Type-safe wrapper for a byte array that uniquely identifies an author. */ -public class AuthorId { - - public static final int LENGTH = 32; - - // FIXME: Replace this with an isSelf() method that compares an AuthorId - // to any and all local AuthorIds. - public static final AuthorId SELF = new AuthorId(new byte[] { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 - }); - - private final byte[] id; +public class AuthorId extends UniqueId { public AuthorId(byte[] id) { - assert id.length == LENGTH; - this.id = id; - } - - public byte[] getBytes() { - return id; + super(id); } @Override @@ -31,9 +15,4 @@ public class AuthorId { return Arrays.equals(id, ((AuthorId) o).id); return false; } - - @Override - public int hashCode() { - return Arrays.hashCode(id); - } } diff --git a/api/net/sf/briar/api/protocol/BatchId.java b/api/net/sf/briar/api/protocol/BatchId.java index 4b4578b03f32ace399bd6482e9206ce861cbe8f3..ab33800761d9558416896dde3862f3c69d83793e 100644 --- a/api/net/sf/briar/api/protocol/BatchId.java +++ b/api/net/sf/briar/api/protocol/BatchId.java @@ -6,19 +6,10 @@ import java.util.Arrays; * Type-safe wrapper for a byte array that uniquely identifies a batch of * messages. */ -public class BatchId { - - public static final int LENGTH = 32; - - private final byte[] id; +public class BatchId extends UniqueId { public BatchId(byte[] id) { - assert id.length == LENGTH; - this.id = id; - } - - public byte[] getBytes() { - return id; + super(id); } @Override @@ -27,9 +18,4 @@ public class BatchId { return Arrays.equals(id, ((BatchId) o).id); return false; } - - @Override - public int hashCode() { - return Arrays.hashCode(id); - } } diff --git a/api/net/sf/briar/api/protocol/BundleId.java b/api/net/sf/briar/api/protocol/BundleId.java index 2dd7e8f59f5483fa68cf0bc470a34eb429010b04..dfc9f285a5f326c854a7e2a50448d019f7619cf6 100644 --- a/api/net/sf/briar/api/protocol/BundleId.java +++ b/api/net/sf/briar/api/protocol/BundleId.java @@ -6,24 +6,15 @@ import java.util.Arrays; * Type-safe wrapper for a byte array that uniquely identifies a bundle of * acknowledgements, subscriptions, and batches of messages. */ -public class BundleId { +public class BundleId extends UniqueId { public static final BundleId NONE = new BundleId(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 }); - public static final int LENGTH = 32; - - private final byte[] id; - public BundleId(byte[] id) { - assert id.length == LENGTH; - this.id = id; - } - - public byte[] getBytes() { - return id; + super(id); } @Override @@ -32,9 +23,4 @@ public class BundleId { return Arrays.equals(id, ((BundleId) o).id); return false; } - - @Override - public int hashCode() { - return Arrays.hashCode(id); - } } diff --git a/api/net/sf/briar/api/protocol/GroupId.java b/api/net/sf/briar/api/protocol/GroupId.java index 46c0456970c737cfaf5d057fe752f012750f4f86..532941867942b4bb02f4e8a6ed828820c7cbb20f 100644 --- a/api/net/sf/briar/api/protocol/GroupId.java +++ b/api/net/sf/briar/api/protocol/GroupId.java @@ -6,19 +6,10 @@ import java.util.Arrays; * Type-safe wrapper for a byte array that uniquely identifies a group to which * users may subscribe. */ -public class GroupId { - - public static final int LENGTH = 32; - - private final byte[] id; +public class GroupId extends UniqueId { public GroupId(byte[] id) { - assert id.length == LENGTH; - this.id = id; - } - - public byte[] getBytes() { - return id; + super(id); } @Override @@ -27,9 +18,4 @@ public class GroupId { return Arrays.equals(id, ((GroupId) o).id); return false; } - - @Override - public int hashCode() { - return Arrays.hashCode(id); - } } diff --git a/api/net/sf/briar/api/protocol/MessageId.java b/api/net/sf/briar/api/protocol/MessageId.java index ee25caddc0f9a4e2b9b107e1b3c91859b6e0a350..64e0fd0369ce2a47a226078bc115e455990dcce6 100644 --- a/api/net/sf/briar/api/protocol/MessageId.java +++ b/api/net/sf/briar/api/protocol/MessageId.java @@ -3,7 +3,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; /** Type-safe wrapper for a byte array that uniquely identifies a message. */ -public class MessageId { +public class MessageId extends UniqueId { /** Used to indicate that the first message in a thread has no parent. */ public static final MessageId NONE = new MessageId(new byte[] { @@ -11,17 +11,8 @@ public class MessageId { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); - public static final int LENGTH = 32; - - private final byte[] id; - public MessageId(byte[] id) { - assert id.length == LENGTH; - this.id = id; - } - - public byte[] getBytes() { - return id; + super(id); } @Override @@ -30,9 +21,4 @@ public class MessageId { return Arrays.equals(id, ((MessageId) o).id); return false; } - - @Override - public int hashCode() { - return Arrays.hashCode(id); - } } diff --git a/api/net/sf/briar/api/protocol/UniqueId.java b/api/net/sf/briar/api/protocol/UniqueId.java new file mode 100644 index 0000000000000000000000000000000000000000..3eb9cdd15d8eb4d9beaaf620d6a903a699c62c91 --- /dev/null +++ b/api/net/sf/briar/api/protocol/UniqueId.java @@ -0,0 +1,24 @@ +package net.sf.briar.api.protocol; + +import java.util.Arrays; + +public abstract class UniqueId { + + public static final int LENGTH = 32; + + protected final byte[] id; + + protected UniqueId(byte[] id) { + assert id.length == LENGTH; + this.id = id; + } + + public byte[] getBytes() { + return id; + } + + @Override + public int hashCode() { + return Arrays.hashCode(id); + } +} diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 82bd7156e1550ae011d6ca9dfd36b86842255b54..edd4fe55661c895eb4fac16f12f51a7e30870f71 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -61,7 +61,7 @@ DatabaseCleaner.Callback { private int calculateSendability(Txn txn, Message m) throws DbException { int sendability = 0; // One point for a good rating - if(getRating(m.getAuthor()) == Rating.GOOD) sendability++; + if(db.getRating(txn, m.getAuthor()) == Rating.GOOD) sendability++; // One point per sendable child (backward inclusion) sendability += db.getNumberOfSendableChildren(txn, m.getId()); return sendability; diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..30054c911359ee79a363cb34d80396bdaff565e5 --- /dev/null +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -0,0 +1,519 @@ +package net.sf.briar.db; + +import java.io.File; +import java.util.Collections; +import java.util.Random; +import java.util.Set; + +import junit.framework.TestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.db.ContactId; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.Rating; +import net.sf.briar.api.db.Status; +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.Batch; +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.protocol.MessageImpl; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.inject.Provider; + +public abstract class DatabaseComponentTest extends TestCase { + + private final File testDir = TestUtils.getTestDirectory(); + private final Random random = new Random(); + private final AuthorId authorId; + private final ContactId contactId; + private final GroupId groupId; + private final MessageId messageId, parentId; + private final long timestamp; + private final int size; + private final byte[] body; + private final Message message; + private final Object txn = new Object(); + + public DatabaseComponentTest() { + super(); + authorId = new AuthorId(getRandomId()); + contactId = new ContactId(123); + groupId = new GroupId(getRandomId()); + messageId = new MessageId(getRandomId()); + parentId = new MessageId(getRandomId()); + timestamp = System.currentTimeMillis(); + size = 1234; + body = new byte[size]; + random.nextBytes(body); + message = new MessageImpl(messageId, MessageId.NONE, groupId, authorId, + timestamp, body); + } + + protected abstract <T> DatabaseComponent createDatabaseComponent( + Database<T> database, DatabaseCleaner cleaner, + Provider<Batch> batchProvider); + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testSimpleCalls() throws DbException { + 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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // getRating(authorId) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + oneOf(database).commitTransaction(txn); + // addContact(contactId) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).addContact(txn, contactId); + oneOf(database).commitTransaction(txn); + // subscribe(groupId) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).addSubscription(txn, groupId); + oneOf(database).commitTransaction(txn); + // getSubscriptions() + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getSubscriptions(txn); + will(returnValue(subs)); + oneOf(database).commitTransaction(txn); + // unsubscribe(groupId) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).removeSubscription(txn, groupId); + oneOf(database).commitTransaction(txn); + // removeContact(contactId) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).removeContact(txn, contactId); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + assertEquals(Rating.UNRATED, db.getRating(authorId)); + db.addContact(contactId); + db.subscribe(groupId); + assertEquals(Collections.singleton(groupId), db.getSubscriptions()); + db.unsubscribe(groupId); + db.removeContact(contactId); + db.close(); + + context.assertIsSatisfied(); + } + + @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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // setRating(Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(messages)); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // Backward inclusion stops when the message has no parent + oneOf(database).getParent(txn, messageId); + will(returnValue(MessageId.NONE)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.setRating(authorId, Rating.GOOD); + db.close(); + + context.assertIsSatisfied(); + } + + @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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // setRating(Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(messages)); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists + oneOf(database).getParent(txn, messageId); + will(returnValue(parentId)); + // The parent isn't in the DB + oneOf(database).containsMessage(txn, parentId); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.setRating(authorId, Rating.GOOD); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testChangingGroupsStopsBackwardInclusion() throws DbException { + final GroupId groupId1 = new GroupId(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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // setRating(Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(messages)); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists and is in the database + oneOf(database).getParent(txn, messageId); + will(returnValue(parentId)); + oneOf(database).containsMessage(txn, parentId); + will(returnValue(true)); + // The parent is in a different group + oneOf(database).getGroup(txn, messageId); + will(returnValue(groupId)); + oneOf(database).getGroup(txn, parentId); + will(returnValue(groupId1)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.setRating(authorId, Rating.GOOD); + db.close(); + + context.assertIsSatisfied(); + } + + @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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // setRating(Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(messages)); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists, is in the DB, and is in the same group + oneOf(database).getParent(txn, messageId); + will(returnValue(parentId)); + oneOf(database).containsMessage(txn, parentId); + will(returnValue(true)); + oneOf(database).getGroup(txn, messageId); + will(returnValue(groupId)); + oneOf(database).getGroup(txn, parentId); + will(returnValue(groupId)); + // The parent is already sendable + oneOf(database).getSendability(txn, parentId); + will(returnValue(1)); + oneOf(database).setSendability(txn, parentId, 2); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.setRating(authorId, Rating.GOOD); + db.close(); + + context.assertIsSatisfied(); + } + + @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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // setRating(Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(messages)); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists, is in the DB, and is in the same group + oneOf(database).getParent(txn, messageId); + will(returnValue(parentId)); + oneOf(database).containsMessage(txn, parentId); + will(returnValue(true)); + oneOf(database).getGroup(txn, messageId); + will(returnValue(groupId)); + oneOf(database).getGroup(txn, parentId); + will(returnValue(groupId)); + // The parent is not already sendable + oneOf(database).getSendability(txn, parentId); + will(returnValue(0)); + oneOf(database).setSendability(txn, parentId, 1); + oneOf(database).getParent(txn, parentId); + will(returnValue(MessageId.NONE)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.setRating(authorId, Rating.GOOD); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testMessagesAreNotStoredUnlessSubscribed() + throws DbException { + 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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // addLocallyGeneratedMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.addLocallyGeneratedMessage(message); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testDuplicateMessagesAreNotStored() throws DbException { + 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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // addLocallyGeneratedMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(true)); + oneOf(database).addMessage(txn, message); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.addLocallyGeneratedMessage(message); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testAddLocallyGeneratedMessage() throws DbException { + 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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // addLocallyGeneratedMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(true)); + oneOf(database).addMessage(txn, message); + will(returnValue(true)); + oneOf(database).getContacts(txn); + will(returnValue(Collections.singleton(contactId))); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + // The author is unrated and there are no sendable children + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 0); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.addLocallyGeneratedMessage(message); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingASendableMessageTriggersBackwardInclusion() + throws DbException { + 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<Batch> batchProvider = context.mock(Provider.class); + context.checking(new Expectations() {{ + oneOf(database).open(false); + oneOf(cleaner).startCleaning(); + // addLocallyGeneratedMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(true)); + oneOf(database).addMessage(txn, message); + will(returnValue(true)); + oneOf(database).getContacts(txn); + will(returnValue(Collections.singleton(contactId))); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + // The author is rated GOOD and there are two sendable children + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.GOOD)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(2)); + oneOf(database).setSendability(txn, messageId, 3); + // The sendability of the message's ancestors should be updated + oneOf(database).getParent(txn, messageId); + will(returnValue(MessageId.NONE)); + oneOf(database).commitTransaction(txn); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + batchProvider); + + db.open(false); + db.addLocallyGeneratedMessage(message); + db.close(); + + context.assertIsSatisfied(); + } + + private byte[] getRandomId() { + byte[] b = new byte[32]; + random.nextBytes(b); + return b; + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index 7b87d0b84b5cad257ad6e7d50ddced3e7228f976..483b507af2c0dae8b13848c00d44626e109b945d 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -40,7 +40,7 @@ public class H2DatabaseTest extends TestCase { private final File testDir = TestUtils.getTestDirectory(); // The password has the format <file password> <space> <user password> private final String passwordString = "foo bar"; - private final Random random; + private final Random random = new Random(); private final AuthorId authorId; private final BatchId batchId; private final ContactId contactId; @@ -53,7 +53,6 @@ public class H2DatabaseTest extends TestCase { public H2DatabaseTest() { super(); - random = new Random(); authorId = new AuthorId(getRandomId()); batchId = new BatchId(getRandomId()); contactId = new ContactId(123); diff --git a/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java b/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8de0dc1802bd98ed242a6f33396eaccf28fd76ef --- /dev/null +++ b/test/net/sf/briar/db/ReadWriteLockDatabaseComponentTest.java @@ -0,0 +1,17 @@ +package net.sf.briar.db; + +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.protocol.Batch; + +import com.google.inject.Provider; + +public class ReadWriteLockDatabaseComponentTest extends DatabaseComponentTest { + + @Override + protected <T> DatabaseComponent createDatabaseComponent( + Database<T> database, DatabaseCleaner cleaner, + Provider<Batch> batchProvider) { + return new ReadWriteLockDatabaseComponent<T>(database, cleaner, + batchProvider); + } +} diff --git a/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java b/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..758ce1ce4815fb106d83b6dab454db08448487a3 --- /dev/null +++ b/test/net/sf/briar/db/SynchronizedDatabaseComponentTest.java @@ -0,0 +1,18 @@ +package net.sf.briar.db; + +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.protocol.Batch; + +import com.google.inject.Provider; + +public class SynchronizedDatabaseComponentTest extends DatabaseComponentTest { + + @Override + protected <T> DatabaseComponent createDatabaseComponent( + Database<T> database, DatabaseCleaner cleaner, + Provider<Batch> batchProvider) { + return new SynchronizedDatabaseComponent<T>(database, cleaner, + batchProvider); + } + +}