Recipient Id cache.
This commit is contained in:
parent
a9ecdbdfec
commit
a510bc74e6
6 changed files with 493 additions and 25 deletions
|
@ -128,11 +128,11 @@ public final class LiveRecipient {
|
||||||
Log.w(TAG, "[Resolve][MAIN] " + getId(), new Throwable());
|
Log.w(TAG, "[Resolve][MAIN] " + getId(), new Throwable());
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient updated = fetchRecipientFromDisk(getId());
|
Recipient updated = fetchAndCacheRecipientFromDisk(getId());
|
||||||
List<Recipient> participants = Stream.of(updated.getParticipants())
|
List<Recipient> participants = Stream.of(updated.getParticipants())
|
||||||
.filter(Recipient::isResolving)
|
.filter(Recipient::isResolving)
|
||||||
.map(Recipient::getId)
|
.map(Recipient::getId)
|
||||||
.map(this::fetchRecipientFromDisk)
|
.map(this::fetchAndCacheRecipientFromDisk)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (Recipient participant : participants) {
|
for (Recipient participant : participants) {
|
||||||
|
@ -155,10 +155,10 @@ public final class LiveRecipient {
|
||||||
Log.w(TAG, "[Refresh][MAIN] " + getId(), new Throwable());
|
Log.w(TAG, "[Refresh][MAIN] " + getId(), new Throwable());
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient recipient = fetchRecipientFromDisk(getId());
|
Recipient recipient = fetchAndCacheRecipientFromDisk(getId());
|
||||||
List<Recipient> participants = Stream.of(recipient.getParticipants())
|
List<Recipient> participants = Stream.of(recipient.getParticipants())
|
||||||
.map(Recipient::getId)
|
.map(Recipient::getId)
|
||||||
.map(this::fetchRecipientFromDisk)
|
.map(this::fetchAndCacheRecipientFromDisk)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
for (Recipient participant : participants) {
|
for (Recipient participant : participants) {
|
||||||
|
@ -172,12 +172,14 @@ public final class LiveRecipient {
|
||||||
return liveData;
|
return liveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull Recipient fetchRecipientFromDisk(RecipientId id) {
|
private @NonNull Recipient fetchAndCacheRecipientFromDisk(@NonNull RecipientId id) {
|
||||||
RecipientSettings settings = recipientDatabase.getRecipientSettings(id);
|
RecipientSettings settings = recipientDatabase.getRecipientSettings(id);
|
||||||
RecipientDetails details = settings.getGroupId() != null ? getGroupRecipientDetails(settings)
|
RecipientDetails details = settings.getGroupId() != null ? getGroupRecipientDetails(settings)
|
||||||
: getIndividualRecipientDetails(settings);
|
: getIndividualRecipientDetails(settings);
|
||||||
|
|
||||||
return new Recipient(id, details);
|
Recipient recipient = new Recipient(id, details);
|
||||||
|
RecipientIdCache.INSTANCE.put(recipient);
|
||||||
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull RecipientDetails getIndividualRecipientDetails(RecipientSettings settings) {
|
private @NonNull RecipientDetails getIndividualRecipientDetails(RecipientSettings settings) {
|
||||||
|
@ -194,7 +196,7 @@ public final class LiveRecipient {
|
||||||
|
|
||||||
if (groupRecord.isPresent()) {
|
if (groupRecord.isPresent()) {
|
||||||
String title = groupRecord.get().getTitle();
|
String title = groupRecord.get().getTitle();
|
||||||
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
|
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchAndCacheRecipientFromDisk).toList();
|
||||||
Optional<Long> avatarId = Optional.absent();
|
Optional<Long> avatarId = Optional.absent();
|
||||||
|
|
||||||
if (settings.getGroupId() != null && settings.getGroupId().isPush() && title == null) {
|
if (settings.getGroupId() != null && settings.getGroupId().isPush() && title == null) {
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
package org.thoughtcrime.securesms.recipients;
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.AnyThread;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
import org.thoughtcrime.securesms.util.DelimiterUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
||||||
|
@ -39,6 +46,21 @@ public class RecipientId implements Parcelable, Comparable<RecipientId> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always supply both {@param uuid} and {@param e164} if you have both.
|
||||||
|
*/
|
||||||
|
@AnyThread
|
||||||
|
@SuppressLint("WrongThread")
|
||||||
|
public static @NonNull RecipientId from(@Nullable UUID uuid, @Nullable String e164) {
|
||||||
|
RecipientId recipientId = RecipientIdCache.INSTANCE.get(uuid, e164);
|
||||||
|
|
||||||
|
if (recipientId == null) {
|
||||||
|
recipientId = Recipient.externalPush(ApplicationDependencies.getApplication(), uuid, e164).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientId;
|
||||||
|
}
|
||||||
|
|
||||||
private RecipientId(long id) {
|
private RecipientId(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread safe cache that allows faster looking up of {@link RecipientId}s without hitting the database.
|
||||||
|
*/
|
||||||
|
final class RecipientIdCache {
|
||||||
|
|
||||||
|
private static final int INSTANCE_CACHE_LIMIT = 1000;
|
||||||
|
|
||||||
|
static final RecipientIdCache INSTANCE = new RecipientIdCache(INSTANCE_CACHE_LIMIT);
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(RecipientIdCache.class);
|
||||||
|
|
||||||
|
private final Map<Object, RecipientId> ids;
|
||||||
|
|
||||||
|
RecipientIdCache(int limit) {
|
||||||
|
ids = new LinkedHashMap<Object, RecipientId>(128, 0.75f, true) {
|
||||||
|
@Override
|
||||||
|
protected boolean removeEldestEntry(Entry<Object, RecipientId> eldest) {
|
||||||
|
return size() > limit;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void put(@NonNull Recipient recipient) {
|
||||||
|
RecipientId recipientId = recipient.getId();
|
||||||
|
Optional<String> e164 = recipient.getE164();
|
||||||
|
Optional<UUID> uuid = recipient.getUuid();
|
||||||
|
|
||||||
|
if (e164.isPresent()) {
|
||||||
|
ids.put(e164.get(), recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid.isPresent()) {
|
||||||
|
ids.put(uuid.get(), recipientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized @Nullable RecipientId get(@Nullable UUID uuid, @Nullable String e164) {
|
||||||
|
if (uuid != null && e164 != null) {
|
||||||
|
RecipientId recipientIdByUuid = ids.get(uuid);
|
||||||
|
if (recipientIdByUuid == null) return null;
|
||||||
|
|
||||||
|
RecipientId recipientIdByE164 = ids.get(e164);
|
||||||
|
if (recipientIdByE164 == null) return null;
|
||||||
|
|
||||||
|
if (recipientIdByUuid.equals(recipientIdByE164)) {
|
||||||
|
return recipientIdByUuid;
|
||||||
|
} else {
|
||||||
|
ids.remove(uuid);
|
||||||
|
ids.remove(e164);
|
||||||
|
Log.w(TAG, "Seen invalid RecipientIdCacheState");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (uuid != null) {
|
||||||
|
return ids.get(uuid);
|
||||||
|
} else if (e164 != null) {
|
||||||
|
return ids.get(e164);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,12 +17,12 @@ import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupContext;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -131,9 +131,9 @@ public final class GroupUtil {
|
||||||
|
|
||||||
@NonNull private final Context context;
|
@NonNull private final Context context;
|
||||||
@Nullable private final GroupContext groupContext;
|
@Nullable private final GroupContext groupContext;
|
||||||
@Nullable private final List<Recipient> members;
|
@Nullable private final List<RecipientId> members;
|
||||||
|
|
||||||
public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
|
GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.groupContext = groupContext;
|
this.groupContext = groupContext;
|
||||||
|
|
||||||
|
@ -143,9 +143,9 @@ public final class GroupUtil {
|
||||||
this.members = new LinkedList<>();
|
this.members = new LinkedList<>();
|
||||||
|
|
||||||
for (GroupContext.Member member : groupContext.getMembersList()) {
|
for (GroupContext.Member member : groupContext.getMembersList()) {
|
||||||
Recipient recipient = Recipient.externalPush(context, new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164()));
|
RecipientId recipientId = RecipientId.from(UuidUtil.parseOrNull(member.getUuid()), member.getE164());
|
||||||
if (!recipient.isLocalNumber()) {
|
if (!recipientId.equals(Recipient.self().getId())) {
|
||||||
this.members.add(recipient);
|
this.members.add(recipientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,31 +178,31 @@ public final class GroupUtil {
|
||||||
|
|
||||||
public void addObserver(RecipientForeverObserver listener) {
|
public void addObserver(RecipientForeverObserver listener) {
|
||||||
if (this.members != null) {
|
if (this.members != null) {
|
||||||
for (Recipient member : this.members) {
|
for (RecipientId member : this.members) {
|
||||||
member.live().observeForever(listener);
|
Recipient.live(member).observeForever(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeObserver(RecipientForeverObserver listener) {
|
public void removeObserver(RecipientForeverObserver listener) {
|
||||||
if (this.members != null) {
|
if (this.members != null) {
|
||||||
for (Recipient member : this.members) {
|
for (RecipientId member : this.members) {
|
||||||
member.live().removeForeverObserver(listener);
|
Recipient.live(member).removeForeverObserver(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toString(List<Recipient> recipients) {
|
private String toString(List<RecipientId> recipients) {
|
||||||
String result = "";
|
StringBuilder result = new StringBuilder();
|
||||||
|
|
||||||
for (int i = 0; i < recipients.size(); i++) {
|
for (int i = 0; i < recipients.size(); i++) {
|
||||||
result += recipients.get(i).toShortString(context);
|
result.append(Recipient.live(recipients.get(i)).get().toShortString(context));
|
||||||
|
|
||||||
if (i != recipients.size() -1 )
|
if (i != recipients.size() -1 )
|
||||||
result += ", ";
|
result.append(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
import org.thoughtcrime.securesms.testutil.LogRecorder;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public final class RecipientIdCacheTest {
|
||||||
|
|
||||||
|
private static final int TEST_CACHE_LIMIT = 5;
|
||||||
|
|
||||||
|
private RecipientIdCache recipientIdCache;
|
||||||
|
private LogRecorder logRecorder;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
recipientIdCache = new RecipientIdCache(TEST_CACHE_LIMIT);
|
||||||
|
logRecorder = new LogRecorder();
|
||||||
|
Log.initialize(logRecorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void empty_access_by_nulls() {
|
||||||
|
RecipientId recipientId = recipientIdCache.get(null, null);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void empty_access_by_uuid() {
|
||||||
|
RecipientId recipientId = recipientIdCache.get(UUID.randomUUID(), null);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void empty_access_by_e164() {
|
||||||
|
RecipientId recipientId = recipientIdCache.get(null, "+155512345");
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_hit_by_uuid() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, null);
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_miss_by_uuid() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
UUID uuid2 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid2, null);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_hit_by_uuid_e164_not_supplied_on_get() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, "+15551234567"));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, null);
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_miss_by_uuid_e164_not_supplied_on_put() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, "+15551234567");
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_hit_by_e164() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(null, e164);
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_miss_by_e164() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
String e164a = "+1555123456";
|
||||||
|
String e164b = "+1555123457";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164a));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(null, e164b);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_hit_by_e164_uuid_not_supplied_on_get() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, "+15551234567"));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(null, "+15551234567");
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_miss_by_e164_uuid_not_supplied_on_put() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, e164);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cache_hit_by_both() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, e164));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, e164);
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void full_recipient_id_learned_by_two_puts() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid1, e164);
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void if_cache_state_disagrees_returns_null() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
RecipientId recipientId2 = recipientId();
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164));
|
||||||
|
recipientIdCache.put(recipient(recipientId2, uuid, null));
|
||||||
|
|
||||||
|
RecipientId recipientId = recipientIdCache.get(uuid, e164);
|
||||||
|
|
||||||
|
assertNull(recipientId);
|
||||||
|
|
||||||
|
assertEquals(1, logRecorder.getWarnings().size());
|
||||||
|
assertEquals("Seen invalid RecipientIdCacheState", logRecorder.getWarnings().get(0).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void after_invalid_cache_hit_entries_are_cleared_up() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
RecipientId recipientId2 = recipientId();
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
String e164 = "+1555123456";
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, null, e164));
|
||||||
|
recipientIdCache.put(recipient(recipientId2, uuid, null));
|
||||||
|
|
||||||
|
recipientIdCache.get(uuid, e164);
|
||||||
|
|
||||||
|
assertNull(recipientIdCache.get(uuid, null));
|
||||||
|
assertNull(recipientIdCache.get(null, e164));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multiple_entries() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
RecipientId recipientId2 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
UUID uuid2 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
recipientIdCache.put(recipient(recipientId2, uuid2, null));
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientIdCache.get(uuid1, null));
|
||||||
|
assertEquals(recipientId2, recipientIdCache.get(uuid2, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void drops_oldest_when_reaches_cache_limit() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
|
||||||
|
for (int i = 0; i < TEST_CACHE_LIMIT; i++) {
|
||||||
|
recipientIdCache.put(recipient(recipientId(), UUID.randomUUID(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNull(recipientIdCache.get(uuid1, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void remains_in_cache_when_used_before_reaching_cache_limit() {
|
||||||
|
RecipientId recipientId1 = recipientId();
|
||||||
|
UUID uuid1 = UUID.randomUUID();
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId1, uuid1, null));
|
||||||
|
|
||||||
|
for (int i = 0; i < TEST_CACHE_LIMIT - 1; i++) {
|
||||||
|
recipientIdCache.put(recipient(recipientId(), UUID.randomUUID(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientIdCache.get(uuid1, null));
|
||||||
|
|
||||||
|
recipientIdCache.put(recipient(recipientId(), UUID.randomUUID(), null));
|
||||||
|
|
||||||
|
assertEquals(recipientId1, recipientIdCache.get(uuid1, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull RecipientId recipientId() {
|
||||||
|
return mock(RecipientId.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull Recipient recipient(RecipientId recipientId, @Nullable UUID uuid, @Nullable String e164) {
|
||||||
|
Recipient mock = mock(Recipient.class);
|
||||||
|
|
||||||
|
when(mock.getId()).thenReturn(recipientId);
|
||||||
|
when(mock.getUuid()).thenReturn(Optional.fromNullable(uuid));
|
||||||
|
when(mock.getE164()).thenReturn(Optional.fromNullable(e164));
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.thoughtcrime.securesms.testutil;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class LogRecorder extends Log.Logger {
|
||||||
|
|
||||||
|
private final List<Entry> verbose = new ArrayList<>();
|
||||||
|
private final List<Entry> debug = new ArrayList<>();
|
||||||
|
private final List<Entry> information = new ArrayList<>();
|
||||||
|
private final List<Entry> warnings = new ArrayList<>();
|
||||||
|
private final List<Entry> errors = new ArrayList<>();
|
||||||
|
private final List<Entry> wtf = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void v(String tag, String message, Throwable t) {
|
||||||
|
verbose.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void d(String tag, String message, Throwable t) {
|
||||||
|
debug.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void i(String tag, String message, Throwable t) {
|
||||||
|
information.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void w(String tag, String message, Throwable t) {
|
||||||
|
warnings.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void e(String tag, String message, Throwable t) {
|
||||||
|
errors.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void wtf(String tag, String message, Throwable t) {
|
||||||
|
wtf.add(new Entry(tag, message, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blockUntilAllWritesFinished() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getVerbose() {
|
||||||
|
return verbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getDebug() {
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getInformation() {
|
||||||
|
return information;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getWarnings() {
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Entry> getWtf() {
|
||||||
|
return wtf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Entry {
|
||||||
|
private final String tag;
|
||||||
|
private final String message;
|
||||||
|
private final Throwable throwable;
|
||||||
|
|
||||||
|
private Entry(String tag, String message, Throwable throwable) {
|
||||||
|
this.tag = tag;
|
||||||
|
this.message = message;
|
||||||
|
this.throwable = throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTag() {
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Throwable getThrowable() {
|
||||||
|
return throwable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue