Files
briar/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTest.java
Ernir Erlingsson eaa393a7ed added a cache to the IdentityManager, changed its signature, modified when and where the author is stored
made the author creation single-threaded again in the LifecycleManager, removed redundant code
2016-11-01 12:51:49 +01:00

1135 lines
36 KiB
Java

package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.Bytes;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.forum.ForumInvitationResponse;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPost;
import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumPostHeader;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.SharingInvitationItem;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.system.Clock;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class ForumSharingIntegrationTest extends BriarTestCase {
private LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
private SyncSessionFactory sync0, sync1, sync2;
private ForumManager forumManager0, forumManager1;
private ContactManager contactManager0, contactManager1, contactManager2;
private ContactId contactId0, contactId2, contactId1, contactId21;
private IdentityManager identityManager0, identityManager1, identityManager2;
private LocalAuthor author0, author1, author2;
private Forum forum0;
private SharerListener listener0, listener2;
private InviteeListener listener1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
ForumPostFactory forumPostFactory;
@Inject
CryptoComponent cryptoComponent;
// objects accessed from background threads need to be volatile
private volatile ForumSharingManager forumSharingManager0;
private volatile ForumSharingManager forumSharingManager1;
private volatile ForumSharingManager forumSharingManager2;
private volatile Waiter eventWaiter;
private volatile Waiter msgWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String SHARER2 = "Sharer2";
private boolean respond = true;
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private ForumSharingIntegrationTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown=ExpectedException.none();
@Before
public void setUp() {
ForumSharingIntegrationTestComponent component =
DaggerForumSharingIntegrationTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, SHARER);
t0 = DaggerForumSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, INVITEE);
t1 = DaggerForumSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
File t2Dir = new File(testDir, SHARER2);
t2 = DaggerForumSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
injectEagerSingletons(t2);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
identityManager2 = t2.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
contactManager2 = t2.getContactManager();
forumManager0 = t0.getForumManager();
forumManager1 = t1.getForumManager();
forumSharingManager0 = t0.getForumSharingManager();
forumSharingManager1 = t1.getForumSharingManager();
forumSharingManager2 = t2.getForumSharingManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test
eventWaiter = new Waiter();
msgWaiter = new Waiter();
}
@Test
public void testSuccessfulSharing() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
// invitee has one invitation message from sharer
List<InvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(2, list.size());
// check other things are alright with the forum message
for (InvitationMessage m : list) {
if (m instanceof ForumInvitationRequest) {
ForumInvitationRequest invitation =
(ForumInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals("Hi!", invitation.getMessage());
} else {
ForumInvitationResponse response =
(ForumInvitationResponse) m;
assertEquals(contactId0, response.getContactId());
assertTrue(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can not be shared again
Contact c1 = contactManager0.getContact(contactId1);
assertFalse(forumSharingManager0.canBeShared(forum0.getId(), c1));
Contact c0 = contactManager1.getContact(contactId0);
assertFalse(forumSharingManager1.canBeShared(forum0.getId(), c0));
} finally {
stopLifecycles();
}
}
@Test
public void testDeclinedSharing() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(false);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, null);
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was not added
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// forum is no longer available to invitee who declined
assertEquals(0, forumSharingManager1.getInvitations().size());
// invitee has one invitation message from sharer and one response
List<InvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(2, list.size());
// check things are alright with the forum message
for (InvitationMessage m : list) {
if (m instanceof ForumInvitationRequest) {
ForumInvitationRequest invitation =
(ForumInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals(null, invitation.getMessage());
} else {
ForumInvitationResponse response =
(ForumInvitationResponse) m;
assertEquals(contactId0, response.getContactId());
assertFalse(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can be shared again
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
} finally {
stopLifecycles();
}
}
@Test
public void testInviteeLeavesAfterFinished() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
// sharer shares forum with invitee
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1));
// invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// invitee un-subscribes from forum
forumManager1.removeForum(forum0);
// send leave message to sharer
syncToSharer();
// forum is gone
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// sharer no longer shares forum with invitee
assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1));
// invitee no longer gets forum shared by sharer
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// forum can be shared again
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
Contact c0 = contactManager1.getContact(contactId0);
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
} finally {
stopLifecycles();
}
}
@Test
public void testSharerLeavesAfterFinished() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, null);
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
// sharer shares forum with invitee
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1));
// invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// send leave message to invitee
syncToInvitee();
// forum is gone for sharer, but not invitee
assertEquals(0, forumManager0.getForums().size());
assertEquals(1, forumManager1.getForums().size());
// invitee no longer shares forum with sharer
Contact c0 = contactManager1.getContact(contactId0);
assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(c0));
// sharer no longer gets forum shared by invitee
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// forum can be shared again
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
} finally {
stopLifecycles();
}
}
@Test
public void testSharerLeavesBeforeResponse() throws Exception {
startLifecycles();
try {
// initialize except event listeners
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, null);
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// prevent invitee response before syncing messages
respond = false;
// sync first request message and leave message
deliverMessage(sync0, contactId0, sync1, contactId1, 2,
"Sharer to Invitee");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// ensure that invitee has no forum invitations available
assertEquals(0, forumSharingManager1.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// Try again, this time allow the response
addForumForSharer();
respond = true;
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, null);
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// sync first request message and leave message
deliverMessage(sync0, contactId0, sync1, contactId1, 2,
"Sharer to Invitee");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// ensure that invitee has no forum invitations available
assertEquals(0, forumSharingManager1.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
} finally {
stopLifecycles();
}
}
@Test
public void testSessionIdReuse() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(1, forumManager1.getForums().size());
// reset event received state
listener1.requestReceived = false;
// get SessionId from invitation
List<InvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// get all sorts of stuff needed to send a message
DatabaseComponent db = t0.getDatabaseComponent();
MessageQueueManager queue = t0.getMessageQueueManager();
Contact c1 = contactManager0.getContact(contactId1);
ContactGroupFactory groupFactory = t0.getContactGroupFactory();
Group group = groupFactory
.createContactGroup(forumSharingManager0.getClientId(), c1);
long time = clock.currentTimeMillis();
BdfList bodyList = BdfList.of(SHARE_MSG_TYPE_INVITATION,
sessionId.getBytes(),
TestUtils.getRandomString(42),
TestUtils.getRandomBytes(FORUM_SALT_LENGTH)
);
byte[] body = t0.getClientHelper().toByteArray(bodyList);
// add the message to the queue
Transaction txn = db.startTransaction(false);
try {
queue.sendMessage(txn, group, time, body, new Metadata());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
// actually send the message
syncToInvitee();
// make sure there was no new request received
assertFalse(listener1.requestReceived);
} finally {
stopLifecycles();
}
}
@Test
public void testSharingSameForumWithEachOther() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(1, forumManager1.getForums().size());
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(),
contactId0,
"I am re-sharing this forum with you.");
// sync re-share invitation
syncToSharer();
// make sure that no new request was received
assertFalse(listener0.requestReceived);
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
assertEquals(0, forumSharingManager0.getInvitations().size());
} finally {
stopLifecycles();
}
}
@Test
public void testSharingSameForumWithEachOtherAtSameTime() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// invitee adds the same forum
DatabaseComponent db1 = t1.getDatabaseComponent();
Transaction txn = db1.startTransaction(false);
db1.addGroup(txn, forum0.getGroup());
txn.setComplete();
db1.endTransaction(txn);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(),
contactId0, "I am re-sharing this forum with you.");
// find out who should be Alice, because of random keys
Bytes key0 = new Bytes(author0.getPublicKey());
Bytes key1 = new Bytes(author1.getPublicKey());
// only now sync first request message
boolean alice = key1.compareTo(key0) < 0;
syncToInvitee();
if (alice) {
assertFalse(listener1.requestReceived);
} else {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
}
// sync second invitation
alice = key0.compareTo(key1) < 0;
syncToSharer();
if (alice) {
assertFalse(listener0.requestReceived);
// sharer did not receive request, but response to own request
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
assertEquals(2, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(3, forumSharingManager1
.getInvitationMessages(contactId0).size());
} else {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.requestReceived);
// send response from sharer to invitee and make sure it arrived
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.responseReceived);
assertEquals(3, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(2, forumSharingManager1
.getInvitationMessages(contactId0).size());
}
} finally {
stopLifecycles();
}
}
@Test
public void testContactRemoved() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(1, forumManager1.getForums().size());
assertEquals(1,
forumSharingManager0.getSharedWith(forum0.getId()).size());
// remember SessionId from invitation
List<InvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// contacts now remove each other
contactManager0.removeContact(contactId1);
contactManager2.removeContact(contactId21);
contactManager1.removeContact(contactId0);
contactManager1.removeContact(contactId2);
// make sure sharer does share the forum with nobody now
assertEquals(0,
forumSharingManager0.getSharedWith(forum0.getId()).size());
// contacts add each other again
addDefaultContacts();
// get all sorts of stuff needed to send a message
DatabaseComponent db = t0.getDatabaseComponent();
MessageQueueManager queue = t0.getMessageQueueManager();
Contact c1 = contactManager0.getContact(contactId1);
ContactGroupFactory groupFactory = t0.getContactGroupFactory();
Group group = groupFactory
.createContactGroup(forumSharingManager0.getClientId(), c1);
long time = clock.currentTimeMillis();
// construct a new message re-using the old SessionId
BdfList bodyList = BdfList.of(SHARE_MSG_TYPE_INVITATION,
sessionId.getBytes(),
TestUtils.getRandomString(42),
TestUtils.getRandomBytes(FORUM_SALT_LENGTH)
);
byte[] body = t0.getClientHelper().toByteArray(bodyList);
// add the message to the queue
Transaction txn = db.startTransaction(false);
try {
queue.sendMessage(txn, group, time, body, new Metadata());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
// actually send the message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
// make sure the new request was received with the same sessionId
// as proof that the state got deleted along with contacts
assertTrue(listener1.requestReceived);
} finally {
stopLifecycles();
}
}
@Test
public void testTwoContactsShareSameForum() throws Exception {
startLifecycles();
try {
// initialize
getDefaultIdentities();
addDefaultContacts();
addForumForSharer();
// second sharer adds the same forum
DatabaseComponent db2 = t2.getDatabaseComponent();
Transaction txn = db2.startTransaction(false);
db2.addGroup(txn, forum0.getGroup());
txn.setComplete();
db2.endTransaction(txn);
// add listeners
listener0 = new SharerListener();
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(true, false);
t1.getEventBus().addListener(listener1);
listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
// second sharer sends invitation for same forum
forumSharingManager2
.sendInvitation(forum0.getId(), contactId1, null);
// sync second request message
deliverMessage(sync2, contactId2, sync1, contactId1, 1,
"Sharer2 to Invitee");
// make sure we now have two invitations to the same forum available
Collection<SharingInvitationItem> forums =
forumSharingManager1.getInvitations();
assertEquals(1, forums.size());
assertEquals(2, forums.iterator().next().getNewSharers().size());
assertEquals(forum0, forums.iterator().next().getShareable());
assertEquals(2,
forumSharingManager1.getSharedBy(forum0.getId()).size());
// make sure both sharers actually share the forum
Collection<Contact> contacts =
forumSharingManager1.getSharedBy(forum0.getId());
assertEquals(2, contacts.size());
// answer second request
Contact c2 = contactManager1.getContact(contactId2);
forumSharingManager1.respondToInvitation(forum0, c2, true);
// sync response
deliverMessage(sync1, contactId21, sync2, contactId2, 1,
"Invitee to Sharer2");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener2.responseReceived);
// answer first request
Contact c0 =
contactManager1.getContact(contactId0);
forumSharingManager1.respondToInvitation(forum0, c0, true);
// sync response
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
} finally {
stopLifecycles();
}
}
@Test
public void testSyncAfterReSharing() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
// sharer posts into the forum
long time = clock.currentTimeMillis();
String body = TestUtils.getRandomString(42);
ForumPost p = forumPostFactory
.createPost(forum0.getId(), time, null, author0,
body);
forumManager0.addLocalPost(p);
// sync forum post
syncToInvitee();
// make sure forum post arrived
Collection<ForumPostHeader> headers =
forumManager1.getPostHeaders(forum0.getId());
assertEquals(1, headers.size());
ForumPostHeader header = headers.iterator().next();
assertEquals(p.getMessage().getId(), header.getId());
assertEquals(author0, header.getAuthor());
// now invitee creates a post
time = clock.currentTimeMillis();
body = TestUtils.getRandomString(42);
p = forumPostFactory
.createPost(forum0.getId(), time, null, author1,
body);
forumManager1.addLocalPost(p);
// sync forum post
syncToSharer();
// make sure forum post arrived
headers = forumManager1.getPostHeaders(forum0.getId());
assertEquals(2, headers.size());
boolean found = false;
for (ForumPostHeader h : headers) {
if (p.getMessage().getId().equals(h.getId())) {
found = true;
assertEquals(author1, h.getAuthor());
}
}
assertTrue(found);
// contacts remove each other
contactManager0.removeContact(contactId1);
contactManager1.removeContact(contactId0);
contactManager1.removeContact(contactId2);
contactManager2.removeContact(contactId21);
// contacts add each other back
addDefaultContacts();
// send invitation again
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
// now invitee creates a post
time = clock.currentTimeMillis();
body = TestUtils.getRandomString(42);
p = forumPostFactory
.createPost(forum0.getId(), time, null, author1,
body);
forumManager1.addLocalPost(p);
// sync forum post
syncToSharer();
// make sure forum post arrived
headers = forumManager1.getPostHeaders(forum0.getId());
assertEquals(3, headers.size());
found = false;
for (ForumPostHeader h : headers) {
if (p.getMessage().getId().equals(h.getId())) {
found = true;
assertEquals(author1, h.getAuthor());
}
}
assertTrue(found);
} finally {
stopLifecycles();
}
}
@After
public void tearDown() throws InterruptedException {
TestUtils.deleteTestDirectory(testDir);
}
private class SharerListener implements EventListener {
private volatile boolean requestReceived = false;
private volatile boolean responseReceived = false;
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
LOG.info("TEST: Sharer received message");
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent event =
(ForumInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
responseReceived = true;
eventWaiter.resume();
}
// this is only needed for tests where a forum is re-shared
else if (e instanceof ForumInvitationReceivedEvent) {
ForumInvitationReceivedEvent event =
(ForumInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
requestReceived = true;
Forum f = event.getShareable();
try {
Contact c = contactManager0.getContact(contactId1);
forumSharingManager0.respondToInvitation(f, c, true);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
}
}
private class InviteeListener implements EventListener {
private volatile boolean requestReceived = false;
private volatile boolean responseReceived = false;
private final boolean accept, answer;
private InviteeListener(boolean accept, boolean answer) {
this.accept = accept;
this.answer = answer;
}
private InviteeListener(boolean accept) {
this(accept, true);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
LOG.info("TEST: Invitee received message");
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationReceivedEvent) {
ForumInvitationReceivedEvent event =
(ForumInvitationReceivedEvent) e;
requestReceived = true;
if (!answer) return;
Forum f = event.getShareable();
try {
eventWaiter.assertEquals(1,
forumSharingManager1.getInvitations().size());
SharingInvitationItem invitation =
forumSharingManager1.getInvitations().iterator()
.next();
eventWaiter.assertEquals(f, invitation.getShareable());
if (respond) {
Contact c =
contactManager1.getContact(event.getContactId());
forumSharingManager1.respondToInvitation(f, c, accept);
}
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
// this is only needed for tests where a forum is re-shared
else if (e instanceof ForumInvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent event =
(ForumInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId0, event.getContactId());
responseReceived = true;
eventWaiter.resume();
}
}
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices(SHARER);
lifecycleManager1.startServices(INVITEE);
lifecycleManager2.startServices(SHARER2);
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
lifecycleManager2.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager2.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
lifecycleManager2.waitForShutdown();
}
private void defaultInit(boolean accept) throws DbException {
getDefaultIdentities();
addDefaultContacts();
addForumForSharer();
listenToEvents(accept);
}
private void getDefaultIdentities() throws DbException {
author0 = identityManager0.getLocalAuthor();
author1 = identityManager1.getLocalAuthor();
author2 = identityManager2.getLocalAuthor();
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true, true
);
// second sharer does the same
contactId21 = contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true, true
);
// invitee adds sharers back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contactId2 = contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
}
private void addForumForSharer() throws DbException {
// sharer creates forum
forum0 = forumManager0.addForum("Test Forum");
}
private void listenToEvents(boolean accept) {
listener0 = new SharerListener();
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(accept);
t1.getEventBus().addListener(listener1);
listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
}
private void syncToInvitee() throws IOException, TimeoutException {
deliverMessage(sync0, contactId0, sync1, contactId1, 1,
"Sharer to Invitee");
}
private void syncToSharer() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId0, 1,
"Invitee to Sharer");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, int num, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
// wait for message to actually arrive
msgWaiter.await(TIMEOUT, num);
}
private void injectEagerSingletons(
ForumSharingIntegrationTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new ForumModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}