Mirror the remote auto-delete timer.

This commit is contained in:
akwizgran
2020-12-02 15:00:45 +00:00
committed by Torsten Grote
parent d7a2de5817
commit 27dbe23914
10 changed files with 331 additions and 40 deletions

View File

@@ -328,7 +328,8 @@ public class ConversationViewModel extends DbViewModel
return privateMessageFactory.createPrivateMessage(groupId, return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers); timestamp, text, headers);
} else { } else {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
return privateMessageFactory.createPrivateMessage(groupId, return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, timer); timestamp, text, headers, timer);
} }

View File

@@ -173,7 +173,8 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
Contact contact = contactManager.getContact(txn, c); Contact contact = contactManager.getContact(txn, c);
long timestamp = conversationManager long timestamp = conversationManager
.getTimestampForOutgoingMessage(txn, c); .getTimestampForOutgoingMessage(txn, c);
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
contexts.add(new InvitationContext(contact, timestamp, timer)); contexts.add(new InvitationContext(contact, timestamp, timer));
} catch (NoSuchContactException e) { } catch (NoSuchContactException e) {
// Continue // Continue

View File

@@ -24,8 +24,26 @@ public interface AutoDeleteManager {
*/ */
int MINOR_VERSION = 0; int MINOR_VERSION = 0;
long getAutoDeleteTimer(Transaction txn, ContactId c) throws DbException; /**
* Returns the auto-delete timer duration for the given contact, for use in
* a message with the given timestamp. The timestamp is stored.
*/
long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException;
/**
* Sets the auto-delete timer duration for the given contact.
*/
void setAutoDeleteTimer(Transaction txn, ContactId c, long timer) void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException; throws DbException;
/**
* Receives an auto-delete timer duration from the given contact, carried
* in a message with the given timestamp. The local timer is set to the
* same duration unless it has been
* {@link #setAutoDeleteTimer(Transaction, ContactId, long) changed} more
* recently than the remote timer.
*/
void receiveAutoDeleteTimer(Transaction txn, ContactId c, long timer,
long timestamp) throws DbException;
} }

View File

@@ -2,6 +2,28 @@ package org.briarproject.briar.autodelete;
interface AutoDeleteConstants { interface AutoDeleteConstants {
// Group metadata key for storing the auto-delete timer /**
String GROUP_KEY_AUTO_DELETE_TIMER = "autoDeleteTimer"; * Group metadata key for storing the auto-delete timer duration.
*/
String GROUP_KEY_TIMER = "autoDeleteTimer";
/**
* Group metadata key for storing the timestamp of the latest incoming or
* outgoing message carrying an auto-delete timer (including a null timer).
*/
String GROUP_KEY_TIMESTAMP = "autoDeleteTimestamp";
/**
* Group metadata key for storing the previous auto-delete timer duration.
* This is used to decide whether a local change to the duration should be
* overwritten by a duration received from the contact.
*/
String GROUP_KEY_PREVIOUS_TIMER = "autoDeletePreviousTimer";
/**
* Special value for {@link #GROUP_KEY_PREVIOUS_TIMER} indicating that
* there are no local changes to the auto-delete timer duration that need
* to be compared with durations received from the contact.
*/
long NO_PREVIOUS_TIMER = 0;
} }

View File

@@ -17,19 +17,29 @@ import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupFactory; import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_PREVIOUS_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMESTAMP;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.NO_PREVIOUS_TIMER;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class AutoDeleteManagerImpl class AutoDeleteManagerImpl
implements AutoDeleteManager, OpenDatabaseHook, ContactHook { implements AutoDeleteManager, OpenDatabaseHook, ContactHook {
private static final Logger LOG =
getLogger(AutoDeleteManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final GroupFactory groupFactory; private final GroupFactory groupFactory;
@@ -69,14 +79,22 @@ class AutoDeleteManagerImpl
} }
@Override @Override
public long getAutoDeleteTimer(Transaction txn, ContactId c) public long getAutoDeleteTimer(Transaction txn, ContactId c, long timestamp)
throws DbException { throws DbException {
try { try {
Group g = getGroup(db.getContact(txn, c)); Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta = BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId()); clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
return meta.getLong(GROUP_KEY_AUTO_DELETE_TIMER, long timer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
NO_AUTO_DELETE_TIMER); if (LOG.isLoggable(INFO)) {
LOG.info("Sending message with auto-delete timer " + timer);
}
// Update the timestamp and clear the previous timer, if any
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, timestamp),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
return timer;
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -85,18 +103,60 @@ class AutoDeleteManagerImpl
@Override @Override
public void setAutoDeleteTimer(Transaction txn, ContactId c, long timer) public void setAutoDeleteTimer(Transaction txn, ContactId c, long timer)
throws DbException { throws DbException {
if (timer != NO_AUTO_DELETE_TIMER && validateTimer(timer);
(timer < MIN_AUTO_DELETE_TIMER_MS ||
timer > MAX_AUTO_DELETE_TIMER_MS)) {
throw new IllegalArgumentException();
}
try { try {
Group g = getGroup(db.getContact(txn, c)); Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta =
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer)); clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimer = meta.getLong(GROUP_KEY_TIMER, NO_AUTO_DELETE_TIMER);
if (timer == oldTimer) return;
if (LOG.isLoggable(INFO)) {
LOG.info("Setting auto-delete timer to " + timer);
}
// Store the new timer and the previous timer
meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, timer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldTimer));
clientHelper.mergeGroupMetadata(txn, g.getId(), meta); clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) { } catch (FormatException e) {
throw new AssertionError(e); throw new DbException(e);
}
}
@Override
public void receiveAutoDeleteTimer(Transaction txn, ContactId c,
long timer, long timestamp) throws DbException {
validateTimer(timer);
try {
Group g = getGroup(db.getContact(txn, c));
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g.getId());
long oldTimestamp = meta.getLong(GROUP_KEY_TIMESTAMP, 0L);
if (timestamp <= oldTimestamp) return;
long oldTimer =
meta.getLong(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
meta = new BdfDictionary();
if (oldTimer == NO_PREVIOUS_TIMER) {
// We don't have an unsent change. Mirror their timer
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer);
}
meta.put(GROUP_KEY_TIMER, timer);
} else if (timer != oldTimer) {
// Their sent change trumps our unsent change. Mirror their
// timer and clear the previous timer to drop our unsent change
if (LOG.isLoggable(INFO)) {
LOG.info("Mirroring auto-delete timer " + timer
+ " and forgetting unsent change");
}
meta.put(GROUP_KEY_TIMER, timer);
meta.put(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER);
}
// Always update the timestamp
meta.put(GROUP_KEY_TIMESTAMP, timestamp);
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new DbException(e);
} }
} }
@@ -104,4 +164,12 @@ class AutoDeleteManagerImpl
byte[] descriptor = c.getAuthor().getId().getBytes(); byte[] descriptor = c.getAuthor().getId().getBytes();
return groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION, descriptor); return groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION, descriptor);
} }
private void validateTimer(long timer) {
if (timer != NO_AUTO_DELETE_TIMER &&
(timer < MIN_AUTO_DELETE_TIMER_MS ||
timer > MAX_AUTO_DELETE_TIMER_MS)) {
throw new IllegalArgumentException();
}
}
} }

View File

@@ -100,7 +100,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m; Message m;
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeRequestMessage(s.getContactGroupId(), m = messageEncoder.encodeRequestMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), author, text, timer); timestamp, s.getLastLocalMessageId(), author, text, timer);
sendMessage(txn, REQUEST, s.getSessionId(), m, true, timer); sendMessage(txn, REQUEST, s.getSessionId(), m, true, timer);
@@ -120,7 +121,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m; Message m;
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(), timestamp, s.getLastLocalMessageId(), s.getSessionId(),
ephemeralPublicKey, acceptTimestamp, transportProperties, ephemeralPublicKey, acceptTimestamp, transportProperties,
@@ -141,7 +143,8 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
Message m; Message m;
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
timestamp, s.getLastLocalMessageId(), s.getSessionId(), timestamp, s.getLastLocalMessageId(), s.getSessionId(),
timer); timer);

View File

@@ -152,9 +152,11 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation // Set auto-delete timer if manually accepting an invitation
long timer = visibleInUi long timer = NO_AUTO_DELETE_TIMER;
? autoDeleteManager.getAutoDeleteTimer(txn, c) if (visibleInUi) {
: NO_AUTO_DELETE_TIMER; timer = autoDeleteManager
.getAutoDeleteTimer(txn, c, localTimestamp);
}
m = messageEncoder.encodeJoinMessage(s.getContactGroupId(), m = messageEncoder.encodeJoinMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp, s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
@@ -179,9 +181,11 @@ abstract class AbstractProtocolEngine<S extends Session<?>>
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
// Set auto-delete timer if manually accepting an invitation // Set auto-delete timer if manually accepting an invitation
long timer = visibleInUi long timer = NO_AUTO_DELETE_TIMER;
? autoDeleteManager.getAutoDeleteTimer(txn, c) if (visibleInUi) {
: NO_AUTO_DELETE_TIMER; timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
}
m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(), m = messageEncoder.encodeLeaveMessage(s.getContactGroupId(),
s.getPrivateGroupId(), localTimestamp, s.getPrivateGroupId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);

View File

@@ -142,7 +142,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s); long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeInviteMessage(s.getContactGroupId(), m = messageEncoder.encodeInviteMessage(s.getContactGroupId(),
localTimestamp, s.getLastLocalMessageId(), descriptor, localTimestamp, s.getLastLocalMessageId(), descriptor,
text, timer); text, timer);
@@ -209,7 +210,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s); long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(), m = messageEncoder.encodeAcceptMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);
@@ -263,7 +265,8 @@ abstract class ProtocolEngineImpl<S extends Shareable>
long localTimestamp = getTimestampForVisibleMessage(txn, s); long localTimestamp = getTimestampForVisibleMessage(txn, s);
ContactId c = getContactId(txn, s.getContactGroupId()); ContactId c = getContactId(txn, s.getContactGroupId());
if (contactSupportsAutoDeletion(txn, c)) { if (contactSupportsAutoDeletion(txn, c)) {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c); long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
localTimestamp);
m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(), m = messageEncoder.encodeDeclineMessage(s.getContactGroupId(),
s.getShareableId(), localTimestamp, s.getShareableId(), localTimestamp,
s.getLastLocalMessageId(), timer); s.getLastLocalMessageId(), timer);

View File

@@ -18,12 +18,18 @@ import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KE
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MAX_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.autodelete.AutoDeleteManager.CLIENT_ID; import static org.briarproject.briar.api.autodelete.AutoDeleteManager.CLIENT_ID;
import static org.briarproject.briar.api.autodelete.AutoDeleteManager.MAJOR_VERSION; import static org.briarproject.briar.api.autodelete.AutoDeleteManager.MAJOR_VERSION;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_AUTO_DELETE_TIMER; import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_PREVIOUS_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMER;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.GROUP_KEY_TIMESTAMP;
import static org.briarproject.briar.autodelete.AutoDeleteConstants.NO_PREVIOUS_TIMER;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
// Thank you, I'm using them for readability
@SuppressWarnings("UnnecessaryLocalVariable")
public class AutoDeleteManagerImplTest extends BrambleMockTestCase { public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
@@ -35,6 +41,7 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = getContact(); private final Contact contact = getContact();
private final long now = System.currentTimeMillis();
private final AutoDeleteManagerImpl autoDeleteManager; private final AutoDeleteManagerImpl autoDeleteManager;
@@ -113,15 +120,44 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testStoresTimer() throws Exception { public void testStoresTimer() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
long timer = MAX_AUTO_DELETE_TIMER_MS; long oldTimer = MIN_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of( long newTimer = MAX_AUTO_DELETE_TIMER_MS;
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer)); BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, oldTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, newTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldTimer));
expectGetContact(txn); expectGetContact(txn);
expectGetContactGroup(); expectGetContactGroup();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn, oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), meta); contactGroup.getId(), newMeta);
}});
autoDeleteManager.setAutoDeleteTimer(txn, contact.getId(), newTimer);
}
@Test
public void testDoesNotStoreTimerIfUnchanged() throws Exception {
Transaction txn = new Transaction(null, false);
long timer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, timer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(meta));
}}); }});
autoDeleteManager.setAutoDeleteTimer(txn, contact.getId(), timer); autoDeleteManager.setAutoDeleteTimer(txn, contact.getId(), timer);
@@ -133,7 +169,10 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
long timer = MAX_AUTO_DELETE_TIMER_MS; long timer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()), new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()),
new BdfEntry(GROUP_KEY_AUTO_DELETE_TIMER, timer)); new BdfEntry(GROUP_KEY_TIMER, timer));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, now),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn); expectGetContact(txn);
expectGetContactGroup(); expectGetContactGroup();
@@ -141,10 +180,12 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
oneOf(clientHelper).getGroupMetadataAsDictionary(txn, oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
will(returnValue(meta)); will(returnValue(meta));
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
newMeta);
}}); }});
assertEquals(timer, assertEquals(timer, autoDeleteManager
autoDeleteManager.getAutoDeleteTimer(txn, contact.getId())); .getAutoDeleteTimer(txn, contact.getId(), now));
} }
@Test @Test
@@ -152,6 +193,44 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
BdfDictionary meta = BdfDictionary.of( BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt())); new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, now),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(meta));
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
newMeta);
}});
assertEquals(NO_AUTO_DELETE_TIMER, autoDeleteManager
.getAutoDeleteTimer(txn, contact.getId(), now));
}
@Test
public void testIgnoresReceivedTimerWithEarlierTimestamp()
throws Exception {
testIgnoresReceivedTimerWithTimestamp(now - 1);
}
@Test
public void testIgnoresReceivedTimerWithEqualTimestamp() throws Exception {
testIgnoresReceivedTimerWithTimestamp(now);
}
private void testIgnoresReceivedTimerWithTimestamp(long remoteTimestamp)
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
BdfDictionary meta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
expectGetContact(txn); expectGetContact(txn);
expectGetContactGroup(); expectGetContactGroup();
@@ -161,8 +240,99 @@ public class AutoDeleteManagerImplTest extends BrambleMockTestCase {
will(returnValue(meta)); will(returnValue(meta));
}}); }});
assertEquals(NO_AUTO_DELETE_TIMER, autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
autoDeleteManager.getAutoDeleteTimer(txn, contact.getId())); remoteTimer, remoteTimestamp);
}
@Test
public void testMirrorsRemoteTimestampIfNoUnsentChange() throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated and the timer should be mirrored
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp),
new BdfEntry(GROUP_KEY_TIMER, remoteTimer));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
remoteTimer, remoteTimestamp);
}
@Test
public void testDoesNotMirrorUnchangedRemoteTimestampIfUnsentChange()
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long remoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, remoteTimer),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated but the timer should not revert
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
remoteTimer, remoteTimestamp);
}
@Test
public void testMirrorsChangedRemoteTimestampIfUnsentChange()
throws Exception {
Transaction txn = new Transaction(null, false);
long localTimer = MIN_AUTO_DELETE_TIMER_MS;
long oldRemoteTimer = MAX_AUTO_DELETE_TIMER_MS;
long newRemoteTimer = MAX_AUTO_DELETE_TIMER_MS - 1;
long remoteTimestamp = now + 1;
BdfDictionary oldMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMER, localTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, oldRemoteTimer),
new BdfEntry(GROUP_KEY_TIMESTAMP, now));
// The timestamp should be updated , the timer should be mirrored and
// the previous timer should be cleared
BdfDictionary newMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_TIMESTAMP, remoteTimestamp),
new BdfEntry(GROUP_KEY_TIMER, newRemoteTimer),
new BdfEntry(GROUP_KEY_PREVIOUS_TIMER, NO_PREVIOUS_TIMER));
expectGetContact(txn);
expectGetContactGroup();
context.checking(new Expectations() {{
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(oldMeta));
oneOf(clientHelper).mergeGroupMetadata(txn,
contactGroup.getId(), newMeta);
}});
autoDeleteManager.receiveAutoDeleteTimer(txn, contact.getId(),
newRemoteTimer, remoteTimestamp);
} }
private void expectGetContact(Transaction txn) throws Exception { private void expectGetContact(Transaction txn) throws Exception {

View File

@@ -256,7 +256,8 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
void expectGetAutoDeleteTimer() throws Exception { void expectGetAutoDeleteTimer() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId); oneOf(autoDeleteManager).getAutoDeleteTimer(txn, contactId,
localTimestamp);
will(returnValue(NO_AUTO_DELETE_TIMER)); will(returnValue(NO_AUTO_DELETE_TIMER));
}}); }});
} }