Move from AssertJ to AssertK.

Resolves #13841
This commit is contained in:
Jameson Williams
2024-12-12 20:35:57 -06:00
committed by Greyson Parrelli
parent 56d53f0b6a
commit a96c8867ae
13 changed files with 431 additions and 365 deletions

View File

@@ -1,110 +0,0 @@
package org.thoughtcrime.securesms.groups.v2;
import androidx.annotation.NonNull;
import org.junit.Test;
import org.signal.core.util.Base64;
import org.signal.libsignal.zkgroup.InvalidInputException;
import org.signal.storageservice.protos.groups.GroupInviteLink;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import okio.ByteString;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertNull;
public final class GroupInviteLinkUrl_InvalidGroupLinkException_Test {
@Test
public void empty_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
assertNull(GroupInviteLinkUrl.fromUri(""));
}
@Test
public void not_a_url_string() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
assertNull(GroupInviteLinkUrl.fromUri("abc"));
}
@Test
public void wrong_host() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
assertNull(GroupInviteLinkUrl.fromUri("https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"));
}
@Test
public void wrong_scheme() throws GroupInviteLinkUrl.InvalidGroupLinkException, GroupInviteLinkUrl.UnknownGroupLinkVersionException {
assertNull(GroupInviteLinkUrl.fromUri("http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"));
}
@Test
public void has_path() {
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasMessage("No path was expected in uri");
}
@Test
public void missing_ref() {
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/"))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasMessage("No reference was in the uri");
}
@Test
public void empty_ref() {
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#"))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasMessage("No reference was in the uri");
}
@Test
public void bad_base64() {
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY"))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasCauseExactlyInstanceOf(IOException.class);
}
@Test
public void bad_protobuf() {
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri("https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY"))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasCauseExactlyInstanceOf(IllegalStateException.class);
}
@Test
public void version_999_url() {
String url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3";
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url))
.isInstanceOf(GroupInviteLinkUrl.UnknownGroupLinkVersionException.class)
.hasMessage("Url contains no known group link content");
}
@Test
public void bad_master_key_length() {
byte[] masterKeyBytes = Util.getSecretBytes(33);
GroupLinkPassword password = GroupLinkPassword.createNew();
String encoding = createEncodedProtobuf(masterKeyBytes, password.serialize());
String url = "https://signal.group/#" + encoding;
assertThatThrownBy(() -> GroupInviteLinkUrl.fromUri(url))
.isInstanceOf(GroupInviteLinkUrl.InvalidGroupLinkException.class)
.hasCauseExactlyInstanceOf(InvalidInputException.class);
}
private static String createEncodedProtobuf(@NonNull byte[] groupMasterKey,
@NonNull byte[] passwordBytes)
{
return Base64.encodeUrlSafeWithoutPadding(new GroupInviteLink.Builder()
.v1Contents(new GroupInviteLink.GroupInviteLinkContentsV1.Builder()
.groupMasterKey(ByteString.of(groupMasterKey))
.inviteLinkPassword(ByteString.of(passwordBytes))
.build())
.build()
.encode());
}
}

View File

@@ -0,0 +1,130 @@
package org.thoughtcrime.securesms.groups.v2
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isInstanceOf
import assertk.assertions.isNull
import assertk.assertions.messageContains
import assertk.assertions.rootCause
import okio.ByteString
import org.junit.Test
import org.signal.core.util.Base64.encodeUrlSafeWithoutPadding
import org.signal.libsignal.zkgroup.InvalidInputException
import org.signal.storageservice.protos.groups.GroupInviteLink
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.InvalidGroupLinkException
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl.UnknownGroupLinkVersionException
import org.thoughtcrime.securesms.util.Util
import java.io.IOException
@Suppress("ClassName")
class GroupInviteLinkUrl_InvalidGroupLinkException_Test {
@Test
fun empty_string() {
val uri = ""
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
}
@Test
fun not_a_url_string() {
val uri = "abc"
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
}
@Test
fun wrong_host() {
val uri = "https://x.signal.org/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
}
@Test
fun wrong_scheme() {
val uri = "http://signal.group/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
assertThat(GroupInviteLinkUrl.fromUri(uri)).isNull()
}
@Test
fun has_path() {
val uri = "https://signal.group/not_expected/#CjQKIAD34MKnGrBkzDztTATwjXt-9LhLLCIG9pgzvmz-NN-AEhCbwyTuxDfP2mrluK779H7o"
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
.isInstanceOf<InvalidGroupLinkException>()
.hasMessage("No path was expected in uri")
}
@Test
fun missing_ref() {
val uri = "https://signal.group/"
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
.isInstanceOf<InvalidGroupLinkException>()
.hasMessage("No reference was in the uri")
}
@Test
fun empty_ref() {
val uri = "https://signal.group/#"
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
.isInstanceOf<InvalidGroupLinkException>()
.hasMessage("No reference was in the uri")
}
@Test
fun bad_base64() {
val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCX;mwVi31USVY"
assertFailure { GroupInviteLinkUrl.fromUri(uri) }
.isInstanceOf<InvalidGroupLinkException>()
.rootCause()
.isInstanceOf<IOException>()
}
@Test
fun bad_protobuf() {
val uri = "https://signal.group/#CAESNAogpQEzURH6BON1bCS264cmTi37Yi6HTOReXZUEHdsBIgSEPCLfiL7k4wCXmwVi31USVY"
assertFailure {
GroupInviteLinkUrl.fromUri(uri)
}.isInstanceOf<InvalidGroupLinkException>()
.rootCause()
.isInstanceOf<IllegalStateException>()
}
@Test
fun version_999_url() {
val url = "https://signal.group/#uj4zCiDMSxlNUvF4bQ3z3fYzGyZTFbJ1xEqWbPE3uZSD8bjOrxIP8NxV-0GUz3jpxMLR1rN3"
assertFailure { GroupInviteLinkUrl.fromUri(url) }
.isInstanceOf<UnknownGroupLinkVersionException>()
.messageContains("Url contains no known group link content")
}
@Test
fun bad_master_key_length() {
val masterKeyBytes = Util.getSecretBytes(33)
val password = GroupLinkPassword.createNew()
val encoding = createEncodedProtobuf(masterKeyBytes, password.serialize())
val url = "https://signal.group/#$encoding"
assertFailure { GroupInviteLinkUrl.fromUri(url) }
.isInstanceOf<InvalidGroupLinkException>()
.rootCause()
.isInstanceOf<InvalidInputException>()
}
companion object {
private fun createEncodedProtobuf(
groupMasterKey: ByteArray,
passwordBytes: ByteArray
): String {
return encodeUrlSafeWithoutPadding(
GroupInviteLink.Builder()
.v1Contents(
GroupInviteLink.GroupInviteLinkContentsV1.Builder()
.groupMasterKey(ByteString.of(*groupMasterKey))
.inviteLinkPassword(ByteString.of(*passwordBytes))
.build()
)
.build()
.encode()
)
}
}
}

View File

@@ -1,122 +0,0 @@
package org.thoughtcrime.securesms.payments;
import org.junit.Before;
import org.junit.Test;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.IdentityKeyPair;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.signalservice.internal.push.PaymentAddress;
import okio.ByteString;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertArrayEquals;
import static org.whispersystems.signalservice.test.LibSignalLibraryUtil.assumeLibSignalSupportedOnOS;
public final class MobileCoinPublicAddressProfileUtilTest {
@Before
public void ensureNativeSupported() {
assumeLibSignalSupportedOnOS();
}
@Test
public void can_verify_an_address() throws PaymentsAddressException {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
byte[] address = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
byte[] paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.getPublicKey());
assertArrayEquals(address, paymentsAddress);
}
@Test
public void can_not_verify_an_address_with_the_wrong_key() {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
IdentityKey wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().getPublicKey();
byte[] address = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey))
.isInstanceOf(PaymentsAddressException.class)
.hasMessage("Invalid MobileCoin address signature on payments address proto");
}
@Test
public void can_not_verify_a_tampered_signature() {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
byte[] address = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
byte[] signature = signedPaymentAddress.mobileCoinAddress.signature.toByteArray();
signature[0] = (byte) (signature[0] ^ 0x01);
PaymentAddress tamperedSignature = signedPaymentAddress.newBuilder()
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
.newBuilder()
.signature(ByteString.of(signature))
.build())
.build();
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.getPublicKey()))
.isInstanceOf(PaymentsAddressException.class)
.hasMessage("Invalid MobileCoin address signature on payments address proto");
}
@Test
public void can_not_verify_a_tampered_address() {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
byte[] addressBytes = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair);
byte[] address = signedPaymentAddress.mobileCoinAddress.address.toByteArray();
address[0] = (byte) (address[0] ^ 0x01);
PaymentAddress tamperedAddress = signedPaymentAddress.newBuilder()
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
.newBuilder()
.address(ByteString.of(address))
.build())
.build();
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.getPublicKey()))
.isInstanceOf(PaymentsAddressException.class)
.hasMessage("Invalid MobileCoin address signature on payments address proto");
}
@Test
public void can_not_verify_a_missing_signature() {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
byte[] address = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
PaymentAddress removedSignature = signedPaymentAddress.newBuilder()
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
.newBuilder()
.signature(null)
.build())
.build();
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.getPublicKey()))
.isInstanceOf(PaymentsAddressException.class)
.hasMessage("Invalid MobileCoin address signature on payments address proto");
}
@Test
public void can_not_verify_a_missing_address() {
IdentityKeyPair identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair();
byte[] address = Util.getSecretBytes(100);
PaymentAddress signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair);
PaymentAddress removedAddress = signedPaymentAddress.newBuilder()
.mobileCoinAddress(signedPaymentAddress.mobileCoinAddress
.newBuilder()
.address(null)
.build())
.build();
assertThatThrownBy(() -> MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.getPublicKey()))
.isInstanceOf(PaymentsAddressException.class)
.hasMessage("Invalid MobileCoin address signature on payments address proto");
}
}

View File

@@ -0,0 +1,129 @@
package org.thoughtcrime.securesms.payments
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import okio.ByteString
import org.junit.Before
import org.junit.Test
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.test.LibSignalLibraryUtil
class MobileCoinPublicAddressProfileUtilTest {
@Before
fun ensureNativeSupported() {
LibSignalLibraryUtil.assumeLibSignalSupportedOnOS()
}
@Test
fun can_verify_an_address() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val address = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
val paymentsAddress = MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, identityKeyPair.publicKey)
assertThat(paymentsAddress).isEqualTo(address)
}
@Test
fun can_not_verify_an_address_with_the_wrong_key() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val wrongPublicKey = IdentityKeyUtil.generateIdentityKeyPair().publicKey
val address = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(signedPaymentAddress, wrongPublicKey) }
.isInstanceOf<PaymentsAddressException>()
.hasMessage("Invalid MobileCoin address signature on payments address proto")
}
@Test
fun can_not_verify_a_tampered_signature() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val address = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!!
val signature = mobileCoinAddress.signature!!.toByteArray()
signature[0] = (signature[0].toInt() xor 0x01).toByte()
val tamperedSignature = signedPaymentAddress.newBuilder()
.mobileCoinAddress(
mobileCoinAddress
.newBuilder()
.signature(ByteString.of(*signature))
.build()
)
.build()
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedSignature, identityKeyPair.publicKey) }
.isInstanceOf<PaymentsAddressException>()
.hasMessage("Invalid MobileCoin address signature on payments address proto")
}
@Test
fun can_not_verify_a_tampered_address() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val addressBytes = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(addressBytes, identityKeyPair)
val mobileCoinAddress = signedPaymentAddress.mobileCoinAddress!!
val address = mobileCoinAddress.address!!.toByteArray()
address[0] = (address[0].toInt() xor 0x01).toByte()
val tamperedAddress = signedPaymentAddress.newBuilder()
.mobileCoinAddress(
mobileCoinAddress
.newBuilder()
.address(ByteString.of(*address))
.build()
)
.build()
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(tamperedAddress, identityKeyPair.publicKey) }
.isInstanceOf<PaymentsAddressException>()
.hasMessage("Invalid MobileCoin address signature on payments address proto")
}
@Test
fun can_not_verify_a_missing_signature() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val address = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
val removedSignature = signedPaymentAddress.newBuilder()
.mobileCoinAddress(
signedPaymentAddress.mobileCoinAddress!!
.newBuilder()
.signature(null)
.build()
)
.build()
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedSignature, identityKeyPair.publicKey) }
.isInstanceOf<PaymentsAddressException>()
.hasMessage("Invalid MobileCoin address signature on payments address proto")
}
@Test
fun can_not_verify_a_missing_address() {
val identityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
val address = Util.getSecretBytes(100)
val signedPaymentAddress = MobileCoinPublicAddressProfileUtil.signPaymentsAddress(address, identityKeyPair)
val removedAddress = signedPaymentAddress.newBuilder()
.mobileCoinAddress(
signedPaymentAddress.mobileCoinAddress!!
.newBuilder()
.address(null)
.build()
)
.build()
assertFailure { MobileCoinPublicAddressProfileUtil.verifyPaymentsAddress(removedAddress, identityKeyPair.publicKey) }
.isInstanceOf<PaymentsAddressException>()
.hasMessage("Invalid MobileCoin address signature on payments address proto")
}
}

View File

@@ -1,120 +0,0 @@
package org.thoughtcrime.securesms.util.livedata;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import org.assertj.core.api.Assertions;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import static org.junit.Assert.assertEquals;
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.assertNoValue;
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.observeAndGetOneValue;
public final class LiveDataUtilTest_skip {
@Rule
public TestRule rule = new LiveDataRule();
@Test
public void skip_no_value() {
MutableLiveData<String> liveData = new MutableLiveData<>();
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
assertNoValue(skipped);
}
@Test
public void skip_same_value_with_zero_skip() {
MutableLiveData<String> liveData = new MutableLiveData<>();
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
liveData.setValue("A");
assertEquals("A", observeAndGetOneValue(skipped));
}
@Test
public void skip_second_value_with_skip_one() {
MutableLiveData<String> liveData = new MutableLiveData<>();
TestObserver<String> testObserver = new TestObserver<>();
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
skipped.observeForever(testObserver);
liveData.setValue("A");
liveData.setValue("B");
skipped.removeObserver(testObserver);
Assertions.assertThat(testObserver.getValues())
.containsExactly("B");
}
@Test
public void skip_no_value_with_skip() {
MutableLiveData<String> liveData = new MutableLiveData<>();
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
liveData.setValue("A");
assertNoValue(skipped);
}
@Test
public void skip_third_and_fourth_value_with_skip_two() {
MutableLiveData<String> liveData = new MutableLiveData<>();
TestObserver<String> testObserver = new TestObserver<>();
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
skipped.observeForever(testObserver);
liveData.setValue("A");
liveData.setValue("B");
liveData.setValue("C");
liveData.setValue("D");
skipped.removeObserver(testObserver);
Assertions.assertThat(testObserver.getValues())
.containsExactly("C", "D");
}
@Test
public void skip_set_one_before_then_skip() {
MutableLiveData<String> liveData = new MutableLiveData<>();
TestObserver<String> testObserver = new TestObserver<>();
liveData.setValue("A");
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
skipped.observeForever(testObserver);
liveData.setValue("B");
liveData.setValue("C");
liveData.setValue("D");
skipped.removeObserver(testObserver);
Assertions.assertThat(testObserver.getValues())
.containsExactly("C", "D");
}
@Test
public void skip_set_two_before_then_skip() {
MutableLiveData<String> liveData = new MutableLiveData<>();
TestObserver<String> testObserver = new TestObserver<>();
liveData.setValue("A");
liveData.setValue("B");
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
skipped.observeForever(testObserver);
liveData.setValue("C");
liveData.setValue("D");
skipped.removeObserver(testObserver);
Assertions.assertThat(testObserver.getValues())
.containsExactly("D");
}
}

View File

@@ -0,0 +1,112 @@
package org.thoughtcrime.securesms.util.livedata
import androidx.lifecycle.MutableLiveData
import assertk.assertThat
import assertk.assertions.containsExactlyInAnyOrder
import assertk.assertions.isEqualTo
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
@Suppress("ClassName")
class LiveDataUtilTest_skip {
@get:Rule
val rule: TestRule = LiveDataRule()
@Test
fun skip_no_value() {
val liveData = MutableLiveData<String>()
val skipped = LiveDataUtil.skip(liveData, 0)
LiveDataTestUtil.assertNoValue(skipped)
}
@Test
fun skip_same_value_with_zero_skip() {
val liveData = MutableLiveData<String>()
val skipped = LiveDataUtil.skip(liveData, 0)
liveData.value = "A"
assertThat(LiveDataTestUtil.observeAndGetOneValue(skipped)).isEqualTo("A")
}
@Test
fun skip_second_value_with_skip_one() {
val liveData = MutableLiveData<String>()
val testObserver = TestObserver<String>()
val skipped = LiveDataUtil.skip(liveData, 1)
skipped.observeForever(testObserver)
liveData.value = "A"
liveData.value = "B"
skipped.removeObserver(testObserver)
assertThat(testObserver.values).containsExactlyInAnyOrder("B")
}
@Test
fun skip_no_value_with_skip() {
val liveData = MutableLiveData<String>()
val skipped = LiveDataUtil.skip(liveData, 1)
liveData.value = "A"
LiveDataTestUtil.assertNoValue(skipped)
}
@Test
fun skip_third_and_fourth_value_with_skip_two() {
val liveData = MutableLiveData<String>()
val testObserver = TestObserver<String>()
val skipped = LiveDataUtil.skip(liveData, 2)
skipped.observeForever(testObserver)
liveData.value = "A"
liveData.value = "B"
liveData.value = "C"
liveData.value = "D"
skipped.removeObserver(testObserver)
assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D")
}
@Test
fun skip_set_one_before_then_skip() {
val liveData = MutableLiveData<String>()
val testObserver = TestObserver<String>()
liveData.value = "A"
val skipped = LiveDataUtil.skip(liveData, 2)
skipped.observeForever(testObserver)
liveData.value = "B"
liveData.value = "C"
liveData.value = "D"
skipped.removeObserver(testObserver)
assertThat(testObserver.values).containsExactlyInAnyOrder("C", "D")
}
@Test
fun skip_set_two_before_then_skip() {
val liveData = MutableLiveData<String>()
val testObserver = TestObserver<String>()
liveData.value = "A"
liveData.value = "B"
val skipped = LiveDataUtil.skip(liveData, 2)
skipped.observeForever(testObserver)
liveData.value = "C"
liveData.value = "D"
skipped.removeObserver(testObserver)
assertThat(testObserver.values).containsExactlyInAnyOrder("D")
}
}