mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-03-01 06:07:37 +00:00
Move all files to natural position.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyFloat;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyLong;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Log.class, Handler.class, Looper.class, TextUtils.class, PreferenceManager.class })
|
||||
public abstract class BaseUnitTest {
|
||||
|
||||
protected Context context = mock(Context.class);
|
||||
protected SharedPreferences sharedPreferences = mock(SharedPreferences.class);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mockStatic(Looper.class);
|
||||
mockStatic(Log.class);
|
||||
mockStatic(Handler.class);
|
||||
mockStatic(TextUtils.class);
|
||||
mockStatic(PreferenceManager.class);
|
||||
|
||||
when(PreferenceManager.getDefaultSharedPreferences(any(Context.class))).thenReturn(sharedPreferences);
|
||||
when(Looper.getMainLooper()).thenReturn(null);
|
||||
PowerMockito.whenNew(Handler.class).withAnyArguments().thenReturn(null);
|
||||
|
||||
Answer<?> logAnswer = new Answer<Void>() {
|
||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
final String tag = (String)invocation.getArguments()[0];
|
||||
final String msg = (String)invocation.getArguments()[1];
|
||||
System.out.println(invocation.getMethod().getName().toUpperCase() + "/[" + tag + "] " + msg);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
PowerMockito.doAnswer(logAnswer).when(Log.class, "d", anyString(), anyString());
|
||||
PowerMockito.doAnswer(logAnswer).when(Log.class, "i", anyString(), anyString());
|
||||
PowerMockito.doAnswer(logAnswer).when(Log.class, "w", anyString(), anyString());
|
||||
PowerMockito.doAnswer(logAnswer).when(Log.class, "e", anyString(), anyString());
|
||||
PowerMockito.doAnswer(logAnswer).when(Log.class, "wtf", anyString(), anyString());
|
||||
|
||||
PowerMockito.doAnswer(new Answer<Boolean>() {
|
||||
@Override
|
||||
public Boolean answer(InvocationOnMock invocation) throws Throwable {
|
||||
final String s = (String)invocation.getArguments()[0];
|
||||
return s == null || s.length() == 0;
|
||||
}
|
||||
}).when(TextUtils.class, "isEmpty", anyString());
|
||||
|
||||
when(sharedPreferences.getString(anyString(), anyString())).thenReturn("");
|
||||
when(sharedPreferences.getLong(anyString(), anyLong())).thenReturn(0L);
|
||||
when(sharedPreferences.getInt(anyString(), anyInt())).thenReturn(0);
|
||||
when(sharedPreferences.getBoolean(anyString(), anyBoolean())).thenReturn(false);
|
||||
when(sharedPreferences.getFloat(anyString(), anyFloat())).thenReturn(0f);
|
||||
when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences);
|
||||
when(context.getPackageName()).thenReturn("org.thoughtcrime.securesms");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.thoughtcrime.securesms.camera;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.mediasend.OrderEnforcer;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
public class OrderEnforcerTest {
|
||||
|
||||
@Test
|
||||
public void markCompleted_singleEntry() {
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
|
||||
enforcer.run(Stage.A, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.markCompleted(Stage.A);
|
||||
assertEquals(1, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void markCompleted_singleEntry_waterfall() {
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
|
||||
enforcer.run(Stage.C, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.markCompleted(Stage.A);
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.markCompleted(Stage.C);
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.markCompleted(Stage.B);
|
||||
assertEquals(1, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void markCompleted_multipleEntriesPerStage_waterfall() {
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
|
||||
|
||||
enforcer.run(Stage.A, new CountRunnable(counter));
|
||||
enforcer.run(Stage.A, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.run(Stage.B, new CountRunnable(counter));
|
||||
enforcer.run(Stage.B, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.run(Stage.C, new CountRunnable(counter));
|
||||
enforcer.run(Stage.C, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.run(Stage.D, new CountRunnable(counter));
|
||||
enforcer.run(Stage.D, new CountRunnable(counter));
|
||||
assertEquals(0, counter.get());
|
||||
|
||||
enforcer.markCompleted(Stage.A);
|
||||
assertEquals(counter.get(), 2);
|
||||
|
||||
enforcer.markCompleted(Stage.D);
|
||||
assertEquals(counter.get(), 2);
|
||||
|
||||
enforcer.markCompleted(Stage.B);
|
||||
assertEquals(counter.get(), 4);
|
||||
|
||||
enforcer.markCompleted(Stage.C);
|
||||
assertEquals(counter.get(), 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void run_alreadyCompleted() {
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
OrderEnforcer<Stage> enforcer = new OrderEnforcer<>(Stage.A, Stage.B, Stage.C, Stage.D);
|
||||
enforcer.markCompleted(Stage.A);
|
||||
enforcer.markCompleted(Stage.B);
|
||||
|
||||
enforcer.run(Stage.B, new CountRunnable(counter));
|
||||
assertEquals(1, counter.get());
|
||||
}
|
||||
|
||||
private static class CountRunnable implements Runnable {
|
||||
private final AtomicInteger counter;
|
||||
|
||||
public CountRunnable(AtomicInteger counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
counter.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private enum Stage {
|
||||
A, B, C, D
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package org.thoughtcrime.securesms.contacts.sync;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.contacts.sync.StorageSyncHelper.KeyDifferenceResult;
|
||||
import org.thoughtcrime.securesms.contacts.sync.StorageSyncHelper.MergeResult;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
|
||||
import org.whispersystems.signalservice.api.storage.SignalStorageRecord;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Arrays;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class StorageSyncHelperTest {
|
||||
|
||||
private static final UUID UUID_A = UuidUtil.parseOrThrow("ebef429e-695e-4f51-bcc4-526a60ac68c7");
|
||||
private static final UUID UUID_B = UuidUtil.parseOrThrow("32119989-77fb-4e18-af70-81d55185c6b1");
|
||||
private static final UUID UUID_C = UuidUtil.parseOrThrow("b5552203-2bca-44aa-b6f5-9f5d87a335b6");
|
||||
private static final UUID UUID_D = UuidUtil.parseOrThrow("94829a32-7199-4a7b-8fb4-7e978509ab84");
|
||||
|
||||
private static final String E164_A = "+16108675309";
|
||||
private static final String E164_B = "+16101234567";
|
||||
private static final String E164_C = "+16101112222";
|
||||
private static final String E164_D = "+16103334444";
|
||||
|
||||
private static final int UNKNOWN_TYPE = Integer.MAX_VALUE;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
StorageSyncHelper.setTestKeyGenerator(null);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void findKeyDifference_allOverlap() {
|
||||
KeyDifferenceResult result = StorageSyncHelper.findKeyDifference(byteListOf(1, 2, 3), byteListOf(1, 2, 3));
|
||||
assertTrue(result.getLocalOnlyKeys().isEmpty());
|
||||
assertTrue(result.getRemoteOnlyKeys().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findKeyDifference_noOverlap() {
|
||||
KeyDifferenceResult result = StorageSyncHelper.findKeyDifference(byteListOf(1, 2, 3), byteListOf(4, 5, 6));
|
||||
assertByteListEquals(byteListOf(1, 2, 3), result.getRemoteOnlyKeys());
|
||||
assertByteListEquals(byteListOf(4, 5, 6), result.getLocalOnlyKeys());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findKeyDifference_someOverlap() {
|
||||
KeyDifferenceResult result = StorageSyncHelper.findKeyDifference(byteListOf(1, 2, 3), byteListOf(2, 3, 4));
|
||||
assertByteListEquals(byteListOf(1), result.getRemoteOnlyKeys());
|
||||
assertByteListEquals(byteListOf(4), result.getLocalOnlyKeys());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveConflict_noOverlap() {
|
||||
SignalContactRecord remote1 = contact(1, UUID_A, E164_A, "a");
|
||||
SignalContactRecord local1 = contact(2, UUID_B, E164_B, "b");
|
||||
|
||||
MergeResult result = StorageSyncHelper.resolveConflict(recordSetOf(remote1), recordSetOf(local1));
|
||||
|
||||
assertEquals(setOf(remote1), result.getLocalContactInserts());
|
||||
assertTrue(result.getLocalContactUpdates().isEmpty());
|
||||
assertEquals(setOf(local1), result.getRemoteContactInserts());
|
||||
assertTrue(result.getRemoteContactUpdates().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveConflict_sameAsRemote() {
|
||||
SignalContactRecord remote1 = contact(1, UUID_A, E164_A, "a");
|
||||
SignalContactRecord local1 = contact(2, UUID_A, E164_A, "a");
|
||||
|
||||
MergeResult result = StorageSyncHelper.resolveConflict(recordSetOf(remote1), recordSetOf(local1));
|
||||
|
||||
SignalContactRecord expectedMerge = contact(1, UUID_A, E164_A, "a");
|
||||
|
||||
assertTrue(result.getLocalContactInserts().isEmpty());
|
||||
assertEquals(setOf(contactUpdate(local1, expectedMerge)), result.getLocalContactUpdates());
|
||||
assertTrue(result.getRemoteContactInserts().isEmpty());
|
||||
assertTrue(result.getRemoteContactUpdates().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveConflict_sameAsLocal() {
|
||||
SignalContactRecord remote1 = contact(1, UUID_A, E164_A, null);
|
||||
SignalContactRecord local1 = contact(2, UUID_A, E164_A, "a");
|
||||
|
||||
MergeResult result = StorageSyncHelper.resolveConflict(recordSetOf(remote1), recordSetOf(local1));
|
||||
|
||||
SignalContactRecord expectedMerge = contact(2, UUID_A, E164_A, "a");
|
||||
|
||||
assertTrue(result.getLocalContactInserts().isEmpty());
|
||||
assertTrue(result.getLocalContactUpdates().isEmpty());
|
||||
assertTrue(result.getRemoteContactInserts().isEmpty());
|
||||
assertEquals(setOf(contactUpdate(remote1, expectedMerge)), result.getRemoteContactUpdates());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveConflict_unknowns() {
|
||||
SignalStorageRecord remote1 = unknown(3);
|
||||
SignalStorageRecord remote2 = unknown(4);
|
||||
SignalStorageRecord local1 = unknown(1);
|
||||
SignalStorageRecord local2 = unknown(2);
|
||||
|
||||
MergeResult result = StorageSyncHelper.resolveConflict(setOf(remote1, remote2), setOf(local1, local2));
|
||||
|
||||
assertTrue(result.getLocalContactInserts().isEmpty());
|
||||
assertTrue(result.getLocalContactUpdates().isEmpty());
|
||||
assertEquals(setOf(remote1, remote2), result.getLocalUnknownInserts());
|
||||
assertEquals(setOf(local1, local2), result.getLocalUnknownDeletes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveConflict_complex() {
|
||||
SignalContactRecord remote1 = contact(1, UUID_A, null, "a");
|
||||
SignalContactRecord local1 = contact(2, UUID_A, E164_A, "a");
|
||||
|
||||
SignalContactRecord remote2 = contact(3, UUID_B, E164_B, null);
|
||||
SignalContactRecord local2 = contact(4, UUID_B, null, "b");
|
||||
|
||||
SignalContactRecord remote3 = contact(5, UUID_C, E164_C, "c");
|
||||
SignalContactRecord local3 = contact(6, UUID_D, E164_D, "d");
|
||||
|
||||
SignalStorageRecord unknownRemote = unknown(7);
|
||||
SignalStorageRecord unknownLocal = unknown(8);
|
||||
|
||||
StorageSyncHelper.setTestKeyGenerator(new TestGenerator(999));
|
||||
|
||||
Set<SignalStorageRecord> remoteOnly = recordSetOf(remote1, remote2, remote3);
|
||||
Set<SignalStorageRecord> localOnly = recordSetOf(local1, local2, local3);
|
||||
|
||||
remoteOnly.add(unknownRemote);
|
||||
localOnly.add(unknownLocal);
|
||||
|
||||
MergeResult result = StorageSyncHelper.resolveConflict(remoteOnly, localOnly);
|
||||
|
||||
SignalContactRecord merge1 = contact(2, UUID_A, E164_A, "a");
|
||||
SignalContactRecord merge2 = contact(999, UUID_B, E164_B, "b");
|
||||
|
||||
assertEquals(setOf(remote3), result.getLocalContactInserts());
|
||||
assertEquals(setOf(contactUpdate(local2, merge2)), result.getLocalContactUpdates());
|
||||
assertEquals(setOf(local3), result.getRemoteContactInserts());
|
||||
assertEquals(setOf(contactUpdate(remote1, merge1), contactUpdate(remote2, merge2)), result.getRemoteContactUpdates());
|
||||
assertEquals(setOf(unknownRemote), result.getLocalUnknownInserts());
|
||||
assertEquals(setOf(unknownLocal), result.getLocalUnknownDeletes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeContacts_alwaysPreferRemoteExceptNickname() {
|
||||
SignalContactRecord remote = new SignalContactRecord.Builder(byteArray(1), new SignalServiceAddress(UUID_A, E164_A))
|
||||
.setBlocked(true)
|
||||
.setIdentityKey(byteArray(2))
|
||||
.setIdentityState(SignalContactRecord.IdentityState.VERIFIED)
|
||||
.setProfileKey(byteArray(3))
|
||||
.setProfileName("profile name A")
|
||||
.setUsername("username A")
|
||||
.setNickname("nickname A")
|
||||
.setProfileSharingEnabled(true)
|
||||
.build();
|
||||
SignalContactRecord local = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B))
|
||||
.setBlocked(false)
|
||||
.setIdentityKey(byteArray(99))
|
||||
.setIdentityState(SignalContactRecord.IdentityState.DEFAULT)
|
||||
.setProfileKey(byteArray(999))
|
||||
.setProfileName("profile name B")
|
||||
.setUsername("username B")
|
||||
.setNickname("nickname B")
|
||||
.setProfileSharingEnabled(false)
|
||||
.build();
|
||||
SignalContactRecord merged = StorageSyncHelper.mergeContacts(remote, local);
|
||||
|
||||
assertEquals(UUID_A, merged.getAddress().getUuid().get());
|
||||
assertEquals(E164_A, merged.getAddress().getNumber().get());
|
||||
assertTrue(merged.isBlocked());
|
||||
assertArrayEquals(byteArray(2), merged.getIdentityKey().get());
|
||||
assertEquals(SignalContactRecord.IdentityState.VERIFIED, merged.getIdentityState());
|
||||
assertArrayEquals(byteArray(3), merged.getProfileKey().get());
|
||||
assertEquals("profile name A", merged.getProfileName().get());
|
||||
assertEquals("username A", merged.getUsername().get());
|
||||
assertEquals("nickname B", merged.getNickname().get());
|
||||
assertTrue(merged.isProfileSharingEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeContacts_fillInGaps() {
|
||||
SignalContactRecord remote = new SignalContactRecord.Builder(byteArray(1), new SignalServiceAddress(UUID_A, null))
|
||||
.setBlocked(true)
|
||||
.setProfileName("profile name A")
|
||||
.setProfileSharingEnabled(true)
|
||||
.build();
|
||||
SignalContactRecord local = new SignalContactRecord.Builder(byteArray(2), new SignalServiceAddress(UUID_B, E164_B))
|
||||
.setBlocked(false)
|
||||
.setIdentityKey(byteArray(2))
|
||||
.setProfileKey(byteArray(3))
|
||||
.setProfileName("profile name B")
|
||||
.setUsername("username B")
|
||||
.setProfileSharingEnabled(false)
|
||||
.build();
|
||||
SignalContactRecord merged = StorageSyncHelper.mergeContacts(remote, local);
|
||||
|
||||
assertEquals(UUID_A, merged.getAddress().getUuid().get());
|
||||
assertEquals(E164_B, merged.getAddress().getNumber().get());
|
||||
assertTrue(merged.isBlocked());
|
||||
assertArrayEquals(byteArray(2), merged.getIdentityKey().get());
|
||||
assertEquals(SignalContactRecord.IdentityState.DEFAULT, merged.getIdentityState());
|
||||
assertArrayEquals(byteArray(3), merged.getProfileKey().get());
|
||||
assertEquals("profile name A", merged.getProfileName().get());
|
||||
assertEquals("username B", merged.getUsername().get());
|
||||
assertTrue(merged.isProfileSharingEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWriteOperation_generic() {
|
||||
List<byte[]> localKeys = byteListOf(1, 2, 3, 4);
|
||||
SignalContactRecord insert1 = contact(6, UUID_A, E164_A, "a" );
|
||||
SignalContactRecord old1 = contact(1, UUID_B, E164_B, "b" );
|
||||
SignalContactRecord new1 = contact(5, UUID_B, E164_B, "z" );
|
||||
SignalContactRecord insert2 = contact(7, UUID_C, E164_C, "c" );
|
||||
SignalContactRecord old2 = contact(2, UUID_D, E164_D, "d" );
|
||||
SignalContactRecord new2 = contact(8, UUID_D, E164_D, "z2");
|
||||
SignalStorageRecord unknownInsert = unknown(9);
|
||||
SignalStorageRecord unknownDelete = unknown(10);
|
||||
|
||||
StorageSyncHelper.WriteOperationResult result = StorageSyncHelper.createWriteOperation(1,
|
||||
localKeys,
|
||||
new MergeResult(setOf(insert2),
|
||||
setOf(contactUpdate(old2, new2)),
|
||||
setOf(insert1),
|
||||
setOf(contactUpdate(old1, new1)),
|
||||
setOf(unknownInsert),
|
||||
setOf(unknownDelete)));
|
||||
|
||||
assertEquals(2, result.getManifest().getVersion());
|
||||
assertByteListEquals(byteListOf(3, 4, 5, 6, 7, 8, 9), result.getManifest().getStorageKeys());
|
||||
assertTrue(recordSetOf(insert1, new1).containsAll(result.getInserts()));
|
||||
assertEquals(2, result.getInserts().size());
|
||||
assertByteListEquals(byteListOf(1), result.getDeletes());
|
||||
}
|
||||
|
||||
private static <E> Set<E> setOf(E... vals) {
|
||||
return new LinkedHashSet<E>(Arrays.asList(vals));
|
||||
}
|
||||
|
||||
private static Set<SignalStorageRecord> recordSetOf(SignalContactRecord... contactRecords) {
|
||||
LinkedHashSet<SignalStorageRecord> storageRecords = new LinkedHashSet<>();
|
||||
|
||||
for (SignalContactRecord contactRecord : contactRecords) {
|
||||
storageRecords.add(SignalStorageRecord.forContact(contactRecord.getKey(), contactRecord));
|
||||
}
|
||||
|
||||
return storageRecords;
|
||||
}
|
||||
|
||||
private static SignalContactRecord contact(int key,
|
||||
UUID uuid,
|
||||
String e164,
|
||||
String profileName)
|
||||
{
|
||||
return new SignalContactRecord.Builder(byteArray(key), new SignalServiceAddress(uuid, e164))
|
||||
.setProfileName(profileName)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static StorageSyncHelper.ContactUpdate contactUpdate(SignalContactRecord oldContact, SignalContactRecord newContact) {
|
||||
return new StorageSyncHelper.ContactUpdate(oldContact, newContact);
|
||||
}
|
||||
|
||||
private static SignalStorageRecord unknown(int key) {
|
||||
return SignalStorageRecord.forUnknown(byteArray(key), UNKNOWN_TYPE);
|
||||
}
|
||||
|
||||
private static List<byte[]> byteListOf(int... vals) {
|
||||
List<byte[]> list = new ArrayList<>(vals.length);
|
||||
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
list.add(Conversions.intToByteArray(vals[i]));
|
||||
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static byte[] byteArray(int a) {
|
||||
return Conversions.intToByteArray(a);
|
||||
}
|
||||
|
||||
private static void assertByteListEquals(List<byte[]> a, List<byte[]> b) {
|
||||
assertEquals(a.size(), b.size());
|
||||
|
||||
List<ByteBuffer> aBuffer = Stream.of(a).map(ByteBuffer::wrap).toList();
|
||||
List<ByteBuffer> bBuffer = Stream.of(b).map(ByteBuffer::wrap).toList();
|
||||
|
||||
assertTrue(aBuffer.containsAll(bBuffer));
|
||||
}
|
||||
|
||||
private static class TestGenerator implements StorageSyncHelper.KeyGenerator {
|
||||
private final byte[] key;
|
||||
|
||||
private TestGenerator(int key) {
|
||||
this.key = byteArray(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull byte[] generate() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.thoughtcrime.securesms.contactshare;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public final class ContactUtilTest_getPrettyPhoneNumber {
|
||||
|
||||
private final Locale locale;
|
||||
private final String input;
|
||||
private final String expected;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][]{
|
||||
|
||||
/* Already international */
|
||||
{ Locale.US, "+15551234567", "+1 555-123-4567" },
|
||||
{ Locale.US, "+44 7700900000", "+44 7700 900000" },
|
||||
|
||||
/* US */
|
||||
{ Locale.US, "555-123-4567", "+1 555-123-4567" },
|
||||
|
||||
/* GB */
|
||||
{ new Locale("en" ,"GB"), "07700900000", "+44 7700 900000" },
|
||||
|
||||
/* Hungary */
|
||||
{ new Locale("hu" ,"HU"), "0655153211", "+36 55 153 211" },
|
||||
|
||||
/* Canaries is a region that does not have an ISO3 country code */
|
||||
{ new Locale("es", "IC"), "+345551224116", "+34 5551224116" },
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public ContactUtilTest_getPrettyPhoneNumber(Locale locale, String input, String expected) {
|
||||
this.locale = locale;
|
||||
this.input = input;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prettyPhoneNumber() {
|
||||
String phoneNumber = ContactUtil.getPrettyPhoneNumber(new Contact.Phone(input, Contact.Phone.Type.MOBILE, null), locale);
|
||||
|
||||
assertEquals(expected, phoneNumber);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.thoughtcrime.securesms.conversation;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.BaseUnitTest;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationAdapter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
public class ConversationAdapterTest extends BaseUnitTest {
|
||||
private Cursor cursor = mock(Cursor.class);
|
||||
private ConversationAdapter adapter;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
adapter = new ConversationAdapter(context, cursor);
|
||||
when(cursor.getColumnIndexOrThrow(anyString())).thenReturn(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO: Fix test")
|
||||
public void testGetItemIdEquals() throws Exception {
|
||||
when(cursor.getString(anyInt())).thenReturn(null).thenReturn("SMS::1::1");
|
||||
long firstId = adapter.getItemId(cursor);
|
||||
when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::1::1");
|
||||
long secondId = adapter.getItemId(cursor);
|
||||
assertNotEquals(firstId, secondId);
|
||||
when(cursor.getString(anyInt())).thenReturn(null).thenReturn("MMS::2::1");
|
||||
long thirdId = adapter.getItemId(cursor);
|
||||
assertNotEquals(secondId, thirdId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class CursorRecyclerViewAdapterTest {
|
||||
private CursorRecyclerViewAdapter adapter;
|
||||
private Context context;
|
||||
private Cursor cursor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = mock(Context.class);
|
||||
cursor = mock(Cursor.class);
|
||||
when(cursor.getCount()).thenReturn(100);
|
||||
when(cursor.moveToPosition(anyInt())).thenReturn(true);
|
||||
|
||||
adapter = new CursorRecyclerViewAdapter(context, cursor) {
|
||||
@Override
|
||||
public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanityCount() throws Exception {
|
||||
assertEquals(adapter.getItemCount(), 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderCount() throws Exception {
|
||||
adapter.setHeaderView(new View(context));
|
||||
assertEquals(adapter.getItemCount(), 101);
|
||||
|
||||
assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.HEADER_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFooterCount() throws Exception {
|
||||
adapter.setFooterView(new View(context));
|
||||
assertEquals(adapter.getItemCount(), 101);
|
||||
assertEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.FOOTER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(99), CursorRecyclerViewAdapter.FOOTER_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderFooterCount() throws Exception {
|
||||
adapter.setHeaderView(new View(context));
|
||||
adapter.setFooterView(new View(context));
|
||||
assertEquals(adapter.getItemCount(), 102);
|
||||
assertEquals(adapter.getItemViewType(101), CursorRecyclerViewAdapter.FOOTER_TYPE);
|
||||
assertEquals(adapter.getItemViewType(0), CursorRecyclerViewAdapter.HEADER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(1), CursorRecyclerViewAdapter.HEADER_TYPE);
|
||||
assertNotEquals(adapter.getItemViewType(100), CursorRecyclerViewAdapter.FOOTER_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentValues;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE, application = Application.class)
|
||||
public class SqlUtilTest {
|
||||
|
||||
@Test
|
||||
public void buildTrueUpdateQuery_simple() {
|
||||
String selection = "_id = ?";
|
||||
String[] args = new String[]{"1"};
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("a", 2);
|
||||
|
||||
Pair<String, String[]> result = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||
|
||||
assertEquals("(_id = ?) AND (a != ? OR a IS NULL)", result.first());
|
||||
assertArrayEquals(new String[] { "1", "2" }, result.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildTrueUpdateQuery_complexSelection() {
|
||||
String selection = "_id = ? AND (foo = ? OR bar != ?)";
|
||||
String[] args = new String[]{"1", "2", "3"};
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("a", 4);
|
||||
|
||||
Pair<String, String[]> result = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||
|
||||
assertEquals("(_id = ? AND (foo = ? OR bar != ?)) AND (a != ? OR a IS NULL)", result.first());
|
||||
assertArrayEquals(new String[] { "1", "2", "3", "4" }, result.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildTrueUpdateQuery_multipleContentValues() {
|
||||
String selection = "_id = ?";
|
||||
String[] args = new String[]{"1"};
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("a", 2);
|
||||
values.put("b", 3);
|
||||
values.put("c", 4);
|
||||
|
||||
Pair<String, String[]> result = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||
|
||||
assertEquals("(_id = ?) AND (a != ? OR a IS NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL)", result.first());
|
||||
assertArrayEquals(new String[] { "1", "2", "3", "4"}, result.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildTrueUpdateQuery_nullContentValue() {
|
||||
String selection = "_id = ?";
|
||||
String[] args = new String[]{"1"};
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("a", (String) null);
|
||||
|
||||
Pair<String, String[]> result = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||
|
||||
assertEquals("(_id = ?) AND (a NOT NULL)", result.first());
|
||||
assertArrayEquals(new String[] { "1" }, result.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildTrueUpdateQuery_complexContentValue() {
|
||||
String selection = "_id = ?";
|
||||
String[] args = new String[]{"1"};
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("a", (String) null);
|
||||
values.put("b", 2);
|
||||
values.put("c", 3);
|
||||
values.put("d", (String) null);
|
||||
values.put("e", (String) null);
|
||||
|
||||
Pair<String, String[]> result = SqlUtil.buildTrueUpdateQuery(selection, args, values);
|
||||
|
||||
assertEquals("(_id = ?) AND (a NOT NULL OR b != ? OR b IS NULL OR c != ? OR c IS NULL OR d NOT NULL OR e NOT NULL)", result.first());
|
||||
assertArrayEquals(new String[] { "1", "2", "3" }, result.second());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.thoughtcrime.securesms.jobmanager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.JobStorage;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class JobMigratorTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
Log.initialize(mock(Log.Logger.class));
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void JobMigrator_crashWhenTooFewMigrations() {
|
||||
new JobMigrator(1, 2, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void JobMigrator_crashWhenTooManyMigrations() {
|
||||
new JobMigrator(1, 2, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3)));
|
||||
}
|
||||
|
||||
@Test(expected = AssertionError.class)
|
||||
public void JobMigrator_crashWhenSkippingMigrations() {
|
||||
new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void JobMigrator_properInitialization() {
|
||||
new JobMigrator(1, 3, Arrays.asList(new EmptyMigration(2), new EmptyMigration(3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_callsAppropriateMigrations_fullSet() {
|
||||
JobMigration migration1 = spy(new EmptyMigration(2));
|
||||
JobMigration migration2 = spy(new EmptyMigration(3));
|
||||
|
||||
JobMigrator subject = new JobMigrator(1, 3, Arrays.asList(migration1, migration2));
|
||||
int version = subject.migrate(simpleJobStorage(), mock(Data.Serializer.class));
|
||||
|
||||
assertEquals(3, version);
|
||||
verify(migration1).migrate(any());
|
||||
verify(migration2).migrate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_callsAppropriateMigrations_subset() {
|
||||
JobMigration migration1 = spy(new EmptyMigration(2));
|
||||
JobMigration migration2 = spy(new EmptyMigration(3));
|
||||
|
||||
JobMigrator subject = new JobMigrator(2, 3, Arrays.asList(migration1, migration2));
|
||||
int version = subject.migrate(simpleJobStorage(), mock(Data.Serializer.class));
|
||||
|
||||
assertEquals(3, version);
|
||||
verify(migration1, never()).migrate(any());
|
||||
verify(migration2).migrate(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_callsAppropriateMigrations_none() {
|
||||
JobMigration migration1 = spy(new EmptyMigration(2));
|
||||
JobMigration migration2 = spy(new EmptyMigration(3));
|
||||
|
||||
JobMigrator subject = new JobMigrator(3, 3, Arrays.asList(migration1, migration2));
|
||||
int version = subject.migrate(simpleJobStorage(), mock(Data.Serializer.class));
|
||||
|
||||
assertEquals(3, version);
|
||||
verify(migration1, never()).migrate(any());
|
||||
verify(migration2, never()).migrate(any());
|
||||
}
|
||||
|
||||
private static JobStorage simpleJobStorage() {
|
||||
JobStorage jobStorage = mock(JobStorage.class);
|
||||
when(jobStorage.getAllJobSpecs()).thenReturn(new ArrayList<>(Collections.singletonList(new JobSpec("1", "f1", null, 1, 1, 1, 1, 1, 1, 1, "", false))));
|
||||
return jobStorage;
|
||||
}
|
||||
|
||||
private static class EmptyMigration extends JobMigration {
|
||||
|
||||
protected EmptyMigration(int endVersion) {
|
||||
super(endVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NonNull JobData migrate(@NonNull JobData jobData) {
|
||||
return jobData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.impl;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public final class JsonDataSerializerTest {
|
||||
|
||||
private static final float FloatDelta = 0.00001f;
|
||||
|
||||
@Test
|
||||
public void deserialize_dataMatchesExpected() throws IOException {
|
||||
Data data = new JsonDataSerializer().deserialize(Util.readFullyAsString(ClassLoader.getSystemClassLoader().getResourceAsStream("data/data_serialized.json")));
|
||||
|
||||
assertEquals("s1 value", data.getString("s1"));
|
||||
assertEquals("s2 value", data.getString("s2"));
|
||||
assertArrayEquals(new String[]{ "a", "b", "c" }, data.getStringArray("s_array_1"));
|
||||
|
||||
assertEquals(1, data.getInt("i1"));
|
||||
assertEquals(2, data.getInt("i2"));
|
||||
assertEquals(Integer.MAX_VALUE, data.getInt("max"));
|
||||
assertEquals(Integer.MIN_VALUE, data.getInt("min"));
|
||||
assertArrayEquals(new int[]{ 1, 2, 3, Integer.MAX_VALUE, Integer.MIN_VALUE }, data.getIntegerArray("i_array_1"));
|
||||
|
||||
assertEquals(10, data.getLong("l1"));
|
||||
assertEquals(20, data.getLong("l2"));
|
||||
assertEquals(Long.MAX_VALUE, data.getLong("max"));
|
||||
assertEquals(Long.MIN_VALUE, data.getLong("min"));
|
||||
assertArrayEquals(new long[]{ 1, 2, 3, Long.MAX_VALUE, Long.MIN_VALUE }, data.getLongArray("l_array_1"));
|
||||
|
||||
assertEquals(1.2f, data.getFloat("f1"), FloatDelta);
|
||||
assertEquals(3.4f, data.getFloat("f2"), FloatDelta);
|
||||
assertArrayEquals(new float[]{ 5.6f, 7.8f }, data.getFloatArray("f_array_1"), FloatDelta);
|
||||
|
||||
assertEquals(10.2, data.getDouble("d1"), FloatDelta);
|
||||
assertEquals(30.4, data.getDouble("d2"), FloatDelta);
|
||||
assertArrayEquals(new double[]{ 50.6, 70.8 }, data.getDoubleArray("d_array_1"), FloatDelta);
|
||||
|
||||
assertTrue(data.getBoolean("b1"));
|
||||
assertFalse(data.getBoolean("b2"));
|
||||
assertArrayEquals(new boolean[]{ false, true }, data.getBooleanArray("b_array_1"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
|
||||
import org.thoughtcrime.securesms.jobs.FailingJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Recipient.class, Job.Parameters.class })
|
||||
public class RecipientIdFollowUpJobMigrationTest {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
mockStatic(Recipient.class);
|
||||
mockStatic(Job.Parameters.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob_good() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "1")
|
||||
.putString("group_id", "__textsecure_group__!abcd")
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RequestGroupInfoJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcd", converted.getData().getString("group_id"));
|
||||
|
||||
new RequestGroupInfoJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob_bad() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "1")
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("FailingJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
|
||||
new FailingJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_sendDeliveryReceiptJob_good() throws Exception {
|
||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("recipient", "1")
|
||||
.putLong("message_id", 1)
|
||||
.putLong("timestamp", 2)
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
assertEquals(2, converted.getData().getLong("timestamp"));
|
||||
|
||||
new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_sendDeliveryReceiptJob_bad() throws Exception {
|
||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("recipient", "1")
|
||||
.build());
|
||||
RecipientIdFollowUpJobMigration subject = new RecipientIdFollowUpJobMigration();
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("FailingJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
|
||||
new FailingJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
package org.thoughtcrime.securesms.jobmanager.migrations;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobMigration.JobData;
|
||||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.NewSerializableSyncMessageId;
|
||||
import org.thoughtcrime.securesms.jobmanager.migrations.RecipientIdJobMigration.OldSerializableSyncMessageId;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceVerifiedUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceViewOnceOpenJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.powermock.api.mockito.PowerMockito.doReturn;
|
||||
import static org.powermock.api.mockito.PowerMockito.mock;
|
||||
import static org.powermock.api.mockito.PowerMockito.mockStatic;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Recipient.class, Job.Parameters.class })
|
||||
public class RecipientIdJobMigrationTest {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
mockStatic(Recipient.class);
|
||||
mockStatic(Job.Parameters.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_multiDeviceContactUpdateJob() throws Exception {
|
||||
JobData testData = new JobData("MultiDeviceContactUpdateJob", "MultiDeviceContactUpdateJob", new Data.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("MultiDeviceContactUpdateJob", converted.getFactoryKey());
|
||||
assertEquals("MultiDeviceContactUpdateJob", converted.getQueueKey());
|
||||
assertFalse(converted.getData().getBoolean("force_sync"));
|
||||
assertFalse(converted.getData().hasString("address"));
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
|
||||
new MultiDeviceContactUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_multiDeviceViewOnceOpenJob() throws Exception {
|
||||
OldSerializableSyncMessageId oldId = new OldSerializableSyncMessageId("+16101234567", 1);
|
||||
JobData testData = new JobData("MultiDeviceRevealUpdateJob", null, new Data.Builder().putString("message_id", JsonUtils.toJson(oldId)).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("MultiDeviceRevealUpdateJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), converted.getData().getString("message_id"));
|
||||
|
||||
new MultiDeviceViewOnceOpenJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_requestGroupInfoJob() throws Exception {
|
||||
JobData testData = new JobData("RequestGroupInfoJob", null, new Data.Builder().putString("source", "+16101234567")
|
||||
.putString("group_id", "__textsecure_group__!abcd")
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RequestGroupInfoJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcd", converted.getData().getString("group_id"));
|
||||
|
||||
new RequestGroupInfoJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_sendDeliveryReceiptJob() throws Exception {
|
||||
JobData testData = new JobData("SendDeliveryReceiptJob", null, new Data.Builder().putString("address", "+16101234567")
|
||||
.putLong("message_id", 1)
|
||||
.putLong("timestamp", 2)
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("SendDeliveryReceiptJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
assertEquals(2, converted.getData().getLong("timestamp"));
|
||||
|
||||
new SendDeliveryReceiptJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_multiDeviceVerifiedUpdateJob() throws Exception {
|
||||
JobData testData = new JobData("MultiDeviceVerifiedUpdateJob", "__MULTI_DEVICE_VERIFIED_UPDATE__", new Data.Builder().putString("destination", "+16101234567")
|
||||
.putString("identity_key", "abcd")
|
||||
.putInt("verified_status", 1)
|
||||
.putLong("timestamp", 123)
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("MultiDeviceVerifiedUpdateJob", converted.getFactoryKey());
|
||||
assertEquals("__MULTI_DEVICE_VERIFIED_UPDATE__", converted.getQueueKey());
|
||||
assertEquals("abcd", converted.getData().getString("identity_key"));
|
||||
assertEquals(1, converted.getData().getInt("verified_status"));
|
||||
assertEquals(123, converted.getData().getLong("timestamp"));
|
||||
assertEquals("1", converted.getData().getString("destination"));
|
||||
|
||||
new MultiDeviceVerifiedUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_retrieveProfileJob() throws Exception {
|
||||
JobData testData = new JobData("RetrieveProfileJob", null, new Data.Builder().putString("address", "+16101234567").build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RetrieveProfileJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
|
||||
new RetrieveProfileJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushGroupSendJob_null() throws Exception {
|
||||
JobData testData = new JobData("PushGroupSendJob", "someGroupId", new Data.Builder().putString("filter_address", null)
|
||||
.putLong("message_id", 123)
|
||||
.build());
|
||||
mockRecipientResolve("someGroupId", 5);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushGroupSendJob", converted.getFactoryKey());
|
||||
assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey());
|
||||
assertNull(converted.getData().getString("filter_recipient"));
|
||||
assertFalse(converted.getData().hasString("filter_address"));
|
||||
|
||||
new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushGroupSendJob_nonNull() throws Exception {
|
||||
JobData testData = new JobData("PushGroupSendJob", "someGroupId", new Data.Builder().putString("filter_address", "+16101234567")
|
||||
.putLong("message_id", 123)
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
mockRecipientResolve("someGroupId", 5);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushGroupSendJob", converted.getFactoryKey());
|
||||
assertEquals(RecipientId.from(5).toQueueKey(), converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("filter_recipient"));
|
||||
assertFalse(converted.getData().hasString("filter_address"));
|
||||
|
||||
new PushGroupSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushGroupUpdateJob() throws Exception {
|
||||
JobData testData = new JobData("PushGroupUpdateJob", null, new Data.Builder().putString("source", "+16101234567")
|
||||
.putString("group_id", "__textsecure_group__!abcd")
|
||||
.build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushGroupUpdateJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("source"));
|
||||
assertEquals("__textsecure_group__!abcd", converted.getData().getString("group_id"));
|
||||
|
||||
new PushGroupUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_directoryRefreshJob_null() throws Exception {
|
||||
JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", new Data.Builder().putString("address", null).putBoolean("notify_of_new_users", true).build());
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("DirectoryRefreshJob", converted.getFactoryKey());
|
||||
assertEquals("DirectoryRefreshJob", converted.getQueueKey());
|
||||
assertNull(converted.getData().getString("recipient"));
|
||||
assertTrue(converted.getData().getBoolean("notify_of_new_users"));
|
||||
assertFalse(converted.getData().hasString("address"));
|
||||
|
||||
new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_directoryRefreshJob_nonNull() throws Exception {
|
||||
JobData testData = new JobData("DirectoryRefreshJob", "DirectoryRefreshJob", new Data.Builder().putString("address", "+16101234567").putBoolean("notify_of_new_users", true).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("DirectoryRefreshJob", converted.getFactoryKey());
|
||||
assertEquals("DirectoryRefreshJob", converted.getQueueKey());
|
||||
assertTrue(converted.getData().getBoolean("notify_of_new_users"));
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
assertFalse(converted.getData().hasString("address"));
|
||||
|
||||
new DirectoryRefreshJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_retrieveProfileAvatarJob() throws Exception {
|
||||
JobData testData = new JobData("RetrieveProfileAvatarJob", "RetrieveProfileAvatarJob+16101234567", new Data.Builder().putString("address", "+16101234567").putString("profile_avatar", "abc").build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("RetrieveProfileAvatarJob", converted.getFactoryKey());
|
||||
assertEquals("RetrieveProfileAvatarJob::" + RecipientId.from(1).toQueueKey(), converted.getQueueKey());
|
||||
assertEquals("1", converted.getData().getString("recipient"));
|
||||
|
||||
|
||||
new RetrieveProfileAvatarJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_multiDeviceReadUpdateJob_empty() throws Exception {
|
||||
JobData testData = new JobData("MultiDeviceReadUpdateJob", null, new Data.Builder().putStringArray("message_ids", new String[0]).build());
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals(0, converted.getData().getStringArray("message_ids").length);
|
||||
|
||||
new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_multiDeviceReadUpdateJob_twoIds() throws Exception {
|
||||
OldSerializableSyncMessageId id1 = new OldSerializableSyncMessageId("+16101234567", 1);
|
||||
OldSerializableSyncMessageId id2 = new OldSerializableSyncMessageId("+16101112222", 2);
|
||||
|
||||
JobData testData = new JobData("MultiDeviceReadUpdateJob", null, new Data.Builder().putStringArray("message_ids", new String[]{ JsonUtils.toJson(id1), JsonUtils.toJson(id2) }).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
mockRecipientResolve("+16101112222", 2);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("MultiDeviceReadUpdateJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
|
||||
String[] updated = converted.getData().getStringArray("message_ids");
|
||||
assertEquals(2, updated.length);
|
||||
|
||||
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("1", 1)), updated[0]);
|
||||
assertEquals(JsonUtils.toJson(new NewSerializableSyncMessageId("2", 2)), updated[1]);
|
||||
|
||||
new MultiDeviceReadUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushTextSendJob() throws Exception {
|
||||
JobData testData = new JobData("PushTextSendJob", "+16101234567", new Data.Builder().putLong("message_id", 1).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushTextSendJob", converted.getFactoryKey());
|
||||
assertEquals(RecipientId.from(1).toQueueKey(), converted.getQueueKey());
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
|
||||
new PushTextSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_pushMediaSendJob() throws Exception {
|
||||
JobData testData = new JobData("PushMediaSendJob", "+16101234567", new Data.Builder().putLong("message_id", 1).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("PushMediaSendJob", converted.getFactoryKey());
|
||||
assertEquals(RecipientId.from(1).toQueueKey(), converted.getQueueKey());
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
|
||||
new PushMediaSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_smsSendJob_nonNull() throws Exception {
|
||||
JobData testData = new JobData("SmsSendJob", "+16101234567", new Data.Builder().putLong("message_id", 1).putInt("run_attempt", 0).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("SmsSendJob", converted.getFactoryKey());
|
||||
assertEquals(RecipientId.from(1).toQueueKey(), converted.getQueueKey());
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
assertEquals(0, converted.getData().getInt("run_attempt"));
|
||||
|
||||
new SmsSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migrate_smsSendJob_null() throws Exception {
|
||||
JobData testData = new JobData("SmsSendJob", null, new Data.Builder().putLong("message_id", 1).putInt("run_attempt", 0).build());
|
||||
mockRecipientResolve("+16101234567", 1);
|
||||
|
||||
RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
|
||||
JobData converted = subject.migrate(testData);
|
||||
|
||||
assertEquals("SmsSendJob", converted.getFactoryKey());
|
||||
assertNull(converted.getQueueKey());
|
||||
assertEquals(1, converted.getData().getLong("message_id"));
|
||||
assertEquals(0, converted.getData().getInt("run_attempt"));
|
||||
|
||||
new SmsSendJob.Factory().create(mock(Job.Parameters.class), converted.getData());
|
||||
}
|
||||
|
||||
private void mockRecipientResolve(String address, long recipientId) throws Exception {
|
||||
doReturn(mockRecipient(recipientId)).when(Recipient.class, "external", any(Context.class), eq(address));
|
||||
}
|
||||
|
||||
private Recipient mockRecipient(long id) {
|
||||
Recipient recipient = mock(Recipient.class);
|
||||
when(recipient.getId()).thenReturn(RecipientId.from(id));
|
||||
return recipient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.database.JobDatabase;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.ConstraintSpec;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.DependencySpec;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.FullSpec;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.JobSpec;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FastJobStorageTest {
|
||||
|
||||
private static final JsonDataSerializer serializer = new JsonDataSerializer();
|
||||
private static final String EMPTY_DATA = serializer.serialize(Data.EMPTY);
|
||||
|
||||
@Test
|
||||
public void init_allStoredDataAvailable() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
|
||||
subject.init();
|
||||
|
||||
DataSet1.assertJobsMatch(subject.getAllJobSpecs());
|
||||
DataSet1.assertConstraintsMatch(subject.getAllConstraintSpecs());
|
||||
DataSet1.assertDependenciesMatch(subject.getAllDependencySpecs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertJobs_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
|
||||
subject.insertJobs(DataSet1.FULL_SPECS);
|
||||
|
||||
verify(database).insertJobs(DataSet1.FULL_SPECS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertJobs_dataCanBeFound() {
|
||||
FastJobStorage subject = new FastJobStorage(noopDatabase());
|
||||
|
||||
subject.insertJobs(DataSet1.FULL_SPECS);
|
||||
|
||||
DataSet1.assertJobsMatch(subject.getAllJobSpecs());
|
||||
DataSet1.assertConstraintsMatch(subject.getAllConstraintSpecs());
|
||||
DataSet1.assertDependenciesMatch(subject.getAllDependencySpecs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertJobs_individualJobCanBeFound() {
|
||||
FastJobStorage subject = new FastJobStorage(noopDatabase());
|
||||
|
||||
subject.insertJobs(DataSet1.FULL_SPECS);
|
||||
|
||||
assertEquals(DataSet1.JOB_1, subject.getJobSpec(DataSet1.JOB_1.getId()));
|
||||
assertEquals(DataSet1.JOB_2, subject.getJobSpec(DataSet1.JOB_2.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateAllJobsToBePending_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
|
||||
subject.updateAllJobsToBePending();
|
||||
|
||||
verify(database).updateAllJobsToBePending();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateAllJobsToBePending_allArePending() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", null, 1, 1, 1, 1, 1, 1, 1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", null, 1, 1, 1, 1, 1, 1, 1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
|
||||
subject.init();
|
||||
subject.updateAllJobsToBePending();
|
||||
|
||||
assertFalse(subject.getJobSpec("1").isRunning());
|
||||
assertFalse(subject.getJobSpec("2").isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobs_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
List<JobSpec> jobs = Collections.emptyList();
|
||||
|
||||
subject.updateJobs(jobs);
|
||||
|
||||
verify(database).updateJobs(jobs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobs_updatesAllFields() {
|
||||
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", null, 1, 1, 1, 1, 1, 1, 1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", null, 1, 1, 1, 1, 1, 1, 1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec3 = new FullSpec(new JobSpec("3", "f3", null, 1, 1, 1, 1, 1, 1, 1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2, fullSpec3)));
|
||||
|
||||
JobSpec update1 = new JobSpec("1", "g1", "q1", 2, 2, 2, 2, 2, 2, 2, "abc", true);
|
||||
JobSpec update2 = new JobSpec("2", "g2", "q2", 3, 3, 3, 3, 3, 3, 3, "def", true);
|
||||
|
||||
subject.init();
|
||||
subject.updateJobs(Arrays.asList(update1, update2));
|
||||
|
||||
assertEquals(update1, subject.getJobSpec("1"));
|
||||
assertEquals(update2, subject.getJobSpec("2"));
|
||||
assertEquals(fullSpec3.getJobSpec(), subject.getJobSpec("3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobRunningState_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
|
||||
subject.updateJobRunningState("1", true);
|
||||
|
||||
verify(database).updateJobRunningState("1", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobRunningState_stateUpdated() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
subject.init();
|
||||
|
||||
subject.updateJobRunningState(DataSet1.JOB_1.getId(), true);
|
||||
assertTrue(subject.getJobSpec(DataSet1.JOB_1.getId()).isRunning());
|
||||
|
||||
subject.updateJobRunningState(DataSet1.JOB_1.getId(), false);
|
||||
assertFalse(subject.getJobSpec(DataSet1.JOB_1.getId()).isRunning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobAfterRetry_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
|
||||
subject.updateJobAfterRetry("1", true, 1, 10, "a");
|
||||
|
||||
verify(database).updateJobAfterRetry("1", true, 1, 10, "a");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateJobAfterRetry_stateUpdated() {
|
||||
FullSpec fullSpec = new FullSpec(new JobSpec("1", "f1", null, 0, 0, 0, 3, 30000, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Collections.singletonList(fullSpec)));
|
||||
|
||||
subject.init();
|
||||
subject.updateJobAfterRetry("1", false, 1, 10, "a");
|
||||
|
||||
JobSpec job = subject.getJobSpec("1");
|
||||
|
||||
assertNotNull(job);
|
||||
assertFalse(job.isRunning());
|
||||
assertEquals(1, job.getRunAttempt());
|
||||
assertEquals(10, job.getNextRunAttemptTime());
|
||||
assertEquals("a", job.getSerializedData());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_noneWhenEarlierItemInQueueInRunning() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(0, subject.getPendingJobsWithNoDependenciesInCreatedOrder(1).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_noneWhenAllJobsAreRunning() {
|
||||
FullSpec fullSpec = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Collections.singletonList(fullSpec)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(0, subject.getPendingJobsWithNoDependenciesInCreatedOrder(10).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_noneWhenNextRunTimeIsAfterCurrentTime() {
|
||||
FullSpec fullSpec = new FullSpec(new JobSpec("1", "f1", "q", 0, 10, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Collections.singletonList(fullSpec)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(0, subject.getPendingJobsWithNoDependenciesInCreatedOrder(0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_noneWhenDependentOnAnotherJob() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.singletonList(new DependencySpec("2", "1")));
|
||||
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(0, subject.getPendingJobsWithNoDependenciesInCreatedOrder(0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_singleEligibleJob() {
|
||||
FullSpec fullSpec = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Collections.singletonList(fullSpec)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(1, subject.getPendingJobsWithNoDependenciesInCreatedOrder(10).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_multipleEligibleJobs() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
subject.init();
|
||||
|
||||
assertEquals(2, subject.getPendingJobsWithNoDependenciesInCreatedOrder(10).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_singleEligibleJobInMixedList() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", null, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(1, jobs.size());
|
||||
assertEquals("2", jobs.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_firstItemInQueue() {
|
||||
FullSpec fullSpec1 = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec fullSpec2 = new FullSpec(new JobSpec("2", "f2", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(fullSpec1, fullSpec2)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(1, jobs.size());
|
||||
assertEquals("1", jobs.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_migrationJobTakesPrecedence() {
|
||||
FullSpec plainSpec = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec migrationSpec = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(plainSpec, migrationSpec)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(1, jobs.size());
|
||||
assertEquals("2", jobs.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_runningMigrationBlocksNormalJobs() {
|
||||
FullSpec plainSpec = new FullSpec(new JobSpec("1", "f1", "q", 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec migrationSpec = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(plainSpec, migrationSpec)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(0, jobs.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_runningMigrationBlocksLaterMigrationJobs() {
|
||||
FullSpec migrationSpec1 = new FullSpec(new JobSpec("1", "f1", Job.Parameters.MIGRATION_QUEUE_KEY, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, true),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec migrationSpec2 = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(migrationSpec1, migrationSpec2)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(0, jobs.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_onlyReturnFirstEligibleMigrationJob() {
|
||||
FullSpec migrationSpec1 = new FullSpec(new JobSpec("1", "f1", Job.Parameters.MIGRATION_QUEUE_KEY, 0, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec migrationSpec2 = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(migrationSpec1, migrationSpec2)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertEquals(1, jobs.size());
|
||||
assertEquals("1", jobs.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPendingJobsWithNoDependenciesInCreatedOrder_onlyMigrationJobWithAppropriateNextRunTime() {
|
||||
FullSpec migrationSpec1 = new FullSpec(new JobSpec("1", "f1", Job.Parameters.MIGRATION_QUEUE_KEY, 0, 999, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
FullSpec migrationSpec2 = new FullSpec(new JobSpec("2", "f2", Job.Parameters.MIGRATION_QUEUE_KEY, 5, 0, 0, 0, 0, -1, -1, EMPTY_DATA, false),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(Arrays.asList(migrationSpec1, migrationSpec2)));
|
||||
subject.init();
|
||||
|
||||
List<JobSpec> jobs = subject.getPendingJobsWithNoDependenciesInCreatedOrder(10);
|
||||
|
||||
assertTrue(jobs.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteJobs_writesToDatabase() {
|
||||
JobDatabase database = noopDatabase();
|
||||
FastJobStorage subject = new FastJobStorage(database);
|
||||
List<String> ids = Arrays.asList("1", "2");
|
||||
|
||||
subject.deleteJobs(ids);
|
||||
|
||||
verify(database).deleteJobs(ids);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteJobs_deletesAllRelevantPieces() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
|
||||
subject.init();
|
||||
subject.deleteJobs(Collections.singletonList("id1"));
|
||||
|
||||
List<JobSpec> jobs = subject.getAllJobSpecs();
|
||||
List<ConstraintSpec> constraints = subject.getAllConstraintSpecs();
|
||||
List<DependencySpec> dependencies = subject.getAllDependencySpecs();
|
||||
|
||||
assertEquals(2, jobs.size());
|
||||
assertEquals(DataSet1.JOB_2, jobs.get(0));
|
||||
assertEquals(DataSet1.JOB_3, jobs.get(1));
|
||||
assertEquals(1, constraints.size());
|
||||
assertEquals(DataSet1.CONSTRAINT_2, constraints.get(0));
|
||||
assertEquals(1, dependencies.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDependencySpecsThatDependOnJob_startOfChain() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
|
||||
subject.init();
|
||||
|
||||
List<DependencySpec> result = subject.getDependencySpecsThatDependOnJob("id1");
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(DataSet1.DEPENDENCY_2, result.get(0));
|
||||
assertEquals(DataSet1.DEPENDENCY_3, result.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDependencySpecsThatDependOnJob_midChain() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
|
||||
subject.init();
|
||||
|
||||
List<DependencySpec> result = subject.getDependencySpecsThatDependOnJob("id2");
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(DataSet1.DEPENDENCY_3, result.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDependencySpecsThatDependOnJob_endOfChain() {
|
||||
FastJobStorage subject = new FastJobStorage(fixedDataDatabase(DataSet1.FULL_SPECS));
|
||||
|
||||
subject.init();
|
||||
|
||||
List<DependencySpec> result = subject.getDependencySpecsThatDependOnJob("id3");
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
private JobDatabase noopDatabase() {
|
||||
JobDatabase database = mock(JobDatabase.class);
|
||||
|
||||
when(database.getAllJobSpecs()).thenReturn(Collections.emptyList());
|
||||
when(database.getAllConstraintSpecs()).thenReturn(Collections.emptyList());
|
||||
when(database.getAllDependencySpecs()).thenReturn(Collections.emptyList());
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
private JobDatabase fixedDataDatabase(List<FullSpec> fullSpecs) {
|
||||
JobDatabase database = mock(JobDatabase.class);
|
||||
|
||||
when(database.getAllJobSpecs()).thenReturn(Stream.of(fullSpecs).map(FullSpec::getJobSpec).toList());
|
||||
when(database.getAllConstraintSpecs()).thenReturn(Stream.of(fullSpecs).map(FullSpec::getConstraintSpecs).flatMap(Stream::of).toList());
|
||||
when(database.getAllDependencySpecs()).thenReturn(Stream.of(fullSpecs).map(FullSpec::getDependencySpecs).flatMap(Stream::of).toList());
|
||||
|
||||
return database;
|
||||
}
|
||||
|
||||
private static final class DataSet1 {
|
||||
static final JobSpec JOB_1 = new JobSpec("id1", "f1", "q1", 1, 2, 3, 4, 5, 6, 7, EMPTY_DATA, false);
|
||||
static final JobSpec JOB_2 = new JobSpec("id2", "f2", "q2", 1, 2, 3, 4, 5, 6, 7, EMPTY_DATA, false);
|
||||
static final JobSpec JOB_3 = new JobSpec("id3", "f3", "q3", 1, 2, 3, 4, 5, 6, 7, EMPTY_DATA, false);
|
||||
static final ConstraintSpec CONSTRAINT_1 = new ConstraintSpec("id1", "f1");
|
||||
static final ConstraintSpec CONSTRAINT_2 = new ConstraintSpec("id2", "f2");
|
||||
static final DependencySpec DEPENDENCY_2 = new DependencySpec("id2", "id1");
|
||||
static final DependencySpec DEPENDENCY_3 = new DependencySpec("id3", "id2");
|
||||
static final FullSpec FULL_SPEC_1 = new FullSpec(JOB_1, Collections.singletonList(CONSTRAINT_1), Collections.emptyList());
|
||||
static final FullSpec FULL_SPEC_2 = new FullSpec(JOB_2, Collections.singletonList(CONSTRAINT_2), Collections.singletonList(DEPENDENCY_2));
|
||||
static final FullSpec FULL_SPEC_3 = new FullSpec(JOB_3, Collections.emptyList(), Collections.singletonList(DEPENDENCY_3));
|
||||
static final List<FullSpec> FULL_SPECS = Arrays.asList(FULL_SPEC_1, FULL_SPEC_2, FULL_SPEC_3);
|
||||
|
||||
static void assertJobsMatch(@NonNull List<JobSpec> jobs) {
|
||||
assertEquals(jobs.size(), 3);
|
||||
assertTrue(jobs.contains(DataSet1.JOB_1));
|
||||
assertTrue(jobs.contains(DataSet1.JOB_1));
|
||||
assertTrue(jobs.contains(DataSet1.JOB_3));
|
||||
}
|
||||
|
||||
static void assertConstraintsMatch(@NonNull List<ConstraintSpec> constraints) {
|
||||
assertEquals(constraints.size(), 2);
|
||||
assertTrue(constraints.contains(DataSet1.CONSTRAINT_1));
|
||||
assertTrue(constraints.contains(DataSet1.CONSTRAINT_2));
|
||||
}
|
||||
|
||||
static void assertDependenciesMatch(@NonNull List<DependencySpec> dependencies) {
|
||||
assertEquals(dependencies.size(), 2);
|
||||
assertTrue(dependencies.contains(DataSet1.DEPENDENCY_2));
|
||||
assertTrue(dependencies.contains(DataSet1.DEPENDENCY_3));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public final class JobManagerFactoriesTest {
|
||||
|
||||
@Test
|
||||
public void PushContentReceiveJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("PushContentReceiveJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void AttachmentUploadJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("AttachmentUploadJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void MmsSendJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("MmsSendJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.thoughtcrime.securesms.l10n;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE, application = Application.class)
|
||||
public final class LanguageResourcesTest {
|
||||
|
||||
@Test
|
||||
public void language_entries_match_language_values_in_length() {
|
||||
Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
String[] values = resources.getStringArray(R.array.language_values);
|
||||
String[] entries = resources.getStringArray(R.array.language_entries);
|
||||
assertEquals(values.length, entries.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void language_options_matches_available_resources() {
|
||||
Set<String> languageEntries = languageEntries();
|
||||
Set<String> foundResources = buildConfigResources();
|
||||
if (!languageEntries.equals(foundResources)) {
|
||||
assertSubset(foundResources, languageEntries, "Missing language_entries for resources");
|
||||
assertSubset(languageEntries, foundResources, "Missing resources for language_entries");
|
||||
fail("Unexpected");
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> languageEntries() {
|
||||
Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
String[] values = resources.getStringArray(R.array.language_values);
|
||||
|
||||
List<String> tail = Arrays.asList(values).subList(1, values.length);
|
||||
Set<String> set = new HashSet<>(tail);
|
||||
|
||||
assertEquals("First is not the default", "zz", values[0]);
|
||||
assertEquals("List contains duplicates", tail.size(), set.size());
|
||||
return set;
|
||||
}
|
||||
|
||||
private static Set<String> buildConfigResources() {
|
||||
Set<String> set = new HashSet<>();
|
||||
Collections.addAll(set, BuildConfig.LANGUAGES);
|
||||
assertEquals("List contains duplicates", BuildConfig.LANGUAGES.length, set.size());
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fails if "a" is not a subset of "b", lists the additional values found in "a"
|
||||
*/
|
||||
private static void assertSubset(Set<String> a, Set<String> b, String message) {
|
||||
Set<String> delta = subtract(a, b);
|
||||
if (!delta.isEmpty()) {
|
||||
fail(message + ": " + String.join(", ", delta));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a - Set b
|
||||
*/
|
||||
private static Set<String> subtract(Set<String> a, Set<String> b) {
|
||||
Set<String> set = new HashSet<>(a);
|
||||
set.removeAll(b);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.thoughtcrime.securesms.linkpreview;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
public class LinkPreviewUtilTest {
|
||||
|
||||
@Test
|
||||
public void isLegal_allAscii_noProtocol() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("google.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_allAscii_noProtocol_subdomain() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("foo.google.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_allAscii_subdomain() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("https://foo.google.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_allAscii_subdomain_path() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("https://foo.google.com/some/path.html"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_cyrillicHostAsciiTld() {
|
||||
assertFalse(LinkPreviewUtil.isLegalUrl("http://кц.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_cyrillicHostAsciiTld_noProtocol() {
|
||||
assertFalse(LinkPreviewUtil.isLegalUrl("кц.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_mixedHost_noProtocol() {
|
||||
assertFalse(LinkPreviewUtil.isLegalUrl("http://asĸ.com"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_cyrillicHostAndTld_noProtocol() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("кц.рф"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_cyrillicHostAndTld_asciiPath_noProtocol() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("кц.рф/some/path"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_cyrillicHostAndTld_asciiPath() {
|
||||
assertTrue(LinkPreviewUtil.isLegalUrl("https://кц.рф/some/path"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_asciiSubdomain_cyrillicHostAndTld() {
|
||||
assertFalse(LinkPreviewUtil.isLegalUrl("http://foo.кц.рф"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLegal_emptyUrl() {
|
||||
assertFalse(LinkPreviewUtil.isLegalUrl(""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.logging;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public final class LogTest {
|
||||
|
||||
@Test
|
||||
public void tag_short_class_name() {
|
||||
assertEquals("MyClass", Log.tag(MyClass.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tag_23_character_class_name() {
|
||||
String tag = Log.tag(TwentyThreeCharacters23.class);
|
||||
assertEquals("TwentyThreeCharacters23", tag);
|
||||
assertEquals(23, tag.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tag_24_character_class_name() {
|
||||
assertEquals(24, TwentyFour24Characters24.class.getSimpleName().length());
|
||||
String tag = Log.tag(TwentyFour24Characters24.class);
|
||||
assertEquals("TwentyFour24Characters2", tag);
|
||||
assertEquals(23, Log.tag(TwentyThreeCharacters23.class).length());
|
||||
}
|
||||
|
||||
private class MyClass {
|
||||
}
|
||||
|
||||
private class TwentyThreeCharacters23 {
|
||||
}
|
||||
|
||||
private class TwentyFour24Characters24 {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package org.thoughtcrime.securesms.logsubmit.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public final class ScrubberTest {
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][]{
|
||||
|
||||
{ "An E164 number +15551234567",
|
||||
"An E164 number +*********67" },
|
||||
|
||||
{ "A UK number +447700900000",
|
||||
"A UK number +**********00" },
|
||||
|
||||
{ "An avatar filename: file:///data/user/0/org.thoughtcrime.securesms/files/avatars/%2B447700900099",
|
||||
"An avatar filename: file:///data/user/0/org.thoughtcrime.securesms/files/avatars/%2B**********99" },
|
||||
|
||||
{ "Multiple numbers +447700900001 +447700900002",
|
||||
"Multiple numbers +**********01 +**********02" },
|
||||
|
||||
{ "One less than shortest number +155556",
|
||||
"One less than shortest number +155556" },
|
||||
|
||||
{ "Shortest number +1555567",
|
||||
"Shortest number +*****67" },
|
||||
|
||||
{ "Longest number +155556789012345",
|
||||
"Longest number +*************45" },
|
||||
|
||||
{ "One more than longest number +1234567890123456",
|
||||
"One more than longest number +*************456" },
|
||||
|
||||
{ "abc@def.com",
|
||||
"a...@..." },
|
||||
|
||||
{ "An email abc@def.com",
|
||||
"An email a...@..." },
|
||||
|
||||
{ "A short email a@def.com",
|
||||
"A short email a...@..." },
|
||||
|
||||
{ "A email with multiple parts before the @ d.c+b.a@mulitpart.domain.com and a multipart domain",
|
||||
"A email with multiple parts before the @ d...@... and a multipart domain" },
|
||||
|
||||
{ "An avatar email filename: file:///data/user/0/org.thoughtcrime.securesms/files/avatars/abc@signal.org",
|
||||
"An avatar email filename: file:///data/user/0/org.thoughtcrime.securesms/files/avatars/a...@..." },
|
||||
|
||||
{ "An email and a number abc@def.com +155556789012345",
|
||||
"An email and a number a...@... +*************45" },
|
||||
|
||||
{ "__textsecure_group__!abcdefg1234567890",
|
||||
"__...group...90" },
|
||||
|
||||
{ "A group id __textsecure_group__!abcdefg0987654321 surrounded with text",
|
||||
"A group id __...group...21 surrounded with text" },
|
||||
|
||||
{ "a37cb654-c9e0-4c1e-93df-3d11ca3c97f4",
|
||||
"********-****-****-****-**********f4" },
|
||||
|
||||
{ "A UUID a37cb654-c9e0-4c1e-93df-3d11ca3c97f4 surrounded with text",
|
||||
"A UUID ********-****-****-****-**********f4 surrounded with text" },
|
||||
|
||||
{ "JOB::a37cb654-c9e0-4c1e-93df-3d11ca3c97f4",
|
||||
"JOB::a37cb654-c9e0-4c1e-93df-3d11ca3c97f4" },
|
||||
|
||||
{ "All patterns in a row __textsecure_group__!abcdefg1234567890 +1234567890123456 abc@def.com a37cb654-c9e0-4c1e-93df-3d11ca3c97f4 with text after",
|
||||
"All patterns in a row __...group...90 +*************456 a...@... ********-****-****-****-**********f4 with text after"
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private final String input;
|
||||
private final String expected;
|
||||
|
||||
public ScrubberTest(String input, String expected) {
|
||||
this.input = input;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scrub() {
|
||||
assertEquals(expected, Scrubber.scrub(input).toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.thoughtcrime.securesms.phonenumbers;
|
||||
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
public class PhoneNumberFormatterTest {
|
||||
|
||||
@Before
|
||||
public void setup() {}
|
||||
|
||||
@Test
|
||||
public void testAddressString() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+14152222222");
|
||||
assertEquals(formatter.format("bonbon"), "bonbon");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddressShortCode() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+14152222222");
|
||||
assertEquals(formatter.format("40404"), "40404");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailAddress() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+14152222222");
|
||||
assertEquals(formatter.format("junk@junk.net"), "junk@junk.net");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNumberArbitrary() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+14152222222");
|
||||
assertEquals(formatter.format("(415) 111-1122"), "+14151111122");
|
||||
assertEquals(formatter.format("(415) 111 1123"), "+14151111123");
|
||||
assertEquals(formatter.format("415-111-1124"), "+14151111124");
|
||||
assertEquals(formatter.format("415.111.1125"), "+14151111125");
|
||||
assertEquals(formatter.format("+1 415.111.1126"), "+14151111126");
|
||||
assertEquals(formatter.format("+1 415 111 1127"), "+14151111127");
|
||||
assertEquals(formatter.format("+1 (415) 111 1128"), "+14151111128");
|
||||
assertEquals(formatter.format("911"), "911");
|
||||
assertEquals(formatter.format("+456-7890"), "+4567890");
|
||||
|
||||
formatter = new PhoneNumberFormatter("+442079460010");
|
||||
assertEquals(formatter.format("(020) 7946 0018"), "+442079460018");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsNumbers() {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+16105880522");
|
||||
|
||||
assertEquals("+551234567890", formatter.format("+551234567890"));
|
||||
assertEquals("+11234567890", formatter.format("(123) 456-7890"));
|
||||
assertEquals("+11234567890", formatter.format("1234567890"));
|
||||
assertEquals("+16104567890", formatter.format("456-7890"));
|
||||
assertEquals("+16104567890", formatter.format("4567890"));
|
||||
assertEquals("+11234567890", formatter.format("011 1 123 456 7890"));
|
||||
assertEquals("+5511912345678", formatter.format("0115511912345678"));
|
||||
assertEquals("+16105880522", formatter.format("+16105880522"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrNumbers() {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+5521912345678");
|
||||
|
||||
assertEquals("+16105880522", formatter.format("+16105880522"));
|
||||
assertEquals("+552187654321", formatter.format("8765 4321"));
|
||||
assertEquals("+5521987654321", formatter.format("9 8765 4321"));
|
||||
assertEquals("+552287654321", formatter.format("22 8765 4321"));
|
||||
assertEquals("+5522987654321", formatter.format("22 9 8765 4321"));
|
||||
assertEquals("+551234567890", formatter.format("+55 (123) 456-7890"));
|
||||
assertEquals("+14085048577", formatter.format("002214085048577"));
|
||||
assertEquals("+5511912345678", formatter.format("011912345678"));
|
||||
assertEquals("+5511912345678", formatter.format("02111912345678"));
|
||||
assertEquals("+551234567", formatter.format("1234567"));
|
||||
assertEquals("+5521912345678", formatter.format("+5521912345678"));
|
||||
assertEquals("+552112345678", formatter.format("+552112345678"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroup() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("+14152222222");
|
||||
assertEquals(formatter.format("__textsecure_group__!foobar"), "__textsecure_group__!foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLostLocalNumber() throws Exception {
|
||||
PhoneNumberFormatter formatter = new PhoneNumberFormatter("US", true);
|
||||
assertEquals(formatter.format("(415) 111-1122"), "+14151111122");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.thoughtcrime.securesms.recipients;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.provider.ContactsContract;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import static android.provider.ContactsContract.Intents.Insert.NAME;
|
||||
import static android.provider.ContactsContract.Intents.Insert.EMAIL;
|
||||
import static android.provider.ContactsContract.Intents.Insert.PHONE;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE, application = Application.class)
|
||||
public final class RecipientExporterTest {
|
||||
|
||||
@Test
|
||||
public void asAddContactIntent_with_phone_number() {
|
||||
Recipient recipient = givenPhoneRecipient("Alice", "+1555123456");
|
||||
|
||||
Intent intent = RecipientExporter.export(recipient).asAddContactIntent();
|
||||
|
||||
assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction());
|
||||
assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.getType());
|
||||
assertEquals("Alice", intent.getStringExtra(NAME));
|
||||
assertEquals("+1555123456", intent.getStringExtra(PHONE));
|
||||
assertNull(intent.getStringExtra(EMAIL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asAddContactIntent_with_email() {
|
||||
Recipient recipient = givenEmailRecipient("Bob", "bob@signal.org");
|
||||
|
||||
Intent intent = RecipientExporter.export(recipient).asAddContactIntent();
|
||||
|
||||
assertEquals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction());
|
||||
assertEquals(ContactsContract.Contacts.CONTENT_ITEM_TYPE, intent.getType());
|
||||
assertEquals("Bob", intent.getStringExtra(NAME));
|
||||
assertEquals("bob@signal.org", intent.getStringExtra(EMAIL));
|
||||
assertNull(intent.getStringExtra(PHONE));
|
||||
}
|
||||
|
||||
private Recipient givenPhoneRecipient(String profileName, String phone) {
|
||||
Recipient recipient = mock(Recipient.class);
|
||||
when(recipient.getProfileName()).thenReturn(profileName);
|
||||
|
||||
when(recipient.requireE164()).thenReturn(phone);
|
||||
when(recipient.getE164()).thenAnswer(i -> Optional.of(phone));
|
||||
when(recipient.getEmail()).thenAnswer(i -> Optional.absent());
|
||||
|
||||
return recipient;
|
||||
}
|
||||
|
||||
private Recipient givenEmailRecipient(String profileName, String email) {
|
||||
Recipient recipient = mock(Recipient.class);
|
||||
when(recipient.getProfileName()).thenReturn(profileName);
|
||||
|
||||
when(recipient.requireEmail()).thenReturn(email);
|
||||
when(recipient.getEmail()).thenAnswer(i -> Optional.of(email));
|
||||
when(recipient.getE164()).thenAnswer(i -> Optional.absent());
|
||||
|
||||
return recipient;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.thoughtcrime.securesms.registration;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE, application = Application.class)
|
||||
public final class PushChallengeRequestTest {
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_returns_absent_if_times_out() {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
|
||||
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, Optional.of("token"), "+123456", 50L);
|
||||
|
||||
assertFalse(challenge.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_waits_for_specified_period() {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
PushChallengeRequest.getPushChallengeBlocking(signal, Optional.of("token"), "+123456", 250L);
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
assertThat(duration, greaterThanOrEqualTo(250L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_completes_fast_if_posted_to_event_bus() throws IOException {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
doAnswer(invocation -> {
|
||||
AsyncTask.execute(() -> PushChallengeRequest.postChallengeResponse("CHALLENGE"));
|
||||
return null;
|
||||
}).when(signal).requestPushChallenge("token", "+123456");
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, Optional.of("token"), "+123456", 500L);
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
assertThat(duration, lessThan(500L));
|
||||
verify(signal).requestPushChallenge("token", "+123456");
|
||||
verifyNoMoreInteractions(signal);
|
||||
|
||||
assertTrue(challenge.isPresent());
|
||||
assertEquals("CHALLENGE", challenge.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_returns_fast_if_no_fcm_token_supplied() {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
PushChallengeRequest.getPushChallengeBlocking(signal, Optional.absent(), "+123456", 500L);
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
assertThat(duration, lessThan(500L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_returns_absent_if_no_fcm_token_supplied() {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
|
||||
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, Optional.absent(), "+123456", 500L);
|
||||
|
||||
verifyZeroInteractions(signal);
|
||||
assertFalse(challenge.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPushChallengeBlocking_returns_absent_if_any_IOException_is_thrown() throws IOException {
|
||||
SignalServiceAccountManager signal = mock(SignalServiceAccountManager.class);
|
||||
|
||||
doThrow(new IOException()).when(signal).requestPushChallenge(any(), any());
|
||||
|
||||
Optional<String> challenge = PushChallengeRequest.getPushChallengeBlocking(signal, Optional.of("token"), "+123456", 500L);
|
||||
|
||||
assertFalse(challenge.isPresent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.thoughtcrime.securesms.registration.viewmodel;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.registration.service.RegistrationCodeRequest;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class LocalCodeRequestRateLimiterTest {
|
||||
|
||||
@Test
|
||||
public void initially_can_request() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cant_request_within_same_time_period() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
|
||||
limiter.onSuccessfulRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000);
|
||||
|
||||
assertFalse(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000 + 59_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_request_within_same_time_period_if_different_number() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
|
||||
limiter.onSuccessfulRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+15559874566", 1000 + 59_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_request_within_same_time_period_if_different_mode() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
|
||||
limiter.onSuccessfulRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_FCM_NO_LISTENER, "+155512345678", 1000 + 59_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_request_after_time_period() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
|
||||
limiter.onSuccessfulRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000 + 60_001));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_request_within_same_time_period_if_an_unsuccessful_request_is_seen() {
|
||||
LocalCodeRequestRateLimiter limiter = new LocalCodeRequestRateLimiter(60_000);
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000));
|
||||
|
||||
limiter.onSuccessfulRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000);
|
||||
|
||||
limiter.onUnsuccessfulRequest();
|
||||
|
||||
assertTrue(limiter.canRequest(RegistrationCodeRequest.Mode.SMS_NO_FCM, "+155512345678", 1000 + 59_000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.thoughtcrime.securesms.service;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.BaseUnitTest;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.contains;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class VerificationCodeParserTest extends BaseUnitTest {
|
||||
private static Map<String, String> CHALLENGES = new HashMap<String,String>() {{
|
||||
put("Your TextSecure verification code: 337-337", "337337");
|
||||
put("XXX\nYour TextSecure verification code: 1337-1337", "13371337");
|
||||
put("Your TextSecure verification code: 337-1337", "3371337");
|
||||
put("Your TextSecure verification code: 1337-337", "1337337");
|
||||
put("Your TextSecure verification code: 1337-1337", "13371337");
|
||||
put("XXXYour TextSecure verification code: 1337-1337", "13371337");
|
||||
put("Your TextSecure verification code: 1337-1337XXX", "13371337");
|
||||
put("Your TextSecure verification code 1337-1337", "13371337");
|
||||
|
||||
put("Your Signal verification code: 337-337", "337337");
|
||||
put("XXX\nYour Signal verification code: 1337-1337", "13371337");
|
||||
put("Your Signal verification code: 337-1337", "3371337");
|
||||
put("Your Signal verification code: 1337-337", "1337337");
|
||||
put("Your Signal verification code: 1337-1337", "13371337");
|
||||
put("XXXYour Signal verification code: 1337-1337", "13371337");
|
||||
put("Your Signal verification code: 1337-1337XXX", "13371337");
|
||||
put("Your Signal verification code 1337-1337", "13371337");
|
||||
|
||||
put("<#>Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337");
|
||||
put("<#> Your Signal verification code: 1337-1337 aAbBcCdDeEf", "13371337");
|
||||
put("<#>Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337");
|
||||
put("<#> Your Signal verification code: 1337-1337\naAbBcCdDeEf", "13371337");
|
||||
put("<#> Your Signal verification code: 1337-1337\n\naAbBcCdDeEf", "13371337");
|
||||
}};
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
when(sharedPreferences.getBoolean(contains("pref_verifying"), anyBoolean())).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChallenges() {
|
||||
for (Entry<String,String> challenge : CHALLENGES.entrySet()) {
|
||||
Optional<String> result = VerificationCodeParser.parse(context, challenge.getKey());
|
||||
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals(result.get(), challenge.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.testutil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Executor that runs runnables on the same thread {@link #execute(Runnable)} is invoked on.
|
||||
* Only intended to be used for tests.
|
||||
*/
|
||||
public class DirectExecutor implements Executor {
|
||||
@Override
|
||||
public void execute(@NonNull Runnable runnable) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest(TextUtils.class)
|
||||
public class DelimiterUtilTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
PowerMockito.mockStatic(TextUtils.class);
|
||||
PowerMockito.when(TextUtils.isEmpty(Mockito.anyString())).thenAnswer((Answer<Boolean>) invocation -> {
|
||||
if (invocation.getArguments()[0] == null) return true;
|
||||
return ((String) invocation.getArguments()[0]).isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscape() {
|
||||
assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ Music");
|
||||
assertEquals(DelimiterUtil.escape("MTV Music", ' '), "MTV\\ \\ Music");
|
||||
|
||||
assertEquals(DelimiterUtil.escape("MTV,Music", ','), "MTV\\,Music");
|
||||
assertEquals(DelimiterUtil.escape("MTV,,Music", ','), "MTV\\,\\,Music");
|
||||
|
||||
assertEquals(DelimiterUtil.escape("MTV Music", '+'), "MTV Music");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplit() {
|
||||
String[] parts = DelimiterUtil.split("MTV\\ Music", ' ');
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "MTV\\ Music");
|
||||
|
||||
parts = DelimiterUtil.split("MTV Music", ' ');
|
||||
assertEquals(parts.length, 2);
|
||||
assertEquals(parts[0], "MTV");
|
||||
assertEquals(parts[1], "Music");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeSplit() {
|
||||
String input = "MTV Music";
|
||||
String intermediate = DelimiterUtil.escape(input, ' ');
|
||||
String[] parts = DelimiterUtil.split(intermediate, ' ');
|
||||
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "MTV\\ Music");
|
||||
assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV Music");
|
||||
|
||||
input = "MTV\\ Music";
|
||||
intermediate = DelimiterUtil.escape(input, ' ');
|
||||
parts = DelimiterUtil.split(intermediate, ' ');
|
||||
|
||||
assertEquals(parts.length, 1);
|
||||
assertEquals(parts[0], "MTV\\\\ Music");
|
||||
assertEquals(DelimiterUtil.unescape(parts[0], ' '), "MTV\\ Music");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.BaseUnitTest;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ListPartitionTest extends BaseUnitTest {
|
||||
|
||||
@Test public void testPartitionEven() {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
for (int i=0;i<100;i++) {
|
||||
list.add(i);
|
||||
}
|
||||
|
||||
List<List<Integer>> partitions = Util.partition(list, 10);
|
||||
|
||||
assertEquals(partitions.size(), 10);
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (int i=0;i<partitions.size();i++) {
|
||||
List<Integer> partition = partitions.get(i);
|
||||
assertEquals(partition.size(), 10);
|
||||
|
||||
for (int j=0;j<partition.size();j++) {
|
||||
assertEquals((int)partition.get(j), counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testPartitionOdd() {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
for (int i=0;i<100;i++) {
|
||||
list.add(i);
|
||||
}
|
||||
|
||||
list.add(100);
|
||||
|
||||
List<List<Integer>> partitions = Util.partition(list, 10);
|
||||
|
||||
assertEquals(partitions.size(), 11);
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (int i=0;i<partitions.size()-1;i++) {
|
||||
List<Integer> partition = partitions.get(i);
|
||||
assertEquals(partition.size(), 10);
|
||||
|
||||
for (int j=0;j<partition.size();j++) {
|
||||
assertEquals((int)partition.get(j), counter++);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(partitions.get(10).size(), 1);
|
||||
assertEquals((int)partitions.get(10).get(0), 100);
|
||||
}
|
||||
|
||||
@Test public void testPathological() {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
for (int i=0;i<100;i++) {
|
||||
list.add(i);
|
||||
}
|
||||
|
||||
List<List<Integer>> partitions = Util.partition(list, 1);
|
||||
|
||||
assertEquals(partitions.size(), 100);
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (int i=0;i<partitions.size();i++) {
|
||||
List<Integer> partition = partitions.get(i);
|
||||
assertEquals(partition.size(), 1);
|
||||
|
||||
for (int j=0;j<partition.size();j++) {
|
||||
assertEquals((int)partition.get(j), counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.BaseUnitTest;
|
||||
import org.whispersystems.signalservice.api.util.InvalidNumberException;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class PhoneNumberFormatterTest extends BaseUnitTest {
|
||||
private static final String LOCAL_NUMBER_US = "+15555555555";
|
||||
private static final String NUMBER_CH = "+41446681800";
|
||||
private static final String NUMBER_UK = "+442079460018";
|
||||
private static final String NUMBER_DE = "+4930123456";
|
||||
private static final String NUMBER_MOBILE_DE = "+49171123456";
|
||||
private static final String COUNTRY_CODE_CH = "41";
|
||||
private static final String COUNTRY_CODE_UK = "44";
|
||||
private static final String COUNTRY_CODE_DE = "49";
|
||||
|
||||
@Test
|
||||
public void testFormatNumber() throws Exception, InvalidNumberException {
|
||||
assertThat(PhoneNumberFormatter.formatNumber("(555) 555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US);
|
||||
assertThat(PhoneNumberFormatter.formatNumber("555-5555", LOCAL_NUMBER_US)).isEqualTo(LOCAL_NUMBER_US);
|
||||
assertThat(PhoneNumberFormatter.formatNumber("(123) 555-5555", LOCAL_NUMBER_US)).isNotEqualTo(LOCAL_NUMBER_US);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatNumberEmail() throws Exception {
|
||||
try {
|
||||
PhoneNumberFormatter.formatNumber("person@domain.com", LOCAL_NUMBER_US);
|
||||
throw new AssertionFailedError("should have thrown on email");
|
||||
} catch (InvalidNumberException ine) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatNumberE164() throws Exception, InvalidNumberException {
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "(020) 7946 0018")).isEqualTo(NUMBER_UK);
|
||||
// assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "044 20 7946 0018")).isEqualTo(NUMBER_UK);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_UK, "+442079460018")).isEqualTo(NUMBER_UK);
|
||||
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 44 668 18 00")).isEqualTo(NUMBER_CH);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_CH, "+41 (044) 6681800")).isEqualTo(NUMBER_CH);
|
||||
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 030 123456")).isEqualTo(NUMBER_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049 (0)30123456")).isEqualTo(NUMBER_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049((0)30)123456")).isEqualTo(NUMBER_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+49 (0) 30 1 2 3 45 6 ")).isEqualTo(NUMBER_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "030 123456")).isEqualTo(NUMBER_DE);
|
||||
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171123456")).isEqualTo(NUMBER_MOBILE_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0171/123456")).isEqualTo(NUMBER_MOBILE_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "+490171/123456")).isEqualTo(NUMBER_MOBILE_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "00490171/123456")).isEqualTo(NUMBER_MOBILE_DE);
|
||||
assertThat(PhoneNumberFormatter.formatE164(COUNTRY_CODE_DE, "0049171/123456")).isEqualTo(NUMBER_MOBILE_DE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatRemoteNumberE164() throws Exception, InvalidNumberException {
|
||||
assertThat(PhoneNumberFormatter.formatNumber("+4402079460018", LOCAL_NUMBER_US)).isEqualTo(NUMBER_UK);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Open Whisper Systems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.BaseUnitTest;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class Rfc5724UriTest extends BaseUnitTest {
|
||||
|
||||
@Test public void testInvalidPath() throws Exception {
|
||||
final String[] invalidSchemaUris = {
|
||||
"",
|
||||
":",
|
||||
"sms:",
|
||||
":sms",
|
||||
"sms:?goto=fail",
|
||||
"sms:?goto=fail&fail=goto"
|
||||
};
|
||||
|
||||
for (String uri : invalidSchemaUris) {
|
||||
try {
|
||||
new Rfc5724Uri(uri);
|
||||
throw new AssertionFailedError("URISyntaxException should be thrown");
|
||||
} catch (URISyntaxException e) {
|
||||
// success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testGetSchema() throws Exception {
|
||||
final String[][] uriTestPairs = {
|
||||
{"sms:+15555555555", "sms"},
|
||||
{"sMs:+15555555555", "sMs"},
|
||||
{"smsto:+15555555555?", "smsto"},
|
||||
{"mms:+15555555555?a=b", "mms"},
|
||||
{"mmsto:+15555555555?a=b&c=d", "mmsto"}
|
||||
};
|
||||
|
||||
for (String[] uriTestPair : uriTestPairs) {
|
||||
final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]);
|
||||
assertTrue(testUri.getSchema().equals(uriTestPair[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testGetPath() throws Exception {
|
||||
final String[][] uriTestPairs = {
|
||||
{"sms:+15555555555", "+15555555555"},
|
||||
{"sms:%2B555555555", "%2B555555555"},
|
||||
{"smsto:+15555555555?", "+15555555555"},
|
||||
{"mms:+15555555555?a=b", "+15555555555"},
|
||||
{"mmsto:+15555555555?a=b&c=d", "+15555555555"},
|
||||
{"sms:+15555555555,+14444444444", "+15555555555,+14444444444"},
|
||||
{"sms:+15555555555,+14444444444?", "+15555555555,+14444444444"},
|
||||
{"sms:+15555555555,+14444444444?a=b", "+15555555555,+14444444444"},
|
||||
{"sms:+15555555555,+14444444444?a=b&c=d", "+15555555555,+14444444444"}
|
||||
};
|
||||
|
||||
for (String[] uriTestPair : uriTestPairs) {
|
||||
final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]);
|
||||
assertTrue(testUri.getPath().equals(uriTestPair[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test public void testGetQueryParams() throws Exception {
|
||||
final String[][] uriTestPairs = {
|
||||
{"sms:+15555555555", "a", null},
|
||||
{"mms:+15555555555?b=", "a", null},
|
||||
{"mmsto:+15555555555?a=", "a", ""},
|
||||
{"sms:+15555555555?a=b", "a", "b"},
|
||||
{"sms:+15555555555?a=b&c=d", "a", "b"},
|
||||
{"sms:+15555555555?a=b&c=d", "b", null},
|
||||
{"sms:+15555555555?a=b&c=d", "c", "d"},
|
||||
{"sms:+15555555555?a=b&c=d", "d", null}
|
||||
};
|
||||
|
||||
for (String[] uriTestPair : uriTestPairs) {
|
||||
final Rfc5724Uri testUri = new Rfc5724Uri(uriTestPair[0]);
|
||||
final String paramResult = testUri.getQueryParams().get(uriTestPair[1]);
|
||||
|
||||
if (paramResult == null) assertTrue(uriTestPair[2] == null);
|
||||
else assertTrue(paramResult.equals(uriTestPair[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SearchUtilTest {
|
||||
|
||||
private static final Locale LOCALE = Locale.ENGLISH;
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_singleHighlightToken() {
|
||||
String text = "abc";
|
||||
String highlight = "a";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 1)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_singleHighlightTokenWithNewLines() {
|
||||
String text = "123\n\n\nabc";
|
||||
String highlight = "a";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(6, 7)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_multipleHighlightTokens() {
|
||||
String text = "a bc";
|
||||
String highlight = "a b";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 1), new Pair<>(2, 3)), result);
|
||||
|
||||
|
||||
text = "abc def";
|
||||
highlight = "ab de";
|
||||
result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(0, 2), new Pair<>(4, 6)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_onlyHighlightPrefixes() {
|
||||
String text = "abc";
|
||||
String highlight = "b";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
|
||||
text = "abc";
|
||||
highlight = "c";
|
||||
result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getHighlightRanges_resultNotInFirstToken() {
|
||||
String text = "abc def ghi";
|
||||
String highlight = "gh";
|
||||
List<Pair<Integer, Integer>> result = SearchUtil.getHighlightRanges(LOCALE, text, highlight);
|
||||
|
||||
assertEquals(Arrays.asList(new Pair<>(8, 10)), result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
public class ShortCodeUtilTest {
|
||||
|
||||
@Test
|
||||
public void testShortCodes() throws Exception {
|
||||
assertTrue(ShortCodeUtil.isShortCode("+14152222222", "40404"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+14152222222", "431"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "4157778888"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "+14157778888"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "415-777-8888"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "(415) 777-8888"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "8882222"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+14152222222", "888-2222"));
|
||||
|
||||
assertTrue(ShortCodeUtil.isShortCode("+491723742522", "670"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+491723742522", "115"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+491723742522", "089-12345678"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+491723742522", "089/12345678"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+491723742522", "12345678"));
|
||||
|
||||
assertTrue(ShortCodeUtil.isShortCode("+298123456", "4040"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+298123456", "6701"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+298123456", "433"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+298123456", "123456"));
|
||||
|
||||
assertTrue(ShortCodeUtil.isShortCode("+61414915066", "19808948"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+61414915066", "119808948"));
|
||||
|
||||
assertTrue(ShortCodeUtil.isShortCode("+79166503388", "8080"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+79166503388", "6701"));
|
||||
assertTrue(ShortCodeUtil.isShortCode("+79166503388", "431"));
|
||||
assertFalse(ShortCodeUtil.isShortCode("+79166503388", "111-22-33"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class UsernameUtilTest {
|
||||
|
||||
@Test
|
||||
public void checkUsername_tooShort() {
|
||||
assertEquals(UsernameUtil.InvalidReason.TOO_SHORT, UsernameUtil.checkUsername(null).get());
|
||||
assertEquals(UsernameUtil.InvalidReason.TOO_SHORT, UsernameUtil.checkUsername("").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.TOO_SHORT, UsernameUtil.checkUsername("abc").get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkUsername_tooLong() {
|
||||
assertEquals(UsernameUtil.InvalidReason.TOO_LONG, UsernameUtil.checkUsername("abcdefghijklmnopqrstuvwxyz1").get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkUsername_startsWithNumber() {
|
||||
assertEquals(UsernameUtil.InvalidReason.STARTS_WITH_NUMBER, UsernameUtil.checkUsername("0abcdefg").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.STARTS_WITH_NUMBER, UsernameUtil.checkUsername("9abcdefg").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.STARTS_WITH_NUMBER, UsernameUtil.checkUsername("8675309").get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkUsername_invalidCharacters() {
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("$abcd").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername(" abcd").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("ab cde").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("%%%%%").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("-----").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("asĸ_me").get());
|
||||
assertEquals(UsernameUtil.InvalidReason.INVALID_CHARACTERS, UsernameUtil.checkUsername("+18675309").get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkUsername_validUsernames() {
|
||||
assertFalse(UsernameUtil.checkUsername("abcd").isPresent());
|
||||
assertFalse(UsernameUtil.checkUsername("abcdefghijklmnopqrstuvwxyz").isPresent());
|
||||
assertFalse(UsernameUtil.checkUsername("ABCDEFGHIJKLMNOPQRSTUVWXYZ").isPresent());
|
||||
assertFalse(UsernameUtil.checkUsername("web_head").isPresent());
|
||||
assertFalse(UsernameUtil.checkUsername("Spider_Fan_1991").isPresent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class UtilTest {
|
||||
|
||||
@Test
|
||||
public void chunk_oneChunk() {
|
||||
List<String> input = Arrays.asList("A", "B", "C");
|
||||
|
||||
List<List<String>> output = Util.chunk(input, 3);
|
||||
assertEquals(1, output.size());
|
||||
assertEquals(input, output.get(0));
|
||||
|
||||
output = Util.chunk(input, 4);
|
||||
assertEquals(1, output.size());
|
||||
assertEquals(input, output.get(0));
|
||||
|
||||
output = Util.chunk(input, 100);
|
||||
assertEquals(1, output.size());
|
||||
assertEquals(input, output.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chunk_multipleChunks() {
|
||||
List<String> input = Arrays.asList("A", "B", "C", "D", "E");
|
||||
|
||||
List<List<String>> output = Util.chunk(input, 4);
|
||||
assertEquals(2, output.size());
|
||||
assertEquals(Arrays.asList("A", "B", "C", "D"), output.get(0));
|
||||
assertEquals(Arrays.asList("E"), output.get(1));
|
||||
|
||||
output = Util.chunk(input, 2);
|
||||
assertEquals(3, output.size());
|
||||
assertEquals(Arrays.asList("A", "B"), output.get(0));
|
||||
assertEquals(Arrays.asList("C", "D"), output.get(1));
|
||||
assertEquals(Arrays.asList("E"), output.get(2));
|
||||
|
||||
output = Util.chunk(input, 1);
|
||||
assertEquals(5, output.size());
|
||||
assertEquals(Arrays.asList("A"), output.get(0));
|
||||
assertEquals(Arrays.asList("B"), output.get(1));
|
||||
assertEquals(Arrays.asList("C"), output.get(2));
|
||||
assertEquals(Arrays.asList("D"), output.get(3));
|
||||
assertEquals(Arrays.asList("E"), output.get(4));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.thoughtcrime.securesms.util.dynamiclanguage;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public final class LanguageStringTest {
|
||||
|
||||
private final Locale expected;
|
||||
private final String input;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][]{
|
||||
|
||||
/* Language */
|
||||
{ new Locale("en"), "en" },
|
||||
{ new Locale("de"), "de" },
|
||||
{ new Locale("fr"), "FR" },
|
||||
|
||||
/* Language and region */
|
||||
{ new Locale("en", "US"), "en_US" },
|
||||
{ new Locale("es", "US"), "es_US" },
|
||||
{ new Locale("es", "MX"), "es_MX" },
|
||||
{ new Locale("es", "MX"), "es_mx" },
|
||||
{ new Locale("de", "DE"), "de_DE" },
|
||||
|
||||
/* Not parsable input */
|
||||
{ null, null },
|
||||
{ null, "" },
|
||||
{ null, "zz" },
|
||||
{ null, "zz_ZZ" },
|
||||
{ null, "fr_ZZ" },
|
||||
{ null, "zz_FR" },
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public LanguageStringTest(Locale expected, String input) {
|
||||
this.expected = expected;
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parse() {
|
||||
assertEquals(expected, LanguageString.parseLocale(input));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.thoughtcrime.securesms.util.dynamiclanguage;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE, application = Application.class)
|
||||
public final class LocaleParserTest {
|
||||
|
||||
@Test
|
||||
public void findBestMatchingLocaleForLanguage_all_build_config_languages_can_be_resolved() {
|
||||
for (String lang : buildConfigLanguages()) {
|
||||
Locale locale = LocaleParser.findBestMatchingLocaleForLanguage(lang);
|
||||
assertEquals(lang, locale.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "fr")
|
||||
public void findBestMatchingLocaleForLanguage_a_non_build_config_language_defaults_to_device_value_which_is_supported_directly() {
|
||||
String unsupportedLanguage = getUnsupportedLanguage();
|
||||
assertEquals(Locale.FRENCH, LocaleParser.findBestMatchingLocaleForLanguage(unsupportedLanguage));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "en-rCA")
|
||||
public void findBestMatchingLocaleForLanguage_a_non_build_config_language_defaults_to_device_value_which_is_not_supported_directly() {
|
||||
String unsupportedLanguage = getUnsupportedLanguage();
|
||||
assertEquals(Locale.CANADA, LocaleParser.findBestMatchingLocaleForLanguage(unsupportedLanguage));
|
||||
}
|
||||
|
||||
private static String getUnsupportedLanguage() {
|
||||
String unsupportedLanguage = "af";
|
||||
assertFalse("Language should be an unsupported one", buildConfigLanguages().contains(unsupportedLanguage));
|
||||
return unsupportedLanguage;
|
||||
}
|
||||
|
||||
private static List<String> buildConfigLanguages() {
|
||||
return Arrays.asList(BuildConfig.LANGUAGES);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user