mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Add beginning of a ViewModel test
mostly to demonstrate how those could look like
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class AndroidExecutorTestImpl implements AndroidExecutor {
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
public AndroidExecutorTestImpl(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> runOnBackgroundThread(Callable<V> c) {
|
||||
throw new IllegalStateException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnBackgroundThread(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> Future<V> runOnUiThread(Callable<V> c) {
|
||||
throw new IllegalStateException("not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runOnUiThread(Runnable r) {
|
||||
executor.execute(r);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package org.briarproject.briar.android.privategroup.list;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
import org.briarproject.briar.android.AndroidExecutorTestImpl;
|
||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationItem;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
|
||||
|
||||
import static edu.emory.mathcs.backport.java.util.Collections.emptyList;
|
||||
import static edu.emory.mathcs.backport.java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.briar.android.viewmodel.LiveDataTestUtil.getOrAwaitValue;
|
||||
import static org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.MAJOR_VERSION;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class GroupListViewModelTest extends BrambleMockTestCase {
|
||||
|
||||
@Rule
|
||||
public final InstantTaskExecutorRule testRule =
|
||||
new InstantTaskExecutorRule();
|
||||
|
||||
private final LifecycleManager lifecycleManager =
|
||||
context.mock(LifecycleManager.class);
|
||||
private final TransactionManager db =
|
||||
context.mock(TransactionManager.class);
|
||||
private final PrivateGroupManager groupManager =
|
||||
context.mock(PrivateGroupManager.class);
|
||||
private final GroupInvitationManager groupInvitationManager =
|
||||
context.mock(GroupInvitationManager.class);
|
||||
private final ContactManager contactManager =
|
||||
context.mock(ContactManager.class);
|
||||
private final AndroidNotificationManager notificationManager =
|
||||
context.mock(AndroidNotificationManager.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
|
||||
private final GroupListViewModel viewModel;
|
||||
|
||||
|
||||
private final Group g1 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
private final Group g2 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
private final PrivateGroup privateGroup1 =
|
||||
new PrivateGroup(g1, "foo", getAuthor(), getRandomBytes(2));
|
||||
private final PrivateGroup privateGroup2 =
|
||||
new PrivateGroup(g2, "bar", getAuthor(), getRandomBytes(2));
|
||||
private final AuthorInfo authorInfo1 =
|
||||
new AuthorInfo(AuthorInfo.Status.UNVERIFIED);
|
||||
private final AuthorInfo authorInfo2 =
|
||||
new AuthorInfo(AuthorInfo.Status.VERIFIED);
|
||||
|
||||
private final GroupCount groupCount1 = new GroupCount(2, 1, 23L);
|
||||
private final GroupCount groupCount2 = new GroupCount(5, 3, 42L);
|
||||
private final GroupItem item1 =
|
||||
new GroupItem(privateGroup1, authorInfo1, groupCount1, false);
|
||||
private final GroupItem item2 =
|
||||
new GroupItem(privateGroup2, authorInfo2, groupCount2, false);
|
||||
|
||||
public GroupListViewModelTest() {
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
Application app = context.mock(Application.class);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(eventBus).addListener(with(any(EventListener.class)));
|
||||
}});
|
||||
Executor dbExecutor = new ImmediateExecutor();
|
||||
AndroidExecutor androidExecutor =
|
||||
new AndroidExecutorTestImpl(dbExecutor);
|
||||
viewModel = new GroupListViewModel(app, dbExecutor, lifecycleManager,
|
||||
db, androidExecutor, groupManager, groupInvitationManager,
|
||||
contactManager, notificationManager, eventBus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadGroupsException() throws Exception {
|
||||
DbException dbException = new DbException();
|
||||
|
||||
Transaction txn = new Transaction(null, true);
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(lifecycleManager).waitForDatabase();
|
||||
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
||||
oneOf(groupManager).getPrivateGroups(txn);
|
||||
will(throwException(dbException));
|
||||
}});
|
||||
|
||||
viewModel.loadGroups();
|
||||
|
||||
LiveResult<List<GroupItem>> result =
|
||||
getOrAwaitValue(viewModel.getGroupItems());
|
||||
assertTrue(result.hasError());
|
||||
assertEquals(dbException, result.getException());
|
||||
assertNull(result.getResultOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadGroups() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(lifecycleManager).waitForDatabase();
|
||||
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
||||
oneOf(groupManager).getPrivateGroups(txn);
|
||||
will(returnValue(Arrays.asList(privateGroup1, privateGroup2)));
|
||||
}});
|
||||
expectLoadGroup(txn, privateGroup1, authorInfo1, groupCount1, false);
|
||||
expectLoadGroup(txn, privateGroup2, authorInfo2, groupCount2, false);
|
||||
|
||||
viewModel.loadGroups();
|
||||
|
||||
// unpack updated live data
|
||||
LiveResult<List<GroupItem>> result =
|
||||
getOrAwaitValue(viewModel.getGroupItems());
|
||||
assertFalse(result.hasError());
|
||||
List<GroupItem> liveList = result.getResultOrNull();
|
||||
assertNotNull(liveList);
|
||||
// list is sorted by last message timestamp
|
||||
assertEquals(Arrays.asList(item2, item1), liveList);
|
||||
|
||||
// group 1 gets dissolved by creator
|
||||
Event dissolvedEvent = new GroupDissolvedEvent(privateGroup1.getId());
|
||||
viewModel.eventOccurred(dissolvedEvent);
|
||||
result = getOrAwaitValue(viewModel.getGroupItems());
|
||||
liveList = result.getResultOrNull();
|
||||
assertNotNull(liveList);
|
||||
assertEquals(2, liveList.size());
|
||||
// assert that list update includes dissolved group item
|
||||
for (GroupItem item : liveList) {
|
||||
if (item.getId().equals(privateGroup1.getId())) {
|
||||
assertTrue(item.isDissolved());
|
||||
} else if (item.getId().equals(privateGroup2.getId())) {
|
||||
assertFalse(item.isDissolved());
|
||||
} else fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadNumInvitations() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(lifecycleManager).waitForDatabase();
|
||||
oneOf(groupInvitationManager).getInvitations();
|
||||
will(returnValue(emptyList()));
|
||||
}});
|
||||
viewModel.loadNumInvitations();
|
||||
|
||||
int num = getOrAwaitValue(viewModel.getNumInvitations());
|
||||
assertEquals(0, num);
|
||||
|
||||
PrivateGroup pg = context.mock(PrivateGroup.class);
|
||||
Contact c = getContact();
|
||||
GroupInvitationItem item = new GroupInvitationItem(pg, c);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(lifecycleManager).waitForDatabase();
|
||||
oneOf(groupInvitationManager).getInvitations();
|
||||
will(returnValue(singletonList(item)));
|
||||
}});
|
||||
viewModel.loadNumInvitations();
|
||||
|
||||
num = getOrAwaitValue(viewModel.getNumInvitations());
|
||||
assertEquals(1, num);
|
||||
}
|
||||
|
||||
private void expectLoadGroup(Transaction txn, PrivateGroup privateGroup,
|
||||
AuthorInfo authorInfo, GroupCount groupCount, boolean dissolved)
|
||||
throws DbException {
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(contactManager)
|
||||
.getAuthorInfo(txn, privateGroup.getCreator().getId());
|
||||
will(returnValue(authorInfo));
|
||||
oneOf(groupManager).getGroupCount(txn, privateGroup.getId());
|
||||
will(returnValue(groupCount));
|
||||
oneOf(groupManager).isDissolved(txn, privateGroup.getId());
|
||||
will(returnValue(dissolved));
|
||||
}});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright 2019 Google LLC.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
https://gist.github.com/JoseAlcerreca/1e9ee05dcdd6a6a6fa1cbfc125559bba
|
||||
*/
|
||||
|
||||
package org.briarproject.briar.android.viewmodel;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
public class LiveDataTestUtil {
|
||||
public static <T> T getOrAwaitValue(final LiveData<T> liveData)
|
||||
throws InterruptedException {
|
||||
final Object[] data = new Object[1];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Observer<T> observer = new Observer<T>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable T o) {
|
||||
data[0] = o;
|
||||
latch.countDown();
|
||||
liveData.removeObserver(this);
|
||||
}
|
||||
};
|
||||
liveData.observeForever(observer);
|
||||
// Don't wait indefinitely if the LiveData is not set.
|
||||
if (!latch.await(2, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("LiveData value was never set.");
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) data[0];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user