From c79ce61f6dfe849ef084e1f5fc66e3f92030d8d1 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Mon, 24 Oct 2016 12:34:50 -0200
Subject: [PATCH] Add PrivateGroupManager integration tests

---
 .../org/briarproject/BlogManagerTest.java     |   4 +-
 .../briarproject/PrivateGroupManagerTest.java | 544 ++++++++++++++++++
 .../PrivateGroupManagerTestComponent.java     |  81 +++
 3 files changed, 627 insertions(+), 2 deletions(-)
 create mode 100644 briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
 create mode 100644 briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java

diff --git a/briar-android-tests/src/test/java/org/briarproject/BlogManagerTest.java b/briar-android-tests/src/test/java/org/briarproject/BlogManagerTest.java
index 60a1ba3c3f..0339413b19 100644
--- a/briar-android-tests/src/test/java/org/briarproject/BlogManagerTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/BlogManagerTest.java
@@ -61,7 +61,7 @@ import static org.briarproject.api.sync.ValidationManager.State.PENDING;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-public class BlogManagerTest {
+public class BlogManagerTest extends BriarIntegrationTest {
 
 	private LifecycleManager lifecycleManager0, lifecycleManager1;
 	private SyncSessionFactory sync0, sync1;
@@ -94,7 +94,7 @@ public class BlogManagerTest {
 	private final String AUTHOR2 = "Author 2";
 
 	private static final Logger LOG =
-			Logger.getLogger(ForumSharingIntegrationTest.class.getName());
+			Logger.getLogger(BlogManagerTest.class.getName());
 
 	private BlogManagerTestComponent t0, t1;
 
diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
new file mode 100644
index 0000000000..00609e4eba
--- /dev/null
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
@@ -0,0 +1,544 @@
+package org.briarproject;
+
+import net.jodah.concurrentunit.Waiter;
+
+import org.briarproject.api.clients.MessageTracker.GroupCount;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.KeyPair;
+import org.briarproject.api.crypto.SecretKey;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.Transaction;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.MessageStateChangedEvent;
+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.privategroup.GroupMessage;
+import org.briarproject.api.privategroup.GroupMessageFactory;
+import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.JoinMessageHeader;
+import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.PrivateGroupFactory;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+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.lifecycle.LifecycleModule;
+import org.briarproject.privategroup.PrivateGroupModule;
+import org.briarproject.properties.PropertiesModule;
+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.Collection;
+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.identity.Author.Status.VERIFIED;
+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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PrivateGroupManagerTest extends BriarIntegrationTest {
+
+	private LifecycleManager lifecycleManager0, lifecycleManager1;
+	private SyncSessionFactory sync0, sync1;
+	private PrivateGroupManager groupManager0, groupManager1;
+	private ContactManager contactManager0, contactManager1;
+	private ContactId contactId0, contactId1;
+	private IdentityManager identityManager0, identityManager1;
+	private LocalAuthor author0, author1;
+	private PrivateGroup privateGroup0;
+	private GroupId groupId0;
+
+	@Inject
+	Clock clock;
+	@Inject
+	AuthorFactory authorFactory;
+	@Inject
+	CryptoComponent crypto;
+	@Inject
+	PrivateGroupFactory privateGroupFactory;
+	@Inject
+	GroupMessageFactory groupMessageFactory;
+
+	// objects accessed from background threads need to be volatile
+	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 AUTHOR1 = "Author 1";
+	private final String AUTHOR2 = "Author 2";
+
+	private static final Logger LOG =
+			Logger.getLogger(PrivateGroupManagerTest.class.getName());
+
+	private PrivateGroupManagerTestComponent t0, t1;
+
+	@Rule
+	public ExpectedException thrown = ExpectedException.none();
+
+	@Before
+	public void setUp() throws Exception {
+		PrivateGroupManagerTestComponent component =
+				DaggerPrivateGroupManagerTestComponent.builder().build();
+		component.inject(this);
+		injectEagerSingletons(component);
+
+		assertTrue(testDir.mkdirs());
+		File t0Dir = new File(testDir, AUTHOR1);
+		t0 = DaggerPrivateGroupManagerTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
+		injectEagerSingletons(t0);
+		File t1Dir = new File(testDir, AUTHOR2);
+		t1 = DaggerPrivateGroupManagerTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
+		injectEagerSingletons(t1);
+
+		identityManager0 = t0.getIdentityManager();
+		identityManager1 = t1.getIdentityManager();
+		contactManager0 = t0.getContactManager();
+		contactManager1 = t1.getContactManager();
+		groupManager0 = t0.getPrivateGroupManager();
+		groupManager1 = t1.getPrivateGroupManager();
+		sync0 = t0.getSyncSessionFactory();
+		sync1 = t1.getSyncSessionFactory();
+
+		// initialize waiters fresh for each test
+		validationWaiter = new Waiter();
+		deliveryWaiter = new Waiter();
+
+		startLifecycles();
+	}
+
+	@Test
+	public void testSendingMessage() throws Exception {
+		defaultInit();
+
+		// create and add test message
+		long time = clock.currentTimeMillis();
+		String body = "This is a test message!";
+		MessageId previousMsgId =
+				groupManager0.getPreviousMsgId(groupId0);
+		GroupMessage msg = groupMessageFactory
+				.createGroupMessage(groupId0, time, null, author0, body,
+						previousMsgId);
+		groupManager0.addLocalMessage(msg);
+		assertEquals(msg.getMessage().getId(),
+				groupManager0.getPreviousMsgId(groupId0));
+
+		// sync test message
+		sync0To1();
+		deliveryWaiter.await(TIMEOUT, 1);
+
+		// assert that message arrived as expected
+		Collection<GroupMessageHeader> headers =
+				groupManager1.getHeaders(groupId0);
+		assertEquals(3, headers.size());
+		GroupMessageHeader header = null;
+		for (GroupMessageHeader h : headers) {
+			if (!(h instanceof JoinMessageHeader)) {
+				header = h;
+			}
+		}
+		assertTrue(header != null);
+		assertFalse(header.isRead());
+		assertEquals(author0, header.getAuthor());
+		assertEquals(time, header.getTimestamp());
+		assertEquals(VERIFIED, header.getAuthorStatus());
+		assertEquals(body, groupManager1.getMessageBody(header.getId()));
+		GroupCount count = groupManager1.getGroupCount(groupId0);
+		assertEquals(2, count.getUnreadCount());
+		assertEquals(time, count.getLatestMsgTime());
+		assertEquals(3, count.getMsgCount());
+	}
+
+	@Test
+	public void testMessageWithWrongPreviousMsgId() throws Exception {
+		defaultInit();
+
+		// create and add test message with no previousMsgId
+		GroupMessage msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
+						author0, "test", null);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with random previousMsgId
+		MessageId previousMsgId = new MessageId(TestUtils.getRandomId());
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
+						author0, "test", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with wrong previousMsgId
+		previousMsgId = groupManager1.getPreviousMsgId(groupId0);
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
+						author0, "test", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+	}
+
+	@Test
+	public void testMessageWithWrongParentMsgId() throws Exception {
+		defaultInit();
+
+		// create and add test message with random parentMsgId
+		MessageId parentMsgId = new MessageId(TestUtils.getRandomId());
+		MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
+		GroupMessage msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(),
+						parentMsgId, author0, "test", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with wrong parentMsgId
+		parentMsgId = previousMsgId;
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, clock.currentTimeMillis(),
+						parentMsgId, author0, "test", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+	}
+
+	@Test
+	public void testMessageWithWrongTimestamp() throws Exception {
+		defaultInit();
+
+		// create and add test message with wrong timestamp
+		MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
+		GroupMessage msg = groupMessageFactory
+				.createGroupMessage(groupId0, 42, null, author0, "test",
+						previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(2, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with good timestamp
+		long time = clock.currentTimeMillis();
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, time, null, author0, "test",
+						previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		deliveryWaiter.await(TIMEOUT, 1);
+		assertEquals(3, groupManager1.getHeaders(groupId0).size());
+
+		// create and add test message with same timestamp as previous message
+		previousMsgId = msg.getMessage().getId();
+		msg = groupMessageFactory
+				.createGroupMessage(groupId0, time, previousMsgId, author0,
+						"test2", previousMsgId);
+		groupManager0.addLocalMessage(msg);
+
+		// sync test message
+		sync0To1();
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that message did not arrive
+		assertEquals(3, groupManager1.getHeaders(groupId0).size());
+	}
+
+	@Test
+	public void testWrongJoinMessages() throws Exception {
+		addDefaultIdentities();
+		addDefaultContacts();
+		listenToEvents();
+
+		// author0 joins privateGroup0 with later timestamp
+		long joinTime = clock.currentTimeMillis();
+		GroupMessage newMemberMsg = groupMessageFactory
+				.createNewMemberMessage(groupId0, joinTime, author0, author0);
+		GroupMessage joinMsg = groupMessageFactory
+				.createJoinMessage(groupId0, joinTime + 1, author0,
+						newMemberMsg.getMessage().getId());
+		groupManager0.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+		assertEquals(joinMsg.getMessage().getId(),
+				groupManager0.getPreviousMsgId(groupId0));
+
+		// make group visible to 1
+		Transaction txn0 = t0.getDatabaseComponent().startTransaction(false);
+		t0.getDatabaseComponent()
+				.setVisibleToContact(txn0, contactId1, privateGroup0.getId(),
+						true);
+		txn0.setComplete();
+		t0.getDatabaseComponent().endTransaction(txn0);
+
+		// author1 joins privateGroup0 and refers to wrong NEW_MEMBER message
+		joinMsg = groupMessageFactory
+				.createJoinMessage(groupId0, joinTime, author1,
+						newMemberMsg.getMessage().getId());
+		joinTime = clock.currentTimeMillis();
+		newMemberMsg = groupMessageFactory
+				.createNewMemberMessage(groupId0, joinTime, author0, author1);
+		groupManager1.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+		assertEquals(joinMsg.getMessage().getId(),
+				groupManager1.getPreviousMsgId(groupId0));
+
+		// make group visible to 0
+		Transaction txn1 = t1.getDatabaseComponent().startTransaction(false);
+		t1.getDatabaseComponent()
+				.setVisibleToContact(txn1, contactId0, privateGroup0.getId(),
+						true);
+		txn1.setComplete();
+		t1.getDatabaseComponent().endTransaction(txn1);
+
+		// sync join messages
+		sync0To1();
+		deliveryWaiter.await(TIMEOUT, 1);
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that 0 never joined the group from 1's perspective
+		assertEquals(1, groupManager1.getHeaders(groupId0).size());
+
+		sync1To0();
+		deliveryWaiter.await(TIMEOUT, 1);
+		validationWaiter.await(TIMEOUT, 1);
+
+		// assert that 1 never joined the group from 0's perspective
+		assertEquals(1, groupManager0.getHeaders(groupId0).size());
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		stopLifecycles();
+		TestUtils.deleteTestDirectory(testDir);
+	}
+
+	private class Listener implements EventListener {
+		@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");
+						deliveryWaiter.resume();
+					} else if (event.getState() == INVALID ||
+							event.getState() == PENDING) {
+						LOG.info("Validated new " + event.getState().name() +
+								" message");
+						validationWaiter.resume();
+					}
+				}
+			}
+		}
+	}
+
+	private void defaultInit() throws Exception {
+		addDefaultIdentities();
+		addDefaultContacts();
+		listenToEvents();
+		addGroup();
+	}
+
+	private void addDefaultIdentities() throws DbException {
+		KeyPair keyPair0 = crypto.generateSignatureKeyPair();
+		byte[] publicKey0 = keyPair0.getPublic().getEncoded();
+		byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
+		author0 = authorFactory
+				.createLocalAuthor(AUTHOR1, publicKey0, privateKey0);
+		identityManager0.addLocalAuthor(author0);
+		privateGroup0 =
+				privateGroupFactory.createPrivateGroup("Testgroup", author0);
+		groupId0 = privateGroup0.getId();
+
+		KeyPair keyPair1 = crypto.generateSignatureKeyPair();
+		byte[] publicKey1 = keyPair1.getPublic().getEncoded();
+		byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
+		author1 = authorFactory
+				.createLocalAuthor(AUTHOR2, publicKey1, privateKey1);
+		identityManager1.addLocalAuthor(author1);
+	}
+
+	private void addDefaultContacts() throws DbException {
+		// sharer adds invitee as contact
+		contactId1 = contactManager0.addContact(author1,
+				author0.getId(), master, clock.currentTimeMillis(), true,
+				true, true
+		);
+		// invitee adds sharer back
+		contactId0 = contactManager1.addContact(author0,
+				author1.getId(), master, clock.currentTimeMillis(), true,
+				true, true
+		);
+	}
+
+	private void listenToEvents() {
+		Listener listener0 = new Listener();
+		t0.getEventBus().addListener(listener0);
+		Listener listener1 = new Listener();
+		t1.getEventBus().addListener(listener1);
+	}
+
+	private void addGroup() throws Exception {
+		// author0 joins privateGroup0
+		long joinTime = clock.currentTimeMillis();
+		GroupMessage newMemberMsg = groupMessageFactory
+				.createNewMemberMessage(privateGroup0.getId(), joinTime,
+						author0, author0);
+		GroupMessage joinMsg = groupMessageFactory
+				.createJoinMessage(privateGroup0.getId(), joinTime, author0,
+						newMemberMsg.getMessage().getId());
+		groupManager0.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+		assertEquals(joinMsg.getMessage().getId(),
+				groupManager0.getPreviousMsgId(groupId0));
+
+		// make group visible to 1
+		Transaction txn0 = t0.getDatabaseComponent().startTransaction(false);
+		t0.getDatabaseComponent()
+				.setVisibleToContact(txn0, contactId1, privateGroup0.getId(),
+						true);
+		txn0.setComplete();
+		t0.getDatabaseComponent().endTransaction(txn0);
+
+		// author1 joins privateGroup0
+		joinTime = clock.currentTimeMillis();
+		newMemberMsg = groupMessageFactory
+				.createNewMemberMessage(privateGroup0.getId(), joinTime,
+						author0, author1);
+		joinMsg = groupMessageFactory
+				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
+						newMemberMsg.getMessage().getId());
+		groupManager1.addPrivateGroup(privateGroup0, newMemberMsg, joinMsg);
+		assertEquals(joinMsg.getMessage().getId(),
+				groupManager1.getPreviousMsgId(groupId0));
+
+		// make group visible to 0
+		Transaction txn1 = t1.getDatabaseComponent().startTransaction(false);
+		t1.getDatabaseComponent()
+				.setVisibleToContact(txn1, contactId0, privateGroup0.getId(),
+						true);
+		txn1.setComplete();
+		t1.getDatabaseComponent().endTransaction(txn1);
+
+		// sync join messages
+		sync0To1();
+		deliveryWaiter.await(TIMEOUT, 2);
+		sync1To0();
+		deliveryWaiter.await(TIMEOUT, 2);
+	}
+
+	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(
+			PrivateGroupManagerTestComponent component) {
+		component.inject(new LifecycleModule.EagerSingletons());
+		component.inject(new PrivateGroupModule.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());
+	}
+
+}
diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java
new file mode 100644
index 0000000000..7461eb02bc
--- /dev/null
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTestComponent.java
@@ -0,0 +1,81 @@
+package org.briarproject;
+
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.clients.ClientsModule;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.data.DataModule;
+import org.briarproject.db.DatabaseModule;
+import org.briarproject.event.EventModule;
+import org.briarproject.identity.IdentityModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.privategroup.PrivateGroupModule;
+import org.briarproject.properties.PropertiesModule;
+import org.briarproject.sharing.SharingModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.system.SystemModule;
+import org.briarproject.transport.TransportModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestDatabaseModule.class,
+		TestPluginsModule.class,
+		TestSeedProviderModule.class,
+		ClientsModule.class,
+		ContactModule.class,
+		CryptoModule.class,
+		DataModule.class,
+		DatabaseModule.class,
+		EventModule.class,
+		PrivateGroupModule.class,
+		IdentityModule.class,
+		LifecycleModule.class,
+		PropertiesModule.class,
+		SharingModule.class,
+		SyncModule.class,
+		SystemModule.class,
+		TransportModule.class
+})
+interface PrivateGroupManagerTestComponent {
+
+	void inject(PrivateGroupManagerTest testCase);
+
+	void inject(ContactModule.EagerSingletons init);
+
+	void inject(CryptoModule.EagerSingletons init);
+
+	void inject(PrivateGroupModule.EagerSingletons init);
+
+	void inject(LifecycleModule.EagerSingletons init);
+
+	void inject(PropertiesModule.EagerSingletons init);
+
+	void inject(SyncModule.EagerSingletons init);
+
+	void inject(TransportModule.EagerSingletons init);
+
+	LifecycleManager getLifecycleManager();
+
+	EventBus getEventBus();
+
+	IdentityManager getIdentityManager();
+
+	ContactManager getContactManager();
+
+	PrivateGroupManager getPrivateGroupManager();
+
+	SyncSessionFactory getSyncSessionFactory();
+
+	DatabaseComponent getDatabaseComponent();
+
+}
-- 
GitLab