mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Add a bunch of tests highlighting implicit acks
This commit is contained in:
@@ -0,0 +1,268 @@
|
|||||||
|
package org.briarproject.briar.introduction;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
|
import org.briarproject.briar.api.introduction.IntroductionRequest;
|
||||||
|
import org.briarproject.briar.api.introduction.IntroductionResponse;
|
||||||
|
import org.briarproject.briar.autodelete.AbstractAutoDeleteTest;
|
||||||
|
import org.briarproject.briar.test.BriarIntegrationTestComponent;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.cleanup.CleanupManager.BATCH_DELAY_MS;
|
||||||
|
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
|
||||||
|
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS;
|
||||||
|
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||||
|
import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
|
||||||
|
import static org.briarproject.briar.introduction.IntroducerState.AWAIT_RESPONSES;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class ImplicitAcksTest extends AbstractAutoDeleteTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createComponents() {
|
||||||
|
IntroductionIntegrationTestComponent component =
|
||||||
|
DaggerIntroductionIntegrationTestComponent.builder().build();
|
||||||
|
BriarIntegrationTestComponent.Helper.injectEagerSingletons(component);
|
||||||
|
component.inject(this);
|
||||||
|
|
||||||
|
IntroductionIntegrationTestComponent c0 =
|
||||||
|
DaggerIntroductionIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(
|
||||||
|
new TestDatabaseConfigModule(t0Dir))
|
||||||
|
.timeTravelModule(new TimeTravelModule(true))
|
||||||
|
.build();
|
||||||
|
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c0);
|
||||||
|
|
||||||
|
IntroductionIntegrationTestComponent c1 =
|
||||||
|
DaggerIntroductionIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(
|
||||||
|
new TestDatabaseConfigModule(t1Dir))
|
||||||
|
.timeTravelModule(new TimeTravelModule(true))
|
||||||
|
.build();
|
||||||
|
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c1);
|
||||||
|
|
||||||
|
IntroductionIntegrationTestComponent c2 =
|
||||||
|
DaggerIntroductionIntegrationTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(
|
||||||
|
new TestDatabaseConfigModule(t2Dir))
|
||||||
|
.timeTravelModule(new TimeTravelModule(true))
|
||||||
|
.build();
|
||||||
|
BriarIntegrationTestComponent.Helper.injectEagerSingletons(c2);
|
||||||
|
|
||||||
|
this.c0 = c0;
|
||||||
|
this.c1 = c1;
|
||||||
|
this.c2 = c2;
|
||||||
|
|
||||||
|
// Use different times to avoid creating identical messages that are
|
||||||
|
// treated as redundant copies of the same message (#1907)
|
||||||
|
try {
|
||||||
|
c0.getTimeTravel().setCurrentTimeMillis(startTime);
|
||||||
|
c1.getTimeTravel().setCurrentTimeMillis(startTime + 1);
|
||||||
|
c2.getTimeTravel().setCurrentTimeMillis(startTime + 2);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
addTransportProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConversationClient getConversationClient(
|
||||||
|
BriarIntegrationTestComponent component) {
|
||||||
|
return component.getIntroductionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoAck() throws Exception {
|
||||||
|
makeIntroduction(true, true);
|
||||||
|
|
||||||
|
assertIntroducerStatus(AWAIT_RESPONSES);
|
||||||
|
|
||||||
|
timeTravel(c0);
|
||||||
|
|
||||||
|
// no ack, no response, the messages do not self-destruct
|
||||||
|
assertGroupCountAt0With1(1, 0);
|
||||||
|
assertGroupCountAt0With2(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExplicitAck() throws Exception {
|
||||||
|
makeIntroduction(true, true);
|
||||||
|
|
||||||
|
assertIntroducerStatus(AWAIT_RESPONSES);
|
||||||
|
|
||||||
|
// Sync the ack to 0 from 1 and 2 - this starts 0's timer
|
||||||
|
ack1To0(1);
|
||||||
|
ack2To0(1);
|
||||||
|
waitForEvents(c0);
|
||||||
|
|
||||||
|
timeTravel(c0);
|
||||||
|
|
||||||
|
// the introduction has self-destructed
|
||||||
|
assertGroupCountAt0With1(0, 0);
|
||||||
|
assertGroupCountAt0With2(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that responding to an introduction and syncing the response also
|
||||||
|
* triggers the timer to start running at the introducer's side.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testImplicitAck() throws Exception {
|
||||||
|
makeIntroduction(true, true);
|
||||||
|
|
||||||
|
assertIntroducerStatus(AWAIT_RESPONSES);
|
||||||
|
|
||||||
|
// responding to the invitation, this starts 0's timer for the
|
||||||
|
// introduction
|
||||||
|
respondToMostRecentIntroduction(c1, contactId0From1, true);
|
||||||
|
respondToMostRecentIntroduction(c2, contactId0From2, false);
|
||||||
|
sync1To0(1, true);
|
||||||
|
sync2To0(1, true);
|
||||||
|
waitForEvents(c0);
|
||||||
|
|
||||||
|
// 0 has the sent introduction and the unread responses
|
||||||
|
assertGroupCountAt0With1(2, 1);
|
||||||
|
assertGroupCountAt0With2(2, 1);
|
||||||
|
|
||||||
|
timeTravel(c0);
|
||||||
|
|
||||||
|
// 0 has only the received responses, the introduction has
|
||||||
|
// self-destructed
|
||||||
|
assertGroupCountAt0With1(1, 1);
|
||||||
|
assertGroupCountAt0With2(1, 1);
|
||||||
|
|
||||||
|
// the one message we have left is the response, not the introduction
|
||||||
|
forEachHeader(c0, contactId1From0, 1, h -> {
|
||||||
|
assertTrue(h instanceof IntroductionResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeIntroduction(boolean enableTimer1, boolean enableTimer2)
|
||||||
|
throws Exception {
|
||||||
|
if (enableTimer1) {
|
||||||
|
setAutoDeleteTimer(c0, contact1From0.getId(),
|
||||||
|
MIN_AUTO_DELETE_TIMER_MS);
|
||||||
|
}
|
||||||
|
if (enableTimer2) {
|
||||||
|
setAutoDeleteTimer(c0, contact2From0.getId(),
|
||||||
|
MIN_AUTO_DELETE_TIMER_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make introduction
|
||||||
|
c0.getIntroductionManager()
|
||||||
|
.makeIntroduction(contact1From0, contact2From0, "Hi!");
|
||||||
|
|
||||||
|
sync0To1(1, true);
|
||||||
|
sync0To2(1, true);
|
||||||
|
waitForEvents(c1);
|
||||||
|
waitForEvents(c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void respondToMostRecentIntroduction(
|
||||||
|
BriarIntegrationTestComponent c, ContactId contactId,
|
||||||
|
boolean accept) throws Exception {
|
||||||
|
List<ConversationMessageHeader> headers =
|
||||||
|
getMessageHeaders(c, contactId);
|
||||||
|
Collections.reverse(headers);
|
||||||
|
for (ConversationMessageHeader h : headers) {
|
||||||
|
if (h instanceof IntroductionRequest) {
|
||||||
|
IntroductionRequest ir = (IntroductionRequest) h;
|
||||||
|
c.getIntroductionManager().respondToIntroduction(contactId,
|
||||||
|
ir.getSessionId(), accept);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fail("no introduction found");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timeTravel(BriarIntegrationTestComponent c) throws Exception {
|
||||||
|
long timerLatency = MIN_AUTO_DELETE_TIMER_MS + BATCH_DELAY_MS;
|
||||||
|
timeTravel(c, timerLatency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void timeTravel(BriarIntegrationTestComponent c, long timerLatency)
|
||||||
|
throws Exception {
|
||||||
|
c.getTimeTravel().addCurrentTimeMillis(timerLatency);
|
||||||
|
waitForEvents(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertGroupCountAt0With1(int messageCount, int unreadCount)
|
||||||
|
throws Exception {
|
||||||
|
assertGroupCount(c0, contactId1From0, messageCount, unreadCount);
|
||||||
|
assertEquals(messageCount,
|
||||||
|
getMessageHeaders(c0, contactId1From0).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertGroupCountAt0With2(int messageCount, int unreadCount)
|
||||||
|
throws Exception {
|
||||||
|
assertGroupCount(c0, contactId2From0, messageCount, unreadCount);
|
||||||
|
assertEquals(messageCount,
|
||||||
|
getMessageHeaders(c0, contactId2From0).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertIntroducerStatus(IntroducerState state)
|
||||||
|
throws DbException, FormatException {
|
||||||
|
IntroducerSession introducerSession = getIntroducerSession();
|
||||||
|
assertEquals(state, introducerSession.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTransportProperties() throws Exception {
|
||||||
|
TransportPropertyManager tpm0 = c0.getTransportPropertyManager();
|
||||||
|
TransportPropertyManager tpm1 = c1.getTransportPropertyManager();
|
||||||
|
TransportPropertyManager tpm2 = c2.getTransportPropertyManager();
|
||||||
|
|
||||||
|
tpm0.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
|
||||||
|
getTransportProperties(2));
|
||||||
|
sync0To1(1, true);
|
||||||
|
sync0To2(1, true);
|
||||||
|
|
||||||
|
tpm1.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
|
||||||
|
getTransportProperties(2));
|
||||||
|
sync1To0(1, true);
|
||||||
|
|
||||||
|
tpm2.mergeLocalProperties(SIMPLEX_TRANSPORT_ID,
|
||||||
|
getTransportProperties(2));
|
||||||
|
sync2To0(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntroducerSession getIntroducerSession()
|
||||||
|
throws DbException, FormatException {
|
||||||
|
Map<MessageId, BdfDictionary> dicts = c0.getClientHelper()
|
||||||
|
.getMessageMetadataAsDictionary(getLocalGroup().getId());
|
||||||
|
assertEquals(1, dicts.size());
|
||||||
|
BdfDictionary d = dicts.values().iterator().next();
|
||||||
|
SessionParser sessionParser =
|
||||||
|
((IntroductionIntegrationTestComponent) c0).getSessionParser();
|
||||||
|
return sessionParser.parseIntroducerSession(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Group getLocalGroup() {
|
||||||
|
return contactGroupFactory
|
||||||
|
.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user