mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Let the ForumValidator return parent posts as dependency
and add integration tests that make sure that dependencies are handled properly.
This commit is contained in:
@@ -2,13 +2,30 @@ package org.briarproject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
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.sync.GroupId;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
@@ -17,55 +34,101 @@ import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.VALID;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ForumManagerTest {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1;
|
||||
private SyncSessionFactory sync0, sync1;
|
||||
private ForumManager forumManager0, forumManager1;
|
||||
private ContactManager contactManager0, contactManager1;
|
||||
private ContactId contactId0,contactId1;
|
||||
private IdentityManager identityManager0, identityManager1;
|
||||
private LocalAuthor author0, author1;
|
||||
private Forum forum0;
|
||||
|
||||
@Inject
|
||||
protected ForumManager forumManager;
|
||||
Clock clock;
|
||||
@Inject
|
||||
protected ForumPostFactory forumPostFactory;
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
protected DatabaseComponent db;
|
||||
ForumPostFactory forumPostFactory;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile ForumSharingManager forumSharingManager0;
|
||||
private volatile ForumSharingManager forumSharingManager1;
|
||||
private volatile Waiter validationWaiter;
|
||||
private volatile Waiter deliveryWaiter;
|
||||
|
||||
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 static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
|
||||
|
||||
private ForumManagerTestComponent t0, t1;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ForumManagerTestComponent component =
|
||||
DaggerForumManagerTestComponent.builder().build();
|
||||
component.inject(this);
|
||||
injectEagerSingletons(component);
|
||||
|
||||
assertTrue(testDir.mkdirs());
|
||||
File tDir = new File(testDir, "db");
|
||||
File t0Dir = new File(testDir, SHARER);
|
||||
t0 = DaggerForumManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
|
||||
injectEagerSingletons(t0);
|
||||
File t1Dir = new File(testDir, INVITEE);
|
||||
t1 = DaggerForumManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
|
||||
ForumManagerTestComponent component =
|
||||
DaggerForumManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(tDir))
|
||||
.build();
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
forumManager0 = t0.getForumManager();
|
||||
forumManager1 = t1.getForumManager();
|
||||
forumSharingManager0 = t0.getForumSharingManager();
|
||||
forumSharingManager1 = t1.getForumSharingManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
|
||||
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 SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
component.inject(this);
|
||||
// initialize waiters fresh for each test
|
||||
validationWaiter = new Waiter();
|
||||
deliveryWaiter = new Waiter();
|
||||
}
|
||||
|
||||
ForumPost createForumPost(GroupId groupId, ForumPost parent, String body,
|
||||
long ms)
|
||||
throws Exception {
|
||||
private ForumPost createForumPost(GroupId groupId, ForumPost parent,
|
||||
String body, long ms) throws Exception {
|
||||
return forumPostFactory.createAnonymousPost(groupId, ms,
|
||||
parent == null ? null : parent.getMessage().getId(),
|
||||
"text/plain", StringUtils.toUtf8(body));
|
||||
@@ -73,13 +136,12 @@ public class ForumManagerTest {
|
||||
|
||||
@Test
|
||||
public void testForumPost() throws Exception {
|
||||
assertFalse(db.open());
|
||||
assertNotNull(forumManager);
|
||||
Forum forum = forumManager.addForum("TestForum");
|
||||
assertEquals(1, forumManager.getForums().size());
|
||||
final long ms1 = System.currentTimeMillis() - 1000L;
|
||||
startLifecycles();
|
||||
Forum forum = forumManager0.addForum("TestForum");
|
||||
assertEquals(1, forumManager0.getForums().size());
|
||||
final long ms1 = clock.currentTimeMillis() - 1000L;
|
||||
final String body1 = "some forum text";
|
||||
final long ms2 = System.currentTimeMillis();
|
||||
final long ms2 = clock.currentTimeMillis();
|
||||
final String body2 = "some other forum text";
|
||||
ForumPost post1 =
|
||||
createForumPost(forum.getGroup().getId(), null, body1, ms1);
|
||||
@@ -87,16 +149,16 @@ public class ForumManagerTest {
|
||||
ForumPost post2 =
|
||||
createForumPost(forum.getGroup().getId(), post1, body2, ms2);
|
||||
assertEquals(ms2, post2.getMessage().getTimestamp());
|
||||
forumManager.addLocalPost(post1);
|
||||
forumManager.setReadFlag(post1.getMessage().getId(), true);
|
||||
forumManager.addLocalPost(post2);
|
||||
forumManager.setReadFlag(post2.getMessage().getId(), false);
|
||||
forumManager0.addLocalPost(post1);
|
||||
forumManager0.setReadFlag(post1.getMessage().getId(), true);
|
||||
forumManager0.addLocalPost(post2);
|
||||
forumManager0.setReadFlag(post2.getMessage().getId(), false);
|
||||
Collection<ForumPostHeader> headers =
|
||||
forumManager.getPostHeaders(forum.getGroup().getId());
|
||||
forumManager0.getPostHeaders(forum.getGroup().getId());
|
||||
assertEquals(2, headers.size());
|
||||
for (ForumPostHeader h : headers) {
|
||||
final String hBody =
|
||||
StringUtils.fromUtf8(forumManager.getPostBody(h.getId()));
|
||||
StringUtils.fromUtf8(forumManager0.getPostBody(h.getId()));
|
||||
|
||||
boolean isPost1 = h.getId().equals(post1.getMessage().getId());
|
||||
boolean isPost2 = h.getId().equals(post2.getMessage().getId());
|
||||
@@ -114,8 +176,258 @@ public class ForumManagerTest {
|
||||
assertFalse(h.isRead());
|
||||
}
|
||||
}
|
||||
forumManager.removeForum(forum);
|
||||
assertEquals(0, forumManager.getForums().size());
|
||||
db.close();
|
||||
forumManager0.removeForum(forum);
|
||||
assertEquals(0, forumManager0.getForums().size());
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostDelivery() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g, null, "a", time);
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send post to 1
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostDeliveredAfterParent() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post without the parent
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g, null, "a", time);
|
||||
ForumPost post2 = createForumPost(g, post1, "a", time);
|
||||
forumManager0.addLocalPost(post2);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send post to 1 without waiting for message delivery
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// now add the parent post as well
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(2, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// and send it over to 1 and wait for a second message to be delivered
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
assertEquals(2, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostWithParentInOtherGroup() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// share a second forum
|
||||
Forum forum1 = forumManager0.addForum("Test Forum1");
|
||||
GroupId g1 = forum1.getId();
|
||||
forumSharingManager0.sendForumInvitation(g1, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
forumSharingManager1.respondToInvitation(forum1, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post with a parent in another forum
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g1, null, "a", time);
|
||||
ForumPost post = createForumPost(g, post1, "b", time);
|
||||
forumManager0.addLocalPost(post);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send posts to 1
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// now also add the parent post which is in another group
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g1).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g1).size());
|
||||
|
||||
// send posts to 1
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(1, forumManager0.getPostHeaders(g1).size());
|
||||
// the next line is critical, makes sure post doesn't show up
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
assertEquals(1, forumManager1.getPostHeaders(g1).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == VALID ||
|
||||
event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
validationWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultInit() throws DbException {
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
addForum();
|
||||
listenToEvents();
|
||||
}
|
||||
|
||||
private void addDefaultIdentities() throws DbException {
|
||||
author0 = authorFactory.createLocalAuthor(SHARER,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager0.addLocalAuthor(author0);
|
||||
author1 = authorFactory.createLocalAuthor(INVITEE,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager1.addLocalAuthor(author1);
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// sharer adds invitee as contact
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true
|
||||
);
|
||||
// invitee adds sharers back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private void addForum() throws DbException {
|
||||
forum0 = forumManager0.addForum("Test Forum");
|
||||
}
|
||||
|
||||
private void listenToEvents() {
|
||||
Listener listener0 = new Listener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
Listener listener1 = new Listener();
|
||||
t1.getEventBus().addListener(listener1);
|
||||
}
|
||||
|
||||
private void sync0To1() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
}
|
||||
|
||||
private void sync1To0() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, 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();
|
||||
}
|
||||
|
||||
private void startLifecycles() throws InterruptedException {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager0.startServices();
|
||||
lifecycleManager1.startServices();
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(ForumManagerTestComponent 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 SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
@@ -55,4 +62,18 @@ public interface ForumManagerTestComponent {
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
EventBus getEventBus();
|
||||
|
||||
IdentityManager getIdentityManager();
|
||||
|
||||
ContactManager getContactManager();
|
||||
|
||||
ForumSharingManager getForumSharingManager();
|
||||
|
||||
ForumManager getForumManager();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user