Handle invitations to the same forum by multiple contacts

Closes #391
This commit is contained in:
Torsten Grote
2016-05-12 17:00:14 -03:00
parent 1a175beac9
commit 38a4f73cdc
4 changed files with 163 additions and 48 deletions

View File

@@ -49,6 +49,7 @@ 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;
@@ -65,15 +66,15 @@ import static org.junit.Assert.assertTrue;
public class ForumSharingIntegrationTest extends BriarTestCase {
LifecycleManager lifecycleManager0, lifecycleManager1;
SyncSessionFactory sync0, sync1;
ForumManager forumManager0, forumManager1;
ContactManager contactManager0, contactManager1;
ContactId contactId0, contactId1;
IdentityManager identityManager0, identityManager1;
LocalAuthor author0, author1;
LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
SyncSessionFactory sync0, sync1, sync2;
ForumManager forumManager0, forumManager1, forumManager2;
ContactManager contactManager0, contactManager1, contactManager2;
ContactId contactId0, contactId2, contactId1, contactId21;
IdentityManager identityManager0, identityManager1, identityManager2;
LocalAuthor author0, author1, author2;
Forum forum0;
SharerListener listener0;
SharerListener listener0, listener2;
InviteeListener listener1;
@Inject
@@ -84,6 +85,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// 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;
@@ -92,11 +94,12 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String SHARER2 = "Sharer2";
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private ForumSharingIntegrationTestComponent t0, t1;
private ForumSharingIntegrationTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown=ExpectedException.none();
@@ -117,17 +120,26 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
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();
forumManager2 = t2.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();
@@ -474,7 +486,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(1, forumManager1.getForums().size());
// invitee now shares same forum back
forumSharingManager1.sendForumInvitation(forum0.getId(), contactId0,
forumSharingManager1.sendForumInvitation(forum0.getId(),
contactId0,
"I am re-sharing this forum with you.");
// sync re-share invitation
@@ -526,7 +539,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// 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,
@@ -572,6 +587,75 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
}
}
@Test
public void testTwoContactsShareSameForum() throws Exception {
startLifecycles();
try {
// initialize
addDefaultIdentities();
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
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
// second sharer sends invitation for same forum
forumSharingManager2
.sendForumInvitation(forum0.getId(), contactId1, null);
// sync second request message
deliverMessage(sync2, contactId2, sync1, contactId1,
"Sharer2 to Invitee");
// make sure we have only one forum available
Collection<Forum> forums =
forumSharingManager1.getAvailableForums();
assertEquals(1, forums.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,
"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();
}
}
@After
public void tearDown() throws InterruptedException {
@@ -609,7 +693,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
requestReceived = true;
Forum f = event.getForum();
try {
forumSharingManager0.respondToInvitation(f, true);
Contact c = contactManager0.getContact(contactId1);
forumSharingManager0.respondToInvitation(f, c, true);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
@@ -624,10 +709,15 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
public volatile boolean requestReceived = false;
public volatile boolean responseReceived = false;
private final boolean accept;
private final boolean accept, answer;
InviteeListener(boolean accept, boolean answer) {
this.accept = accept;
this.answer = answer;
}
InviteeListener(boolean accept) {
this.accept = accept;
this(accept, true);
}
public void eventOccurred(Event e) {
@@ -644,11 +734,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
} else if (e instanceof ForumInvitationReceivedEvent) {
ForumInvitationReceivedEvent event =
(ForumInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId0, event.getContactId());
requestReceived = true;
if (!answer) return;
Forum f = event.getForum();
try {
forumSharingManager1.respondToInvitation(f, accept);
Contact c =
contactManager1.getContact(event.getContactId());
forumSharingManager1.respondToInvitation(f, c, accept);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
@@ -670,18 +762,23 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices();
lifecycleManager1.startServices();
lifecycleManager2.startServices();
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 {
@@ -700,6 +797,10 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
identityManager1.addLocalAuthor(author1);
author2 = authorFactory.createLocalAuthor(SHARER2,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
identityManager2.addLocalAuthor(author2);
}
private void addDefaultContacts() throws DbException {
@@ -708,11 +809,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharer back
// second sharer does the same
contactId21 = contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharers back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
contactId2 = contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
}
private void addForumForSharer() throws DbException {
@@ -725,6 +835,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
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 {

View File

@@ -153,7 +153,7 @@ public class AvailableForumsActivity extends BriarActivity
@Override
public void onItemClick(AvailableForumsItem item, boolean accept) {
respondToInvitation(item.getForum(), accept);
respondToInvitation(item, accept);
// show toast
int res = R.string.forum_declined_toast;
@@ -161,12 +161,16 @@ public class AvailableForumsActivity extends BriarActivity
Toast.makeText(this, res, LENGTH_SHORT).show();
}
private void respondToInvitation(final Forum f, final boolean accept) {
runOnDbThread(new Runnable() {
private void respondToInvitation(final AvailableForumsItem item,
final boolean accept) {
briarController.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
forumSharingManager.respondToInvitation(f, accept);
Forum f = item.getForum();
for (Contact c : item.getContacts()) {
forumSharingManager.respondToInvitation(f, c, accept);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);

View File

@@ -24,7 +24,8 @@ public interface ForumSharingManager {
/**
* Responds to a pending forum invitation
*/
void respondToInvitation(Forum f, boolean accept) throws DbException;
void respondToInvitation(Forum f, Contact c, boolean accept)
throws DbException;
/**
* Returns all forum sharing messages sent by the Contact

View File

@@ -283,13 +283,14 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
@Override
public void respondToInvitation(Forum f, boolean accept)
public void respondToInvitation(Forum f, Contact c, boolean accept)
throws DbException {
Transaction txn = db.startTransaction(false);
try {
// find session state based on forum
InviteeSessionState localState = getSessionStateForResponse(txn, f);
InviteeSessionState localState =
getSessionStateForResponse(txn, f, c);
// define action
InviteeSessionState.Action localAction;
@@ -562,17 +563,15 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
"More than one session state found for message with session ID " +
Arrays.hashCode(sessionId.getBytes()));
}
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
return fromBdfDictionary(m.getValue());
if (map.isEmpty()) {
if (warn && LOG.isLoggable(WARNING)) {
LOG.warning(
"No session state found for message with session ID " +
Arrays.hashCode(sessionId.getBytes()));
}
throw new FormatException();
}
if (warn && LOG.isLoggable(WARNING)) {
LOG.warning(
"No session state found for message with session ID " +
Arrays.hashCode(sessionId.getBytes()));
}
throw new FormatException();
return fromBdfDictionary(map.values().iterator().next());
}
}
@@ -590,11 +589,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
}
private InviteeSessionState getSessionStateForResponse(Transaction txn,
Forum f) throws DbException, FormatException {
Forum f, Contact c) throws DbException, FormatException {
// query for invitee states for that forum in state await response
BdfDictionary query = BdfDictionary.of(
new BdfEntry(IS_SHARER, false),
new BdfEntry(CONTACT_ID, c.getId().getInt()),
new BdfEntry(FORUM_ID, f.getId()),
new BdfEntry(STATE,
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE
@@ -604,26 +604,24 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, localGroup.getId(), query);
// TODO we might have been invited to the same forum from several people
// in different sessions, so we need to handle this (#391)
if (map.size() > 1 && LOG.isLoggable(WARNING)) {
LOG.warning(
"More than one session state found for forum with ID " +
Arrays.hashCode(f.getId().getBytes()) +
" in state AWAIT_LOCAL_RESPONSE");
" in state AWAIT_LOCAL_RESPONSE for contact " +
c.getAuthor().getName());
}
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
return (InviteeSessionState) fromBdfDictionary(m.getValue());
if (map.isEmpty()) {
if (LOG.isLoggable(WARNING)) {
LOG.warning(
"No session state found for forum with ID " +
Arrays.hashCode(f.getId().getBytes()) +
" in state AWAIT_LOCAL_RESPONSE");
}
throw new DbException();
}
if (LOG.isLoggable(WARNING)) {
LOG.warning(
"No session state found for forum with ID " +
Arrays.hashCode(f.getId().getBytes()) +
" in state AWAIT_LOCAL_RESPONSE");
}
throw new DbException();
return (InviteeSessionState) fromBdfDictionary(
map.values().iterator().next());
}
private ForumSharingSessionState getSessionStateForLeaving(Transaction txn,