mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Merge branch '2069-transport-key-agreement-integration-tests' into '1802-sync-via-removable-storage'
Integration tests for transport key agreement client See merge request briar/briar!1492
This commit is contained in:
@@ -21,6 +21,7 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||||
|
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation "org.jmock:jmock:2.8.2"
|
testImplementation "org.jmock:jmock:2.8.2"
|
||||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ abstract class Connection {
|
|||||||
@Nullable
|
@Nullable
|
||||||
StreamContext recogniseTag(TransportConnectionReader reader,
|
StreamContext recogniseTag(TransportConnectionReader reader,
|
||||||
TransportId transportId) {
|
TransportId transportId) {
|
||||||
StreamContext ctx;
|
|
||||||
try {
|
try {
|
||||||
byte[] tag = readTag(reader.getInputStream());
|
byte[] tag = readTag(reader.getInputStream());
|
||||||
return keyManager.getStreamContext(transportId, tag);
|
return keyManager.getStreamContext(transportId, tag);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
|||||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||||
import org.briarproject.bramble.system.TimeTravelModule;
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
|
import org.briarproject.bramble.test.TestFeatureFlagModule;
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -25,6 +26,7 @@ import dagger.Component;
|
|||||||
DefaultEventExecutorModule.class,
|
DefaultEventExecutorModule.class,
|
||||||
DefaultWakefulIoExecutorModule.class,
|
DefaultWakefulIoExecutorModule.class,
|
||||||
TestDatabaseConfigModule.class,
|
TestDatabaseConfigModule.class,
|
||||||
|
TestFeatureFlagModule.class,
|
||||||
RemovableDriveIntegrationTestModule.class,
|
RemovableDriveIntegrationTestModule.class,
|
||||||
RemovableDriveModule.class,
|
RemovableDriveModule.class,
|
||||||
TestSecureRandomModule.class,
|
TestSecureRandomModule.class,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.plugin.file;
|
package org.briarproject.bramble.plugin.file;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -52,30 +51,4 @@ class RemovableDriveIntegrationTestModule {
|
|||||||
};
|
};
|
||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
FeatureFlags provideFeatureFlags() {
|
|
||||||
return new FeatureFlags() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableImageAttachments() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableProfilePictures() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableDisappearingMessages() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableConnectViaBluetooth() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,22 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FeatureFlags;
|
|
||||||
import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
|
import org.briarproject.bramble.battery.DefaultBatteryManagerModule;
|
||||||
import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
||||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||||
import org.briarproject.bramble.system.TimeTravelModule;
|
import org.briarproject.bramble.system.TimeTravelModule;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
DefaultBatteryManagerModule.class,
|
DefaultBatteryManagerModule.class,
|
||||||
DefaultEventExecutorModule.class,
|
DefaultEventExecutorModule.class,
|
||||||
DefaultWakefulIoExecutorModule.class,
|
DefaultWakefulIoExecutorModule.class,
|
||||||
TestDatabaseConfigModule.class,
|
TestDatabaseConfigModule.class,
|
||||||
|
TestFeatureFlagModule.class,
|
||||||
TestPluginConfigModule.class,
|
TestPluginConfigModule.class,
|
||||||
TestSecureRandomModule.class,
|
TestSecureRandomModule.class,
|
||||||
TimeTravelModule.class
|
TimeTravelModule.class
|
||||||
})
|
})
|
||||||
public class BrambleCoreIntegrationTestModule {
|
public class BrambleCoreIntegrationTestModule {
|
||||||
|
|
||||||
@Provides
|
|
||||||
FeatureFlags provideFeatureFlags() {
|
|
||||||
return new FeatureFlags() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableImageAttachments() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableProfilePictures() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableDisappearingMessages() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldEnableConnectViaBluetooth() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,336 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import net.jodah.concurrentunit.Waiter;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.data.BdfStringUtils;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessageStateChangedEvent;
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
|
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public abstract class BrambleIntegrationTest<C extends BrambleIntegrationTestComponent>
|
||||||
|
extends BrambleTestCase {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(BrambleIntegrationTest.class.getName());
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
protected final static int TIMEOUT = 15000;
|
||||||
|
|
||||||
|
// objects accessed from background threads need to be volatile
|
||||||
|
private volatile Waiter validationWaiter;
|
||||||
|
private volatile Waiter deliveryWaiter;
|
||||||
|
private volatile Waiter ackWaiter;
|
||||||
|
private volatile boolean expectAck = false;
|
||||||
|
|
||||||
|
private final Semaphore messageSemaphore = new Semaphore(0);
|
||||||
|
private final AtomicInteger deliveryCounter = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger validationCounter = new AtomicInteger(0);
|
||||||
|
private final AtomicInteger ackCounter = new AtomicInteger(0);
|
||||||
|
|
||||||
|
protected final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
assertTrue(testDir.mkdirs());
|
||||||
|
|
||||||
|
// initialize waiters fresh for each test
|
||||||
|
validationWaiter = new Waiter();
|
||||||
|
deliveryWaiter = new Waiter();
|
||||||
|
ackWaiter = new Waiter();
|
||||||
|
deliveryCounter.set(0);
|
||||||
|
validationCounter.set(0);
|
||||||
|
ackCounter.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addEventListener(C c) {
|
||||||
|
c.getEventBus().addListener(new Listener(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Listener implements EventListener {
|
||||||
|
|
||||||
|
private final ClientHelper clientHelper;
|
||||||
|
private final Executor executor;
|
||||||
|
|
||||||
|
private Listener(C c) {
|
||||||
|
clientHelper = c.getClientHelper();
|
||||||
|
executor = newSingleThreadExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof MessageStateChangedEvent) {
|
||||||
|
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||||
|
if (!event.isLocal()) {
|
||||||
|
if (event.getState() == DELIVERED) {
|
||||||
|
LOG.info("Delivered new message "
|
||||||
|
+ event.getMessageId());
|
||||||
|
deliveryCounter.addAndGet(1);
|
||||||
|
loadAndLogMessage(event.getMessageId());
|
||||||
|
deliveryWaiter.resume();
|
||||||
|
} else if (event.getState() == INVALID ||
|
||||||
|
event.getState() == PENDING) {
|
||||||
|
LOG.info("Validated new " + event.getState().name() +
|
||||||
|
" message " + event.getMessageId());
|
||||||
|
validationCounter.addAndGet(1);
|
||||||
|
loadAndLogMessage(event.getMessageId());
|
||||||
|
validationWaiter.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e instanceof MessagesAckedEvent && expectAck) {
|
||||||
|
MessagesAckedEvent event = (MessagesAckedEvent) e;
|
||||||
|
ackCounter.addAndGet(event.getMessageIds().size());
|
||||||
|
for (MessageId m : event.getMessageIds()) {
|
||||||
|
loadAndLogMessage(m);
|
||||||
|
ackWaiter.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAndLogMessage(MessageId id) {
|
||||||
|
executor.execute(() -> {
|
||||||
|
if (DEBUG) {
|
||||||
|
try {
|
||||||
|
BdfList body = clientHelper.getMessageAsList(id);
|
||||||
|
LOG.info("Contents of " + id + ":\n"
|
||||||
|
+ BdfStringUtils.toString(body));
|
||||||
|
} catch (DbException | FormatException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messageSemaphore.release();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void syncMessage(BrambleIntegrationTestComponent fromComponent,
|
||||||
|
BrambleIntegrationTestComponent toComponent, ContactId toId,
|
||||||
|
TransportId transportId, int num, boolean valid) throws Exception {
|
||||||
|
syncMessage(fromComponent, toComponent, toId, transportId, num, 0,
|
||||||
|
valid ? 0 : num, valid ? num : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void syncMessage(BrambleIntegrationTestComponent fromComponent,
|
||||||
|
BrambleIntegrationTestComponent toComponent, ContactId toId,
|
||||||
|
int num, boolean valid) throws Exception {
|
||||||
|
syncMessage(fromComponent, toComponent, toId, num, 0, valid ? 0 : num,
|
||||||
|
valid ? num : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void syncMessage(BrambleIntegrationTestComponent fromComponent,
|
||||||
|
BrambleIntegrationTestComponent toComponent, ContactId toId,
|
||||||
|
int numNew, int numDupes, int numPendingOrInvalid, int numDelivered)
|
||||||
|
throws Exception {
|
||||||
|
syncMessage(fromComponent, toComponent, toId, SIMPLEX_TRANSPORT_ID,
|
||||||
|
numNew, numDupes, numPendingOrInvalid, numDelivered);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void syncMessage(BrambleIntegrationTestComponent fromComponent,
|
||||||
|
BrambleIntegrationTestComponent toComponent, ContactId toId,
|
||||||
|
TransportId transportId, int numNew, int numDupes,
|
||||||
|
int numPendingOrInvalid, int numDelivered) throws Exception {
|
||||||
|
// Debug output
|
||||||
|
String from =
|
||||||
|
fromComponent.getIdentityManager().getLocalAuthor().getName();
|
||||||
|
String to = toComponent.getIdentityManager().getLocalAuthor().getName();
|
||||||
|
LOG.info("TEST: Sending " + (numNew + numDupes) + " message(s) from "
|
||||||
|
+ from + " to " + to);
|
||||||
|
|
||||||
|
// Listen for messages being sent
|
||||||
|
waitForEvents(fromComponent);
|
||||||
|
SendListener sendListener = new SendListener();
|
||||||
|
fromComponent.getEventBus().addListener(sendListener);
|
||||||
|
|
||||||
|
// Write the messages to a transport stream
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
TestTransportConnectionWriter writer =
|
||||||
|
new TestTransportConnectionWriter(out, false);
|
||||||
|
fromComponent.getConnectionManager().manageOutgoingConnection(toId,
|
||||||
|
transportId, writer);
|
||||||
|
writer.getDisposedLatch().await(TIMEOUT, MILLISECONDS);
|
||||||
|
|
||||||
|
// Check that the expected number of messages were sent
|
||||||
|
waitForEvents(fromComponent);
|
||||||
|
fromComponent.getEventBus().removeListener(sendListener);
|
||||||
|
assertEquals("Messages sent", numNew + numDupes,
|
||||||
|
sendListener.sent.size());
|
||||||
|
|
||||||
|
// Read the messages from the transport stream
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
|
TestTransportConnectionReader reader =
|
||||||
|
new TestTransportConnectionReader(in);
|
||||||
|
toComponent.getConnectionManager().manageIncomingConnection(
|
||||||
|
transportId, reader);
|
||||||
|
|
||||||
|
if (numPendingOrInvalid > 0) {
|
||||||
|
validationWaiter.await(TIMEOUT, numPendingOrInvalid);
|
||||||
|
}
|
||||||
|
assertEquals("Messages validated", numPendingOrInvalid,
|
||||||
|
validationCounter.getAndSet(0));
|
||||||
|
|
||||||
|
if (numDelivered > 0) {
|
||||||
|
deliveryWaiter.await(TIMEOUT, numDelivered);
|
||||||
|
}
|
||||||
|
assertEquals("Messages delivered", numDelivered,
|
||||||
|
deliveryCounter.getAndSet(0));
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageSemaphore.tryAcquire(numNew, TIMEOUT, MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.info("Interrupted while waiting for messages");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void awaitPendingMessageDelivery(int num)
|
||||||
|
throws TimeoutException {
|
||||||
|
deliveryWaiter.await(TIMEOUT, num);
|
||||||
|
assertEquals("Messages delivered", num, deliveryCounter.getAndSet(0));
|
||||||
|
|
||||||
|
try {
|
||||||
|
messageSemaphore.tryAcquire(num, TIMEOUT, MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.info("Interrupted while waiting for messages");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sendAcks(BrambleIntegrationTestComponent fromComponent,
|
||||||
|
BrambleIntegrationTestComponent toComponent, ContactId toId,
|
||||||
|
int num) throws Exception {
|
||||||
|
// Debug output
|
||||||
|
String from =
|
||||||
|
fromComponent.getIdentityManager().getLocalAuthor().getName();
|
||||||
|
String to = toComponent.getIdentityManager().getLocalAuthor().getName();
|
||||||
|
LOG.info("TEST: Sending " + num + " ACKs from " + from + " to " + to);
|
||||||
|
|
||||||
|
expectAck = true;
|
||||||
|
|
||||||
|
// Listen for messages being sent (none should be sent)
|
||||||
|
waitForEvents(fromComponent);
|
||||||
|
SendListener sendListener = new SendListener();
|
||||||
|
fromComponent.getEventBus().addListener(sendListener);
|
||||||
|
|
||||||
|
// start outgoing connection
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
TestTransportConnectionWriter writer =
|
||||||
|
new TestTransportConnectionWriter(out, false);
|
||||||
|
fromComponent.getConnectionManager().manageOutgoingConnection(toId,
|
||||||
|
SIMPLEX_TRANSPORT_ID, writer);
|
||||||
|
writer.getDisposedLatch().await(TIMEOUT, MILLISECONDS);
|
||||||
|
|
||||||
|
// Check that no messages were sent
|
||||||
|
waitForEvents(fromComponent);
|
||||||
|
fromComponent.getEventBus().removeListener(sendListener);
|
||||||
|
assertEquals("Messages sent", 0, sendListener.sent.size());
|
||||||
|
|
||||||
|
// handle incoming connection
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
|
TestTransportConnectionReader reader =
|
||||||
|
new TestTransportConnectionReader(in);
|
||||||
|
toComponent.getConnectionManager().manageIncomingConnection(
|
||||||
|
SIMPLEX_TRANSPORT_ID, reader);
|
||||||
|
|
||||||
|
ackWaiter.await(TIMEOUT, num);
|
||||||
|
assertEquals("ACKs delivered", num, ackCounter.getAndSet(0));
|
||||||
|
assertEquals("No messages delivered", 0, deliveryCounter.get());
|
||||||
|
try {
|
||||||
|
messageSemaphore.tryAcquire(num, TIMEOUT, MILLISECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.info("Interrupted while waiting for messages");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
fail();
|
||||||
|
} finally {
|
||||||
|
expectAck = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts a marker event and waits for it to be delivered, which
|
||||||
|
* indicates that all previously broadcast events have been delivered.
|
||||||
|
*/
|
||||||
|
public static void waitForEvents(BrambleIntegrationTestComponent component)
|
||||||
|
throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
MarkerEvent marker = new MarkerEvent();
|
||||||
|
EventBus eventBus = component.getEventBus();
|
||||||
|
eventBus.addListener(new EventListener() {
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(@Nonnull Event e) {
|
||||||
|
if (e == marker) {
|
||||||
|
latch.countDown();
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
eventBus.broadcast(marker);
|
||||||
|
if (!latch.await(1, MINUTES)) fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MarkerEvent extends Event {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SendListener implements EventListener {
|
||||||
|
|
||||||
|
private final Set<MessageId> sent = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof MessagesSentEvent) {
|
||||||
|
sent.addAll(((MessagesSentEvent) e).getMessageIds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||||
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
|
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {
|
||||||
|
BrambleCoreIntegrationTestModule.class,
|
||||||
|
BrambleCoreModule.class
|
||||||
|
})
|
||||||
|
public interface BrambleIntegrationTestComponent
|
||||||
|
extends BrambleCoreIntegrationTestEagerSingletons {
|
||||||
|
|
||||||
|
IdentityManager getIdentityManager();
|
||||||
|
|
||||||
|
EventBus getEventBus();
|
||||||
|
|
||||||
|
ConnectionManager getConnectionManager();
|
||||||
|
|
||||||
|
ClientHelper getClientHelper();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FeatureFlags;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class TestFeatureFlagModule {
|
||||||
|
@Provides
|
||||||
|
FeatureFlags provideFeatureFlags() {
|
||||||
|
return new FeatureFlags() {
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableImageAttachments() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableProfilePictures() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableDisappearingMessages() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldEnableConnectViaBluetooth() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,14 +27,26 @@ public class TestPluginConfigModule {
|
|||||||
|
|
||||||
public static final TransportId SIMPLEX_TRANSPORT_ID = getTransportId();
|
public static final TransportId SIMPLEX_TRANSPORT_ID = getTransportId();
|
||||||
public static final TransportId DUPLEX_TRANSPORT_ID = getTransportId();
|
public static final TransportId DUPLEX_TRANSPORT_ID = getTransportId();
|
||||||
public static final int MAX_LATENCY = 30_000; // 30 seconds
|
private static final int MAX_LATENCY = 30_000; // 30 seconds
|
||||||
|
|
||||||
|
private final TransportId simplexTransportId, duplexTransportId;
|
||||||
|
|
||||||
|
public TestPluginConfigModule() {
|
||||||
|
this(SIMPLEX_TRANSPORT_ID, DUPLEX_TRANSPORT_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestPluginConfigModule(TransportId simplexTransportId,
|
||||||
|
TransportId duplexTransportId) {
|
||||||
|
this.simplexTransportId = simplexTransportId;
|
||||||
|
this.duplexTransportId = duplexTransportId;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
private final SimplexPluginFactory simplex = new SimplexPluginFactory() {
|
private final SimplexPluginFactory simplex = new SimplexPluginFactory() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportId getId() {
|
public TransportId getId() {
|
||||||
return SIMPLEX_TRANSPORT_ID;
|
return simplexTransportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,7 +66,7 @@ public class TestPluginConfigModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportId getId() {
|
public TransportId getId() {
|
||||||
return DUPLEX_TRANSPORT_ID;
|
return duplexTransportId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,7 +82,7 @@ public class TestPluginConfigModule {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
PluginConfig providePluginConfig() {
|
public PluginConfig providePluginConfig() {
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
PluginConfig pluginConfig = new PluginConfig() {
|
PluginConfig pluginConfig = new PluginConfig() {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,418 @@
|
|||||||
|
package org.briarproject.bramble.transport.agreement;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.bramble.api.identity.Identity;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.test.BrambleIntegrationTest;
|
||||||
|
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||||
|
import org.briarproject.bramble.test.TestPluginConfigModule;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager.CLIENT_ID;
|
||||||
|
import static org.briarproject.bramble.api.transport.agreement.TransportKeyAgreementManager.MAJOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.test.TestPluginConfigModule.DUPLEX_TRANSPORT_ID;
|
||||||
|
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.briarproject.bramble.transport.agreement.TransportKeyAgreementConstants.MSG_KEY_IS_SESSION;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class TransportKeyAgreementIntegrationTest
|
||||||
|
extends BrambleIntegrationTest<TransportKeyAgreementTestComponent> {
|
||||||
|
|
||||||
|
private final File aliceDir = new File(testDir, "alice");
|
||||||
|
private final File bobDir = new File(testDir, "bob");
|
||||||
|
private final SecretKey masterKey = getSecretKey();
|
||||||
|
private final long timestamp = System.currentTimeMillis();
|
||||||
|
private final TransportId newTransportId =
|
||||||
|
new TransportId(getRandomString(8));
|
||||||
|
|
||||||
|
private TransportKeyAgreementTestComponent alice, bob;
|
||||||
|
private Identity aliceIdentity, bobIdentity;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
// Create the devices
|
||||||
|
alice = createComponent(aliceDir, false);
|
||||||
|
bob = createComponent(bobDir, false);
|
||||||
|
|
||||||
|
// Create identities
|
||||||
|
aliceIdentity = alice.getIdentityManager().createIdentity("Alice");
|
||||||
|
bobIdentity = bob.getIdentityManager().createIdentity("Bob");
|
||||||
|
|
||||||
|
// Start both lifecycles
|
||||||
|
startLifecycle(alice, aliceIdentity);
|
||||||
|
startLifecycle(bob, bobIdentity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransportKeyAgreementTestComponent createComponent(
|
||||||
|
File dir, boolean useNewTransport) {
|
||||||
|
TestPluginConfigModule pluginConfigModule = useNewTransport ?
|
||||||
|
new TestPluginConfigModule(SIMPLEX_TRANSPORT_ID, newTransportId)
|
||||||
|
: new TestPluginConfigModule();
|
||||||
|
TransportKeyAgreementTestComponent c =
|
||||||
|
DaggerTransportKeyAgreementTestComponent.builder()
|
||||||
|
.testDatabaseConfigModule(
|
||||||
|
new TestDatabaseConfigModule(dir))
|
||||||
|
.testPluginConfigModule(pluginConfigModule)
|
||||||
|
.build();
|
||||||
|
BrambleCoreIntegrationTestEagerSingletons.Helper
|
||||||
|
.injectEagerSingletons(c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startLifecycle(
|
||||||
|
TransportKeyAgreementTestComponent device,
|
||||||
|
Identity identity) throws Exception {
|
||||||
|
// Listen to message related events first to not miss early ones
|
||||||
|
addEventListener(device);
|
||||||
|
// Register identity before starting lifecycle
|
||||||
|
device.getIdentityManager().registerIdentity(identity);
|
||||||
|
// Start the lifecycle manager
|
||||||
|
LifecycleManager lifecycleManager = device.getLifecycleManager();
|
||||||
|
lifecycleManager.startServices(masterKey); // re-using masterKey here
|
||||||
|
lifecycleManager.waitForStartup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
tearDown(alice);
|
||||||
|
tearDown(bob);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tearDown(TransportKeyAgreementTestComponent device)
|
||||||
|
throws Exception {
|
||||||
|
// Stop the lifecycle manager
|
||||||
|
LifecycleManager lifecycleManager = device.getLifecycleManager();
|
||||||
|
lifecycleManager.stopServices();
|
||||||
|
lifecycleManager.waitForShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBothAddTransportAtTheSameTime() throws Exception {
|
||||||
|
// Alice and Bob add each other.
|
||||||
|
Pair<ContactId, ContactId> contactIds = addContacts(true);
|
||||||
|
ContactId aliceId = contactIds.getFirst();
|
||||||
|
ContactId bobId = contactIds.getSecond();
|
||||||
|
|
||||||
|
// Alice and Bob restart and come back with the new transport.
|
||||||
|
alice = restartWithNewTransport(alice, aliceDir, aliceIdentity);
|
||||||
|
bob = restartWithNewTransport(bob, bobDir, bobIdentity);
|
||||||
|
|
||||||
|
// They can still send via the old simplex,
|
||||||
|
// but not via the new duplex transport
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertFalse(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertFalse(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Bobs has started a session and sends KEY message to Alice
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
|
||||||
|
// Alice now and sends her own KEY as well as her ACTIVATE message.
|
||||||
|
syncMessage(alice, bob, bobId, 2, true);
|
||||||
|
|
||||||
|
// Bob can already send over the new transport while Alice still can't.
|
||||||
|
assertFalse(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Now Bob sends his ACTIVATE message to Alice.
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
|
||||||
|
// Now Alice can also send over the new transport.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Ensure that private key is not stored anymore.
|
||||||
|
assertLocalKeyPairIsNull(alice, bobId);
|
||||||
|
assertLocalKeyPairIsNull(bob, aliceId);
|
||||||
|
|
||||||
|
// Messages can be send over the new transport in both directions.
|
||||||
|
assertTransportMessageArrives(alice, bob, bobId, newTransportId);
|
||||||
|
assertTransportMessageArrives(bob, alice, aliceId, newTransportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAliceAddsTransportBeforeBob() throws Exception {
|
||||||
|
// Alice and Bob add each other.
|
||||||
|
Pair<ContactId, ContactId> contactIds = addContacts(true);
|
||||||
|
ContactId aliceId = contactIds.getFirst();
|
||||||
|
ContactId bobId = contactIds.getSecond();
|
||||||
|
|
||||||
|
// Alice restarts and comes back with the new transport.
|
||||||
|
alice = restartWithNewTransport(alice, aliceDir, aliceIdentity);
|
||||||
|
|
||||||
|
// Alice can still send via the old simplex,
|
||||||
|
// but not via the new duplex transport
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertFalse(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
|
||||||
|
// Alice has started a session and sends KEY message to Bob
|
||||||
|
// which he can't read, as he doesn't support the new transport, yet.
|
||||||
|
syncMessage(alice, bob, bobId, 1, false);
|
||||||
|
|
||||||
|
// Bob restarts and comes back with the new transport.
|
||||||
|
bob = restartWithNewTransport(bob, bobDir, bobIdentity);
|
||||||
|
|
||||||
|
// Alice's pending KEY message now gets delivered async, so wait for it
|
||||||
|
awaitPendingMessageDelivery(1);
|
||||||
|
|
||||||
|
// Bob now sends his own KEY as well as his ACTIVATE message.
|
||||||
|
syncMessage(bob, alice, aliceId, 2, true);
|
||||||
|
|
||||||
|
// Alice can already send over the new transport while Bob still can't.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertFalse(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Now Alice sends her ACTIVATE message to Bob.
|
||||||
|
syncMessage(alice, bob, bobId, 1, true);
|
||||||
|
|
||||||
|
// Now Bob can also send over the new transport.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Ensure that private key is not stored anymore.
|
||||||
|
assertLocalKeyPairIsNull(alice, bobId);
|
||||||
|
assertLocalKeyPairIsNull(bob, aliceId);
|
||||||
|
|
||||||
|
// Messages can be send over the new transport in both directions.
|
||||||
|
assertTransportMessageArrives(alice, bob, bobId, newTransportId);
|
||||||
|
assertTransportMessageArrives(bob, alice, aliceId, newTransportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAliceAlreadyHasTransportWhenAddingBob() throws Exception {
|
||||||
|
// Alice restarts and comes back with the new transport.
|
||||||
|
alice = restartWithNewTransport(alice, aliceDir, aliceIdentity);
|
||||||
|
|
||||||
|
// Alice and Bob add each other.
|
||||||
|
Pair<ContactId, ContactId> contactIds = addContacts(false);
|
||||||
|
ContactId aliceId = contactIds.getFirst();
|
||||||
|
ContactId bobId = contactIds.getSecond();
|
||||||
|
|
||||||
|
// Alice can still send via the old simplex,
|
||||||
|
// but not via the new duplex transport
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
// Normally, Alice should not be able to send streams already.
|
||||||
|
// However, she does already derive keys for the transport.
|
||||||
|
// The UI checks RemovableDriveManager#isTransportSupportedByContact()
|
||||||
|
// in practice to prevent sending streams that Bob can't decrypt.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
|
||||||
|
// Bob restarts and comes back with the new transport.
|
||||||
|
bob = restartWithNewTransport(bob, bobDir, bobIdentity);
|
||||||
|
|
||||||
|
// Bob sends his own KEY message.
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
|
||||||
|
// Alice can already send over the new transport while Bob still can't.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertFalse(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Now Alice sends her KEY and her ACTIVATE message to Bob.
|
||||||
|
syncMessage(alice, bob, bobId, 2, true);
|
||||||
|
|
||||||
|
// Now Bob can also send over the new transport.
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Ensure that private key is not stored anymore.
|
||||||
|
assertLocalKeyPairIsNull(alice, bobId);
|
||||||
|
assertLocalKeyPairIsNull(bob, aliceId);
|
||||||
|
|
||||||
|
// Bobs still sends his ACTIVATE message.
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
|
||||||
|
// Messages can be send over the new transport in both directions.
|
||||||
|
assertTransportMessageArrives(alice, bob, bobId, newTransportId);
|
||||||
|
assertTransportMessageArrives(bob, alice, aliceId, newTransportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAliceActivatesKeysByIncomingMessage() throws Exception {
|
||||||
|
// Alice and Bob add each other.
|
||||||
|
Pair<ContactId, ContactId> contactIds = addContacts(true);
|
||||||
|
ContactId aliceId = contactIds.getFirst();
|
||||||
|
ContactId bobId = contactIds.getSecond();
|
||||||
|
|
||||||
|
// Alice and Bob restart and come back with the new transport.
|
||||||
|
alice = restartWithNewTransport(alice, aliceDir, aliceIdentity);
|
||||||
|
bob = restartWithNewTransport(bob, bobDir, bobIdentity);
|
||||||
|
|
||||||
|
// They can still send via the old simplex,
|
||||||
|
// but not via the new duplex transport
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertFalse(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertFalse(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Bobs has started a session and sends KEY message to Alice
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
|
||||||
|
// Alice now and sends her own KEY as well as her ACTIVATE message.
|
||||||
|
syncMessage(alice, bob, bobId, 2, true);
|
||||||
|
|
||||||
|
// Bob can already send over the new transport while Alice still can't.
|
||||||
|
assertFalse(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, newTransportId));
|
||||||
|
|
||||||
|
// Bob's database mysteriously loses the ACTIVATE message,
|
||||||
|
// so it won't be send to Alice.
|
||||||
|
Contact contact = bob.getContactManager().getContact(aliceId);
|
||||||
|
Group group = getContactGroup(bob, contact);
|
||||||
|
Map<MessageId, BdfDictionary> map = bob.getClientHelper()
|
||||||
|
.getMessageMetadataAsDictionary(group.getId());
|
||||||
|
DatabaseComponent db = bob.getDatabaseComponent();
|
||||||
|
for (Map.Entry<MessageId, BdfDictionary> e : map.entrySet()) {
|
||||||
|
if (e.getValue().getBoolean(MSG_KEY_IS_SESSION)) continue;
|
||||||
|
db.transaction(false, txn -> db.removeMessage(txn, e.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bob sends a message to Alice
|
||||||
|
assertTransportMessageArrives(bob, alice, aliceId, newTransportId);
|
||||||
|
|
||||||
|
// Now without receiving the ACTIVATE, Alice can already send to Bob
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, newTransportId));
|
||||||
|
assertTransportMessageArrives(alice, bob, bobId, newTransportId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<ContactId, ContactId> addContacts(
|
||||||
|
boolean assertOldDuplexSending) throws Exception {
|
||||||
|
ContactId bobId = addContact(alice, bob, true);
|
||||||
|
ContactId aliceId = addContact(bob, alice, false);
|
||||||
|
|
||||||
|
// Alice and Bob can send messages via the default test transports
|
||||||
|
if (assertOldDuplexSending) {
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertTrue(alice.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(bobId, DUPLEX_TRANSPORT_ID));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, SIMPLEX_TRANSPORT_ID));
|
||||||
|
assertTrue(bob.getKeyManager()
|
||||||
|
.canSendOutgoingStreams(aliceId, DUPLEX_TRANSPORT_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync initial client versioning updates
|
||||||
|
syncMessage(alice, bob, bobId, 1, true);
|
||||||
|
syncMessage(bob, alice, aliceId, 1, true);
|
||||||
|
syncMessage(alice, bob, bobId, 1, true);
|
||||||
|
sendAcks(bob, alice, aliceId, 1);
|
||||||
|
|
||||||
|
return new Pair<>(aliceId, bobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContactId addContact(
|
||||||
|
TransportKeyAgreementTestComponent device,
|
||||||
|
TransportKeyAgreementTestComponent remote,
|
||||||
|
boolean alice) throws Exception {
|
||||||
|
// Get remote Author
|
||||||
|
Author remoteAuthor = remote.getIdentityManager().getLocalAuthor();
|
||||||
|
// Get ID of LocalAuthor
|
||||||
|
IdentityManager identityManager = device.getIdentityManager();
|
||||||
|
AuthorId localAuthorId = identityManager.getLocalAuthor().getId();
|
||||||
|
// Add the other user as a contact
|
||||||
|
ContactManager contactManager = device.getContactManager();
|
||||||
|
return contactManager.addContact(remoteAuthor, localAuthorId, masterKey,
|
||||||
|
timestamp, alice, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransportKeyAgreementTestComponent restartWithNewTransport(
|
||||||
|
TransportKeyAgreementTestComponent device, File dir,
|
||||||
|
Identity identity) throws Exception {
|
||||||
|
tearDown(device);
|
||||||
|
TransportKeyAgreementTestComponent newDevice =
|
||||||
|
createComponent(dir, true);
|
||||||
|
startLifecycle(newDevice, identity);
|
||||||
|
return newDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the local key pair (specifically the private key) is removed
|
||||||
|
* from the session as intended when leaving the AWAIT_KEY state.
|
||||||
|
* If it remained on disk after the keys had been activated
|
||||||
|
* then we'd lose forward secrecy.
|
||||||
|
*/
|
||||||
|
private void assertLocalKeyPairIsNull(
|
||||||
|
TransportKeyAgreementTestComponent device, ContactId contactId)
|
||||||
|
throws Exception {
|
||||||
|
Contact contact = device.getContactManager().getContact(contactId);
|
||||||
|
Group group = getContactGroup(device, contact);
|
||||||
|
Map<MessageId, BdfDictionary> map = device.getClientHelper()
|
||||||
|
.getMessageMetadataAsDictionary(group.getId());
|
||||||
|
for (Map.Entry<MessageId, BdfDictionary> e : map.entrySet()) {
|
||||||
|
if (!e.getValue().getBoolean(MSG_KEY_IS_SESSION)) continue;
|
||||||
|
Session s = device.getSessionParser().parseSession(e.getValue());
|
||||||
|
assertNull(s.getLocalKeyPair());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Group getContactGroup(TransportKeyAgreementTestComponent device,
|
||||||
|
Contact c) {
|
||||||
|
return device.getContactGroupFactory().createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTransportMessageArrives(
|
||||||
|
TransportKeyAgreementTestComponent from,
|
||||||
|
TransportKeyAgreementTestComponent to, ContactId toId,
|
||||||
|
TransportId transportId) throws Exception {
|
||||||
|
TransportProperties p = new TransportProperties();
|
||||||
|
p.putBoolean("foo", true);
|
||||||
|
from.getTransportPropertyManager().mergeLocalProperties(transportId, p);
|
||||||
|
syncMessage(from, to, toId, transportId, 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.briarproject.bramble.transport.agreement;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
|
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||||
|
import org.briarproject.bramble.test.BrambleIntegrationTestComponent;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Component;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Component(modules = {
|
||||||
|
BrambleCoreIntegrationTestModule.class,
|
||||||
|
BrambleCoreModule.class
|
||||||
|
})
|
||||||
|
interface TransportKeyAgreementTestComponent
|
||||||
|
extends BrambleIntegrationTestComponent {
|
||||||
|
|
||||||
|
KeyManager getKeyManager();
|
||||||
|
|
||||||
|
TransportKeyAgreementManagerImpl getTransportKeyAgreementManager();
|
||||||
|
|
||||||
|
ContactManager getContactManager();
|
||||||
|
|
||||||
|
LifecycleManager getLifecycleManager();
|
||||||
|
|
||||||
|
ContactGroupFactory getContactGroupFactory();
|
||||||
|
|
||||||
|
SessionParser getSessionParser();
|
||||||
|
|
||||||
|
TransportPropertyManager getTransportPropertyManager();
|
||||||
|
|
||||||
|
DatabaseComponent getDatabaseComponent();
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ dependencyVerification {
|
|||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||||
|
'net.jodah:concurrentunit:0.4.2:concurrentunit-0.4.2.jar:5583078e1acf91734939e985bc9e7ee947b0e93a8eef679da6bb07bbeb47ced3',
|
||||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.briarproject.briar.test;
|
package org.briarproject.briar.test;
|
||||||
|
|
||||||
import net.jodah.concurrentunit.Waiter;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
@@ -10,13 +7,8 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
|
||||||
import org.briarproject.bramble.api.data.BdfStringUtils;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
|
||||||
import org.briarproject.bramble.api.identity.Identity;
|
import org.briarproject.bramble.api.identity.Identity;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
@@ -25,11 +17,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.event.MessageStateChangedEvent;
|
import org.briarproject.bramble.test.BrambleIntegrationTest;
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
|
||||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
|
||||||
import org.briarproject.bramble.test.TestTransportConnectionReader;
|
|
||||||
import org.briarproject.bramble.test.TestTransportConnectionWriter;
|
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
|
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
|
||||||
import org.briarproject.briar.api.blog.BlogFactory;
|
import org.briarproject.briar.api.blog.BlogFactory;
|
||||||
@@ -43,48 +31,19 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Semaphore;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static java.util.logging.Logger.getLogger;
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static junit.framework.Assert.assertNotNull;
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
|
||||||
import static org.briarproject.bramble.test.TestPluginConfigModule.SIMPLEX_TRANSPORT_ID;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class BriarIntegrationTest<C extends BriarIntegrationTestComponent>
|
public abstract class BriarIntegrationTest<C extends BriarIntegrationTestComponent>
|
||||||
extends BriarTestCase {
|
extends BrambleIntegrationTest<C> {
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
getLogger(BriarIntegrationTest.class.getName());
|
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
protected final static int TIMEOUT = 15000;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
protected ContactId contactId1From2, contactId2From1;
|
protected ContactId contactId1From2, contactId2From1;
|
||||||
@@ -94,7 +53,7 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
contact2From0;
|
contact2From0;
|
||||||
protected LocalAuthor author0, author1, author2;
|
protected LocalAuthor author0, author1, author2;
|
||||||
protected ContactManager contactManager0, contactManager1, contactManager2;
|
protected ContactManager contactManager0, contactManager1, contactManager2;
|
||||||
protected IdentityManager identityManager0, identityManager1,
|
private IdentityManager identityManager0, identityManager1,
|
||||||
identityManager2;
|
identityManager2;
|
||||||
protected DatabaseComponent db0, db1, db2;
|
protected DatabaseComponent db0, db1, db2;
|
||||||
protected MessageTracker messageTracker0, messageTracker1, messageTracker2;
|
protected MessageTracker messageTracker0, messageTracker1, messageTracker2;
|
||||||
@@ -123,18 +82,8 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
@Inject
|
@Inject
|
||||||
protected ForumPostFactory forumPostFactory;
|
protected ForumPostFactory forumPostFactory;
|
||||||
|
|
||||||
// objects accessed from background threads need to be volatile
|
|
||||||
private volatile Waiter validationWaiter;
|
|
||||||
private volatile Waiter deliveryWaiter;
|
|
||||||
private volatile Waiter ackWaiter;
|
|
||||||
private volatile boolean expectAck = false;
|
|
||||||
|
|
||||||
protected C c0, c1, c2;
|
protected C c0, c1, c2;
|
||||||
|
|
||||||
private final Semaphore messageSemaphore = new Semaphore(0);
|
|
||||||
private final AtomicInteger deliveryCounter = new AtomicInteger(0);
|
|
||||||
private final AtomicInteger validationCounter = new AtomicInteger(0);
|
|
||||||
private final AtomicInteger ackCounter = new AtomicInteger(0);
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
private final String AUTHOR0 = "Author 0";
|
private final String AUTHOR0 = "Author 0";
|
||||||
private final String AUTHOR1 = "Author 1";
|
private final String AUTHOR1 = "Author 1";
|
||||||
@@ -148,8 +97,9 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
protected final File t2Dir = new File(testDir, AUTHOR2);
|
protected final File t2Dir = new File(testDir, AUTHOR2);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@Override
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
assertTrue(testDir.mkdirs());
|
super.setUp();
|
||||||
createComponents();
|
createComponents();
|
||||||
|
|
||||||
identityManager0 = c0.getIdentityManager();
|
identityManager0 = c0.getIdentityManager();
|
||||||
@@ -165,14 +115,6 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
db1 = c1.getDatabaseComponent();
|
db1 = c1.getDatabaseComponent();
|
||||||
db2 = c2.getDatabaseComponent();
|
db2 = c2.getDatabaseComponent();
|
||||||
|
|
||||||
// initialize waiters fresh for each test
|
|
||||||
validationWaiter = new Waiter();
|
|
||||||
deliveryWaiter = new Waiter();
|
|
||||||
ackWaiter = new Waiter();
|
|
||||||
deliveryCounter.set(0);
|
|
||||||
validationCounter.set(0);
|
|
||||||
ackCounter.set(0);
|
|
||||||
|
|
||||||
createAndRegisterIdentities();
|
createAndRegisterIdentities();
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
listenToEvents();
|
listenToEvents();
|
||||||
@@ -195,68 +137,9 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void listenToEvents() {
|
private void listenToEvents() {
|
||||||
Listener listener0 = new Listener(c0);
|
addEventListener(c0);
|
||||||
c0.getEventBus().addListener(listener0);
|
addEventListener(c1);
|
||||||
Listener listener1 = new Listener(c1);
|
addEventListener(c2);
|
||||||
c1.getEventBus().addListener(listener1);
|
|
||||||
Listener listener2 = new Listener(c2);
|
|
||||||
c2.getEventBus().addListener(listener2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Listener implements EventListener {
|
|
||||||
|
|
||||||
private final ClientHelper clientHelper;
|
|
||||||
private final Executor executor;
|
|
||||||
|
|
||||||
private Listener(C c) {
|
|
||||||
clientHelper = c.getClientHelper();
|
|
||||||
executor = newSingleThreadExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventOccurred(Event e) {
|
|
||||||
if (e instanceof MessageStateChangedEvent) {
|
|
||||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
|
||||||
if (!event.isLocal()) {
|
|
||||||
if (event.getState() == DELIVERED) {
|
|
||||||
LOG.info("Delivered new message "
|
|
||||||
+ event.getMessageId());
|
|
||||||
deliveryCounter.addAndGet(1);
|
|
||||||
loadAndLogMessage(event.getMessageId());
|
|
||||||
deliveryWaiter.resume();
|
|
||||||
} else if (event.getState() == INVALID ||
|
|
||||||
event.getState() == PENDING) {
|
|
||||||
LOG.info("Validated new " + event.getState().name() +
|
|
||||||
" message " + event.getMessageId());
|
|
||||||
validationCounter.addAndGet(1);
|
|
||||||
loadAndLogMessage(event.getMessageId());
|
|
||||||
validationWaiter.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessagesAckedEvent && expectAck) {
|
|
||||||
MessagesAckedEvent event = (MessagesAckedEvent) e;
|
|
||||||
ackCounter.addAndGet(event.getMessageIds().size());
|
|
||||||
for (MessageId m : event.getMessageIds()) {
|
|
||||||
loadAndLogMessage(m);
|
|
||||||
ackWaiter.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAndLogMessage(MessageId id) {
|
|
||||||
executor.execute(() -> {
|
|
||||||
if (DEBUG) {
|
|
||||||
try {
|
|
||||||
BdfList body = clientHelper.getMessageAsList(id);
|
|
||||||
LOG.info("Contents of " + id + ":\n"
|
|
||||||
+ BdfStringUtils.toString(body));
|
|
||||||
} catch (DbException | FormatException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messageSemaphore.release();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAndRegisterIdentities() {
|
private void createAndRegisterIdentities() {
|
||||||
@@ -335,9 +218,10 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@Override
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
stopLifecycles();
|
stopLifecycles();
|
||||||
TestUtils.deleteTestDirectory(testDir);
|
super.tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopLifecycles() throws InterruptedException {
|
private void stopLifecycles() throws InterruptedException {
|
||||||
@@ -402,122 +286,6 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
sendAcks(c1, c2, contactId2From1, num);
|
sendAcks(c1, c2, contactId2From1, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void syncMessage(BriarIntegrationTestComponent fromComponent,
|
|
||||||
BriarIntegrationTestComponent toComponent, ContactId toId, int num,
|
|
||||||
boolean valid) throws Exception {
|
|
||||||
syncMessage(fromComponent, toComponent, toId, num, 0, valid ? 0 : num,
|
|
||||||
valid ? num : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void syncMessage(BriarIntegrationTestComponent fromComponent,
|
|
||||||
BriarIntegrationTestComponent toComponent, ContactId toId,
|
|
||||||
int numNew, int numDupes, int numPendingOrInvalid, int numDelivered)
|
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
// Debug output
|
|
||||||
String from =
|
|
||||||
fromComponent.getIdentityManager().getLocalAuthor().getName();
|
|
||||||
String to = toComponent.getIdentityManager().getLocalAuthor().getName();
|
|
||||||
LOG.info("TEST: Sending " + (numNew + numDupes) + " message(s) from "
|
|
||||||
+ from + " to " + to);
|
|
||||||
|
|
||||||
// Listen for messages being sent
|
|
||||||
waitForEvents(fromComponent);
|
|
||||||
SendListener sendListener = new SendListener();
|
|
||||||
fromComponent.getEventBus().addListener(sendListener);
|
|
||||||
|
|
||||||
// Write the messages to a transport stream
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
TestTransportConnectionWriter writer =
|
|
||||||
new TestTransportConnectionWriter(out, false);
|
|
||||||
fromComponent.getConnectionManager().manageOutgoingConnection(toId,
|
|
||||||
SIMPLEX_TRANSPORT_ID, writer);
|
|
||||||
writer.getDisposedLatch().await(TIMEOUT, MILLISECONDS);
|
|
||||||
|
|
||||||
// Check that the expected number of messages were sent
|
|
||||||
waitForEvents(fromComponent);
|
|
||||||
fromComponent.getEventBus().removeListener(sendListener);
|
|
||||||
assertEquals("Messages sent", numNew + numDupes,
|
|
||||||
sendListener.sent.size());
|
|
||||||
|
|
||||||
// Read the messages from the transport stream
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
|
||||||
TestTransportConnectionReader reader =
|
|
||||||
new TestTransportConnectionReader(in);
|
|
||||||
toComponent.getConnectionManager().manageIncomingConnection(
|
|
||||||
SIMPLEX_TRANSPORT_ID, reader);
|
|
||||||
|
|
||||||
if (numPendingOrInvalid > 0) {
|
|
||||||
validationWaiter.await(TIMEOUT, numPendingOrInvalid);
|
|
||||||
}
|
|
||||||
assertEquals("Messages validated", numPendingOrInvalid,
|
|
||||||
validationCounter.getAndSet(0));
|
|
||||||
|
|
||||||
if (numDelivered > 0) {
|
|
||||||
deliveryWaiter.await(TIMEOUT, numDelivered);
|
|
||||||
}
|
|
||||||
assertEquals("Messages delivered", numDelivered,
|
|
||||||
deliveryCounter.getAndSet(0));
|
|
||||||
|
|
||||||
try {
|
|
||||||
messageSemaphore.tryAcquire(numNew, TIMEOUT, MILLISECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while waiting for messages");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void sendAcks(BriarIntegrationTestComponent fromComponent,
|
|
||||||
BriarIntegrationTestComponent toComponent, ContactId toId, int num)
|
|
||||||
throws Exception {
|
|
||||||
// Debug output
|
|
||||||
String from =
|
|
||||||
fromComponent.getIdentityManager().getLocalAuthor().getName();
|
|
||||||
String to = toComponent.getIdentityManager().getLocalAuthor().getName();
|
|
||||||
LOG.info("TEST: Sending " + num + " ACKs from " + from + " to " + to);
|
|
||||||
|
|
||||||
expectAck = true;
|
|
||||||
|
|
||||||
// Listen for messages being sent (none should be sent)
|
|
||||||
waitForEvents(fromComponent);
|
|
||||||
SendListener sendListener = new SendListener();
|
|
||||||
fromComponent.getEventBus().addListener(sendListener);
|
|
||||||
|
|
||||||
// start outgoing connection
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
TestTransportConnectionWriter writer =
|
|
||||||
new TestTransportConnectionWriter(out, false);
|
|
||||||
fromComponent.getConnectionManager().manageOutgoingConnection(toId,
|
|
||||||
SIMPLEX_TRANSPORT_ID, writer);
|
|
||||||
writer.getDisposedLatch().await(TIMEOUT, MILLISECONDS);
|
|
||||||
|
|
||||||
// Check that no messages were sent
|
|
||||||
waitForEvents(fromComponent);
|
|
||||||
fromComponent.getEventBus().removeListener(sendListener);
|
|
||||||
assertEquals("Messages sent", 0, sendListener.sent.size());
|
|
||||||
|
|
||||||
// handle incoming connection
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
|
||||||
TestTransportConnectionReader reader =
|
|
||||||
new TestTransportConnectionReader(in);
|
|
||||||
toComponent.getConnectionManager().manageIncomingConnection(
|
|
||||||
SIMPLEX_TRANSPORT_ID, reader);
|
|
||||||
|
|
||||||
ackWaiter.await(TIMEOUT, num);
|
|
||||||
assertEquals("ACKs delivered", num, ackCounter.getAndSet(0));
|
|
||||||
assertEquals("No messages delivered", 0, deliveryCounter.get());
|
|
||||||
try {
|
|
||||||
messageSemaphore.tryAcquire(num, TIMEOUT, MILLISECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.info("Interrupted while waiting for messages");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
fail();
|
|
||||||
} finally {
|
|
||||||
expectAck = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void removeAllContacts() throws DbException {
|
protected void removeAllContacts() throws DbException {
|
||||||
contactManager0.removeContact(contactId1From0);
|
contactManager0.removeContact(contactId1From0);
|
||||||
contactManager0.removeContact(contactId2From0);
|
contactManager0.removeContact(contactId2From0);
|
||||||
@@ -562,40 +330,4 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
db.transaction(false, txn -> db.setMessageShared(txn, messageId));
|
db.transaction(false, txn -> db.setMessageShared(txn, messageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcasts a marker event and waits for it to be delivered, which
|
|
||||||
* indicates that all previously broadcast events have been delivered.
|
|
||||||
*/
|
|
||||||
public static void waitForEvents(BriarIntegrationTestComponent component)
|
|
||||||
throws Exception {
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
MarkerEvent marker = new MarkerEvent();
|
|
||||||
EventBus eventBus = component.getEventBus();
|
|
||||||
eventBus.addListener(new EventListener() {
|
|
||||||
@Override
|
|
||||||
public void eventOccurred(@Nonnull Event e) {
|
|
||||||
if (e == marker) {
|
|
||||||
latch.countDown();
|
|
||||||
eventBus.removeListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
eventBus.broadcast(marker);
|
|
||||||
if (!latch.await(1, MINUTES)) fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MarkerEvent extends Event {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SendListener implements EventListener {
|
|
||||||
|
|
||||||
private final Set<MessageId> sent = new HashSet<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventOccurred(Event e) {
|
|
||||||
if (e instanceof MessagesSentEvent) {
|
|
||||||
sent.addAll(((MessagesSentEvent) e).getMessageIds());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,14 @@ package org.briarproject.briar.test;
|
|||||||
|
|
||||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
|
||||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||||
|
import org.briarproject.bramble.test.BrambleIntegrationTestComponent;
|
||||||
import org.briarproject.bramble.test.TimeTravel;
|
import org.briarproject.bramble.test.TimeTravel;
|
||||||
import org.briarproject.briar.api.attachment.AttachmentReader;
|
import org.briarproject.briar.api.attachment.AttachmentReader;
|
||||||
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
|
import org.briarproject.briar.api.autodelete.AutoDeleteManager;
|
||||||
@@ -67,7 +64,7 @@ import dagger.Component;
|
|||||||
SharingModule.class
|
SharingModule.class
|
||||||
})
|
})
|
||||||
public interface BriarIntegrationTestComponent
|
public interface BriarIntegrationTestComponent
|
||||||
extends BrambleCoreIntegrationTestEagerSingletons {
|
extends BrambleIntegrationTestComponent {
|
||||||
|
|
||||||
void inject(BriarIntegrationTest<BriarIntegrationTestComponent> init);
|
void inject(BriarIntegrationTest<BriarIntegrationTestComponent> init);
|
||||||
|
|
||||||
@@ -95,16 +92,10 @@ public interface BriarIntegrationTestComponent
|
|||||||
|
|
||||||
LifecycleManager getLifecycleManager();
|
LifecycleManager getLifecycleManager();
|
||||||
|
|
||||||
EventBus getEventBus();
|
|
||||||
|
|
||||||
IdentityManager getIdentityManager();
|
|
||||||
|
|
||||||
AttachmentReader getAttachmentReader();
|
AttachmentReader getAttachmentReader();
|
||||||
|
|
||||||
AvatarManager getAvatarManager();
|
AvatarManager getAvatarManager();
|
||||||
|
|
||||||
ClientHelper getClientHelper();
|
|
||||||
|
|
||||||
ContactManager getContactManager();
|
ContactManager getContactManager();
|
||||||
|
|
||||||
ConversationManager getConversationManager();
|
ConversationManager getConversationManager();
|
||||||
@@ -139,8 +130,6 @@ public interface BriarIntegrationTestComponent
|
|||||||
|
|
||||||
BlogFactory getBlogFactory();
|
BlogFactory getBlogFactory();
|
||||||
|
|
||||||
ConnectionManager getConnectionManager();
|
|
||||||
|
|
||||||
AutoDeleteManager getAutoDeleteManager();
|
AutoDeleteManager getAutoDeleteManager();
|
||||||
|
|
||||||
Clock getClock();
|
Clock getClock();
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import org.briarproject.bramble.account.AccountModule
|
import org.briarproject.bramble.account.AccountModule
|
||||||
import org.briarproject.bramble.api.FeatureFlags
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig
|
import org.briarproject.bramble.api.db.DatabaseConfig
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig
|
import org.briarproject.bramble.api.plugin.PluginConfig
|
||||||
import org.briarproject.bramble.api.plugin.TransportId
|
import org.briarproject.bramble.api.plugin.TransportId
|
||||||
@@ -18,6 +17,7 @@ import org.briarproject.bramble.system.ClockModule
|
|||||||
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
import org.briarproject.bramble.system.DefaultTaskSchedulerModule
|
||||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule
|
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule
|
||||||
import org.briarproject.bramble.system.JavaSystemModule
|
import org.briarproject.bramble.system.JavaSystemModule
|
||||||
|
import org.briarproject.bramble.test.TestFeatureFlagModule
|
||||||
import org.briarproject.bramble.test.TestSecureRandomModule
|
import org.briarproject.bramble.test.TestSecureRandomModule
|
||||||
import org.briarproject.briar.api.test.TestAvatarCreator
|
import org.briarproject.briar.api.test.TestAvatarCreator
|
||||||
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
|
||||||
@@ -40,6 +40,7 @@ import javax.inject.Singleton
|
|||||||
DefaultTaskSchedulerModule::class,
|
DefaultTaskSchedulerModule::class,
|
||||||
DefaultWakefulIoExecutorModule::class,
|
DefaultWakefulIoExecutorModule::class,
|
||||||
SocksModule::class,
|
SocksModule::class,
|
||||||
|
TestFeatureFlagModule::class,
|
||||||
TestSecureRandomModule::class,
|
TestSecureRandomModule::class,
|
||||||
HeadlessBlogModule::class,
|
HeadlessBlogModule::class,
|
||||||
HeadlessContactModule::class,
|
HeadlessContactModule::class,
|
||||||
@@ -78,14 +79,6 @@ internal class HeadlessTestModule(private val appDir: File) {
|
|||||||
@Singleton
|
@Singleton
|
||||||
internal fun provideObjectMapper() = ObjectMapper()
|
internal fun provideObjectMapper() = ObjectMapper()
|
||||||
|
|
||||||
@Provides
|
|
||||||
internal fun provideFeatureFlags() = object : FeatureFlags {
|
|
||||||
override fun shouldEnableImageAttachments() = false
|
|
||||||
override fun shouldEnableProfilePictures() = false
|
|
||||||
override fun shouldEnableDisappearingMessages() = false
|
|
||||||
override fun shouldEnableConnectViaBluetooth() = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
internal fun provideTestAvatarCreator() = TestAvatarCreator { null }
|
internal fun provideTestAvatarCreator() = TestAvatarCreator { null }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user