mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Add cleanup manager.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
@@ -69,6 +70,8 @@ import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||
@@ -610,11 +613,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the message is in the DB (which it's not)
|
||||
exactly(12).of(database).startTransaction();
|
||||
exactly(15).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(12).of(database).containsMessage(txn, messageId);
|
||||
exactly(15).of(database).containsMessage(txn, messageId);
|
||||
will(returnValue(false));
|
||||
exactly(12).of(database).abortTransaction(txn);
|
||||
exactly(15).of(database).abortTransaction(txn);
|
||||
// Allow other checks to pass
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
@@ -639,7 +642,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessage(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -647,7 +650,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageMetadata(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -655,7 +658,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageState(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -663,7 +666,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageStatus(transaction, contactId, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -678,6 +681,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.setCleanupTimerDuration(transaction, message.getId(),
|
||||
HOURS.toMillis(1)));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.setMessagePermanent(transaction, message.getId()));
|
||||
@@ -703,7 +715,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageDependencies(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -711,12 +723,28 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageDependents(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.startCleanupTimer(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.stopCleanupTimer(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -997,6 +1025,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).startCleanupTimer(txn, messageId);
|
||||
will(returnValue(TIMER_NOT_STARTED)); // No cleanup duration was set
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||
}});
|
||||
@@ -1009,6 +1040,56 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDuplicateAck() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(false)); // Already acked
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
eventExecutor, shutdownManager);
|
||||
|
||||
db.transaction(false, transaction -> {
|
||||
Ack a = new Ack(singletonList(messageId));
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveAckWithCleanupTimer() throws Exception {
|
||||
long deadline = System.currentTimeMillis();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).startCleanupTimer(txn, messageId);
|
||||
will(returnValue(deadline));
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(eventBus).broadcast(with(any(
|
||||
CleanupTimerStartedEvent.class)));
|
||||
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
eventExecutor, shutdownManager);
|
||||
|
||||
db.transaction(false, transaction -> {
|
||||
Ack a = new Ack(singletonList(messageId));
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveMessage() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
|
||||
@@ -57,10 +57,11 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
@@ -355,7 +356,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertTrue(ids.isEmpty());
|
||||
|
||||
// Sharing the message should make it sendable
|
||||
db.setMessageShared(txn, messageId);
|
||||
db.setMessageShared(txn, messageId, true);
|
||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||
assertEquals(singletonList(messageId), ids);
|
||||
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
|
||||
@@ -635,8 +636,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
|
||||
// The group should not be visible to the contact
|
||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||
assertEquals(emptyMap(),
|
||||
db.getGroupVisibility(txn, groupId));
|
||||
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||
|
||||
// Make the group visible to the contact
|
||||
db.addGroupVisibility(txn, contactId, groupId, false);
|
||||
@@ -659,8 +659,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Make the group invisible again
|
||||
db.removeGroupVisibility(txn, contactId, groupId);
|
||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||
assertEquals(emptyMap(),
|
||||
db.getGroupVisibility(txn, groupId));
|
||||
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
@@ -2044,7 +2043,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
|
||||
|
||||
// Share the message - now it should be sendable immediately
|
||||
db.setMessageShared(txn, messageId);
|
||||
db.setMessageShared(txn, messageId, true);
|
||||
assertEquals(0, db.getNextSendTime(txn, contactId));
|
||||
|
||||
// Mark the message as requested - it should still be sendable
|
||||
@@ -2412,6 +2411,87 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertFalse(db.wasDirtyOnInitialisation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanupTimer() throws Exception {
|
||||
long duration = 60_000;
|
||||
long now = System.currentTimeMillis();
|
||||
AtomicLong time = new AtomicLong(now);
|
||||
Database<Connection> db =
|
||||
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Add a group and a message
|
||||
db.addGroup(txn, group);
|
||||
db.addMessage(txn, message, DELIVERED, false, false, null);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Set the message's cleanup timer duration
|
||||
db.setCleanupTimerDuration(txn, messageId, duration);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Start the message's cleanup timer
|
||||
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// The timer can't be started again
|
||||
assertEquals(TIMER_NOT_STARTED, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// No messages should be due for deletion, but the message should be
|
||||
// scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Stop the timer
|
||||
db.stopCleanupTimer(txn, messageId);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Start the timer again
|
||||
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// No messages should be due for deletion, but the message should be
|
||||
// scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// 1 ms before the timer expires, no messages should be due for
|
||||
// deletion but the message should be scheduled for deletion
|
||||
time.set(now + duration - 1);
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// When the timer expires, the message should be due and scheduled for
|
||||
// deletion
|
||||
time.set(now + duration);
|
||||
assertEquals(singletonMap(messageId, groupId),
|
||||
db.getMessagesToDelete(txn));
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// 1 ms after the timer expires, the message should be due and
|
||||
// scheduled for deletion
|
||||
time.set(now + duration + 1);
|
||||
assertEquals(singletonMap(messageId, groupId),
|
||||
db.getMessagesToDelete(txn));
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Once the message has been deleted, it should no longer be due
|
||||
// or scheduled for deletion
|
||||
db.deleteMessage(txn, messageId);
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
}
|
||||
|
||||
private Database<Connection> open(boolean resume) throws Exception {
|
||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user