Merge branch '2228-more-mailbox-client-manager-preliminaries' into 'master'

More mailbox client manager preliminaries

See merge request briar/briar!1694
This commit is contained in:
Torsten Grote
2022-08-15 14:27:23 +00:00
17 changed files with 248 additions and 126 deletions

View File

@@ -18,3 +18,7 @@
-dontnote com.google.common.** -dontnote com.google.common.**
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl -dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
# Keep all Jackson-serialisable classes and their members
-keep interface com.fasterxml.jackson.databind.annotation.JsonSerialize
-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class * { *; }

View File

@@ -349,13 +349,13 @@ public interface DatabaseComponent extends TransactionManager {
Metadata query) throws DbException; Metadata query) throws DbException;
/** /**
* Returns the IDs of some messages received from the given contact that * Returns the IDs of all messages received from the given contact that
* need to be acknowledged, up to the given number of messages. * need to be acknowledged.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<MessageId> getMessagesToAck(Transaction txn, ContactId c, Collection<MessageId> getMessagesToAck(Transaction txn, ContactId c)
int maxMessages) throws DbException; throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
@@ -492,6 +492,8 @@ public interface DatabaseComponent extends TransactionManager {
* Returns the message with the given ID for transmission to the given * Returns the message with the given ID for transmission to the given
* contact over a transport with the given maximum latency. Returns null * contact over a transport with the given maximum latency. Returns null
* if the message is no longer visible to the contact. * if the message is no longer visible to the contact.
* <p/>
* Read-only if {@code markAsSent} is false.
* *
* @param markAsSent True if the message should be marked as sent. * @param markAsSent True if the message should be marked as sent.
* If false it can be marked as sent by calling * If false it can be marked as sent by calling

View File

@@ -85,7 +85,8 @@ public class MailboxProperties {
onion.equals(m.onion) && onion.equals(m.onion) &&
authToken.equals(m.authToken) && authToken.equals(m.authToken) &&
NullSafety.equals(inboxId, m.inboxId) && NullSafety.equals(inboxId, m.inboxId) &&
NullSafety.equals(outboxId, m.outboxId); NullSafety.equals(outboxId, m.outboxId) &&
serverSupports.equals(m.serverSupports);
} }
return false; return false;
} }

View File

@@ -67,6 +67,13 @@ public class MailboxStatus {
return attemptsSinceSuccess; return attemptsSinceSuccess;
} }
/**
* Returns the mailbox's supported API versions.
*/
public List<MailboxVersion> getServerSupports() {
return serverSupports;
}
/** /**
* @return true if this status indicates a problem with the mailbox. * @return true if this status indicates a problem with the mailbox.
*/ */

View File

@@ -0,0 +1,39 @@
package org.briarproject.bramble.api.mailbox.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a mailbox update is sent to a contact.
* <p>
* Note that this event is not broadcast when a mailbox is paired or
* unpaired, although updates are sent to all contacts in those situations.
*
* @see MailboxPairedEvent
* @see MailboxUnpairedEvent
*/
@Immutable
@NotNullByDefault
public class MailboxUpdateSentEvent extends Event {
private final ContactId contactId;
private final MailboxUpdate mailboxUpdate;
public MailboxUpdateSentEvent(ContactId contactId,
MailboxUpdate mailboxUpdate) {
this.contactId = contactId;
this.mailboxUpdate = mailboxUpdate;
}
public ContactId getContactId() {
return contactId;
}
public MailboxUpdate getMailboxUpdate() {
return mailboxUpdate;
}
}

View File

@@ -52,6 +52,7 @@ import java.util.Map.Entry;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.sort;
import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
@@ -474,6 +475,8 @@ class ClientHelperImpl implements ClientHelper {
list.add(new MailboxVersion(element.getLong(0).intValue(), list.add(new MailboxVersion(element.getLong(0).intValue(),
element.getLong(1).intValue())); element.getLong(1).intValue()));
} }
// Sort the list of versions for easier comparison
sort(list);
return list; return list;
} }

View File

@@ -620,11 +620,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public Collection<MessageId> getMessagesToAck(Transaction transaction, public Collection<MessageId> getMessagesToAck(Transaction transaction,
ContactId c, int maxMessages) throws DbException { ContactId c) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
return db.getMessagesToAck(txn, c, maxMessages); return db.getMessagesToAck(txn, c, Integer.MAX_VALUE);
} }
@Override @Override
@@ -746,7 +746,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public Message getMessageToSend(Transaction transaction, ContactId c, public Message getMessageToSend(Transaction transaction, ContactId c,
MessageId m, long maxLatency, boolean markAsSent) MessageId m, long maxLatency, boolean markAsSent)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (markAsSent && transaction.isReadOnly()) {
throw new IllegalArgumentException();
}
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
@@ -1097,11 +1099,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
List<MessageId> visible = new ArrayList<>(acked.size()); db.lowerAckFlag(txn, c, acked);
for (MessageId m : acked) {
if (db.containsVisibleMessage(txn, c, m)) visible.add(m);
}
db.lowerAckFlag(txn, c, visible);
} }
@Override @Override

View File

@@ -5,13 +5,20 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException; import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Logger.getLogger;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl { class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
private static final Logger LOG =
getLogger(ContactMailboxConnectivityChecker.class.getName());
private final MailboxApi mailboxApi; private final MailboxApi mailboxApi;
@Inject @Inject
@@ -25,7 +32,9 @@ class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
ApiCall createConnectivityCheckTask(MailboxProperties properties) { ApiCall createConnectivityCheckTask(MailboxProperties properties) {
if (properties.isOwner()) throw new IllegalArgumentException(); if (properties.isOwner()) throw new IllegalArgumentException();
return new SimpleApiCall(() -> { return new SimpleApiCall(() -> {
LOG.info("Checking connectivity of contact's mailbox");
if (!mailboxApi.checkStatus(properties)) throw new ApiException(); if (!mailboxApi.checkStatus(properties)) throw new ApiException();
LOG.info("Contact's mailbox is reachable");
// Call the observers and cache the result // Call the observers and cache the result
onConnectivityCheckSucceeded(clock.currentTimeMillis()); onConnectivityCheckSucceeded(clock.currentTimeMillis());
}); });

View File

@@ -22,7 +22,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@@ -35,6 +34,7 @@ import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES; import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
import static java.util.Collections.sort;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static okhttp3.internal.Util.EMPTY_REQUEST; import static okhttp3.internal.Util.EMPTY_REQUEST;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
@@ -125,6 +125,8 @@ class MailboxApiImpl implements MailboxApi {
if (major < 0 || minor < 0) throw new ApiException(); if (major < 0 || minor < 0) throw new ApiException();
serverSupports.add(new MailboxVersion(major, minor)); serverSupports.add(new MailboxVersion(major, minor));
} }
// Sort the list of versions for easier comparison
sort(serverSupports);
return serverSupports; return serverSupports;
} }
@@ -245,7 +247,7 @@ class MailboxApiImpl implements MailboxApi {
if (time < 1) throw new ApiException(); if (time < 1) throw new ApiException();
list.add(new MailboxFile(MailboxFileId.fromString(name), time)); list.add(new MailboxFile(MailboxFileId.fromString(name), time));
} }
Collections.sort(list); sort(list);
return list; return list;
} catch (JacksonException | InvalidMailboxIdException e) { } catch (JacksonException | InvalidMailboxIdException e) {
throw new ApiException(); throw new ApiException();

View File

@@ -27,6 +27,7 @@ import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.mailbox.MailboxVersion;
import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent; import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent;
import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent; import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent;
import org.briarproject.bramble.api.mailbox.event.MailboxUpdateSentEvent;
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent; import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -114,6 +115,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
} }
Group g = getContactGroup(c); Group g = getContactGroup(c);
storeMessageReplaceLatest(txn, g.getId(), updated); storeMessageReplaceLatest(txn, g.getId(), updated);
txn.attach(new MailboxUpdateSentEvent(c.getId(), updated));
} }
} else { } else {
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
@@ -146,14 +148,16 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
clientHelper.setContactId(txn, g.getId(), c.getId()); clientHelper.setContactId(txn, g.getId(), c.getId());
MailboxProperties ownProps = MailboxProperties ownProps =
mailboxSettingsManager.getOwnMailboxProperties(txn); mailboxSettingsManager.getOwnMailboxProperties(txn);
MailboxUpdate u;
if (ownProps != null) { if (ownProps != null) {
// We are paired, create and send props to the newly added contact // We are paired, create and send props to the newly added contact
createAndSendUpdateWithMailbox(txn, c, ownProps.getServerSupports(), u = createAndSendUpdateWithMailbox(txn, c,
ownProps.getOnion()); ownProps.getServerSupports(), ownProps.getOnion());
} else { } else {
// Not paired, but we still want to get our clientSupports sent // Not paired, but we still want to get our clientSupports sent
sendUpdateNoMailbox(txn, c); u = sendUpdateNoMailbox(txn, c);
} }
txn.attach(new MailboxUpdateSentEvent(c.getId(), u));
} }
@Override @Override

View File

@@ -328,13 +328,13 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
LOG.info("Uploading file"); LOG.info("Uploading file");
mailboxApi.addFile(mailboxProperties, folderId, file); mailboxApi.addFile(mailboxProperties, folderId, file);
markMessagesSentOrAcked(sessionRecord); markMessagesSentOrAcked(sessionRecord);
delete(file);
synchronized (lock) { synchronized (lock) {
if (state != State.WRITING_UPLOADING) return; if (state != State.WRITING_UPLOADING) return;
state = State.CHECKING_FOR_DATA; state = State.CHECKING_FOR_DATA;
apiCall = null; apiCall = null;
this.file = null; this.file = null;
} }
delete(file);
checkForDataToSend(); checkForDataToSend();
} }

View File

@@ -88,8 +88,10 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
public MailboxWorker createContactListWorkerForOwnMailbox( public MailboxWorker createContactListWorkerForOwnMailbox(
ConnectivityChecker connectivityChecker, ConnectivityChecker connectivityChecker,
MailboxProperties properties) { MailboxProperties properties) {
return new OwnMailboxContactListWorker(ioExecutor, db, eventBus, OwnMailboxContactListWorker worker = new OwnMailboxContactListWorker(
connectivityChecker, mailboxApiCaller, mailboxApi, ioExecutor, db, eventBus, connectivityChecker, mailboxApiCaller,
mailboxUpdateManager, properties); mailboxApi, mailboxUpdateManager, properties);
eventBus.addListener(worker);
return worker;
} }
} }

View File

@@ -59,6 +59,7 @@ class OwnMailboxConnectivityChecker extends ConnectivityCheckerImpl {
private boolean checkConnectivityAndStoreResult( private boolean checkConnectivityAndStoreResult(
MailboxProperties properties) throws DbException { MailboxProperties properties) throws DbException {
try { try {
LOG.info("Checking whether own mailbox is reachable");
List<MailboxVersion> serverSupports = List<MailboxVersion> serverSupports =
mailboxApi.getServerSupports(properties); mailboxApi.getServerSupports(properties);
LOG.info("Own mailbox is reachable"); LOG.info("Own mailbox is reachable");

View File

@@ -14,7 +14,9 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@@ -61,26 +63,32 @@ class MailboxOutgoingSession extends SimplexOutgoingSession {
@Override @Override
void sendAcks() throws DbException, IOException { void sendAcks() throws DbException, IOException {
while (!isInterrupted()) { List<MessageId> idsToAck = loadMessageIdsToAck();
Collection<MessageId> idsToAck = loadMessageIdsToAck(); int idsSent = 0;
if (idsToAck.isEmpty()) break; while (idsSent < idsToAck.size() && !isInterrupted()) {
recordWriter.writeAck(new Ack(idsToAck)); int idsRemaining = idsToAck.size() - idsSent;
sessionRecord.onAckSent(idsToAck); long capacity = getRemainingCapacity();
long idCapacity =
(capacity - RECORD_HEADER_BYTES) / MessageId.LENGTH;
if (idCapacity == 0) break; // Out of capacity
int idsInRecord = (int) min(idCapacity, MAX_MESSAGE_IDS);
int idsToSend = min(idsRemaining, idsInRecord);
List<MessageId> acked =
idsToAck.subList(idsSent, idsSent + idsToSend);
recordWriter.writeAck(new Ack(acked));
sessionRecord.onAckSent(acked);
LOG.info("Sent ack"); LOG.info("Sent ack");
idsSent += idsToSend;
} }
} }
private Collection<MessageId> loadMessageIdsToAck() throws DbException { private List<MessageId> loadMessageIdsToAck() throws DbException {
long idCapacity = (getRemainingCapacity() - RECORD_HEADER_BYTES)
/ MessageId.LENGTH;
if (idCapacity <= 0) return emptyList(); // Out of capacity
int maxMessageIds = (int) min(idCapacity, MAX_MESSAGE_IDS);
Collection<MessageId> ids = db.transactionWithResult(true, txn -> Collection<MessageId> ids = db.transactionWithResult(true, txn ->
db.getMessagesToAck(txn, contactId, maxMessageIds)); db.getMessagesToAck(txn, contactId));
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info(ids.size() + " messages to ack"); LOG.info(ids.size() + " messages to ack");
} }
return ids; return new ArrayList<>(ids);
} }
private long getRemainingCapacity() { private long getRemainingCapacity() {

View File

@@ -389,7 +389,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(true, transaction -> db.transaction(true, transaction ->
db.getMessagesToAck(transaction, contactId, 123)); db.getMessagesToAck(transaction, contactId));
fail(); fail();
} catch (NoSuchContactException expected) { } catch (NoSuchContactException expected) {
// Expected // Expected
@@ -1396,14 +1396,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
// First message is still visible to the contact - flag lowered oneOf(database).lowerAckFlag(txn, contactId, acked);
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
will(returnValue(true));
// Second message is no longer visible - flag not lowered
oneOf(database).containsVisibleMessage(txn, contactId, messageId1);
will(returnValue(false));
oneOf(database)
.lowerAckFlag(txn, contactId, singletonList(messageId));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.mailbox.MailboxVersion;
import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent; import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent;
import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent; import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent;
import org.briarproject.bramble.api.mailbox.event.MailboxUpdateSentEvent;
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent; import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
@@ -185,6 +186,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn);
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
assertNoMailboxPropertiesSent(e, someClientSupportsList);
} }
@Test @Test
@@ -238,11 +242,15 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn);
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
assertMailboxPropertiesSent(e, someClientSupportsList);
} }
@Test @Test
public void testUnchangedClientSupportsOnSecondStartup() throws Exception { public void testUnchangedClientSupportsOnSecondStartup() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn1 = new Transaction(null, false);
Transaction txn2 = new Transaction(null, false);
Map<MessageId, BdfDictionary> emptyMessageMetadata = Map<MessageId, BdfDictionary> emptyMessageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
@@ -251,46 +259,49 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
someClientSupports)); someClientSupports));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn1, localGroup.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(db).addGroup(txn, localGroup); oneOf(db).addGroup(txn1, localGroup);
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn1);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
// addingContact() // addingContact()
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn1, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn1,
contact.getId(), CLIENT_ID, MAJOR_VERSION); contact.getId(), CLIENT_ID, MAJOR_VERSION);
will(returnValue(SHARED)); will(returnValue(SHARED));
oneOf(db).setGroupVisibility(txn, contact.getId(), oneOf(db).setGroupVisibility(txn1, contact.getId(),
contactGroupId, SHARED); contactGroupId, SHARED);
oneOf(clientHelper).setContactId(txn, contactGroupId, oneOf(clientHelper).setContactId(txn1, contactGroupId,
contact.getId()); contact.getId());
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn); oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn1);
will(returnValue(null)); will(returnValue(null));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn1,
contactGroupId); contactGroupId);
will(returnValue(emptyMessageMetadata)); will(returnValue(emptyMessageMetadata));
expectStoreMessage(txn, contactGroupId, 1, someClientSupports, expectStoreMessage(txn1, contactGroupId, 1, someClientSupports,
emptyServerSupports, emptyPropsDict); emptyServerSupports, emptyPropsDict);
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), oneOf(clientHelper).mergeGroupMetadata(txn1, localGroup.getId(),
sentDict); sentDict);
}}); }});
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn1);
MailboxUpdateSentEvent e = getEvent(txn1, MailboxUpdateSentEvent.class);
assertNoMailboxPropertiesSent(e, someClientSupportsList);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn2, localGroup.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(clientHelper).getGroupMetadataAsDictionary(txn, oneOf(clientHelper).getGroupMetadataAsDictionary(txn2,
localGroup.getId()); localGroup.getId());
will(returnValue(sentDict)); will(returnValue(sentDict));
oneOf(clientHelper).parseMailboxVersionList(someClientSupports); oneOf(clientHelper).parseMailboxVersionList(someClientSupports);
@@ -298,13 +309,16 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
}}); }});
t = createInstance(someClientSupportsList); t = createInstance(someClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn2);
assertFalse(hasEvent(txn2, MailboxUpdateSentEvent.class));
} }
@Test @Test
public void testSendsUpdateWhenClientSupportsChangedOnSecondStartup() public void testSendsUpdateWhenClientSupportsChangedOnSecondStartup()
throws Exception { throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn1 = new Transaction(null, false);
Transaction txn2 = new Transaction(null, false);
Map<MessageId, BdfDictionary> emptyMessageMetadata = Map<MessageId, BdfDictionary> emptyMessageMetadata =
new LinkedHashMap<>(); new LinkedHashMap<>();
@@ -313,41 +327,45 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
someClientSupports)); someClientSupports));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn1, localGroup.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(db).addGroup(txn, localGroup); oneOf(db).addGroup(txn1, localGroup);
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn1);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
// addingContact() // addingContact()
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn1, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn1,
contact.getId(), CLIENT_ID, MAJOR_VERSION); contact.getId(), CLIENT_ID, MAJOR_VERSION);
will(returnValue(SHARED)); will(returnValue(SHARED));
oneOf(db).setGroupVisibility(txn, contact.getId(), oneOf(db).setGroupVisibility(txn1, contact.getId(),
contactGroupId, SHARED); contactGroupId, SHARED);
oneOf(clientHelper).setContactId(txn, contactGroupId, oneOf(clientHelper).setContactId(txn1, contactGroupId,
contact.getId()); contact.getId());
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn); oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn1);
will(returnValue(null)); will(returnValue(null));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn1,
contactGroupId); contactGroupId);
will(returnValue(emptyMessageMetadata)); will(returnValue(emptyMessageMetadata));
expectStoreMessage(txn, contactGroupId, 1, someClientSupports, expectStoreMessage(txn1, contactGroupId, 1, someClientSupports,
emptyServerSupports, emptyPropsDict); emptyServerSupports, emptyPropsDict);
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), oneOf(clientHelper).mergeGroupMetadata(txn1, localGroup.getId(),
sentDict); sentDict);
}}); }});
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn1);
MailboxUpdateSentEvent e1 =
getEvent(txn1, MailboxUpdateSentEvent.class);
assertNoMailboxPropertiesSent(e1, someClientSupportsList);
BdfDictionary metaDictionary = BdfDictionary.of( BdfDictionary metaDictionary = BdfDictionary.of(
new BdfEntry(MSG_KEY_VERSION, 1), new BdfEntry(MSG_KEY_VERSION, 1),
@@ -356,58 +374,62 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>(); Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
MessageId messageId = new MessageId(getRandomId()); MessageId messageId = new MessageId(getRandomId());
messageMetadata.put(messageId, metaDictionary); messageMetadata.put(messageId, metaDictionary);
BdfList body = BdfList.of(1, someClientSupports, someServerSupports, BdfList oldBody = BdfList.of(1, someClientSupports, emptyServerSupports,
propsDict); emptyPropsDict);
BdfDictionary newerSentDict = BdfDictionary.of(new BdfEntry( BdfDictionary newerSentDict = BdfDictionary.of(new BdfEntry(
GROUP_KEY_SENT_CLIENT_SUPPORTS, GROUP_KEY_SENT_CLIENT_SUPPORTS,
newerClientSupports)); newerClientSupports));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn2, localGroup.getId());
will(returnValue(true)); will(returnValue(true));
// Find out that we are now on newerClientSupportsList // Find out that we are now on newerClientSupportsList
oneOf(clientHelper).getGroupMetadataAsDictionary(txn, oneOf(clientHelper).getGroupMetadataAsDictionary(txn2,
localGroup.getId()); localGroup.getId());
will(returnValue(sentDict)); will(returnValue(sentDict));
oneOf(clientHelper).parseMailboxVersionList(someClientSupports); oneOf(clientHelper).parseMailboxVersionList(someClientSupports);
will(returnValue(someClientSupportsList)); will(returnValue(someClientSupportsList));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn2);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn2, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
// getLocalUpdate() // getLocalUpdate()
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn2,
contactGroupId); contactGroupId);
will(returnValue(messageMetadata)); will(returnValue(messageMetadata));
oneOf(clientHelper).getMessageAsList(txn, messageId); oneOf(clientHelper).getMessageAsList(txn2, messageId);
will(returnValue(body)); will(returnValue(oldBody));
oneOf(clientHelper).parseAndValidateMailboxUpdate( oneOf(clientHelper).parseAndValidateMailboxUpdate(
someClientSupports, someServerSupports, propsDict); someClientSupports, emptyServerSupports, emptyPropsDict);
will(returnValue(updateWithMailbox)); will(returnValue(updateNoMailbox));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
// storeMessageReplaceLatest() // storeMessageReplaceLatest()
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn2,
contactGroupId); contactGroupId);
will(returnValue(messageMetadata)); will(returnValue(messageMetadata));
expectStoreMessage(txn, contactGroupId, 2, expectStoreMessage(txn2, contactGroupId, 2,
newerClientSupports, someServerSupports, propsDict); newerClientSupports, emptyServerSupports, emptyPropsDict);
oneOf(db).removeMessage(txn, messageId); oneOf(db).removeMessage(txn2, messageId);
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), oneOf(clientHelper).mergeGroupMetadata(txn2, localGroup.getId(),
newerSentDict); newerSentDict);
}}); }});
t = createInstance(newerClientSupportsList); t = createInstance(newerClientSupportsList);
t.onDatabaseOpened(txn); t.onDatabaseOpened(txn2);
MailboxUpdateSentEvent e2 =
getEvent(txn2, MailboxUpdateSentEvent.class);
assertNoMailboxPropertiesSent(e2, newerClientSupportsList);
} }
@Test @Test
@@ -443,6 +465,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.addingContact(txn, contact); t.addingContact(txn, contact);
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
assertNoMailboxPropertiesSent(e, someClientSupportsList);
} }
@Test @Test
@@ -484,6 +509,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList); MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
t.addingContact(txn, contact); t.addingContact(txn, contact);
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
assertMailboxPropertiesSent(e, someClientSupportsList);
} }
@Test @Test
@@ -693,6 +721,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
u.getClientSupports()); u.getClientSupports());
assertEquals(updateWithMailbox.getMailboxProperties(), assertEquals(updateWithMailbox.getMailboxProperties(),
u.getMailboxProperties()); u.getMailboxProperties());
assertFalse(hasEvent(txn, MailboxUpdateSentEvent.class));
} }
@Test @Test
@@ -734,6 +764,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
assertEquals(singleton(contact.getId()), localUpdates.keySet()); assertEquals(singleton(contact.getId()), localUpdates.keySet());
MailboxUpdate u = localUpdates.get(contact.getId()); MailboxUpdate u = localUpdates.get(contact.getId());
assertFalse(u.hasMailbox()); assertFalse(u.hasMailbox());
assertFalse(hasEvent(txn, MailboxUpdateSentEvent.class));
} }
@Test @Test
@@ -915,4 +947,22 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
false); false);
}}); }});
} }
private void assertNoMailboxPropertiesSent(MailboxUpdateSentEvent e,
List<MailboxVersion> clientSupports) {
assertEquals(contact.getId(), e.getContactId());
MailboxUpdate u = e.getMailboxUpdate();
assertEquals(clientSupports, u.getClientSupports());
assertFalse(u.hasMailbox());
}
private void assertMailboxPropertiesSent(MailboxUpdateSentEvent e,
List<MailboxVersion> clientSupports) {
assertEquals(contact.getId(), e.getContactId());
MailboxUpdate u = e.getMailboxUpdate();
assertEquals(clientSupports, u.getClientSupports());
assertTrue(u.hasMailbox());
MailboxUpdateWithMailbox uMailbox = (MailboxUpdateWithMailbox) u;
assertEquals(updateProps, uMailbox.getMailboxProperties());
}
} }

View File

@@ -14,11 +14,13 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.Versions; import org.briarproject.bramble.api.sync.Versions;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
@@ -68,13 +70,10 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// Send the protocol versions // Send the protocol versions
oneOf(recordWriter).writeVersions(with(any(Versions.class))); oneOf(recordWriter).writeVersions(with(any(Versions.class)));
// Calculate capacity for acks
oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes));
// No messages to ack // No messages to ack
oneOf(db).transactionWithResult(with(true), oneOf(db).transactionWithResult(with(true),
withDbCallable(noAckIdTxn)); withDbCallable(noAckIdTxn));
oneOf(db).getMessagesToAck(noAckIdTxn, contactId, MAX_MESSAGE_IDS); oneOf(db).getMessagesToAck(noAckIdTxn, contactId);
will(returnValue(emptyList())); will(returnValue(emptyList()));
// Calculate capacity for messages // Calculate capacity for messages
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
@@ -106,7 +105,6 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
MAX_FILE_PAYLOAD_BYTES); MAX_FILE_PAYLOAD_BYTES);
Transaction ackIdTxn = new Transaction(null, true); Transaction ackIdTxn = new Transaction(null, true);
Transaction noAckIdTxn = new Transaction(null, true);
Transaction msgIdTxn = new Transaction(null, true); Transaction msgIdTxn = new Transaction(null, true);
Transaction msgTxn = new Transaction(null, true); Transaction msgTxn = new Transaction(null, true);
@@ -114,28 +112,24 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
long capacityForMessages = long capacityForMessages =
MAX_FILE_PAYLOAD_BYTES - versionRecordBytes - ackRecordBytes; MAX_FILE_PAYLOAD_BYTES - versionRecordBytes - ackRecordBytes;
AtomicReference<Ack> ack = new AtomicReference<>();
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// Send the protocol versions // Send the protocol versions
oneOf(recordWriter).writeVersions(with(any(Versions.class))); oneOf(recordWriter).writeVersions(with(any(Versions.class)));
// Load the IDs to ack
oneOf(db).transactionWithResult(with(true),
withDbCallable(ackIdTxn));
oneOf(db).getMessagesToAck(ackIdTxn, contactId);
will(returnValue(singletonList(message.getId())));
// Calculate capacity for acks // Calculate capacity for acks
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes)); will(returnValue((long) versionRecordBytes));
// One message to ack
oneOf(db).transactionWithResult(with(true),
withDbCallable(ackIdTxn));
oneOf(db).getMessagesToAck(ackIdTxn, contactId, MAX_MESSAGE_IDS);
will(returnValue(singletonList(message.getId())));
// Send the ack // Send the ack
oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes));
oneOf(recordWriter).writeAck(with(any(Ack.class))); oneOf(recordWriter).writeAck(with(any(Ack.class)));
// No more messages to ack will(new CaptureArgumentAction<>(ack, Ack.class, 0));
oneOf(db).transactionWithResult(with(true),
withDbCallable(noAckIdTxn));
oneOf(db).getMessagesToAck(noAckIdTxn, contactId, MAX_MESSAGE_IDS);
will(returnValue(emptyList()));
// Calculate capacity for messages // Calculate capacity for messages
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes + ackRecordBytes)); will(returnValue((long) versionRecordBytes + ackRecordBytes));
@@ -162,6 +156,7 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
assertEquals(singletonList(message.getId()), assertEquals(singletonList(message.getId()),
sessionRecord.getAckedIds()); sessionRecord.getAckedIds());
assertEquals(singletonList(message.getId()), ack.get().getMessageIds());
assertEquals(singletonList(message1.getId()), assertEquals(singletonList(message1.getId()),
sessionRecord.getSentIds()); sessionRecord.getSentIds());
} }
@@ -178,48 +173,50 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
eventBus, contactId, transportId, MAX_LATENCY, eventBus, contactId, transportId, MAX_LATENCY,
streamWriter, recordWriter, sessionRecord, capacity); streamWriter, recordWriter, sessionRecord, capacity);
Transaction ackIdTxn1 = new Transaction(null, true); Transaction ackIdTxn = new Transaction(null, true);
Transaction ackIdTxn2 = new Transaction(null, true);
int firstAckRecordBytes = int firstAckRecordBytes =
RECORD_HEADER_BYTES + MessageId.LENGTH * MAX_MESSAGE_IDS; RECORD_HEADER_BYTES + MessageId.LENGTH * MAX_MESSAGE_IDS;
int secondAckRecordBytes = RECORD_HEADER_BYTES + MessageId.LENGTH; int secondAckRecordBytes = RECORD_HEADER_BYTES + MessageId.LENGTH;
List<MessageId> idsInFirstAck = new ArrayList<>(MAX_MESSAGE_IDS); // There are MAX_MESSAGE_IDS + 2 messages that need to be acked, but
for (int i = 0; i < MAX_MESSAGE_IDS; i++) { // only enough capacity to ack MAX_MESSAGE_IDS + 1 messages
idsInFirstAck.add(new MessageId(getRandomId())); List<MessageId> idsToAck = new ArrayList<>(MAX_MESSAGE_IDS + 2);
for (int i = 0; i < MAX_MESSAGE_IDS + 2; i++) {
idsToAck.add(new MessageId(getRandomId()));
} }
// The first ack contains MAX_MESSAGE_IDS IDs
List<MessageId> idsInFirstAck = idsToAck.subList(0, MAX_MESSAGE_IDS);
// The second ack contains one ID
List<MessageId> idsInSecondAck = List<MessageId> idsInSecondAck =
singletonList(new MessageId(getRandomId())); idsToAck.subList(MAX_MESSAGE_IDS, MAX_MESSAGE_IDS + 1);
List<MessageId> allIds = new ArrayList<>(MAX_MESSAGE_IDS + 1); List<MessageId> idsAcked = idsToAck.subList(0, MAX_MESSAGE_IDS + 1);
allIds.addAll(idsInFirstAck);
allIds.addAll(idsInSecondAck); AtomicReference<Ack> firstAck = new AtomicReference<>();
AtomicReference<Ack> secondAck = new AtomicReference<>();
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
// Add listener // Add listener
oneOf(eventBus).addListener(session); oneOf(eventBus).addListener(session);
// Send the protocol versions // Send the protocol versions
oneOf(recordWriter).writeVersions(with(any(Versions.class))); oneOf(recordWriter).writeVersions(with(any(Versions.class)));
// Load the IDs to ack
oneOf(db).transactionWithResult(with(true),
withDbCallable(ackIdTxn));
oneOf(db).getMessagesToAck(ackIdTxn, contactId);
will(returnValue(idsToAck));
// Calculate capacity for acks // Calculate capacity for acks
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes)); will(returnValue((long) versionRecordBytes));
// Load the IDs for the first ack record
oneOf(db).transactionWithResult(with(true),
withDbCallable(ackIdTxn1));
oneOf(db).getMessagesToAck(ackIdTxn1, contactId, MAX_MESSAGE_IDS);
will(returnValue(idsInFirstAck));
// Send the first ack record // Send the first ack record
oneOf(recordWriter).writeAck(with(any(Ack.class))); oneOf(recordWriter).writeAck(with(any(Ack.class)));
will(new CaptureArgumentAction<>(firstAck, Ack.class, 0));
// Calculate remaining capacity for acks // Calculate remaining capacity for acks
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes + firstAckRecordBytes)); will(returnValue((long) versionRecordBytes + firstAckRecordBytes));
// Load the IDs for the second ack record
oneOf(db).transactionWithResult(with(true),
withDbCallable(ackIdTxn2));
oneOf(db).getMessagesToAck(ackIdTxn2, contactId, 1);
will(returnValue(idsInSecondAck));
// Send the second ack record // Send the second ack record
oneOf(recordWriter).writeAck(with(any(Ack.class))); oneOf(recordWriter).writeAck(with(any(Ack.class)));
will(new CaptureArgumentAction<>(secondAck, Ack.class, 0));
// Not enough capacity left for another ack // Not enough capacity left for another ack
oneOf(recordWriter).getBytesWritten(); oneOf(recordWriter).getBytesWritten();
will(returnValue((long) versionRecordBytes + firstAckRecordBytes will(returnValue((long) versionRecordBytes + firstAckRecordBytes
@@ -236,7 +233,9 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
session.run(); session.run();
assertEquals(allIds, sessionRecord.getAckedIds()); assertEquals(idsAcked, sessionRecord.getAckedIds());
assertEquals(idsInFirstAck, firstAck.get().getMessageIds());
assertEquals(idsInSecondAck, secondAck.get().getMessageIds());
assertEquals(emptyList(), sessionRecord.getSentIds()); assertEquals(emptyList(), sessionRecord.getSentIds());
} }
} }