mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Add connectivity checkers for our own mailbox and a contact's mailbox.
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import static org.briarproject.bramble.mailbox.ConnectivityCheckerImpl.CONNECTIVITY_CHECK_FRESHNESS_MS;
|
||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
|
||||
public class ConnectivityCheckerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
private final MailboxApiCaller mailboxApiCaller =
|
||||
context.mock(MailboxApiCaller.class);
|
||||
private final ApiCall apiCall = context.mock(ApiCall.class);
|
||||
private final Cancellable task = context.mock(Cancellable.class);
|
||||
private final ConnectivityObserver observer1 =
|
||||
context.mock(ConnectivityObserver.class, "1");
|
||||
private final ConnectivityObserver observer2 =
|
||||
context.mock(ConnectivityObserver.class, "2");
|
||||
|
||||
private final MailboxProperties properties =
|
||||
getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
private final long now = System.currentTimeMillis();
|
||||
|
||||
@Test
|
||||
public void testFirstObserverStartsCheck() {
|
||||
ConnectivityCheckerImpl checker = createChecker();
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer1);
|
||||
|
||||
// When the check succeeds the observer should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(observer1).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
|
||||
// The observer should not be called again when subsequent checks
|
||||
// succeed
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObserverIsAddedToExistingCheck() {
|
||||
ConnectivityCheckerImpl checker = createChecker();
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer1);
|
||||
|
||||
// When checkConnectivity() is called again before the first check
|
||||
// succeeds, the observer should be added to the existing check
|
||||
checker.checkConnectivity(properties, observer2);
|
||||
|
||||
// When the check succeeds both observers should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(observer1).onConnectivityCheckSucceeded();
|
||||
oneOf(observer2).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
|
||||
// The observers should not be called again when subsequent checks
|
||||
// succeed
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFreshResultIsReused() {
|
||||
ConnectivityCheckerImpl checker = createChecker();
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer1);
|
||||
|
||||
// When the check succeeds the observer should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(observer1).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
|
||||
// When checkConnectivity() is called again within
|
||||
// CONNECTIVITY_CHECK_FRESHNESS_MS the observer should be called with
|
||||
// the result of the recent check
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now + CONNECTIVITY_CHECK_FRESHNESS_MS));
|
||||
oneOf(observer2).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaleResultIsNotReused() {
|
||||
ConnectivityCheckerImpl checker = createChecker();
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer1);
|
||||
|
||||
// When the check succeeds the observer should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(observer1).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
|
||||
// When checkConnectivity() is called again after more than
|
||||
// CONNECTIVITY_CHECK_FRESHNESS_MS another check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now + CONNECTIVITY_CHECK_FRESHNESS_MS + 1));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer2);
|
||||
|
||||
// When the check succeeds the observer should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(observer2).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
checker.onConnectivityCheckSucceeded(
|
||||
now + CONNECTIVITY_CHECK_FRESHNESS_MS + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckIsCancelledWhenCheckerIsDestroyed() {
|
||||
ConnectivityCheckerImpl checker = createChecker();
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(apiCall);
|
||||
will(returnValue(task));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer1);
|
||||
|
||||
// When the checker is destroyed the check should be cancelled
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(task).cancel();
|
||||
}});
|
||||
|
||||
checker.destroy();
|
||||
|
||||
// If the check runs anyway (cancellation came too late) the observer
|
||||
// should not be called
|
||||
checker.onConnectivityCheckSucceeded(now);
|
||||
}
|
||||
|
||||
private ConnectivityCheckerImpl createChecker() {
|
||||
|
||||
return new ConnectivityCheckerImpl(clock, mailboxApiCaller) {
|
||||
@Override
|
||||
@Nonnull
|
||||
protected ApiCall createConnectivityCheckTask(
|
||||
@Nonnull MailboxProperties properties) {
|
||||
return apiCall;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.action.DoAllAction;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ContactMailboxConnectivityCheckerTest extends BrambleMockTestCase {
|
||||
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
private final MailboxApiCaller mailboxApiCaller =
|
||||
context.mock(MailboxApiCaller.class);
|
||||
private final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||
private final Cancellable task = context.mock(Cancellable.class);
|
||||
private final ConnectivityObserver observer =
|
||||
context.mock(ConnectivityObserver.class);
|
||||
|
||||
private final MailboxProperties properties =
|
||||
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
private final long now = System.currentTimeMillis();
|
||||
|
||||
@Test
|
||||
public void testObserverIsCalledWhenCheckSucceeds() throws Exception {
|
||||
ContactMailboxConnectivityChecker checker = createChecker();
|
||||
AtomicReference<ApiCall> apiCall = new AtomicReference<>(null);
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(apiCall, ApiCall.class, 0),
|
||||
returnValue(task)
|
||||
));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer);
|
||||
|
||||
// When the check succeeds the observer should be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(properties,
|
||||
requireNonNull(properties.getInboxId()));
|
||||
will(returnValue(emptyList()));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(observer).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
// The call should not be retried
|
||||
assertFalse(apiCall.get().callApi());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObserverIsNotCalledWhenCheckFails() throws Exception {
|
||||
ContactMailboxConnectivityChecker checker = createChecker();
|
||||
AtomicReference<ApiCall> apiCall = new AtomicReference<>(null);
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(apiCall, ApiCall.class, 0),
|
||||
returnValue(task)
|
||||
));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer);
|
||||
|
||||
// When the check fails, the observer should not be called
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(properties,
|
||||
requireNonNull(properties.getInboxId()));
|
||||
will(throwException(new IOException()));
|
||||
}});
|
||||
|
||||
// The call should be retried
|
||||
assertTrue(apiCall.get().callApi());
|
||||
}
|
||||
|
||||
private ContactMailboxConnectivityChecker createChecker() {
|
||||
return new ContactMailboxConnectivityChecker(clock, mailboxApiCaller,
|
||||
mailboxApi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.action.DoAllAction;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.mailbox.MailboxApi.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class OwnMailboxConnectivityCheckerTest extends BrambleMockTestCase {
|
||||
|
||||
private final Clock clock = context.mock(Clock.class);
|
||||
private final MailboxApiCaller mailboxApiCaller =
|
||||
context.mock(MailboxApiCaller.class);
|
||||
private final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||
private final TransactionManager db =
|
||||
context.mock(TransactionManager.class);
|
||||
private final MailboxSettingsManager mailboxSettingsManager =
|
||||
context.mock(MailboxSettingsManager.class);
|
||||
private final Cancellable task = context.mock(Cancellable.class);
|
||||
private final ConnectivityObserver observer =
|
||||
context.mock(ConnectivityObserver.class);
|
||||
|
||||
private final MailboxProperties properties =
|
||||
getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
private final long now = System.currentTimeMillis();
|
||||
|
||||
@Test
|
||||
public void testObserverIsCalledWhenCheckSucceeds() throws Exception {
|
||||
OwnMailboxConnectivityChecker checker = createChecker();
|
||||
AtomicReference<ApiCall> apiCall = new AtomicReference<>(null);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(apiCall, ApiCall.class, 0),
|
||||
returnValue(task)
|
||||
));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer);
|
||||
|
||||
// When the check succeeds, the success should be recorded in the DB
|
||||
// and the observer should be called
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(mailboxApi).getFolders(properties);
|
||||
will(returnValue(emptyList()));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(mailboxSettingsManager).recordSuccessfulConnection(txn, now);
|
||||
oneOf(observer).onConnectivityCheckSucceeded();
|
||||
}});
|
||||
|
||||
// The call should not be retried
|
||||
assertFalse(apiCall.get().callApi());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObserverIsNotCalledWhenCheckFails() throws Exception {
|
||||
OwnMailboxConnectivityChecker checker = createChecker();
|
||||
AtomicReference<ApiCall> apiCall = new AtomicReference<>(null);
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
// When checkConnectivity() is called a check should be started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(apiCall, ApiCall.class, 0),
|
||||
returnValue(task)
|
||||
));
|
||||
}});
|
||||
|
||||
checker.checkConnectivity(properties, observer);
|
||||
|
||||
// When the check fails, the failure should be recorded in the DB and
|
||||
// the observer should not be called
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(mailboxApi).getFolders(properties);
|
||||
will(throwException(new IOException()));
|
||||
oneOf(clock).currentTimeMillis();
|
||||
will(returnValue(now));
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(mailboxSettingsManager)
|
||||
.recordFailedConnectionAttempt(txn, now);
|
||||
}});
|
||||
|
||||
// The call should be retried
|
||||
assertTrue(apiCall.get().callApi());
|
||||
}
|
||||
|
||||
private OwnMailboxConnectivityChecker createChecker() {
|
||||
return new OwnMailboxConnectivityChecker(clock, mailboxApiCaller,
|
||||
mailboxApi, db, mailboxSettingsManager);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user