From 5711b8a0fa9dc7e677ec199e4468d534a0307a7a Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Fri, 15 Oct 2021 10:18:38 -0400 Subject: [PATCH] Add instrumented tests for RecipientDatabase. --- app/build.gradle | 23 +- .../database/RecipientDatabaseTest.kt | 366 ++++++++++++++++++ .../lock/PinHashing_hashPin_Test.java | 4 + .../securesms/database/MmsDatabaseTest.kt | 12 +- .../securesms/database/MmsSmsDatabaseTest.kt | 8 +- .../securesms/database/SmsDatabaseTest.kt | 14 +- .../securesms/database/TestMms.kt | 39 +- .../securesms/database/TestSms.kt | 45 ++- 8 files changed, 446 insertions(+), 65 deletions(-) create mode 100644 app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientDatabaseTest.kt diff --git a/app/build.gradle b/app/build.gradle index a3e4dffae..b330fbdab 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -162,10 +162,10 @@ android { buildConfigField "String", "CDSH_PUBLIC_KEY", "\"2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74\"" buildConfigField "String", "CDSH_CODE_HASH", "\"ec31a51880d19a5e9e0fed404740c1a3ff53a553125564b745acce475f0fded8\"" buildConfigField "String", "CDS_MRENCLAVE", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\"" - buildConfigField "KbsEnclave", "KBS_ENCLAVE", "new KbsEnclave(\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\"," + - "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\", " + - "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")"; - buildConfigField "KbsEnclave[]", "KBS_FALLBACKS", "new KbsEnclave[0]" + buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\"," + + "\"fe7c1bfae98f9b073d220366ea31163ee82f6d04bead774f71ca8e5c40847bfe\", " + + "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")"; + buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[0]" buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\"" buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY\"" buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}' @@ -197,6 +197,11 @@ android { } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments clearPackageData: 'true' + } + + testOptions { + execution 'ANDROIDX_TEST_ORCHESTRATOR' } compileOptions { @@ -342,10 +347,10 @@ android { buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\"" buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\"" buildConfigField "String", "CDS_MRENCLAVE", "\"c98e00a4e3ff977a56afefe7362a27e4961e4f19e211febfbb19b897e6b80b15\"" - buildConfigField "KbsEnclave", "KBS_ENCLAVE", "new KbsEnclave(\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\", " + - "\"16b94ac6d2b7f7b9d72928f36d798dbb35ed32e7bb14c42b4301ad0344b46f29\", " + - "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")" - buildConfigField "KbsEnclave[]", "KBS_FALLBACKS", "new KbsEnclave[0]" + buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"823a3b2c037ff0cbe305cc48928cfcc97c9ed4a8ca6d49af6f7d6981fb60a4e9\", " + + "\"16b94ac6d2b7f7b9d72928f36d798dbb35ed32e7bb14c42b4301ad0344b46f29\", " + + "\"a3baab19ef6ce6f34ab9ebb25ba722725ae44a8872dc0ff08ad6d83a9489de87\")" + buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[0]" buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\"" buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARB\"" buildConfigField "String", "MOBILE_COIN_ENVIRONMENT", "\"testnet\"" @@ -556,6 +561,8 @@ dependencies { implementation libs.rxjava3.rxandroid implementation libs.rxjava3.rxkotlin + + androidTestUtil 'androidx.test:orchestrator:1.4.0' } dependencyVerification { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientDatabaseTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientDatabaseTest.kt new file mode 100644 index 000000000..e89d54198 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/RecipientDatabaseTest.kt @@ -0,0 +1,366 @@ +package org.thoughtcrime.securesms.database + +import android.app.Application +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.recipients.RecipientId +import org.whispersystems.libsignal.util.guava.Optional +import java.lang.IllegalArgumentException +import java.util.UUID + +@RunWith(AndroidJUnit4::class) +class RecipientDatabaseTest { + + private lateinit var recipientDatabase: RecipientDatabase + + @Before + fun setup() { + recipientDatabase = DatabaseFactory.getRecipientDatabase(context) + ensureDbEmpty() + } + + // ============================================================== + // If both the ACI and E164 map to no one + // ============================================================== + + /** If all you have is an ACI, you can just store that, regardless of trust level. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciOnly_highTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, true) + + val recipient = Recipient.resolved(recipientId) + assertEquals(ACI_A, recipient.requireUuid()) + assertFalse(recipient.hasE164()) + } + + /** If all you have is an ACI, you can just store that, regardless of trust level. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciOnly_lowTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, null, false) + + val recipient = Recipient.resolved(recipientId) + assertEquals(ACI_A, recipient.requireUuid()) + assertFalse(recipient.hasE164()) + } + + /** If all you have is an E164, you can just store that, regardless of trust level. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_e164Only_highTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, true) + + val recipient = Recipient.resolved(recipientId) + assertEquals(E164_A, recipient.requireE164()) + assertFalse(recipient.hasUuid()) + } + + /** If all you have is an E164, you can just store that, regardless of trust level. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_e164Only_lowTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(null, E164_A, false) + + val recipient = Recipient.resolved(recipientId) + assertEquals(E164_A, recipient.requireE164()) + assertFalse(recipient.hasUuid()) + } + + /** With high trust, you can associate an ACI-e164 pair. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciAndE164_highTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val recipient = Recipient.resolved(recipientId) + assertEquals(ACI_A, recipient.requireUuid()) + assertEquals(E164_A, recipient.requireE164()) + } + + /** With low trust, you cannot associate an ACI-e164 pair, and therefore can only store the ACI. */ + @Test + fun getAndPossiblyMerge_aciAndE164MapToNoOne_aciAndE164_lowTrust() { + val recipientId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false) + + val recipient = Recipient.resolved(recipientId) + assertEquals(ACI_A, recipient.requireUuid()) + assertFalse(recipient.hasE164()) + } + + // ============================================================== + // If the ACI maps to an existing user, but the E164 doesn't + // ============================================================== + + /** With high trust, you can associate an e164 with an existing ACI. */ + @Test + fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_highTrust() { + val existingId: RecipientId = recipientDatabase.getOrInsertFromUuid(ACI_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + } + + /** With low trust, you cannot associate an ACI-e164 pair, and therefore cannot store the e164. */ + @Test + fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_lowTrust() { + val existingId: RecipientId = recipientDatabase.getOrInsertFromUuid(ACI_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertFalse(retrievedRecipient.hasE164()) + } + + /** Basically the ‘change number’ case. High trust lets you update the existing user. */ + @Test + fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_2_highTrust() { + val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_B, retrievedRecipient.requireE164()) + } + + /** Low trust means you can’t update the underlying data, but you also don’t need to create any new rows. */ + @Test + fun getAndPossiblyMerge_aciMapsToExistingUserButE164DoesNot_aciAndE164_2_lowTrust() { + val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, false) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + } + + // ============================================================== + // If the E164 maps to an existing user, but the ACI doesn't + // ============================================================== + + /** With high trust, you can associate an e164 with an existing ACI. */ + @Test + fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_highTrust() { + val existingId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + } + + /** With low trust, you cannot associate an ACI-e164 pair, and therefore need to create a new person with just the ACI. */ + @Test + fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_lowTrust() { + val existingId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false) + assertNotEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertFalse(retrievedRecipient.hasE164()) + + val existingRecipient = Recipient.resolved(existingId) + assertEquals(E164_A, existingRecipient.requireE164()) + assertFalse(existingRecipient.hasUuid()) + } + + /** We never change the ACI of an existing row. New ACI = new person, regardless of trust. But high trust lets us take the e164 from the current holder. */ + @Test + fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_2_highTrust() { + val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true) + assertNotEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_B, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + + val existingRecipient = Recipient.resolved(existingId) + assertEquals(ACI_A, existingRecipient.requireUuid()) + assertFalse(existingRecipient.hasE164()) + } + + /** We never change the ACI of an existing row. New ACI = new person, regardless of trust. And low trust means we can’t take the e164. */ + @Test + fun getAndPossiblyMerge_e164MapsToExistingUserButAciDoesNot_aciAndE164_2_lowTrust() { + val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, false) + assertNotEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_B, retrievedRecipient.requireUuid()) + assertFalse(retrievedRecipient.hasE164()) + + val existingRecipient = Recipient.resolved(existingId) + assertEquals(ACI_A, existingRecipient.requireUuid()) + assertEquals(E164_A, existingRecipient.requireE164()) + } + + // ============================================================== + // If both the ACI and E164 map to an existing user + // ============================================================== + + /** Regardless of trust, if your ACI and e164 match, you’re good. */ + @Test + fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_highTrust() { + val existingId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + assertEquals(existingId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + } + + /** High trust lets you merge two different users into one. You should prefer the ACI user. Not shown: merging threads, dropping e164 sessions, etc. */ + @Test + fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_merge_highTrust() { + val existingAciId: RecipientId = recipientDatabase.getOrInsertFromUuid(ACI_A) + val existingE164Id: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + assertEquals(existingAciId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + + val existingE164Recipient = Recipient.resolved(existingE164Id) + assertEquals(retrievedId, existingE164Recipient.id) + } + + /** Low trust means you can’t merge. If you’re retrieving a user from the table with this data, prefer the ACI one. */ + @Test + fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_lowTrust() { + val existingAciId: RecipientId = recipientDatabase.getOrInsertFromUuid(ACI_A) + val existingE164Id: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false) + assertEquals(existingAciId, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertFalse(retrievedRecipient.hasE164()) + + val existingE164Recipient = Recipient.resolved(existingE164Id) + assertEquals(E164_A, existingE164Recipient.requireE164()) + assertFalse(existingE164Recipient.hasUuid()) + } + + /** Another high trust case. No new rules here, just a more complex scenario to show how different rules interact. */ + @Test + fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_complex_highTrust() { + val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true) + val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, true) + assertEquals(existingId1, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_A, retrievedRecipient.requireE164()) + + val existingRecipient2 = Recipient.resolved(existingId2) + assertEquals(ACI_B, existingRecipient2.requireUuid()) + assertFalse(existingRecipient2.hasE164()) + } + + /** Another low trust case. No new rules here, just a more complex scenario to show how different rules interact. */ + @Test + fun getAndPossiblyMerge_bothAciAndE164MapToExistingUser_aciAndE164_complex_lowTrust() { + val existingId1: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_B, true) + val existingId2: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_B, E164_A, true) + + val retrievedId: RecipientId = recipientDatabase.getAndPossiblyMerge(ACI_A, E164_A, false) + assertEquals(existingId1, retrievedId) + + val retrievedRecipient = Recipient.resolved(retrievedId) + assertEquals(ACI_A, retrievedRecipient.requireUuid()) + assertEquals(E164_B, retrievedRecipient.requireE164()) + + val existingRecipient2 = Recipient.resolved(existingId2) + assertEquals(ACI_B, existingRecipient2.requireUuid()) + assertEquals(E164_A, existingRecipient2.requireE164()) + } + + // ============================================================== + // Misc + // ============================================================== + + @Test + fun createByE164SanityCheck() { + // GIVEN one recipient + val recipientId: RecipientId = recipientDatabase.getOrInsertFromE164(E164_A) + + // WHEN I retrieve one by E164 + val possible: Optional = recipientDatabase.getByE164(E164_A) + + // THEN I get it back, and it has the properties I expect + assertTrue(possible.isPresent) + assertEquals(recipientId, possible.get()) + + val recipient = Recipient.resolved(recipientId) + assertTrue(recipient.e164.isPresent) + assertEquals(E164_A, recipient.e164.get()) + } + + @Test + fun createByUuidSanityCheck() { + // GIVEN one recipient + val recipientId: RecipientId = recipientDatabase.getOrInsertFromUuid(ACI_A) + + // WHEN I retrieve one by UUID + val possible: Optional = recipientDatabase.getByUuid(ACI_A) + + // THEN I get it back, and it has the properties I expect + assertTrue(possible.isPresent) + assertEquals(recipientId, possible.get()) + + val recipient = Recipient.resolved(recipientId) + assertTrue(recipient.uuid.isPresent) + assertEquals(ACI_A, recipient.uuid.get()) + } + + @Test(expected = IllegalArgumentException::class) + fun getAndPossiblyMerge_noArgs_invalid() { + recipientDatabase.getAndPossiblyMerge(null, null, true) + } + + private val context: Application + get() = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application + + private fun ensureDbEmpty() { + DatabaseFactory.getInstance(context).rawDatabase.rawQuery("SELECT COUNT(*) FROM ${RecipientDatabase.TABLE_NAME}", null).use { cursor -> + assertTrue(cursor.moveToFirst()) + assertEquals(0, cursor.getLong(0)) + } + } + + companion object { + val ACI_A = UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e") + val ACI_B = UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed") + + val E164_A = "+12221234567" + val E164_B = "+13331234567" + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/lock/PinHashing_hashPin_Test.java b/app/src/androidTest/java/org/thoughtcrime/securesms/lock/PinHashing_hashPin_Test.java index 5b8f365b0..69619cce5 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/lock/PinHashing_hashPin_Test.java +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/lock/PinHashing_hashPin_Test.java @@ -1,6 +1,9 @@ package org.thoughtcrime.securesms.lock; +import androidx.test.ext.junit.runners.AndroidJUnit4; + import org.junit.Test; +import org.junit.runner.RunWith; import org.thoughtcrime.securesms.util.Hex; import org.whispersystems.signalservice.api.kbs.HashedPin; import org.whispersystems.signalservice.api.kbs.KbsData; @@ -12,6 +15,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +@RunWith(AndroidJUnit4.class) public final class PinHashing_hashPin_Test { @Test diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/MmsDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/MmsDatabaseTest.kt index e1ddb0ec0..ea6b3a7c5 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/MmsDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/MmsDatabaseTest.kt @@ -38,37 +38,37 @@ class MmsDatabaseTest { @Test fun `isGroupQuitMessage when normal message, return false`() { - val id = TestMms.insertMmsMessage(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT) + val id = TestMms.insert(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT) assertFalse(mmsDatabase.isGroupQuitMessage(id)) } @Test fun `isGroupQuitMessage when legacy quit message, return true`() { - val id = TestMms.insertMmsMessage(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT) + val id = TestMms.insert(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT) assertTrue(mmsDatabase.isGroupQuitMessage(id)) } @Test fun `isGroupQuitMessage when GV2 leave update, return false`() { - val id = TestMms.insertMmsMessage(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT or Types.GROUP_V2_BIT or Types.GROUP_UPDATE_BIT) + val id = TestMms.insert(db, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT or Types.GROUP_V2_BIT or Types.GROUP_UPDATE_BIT) assertFalse(mmsDatabase.isGroupQuitMessage(id)) } @Test fun `getLatestGroupQuitTimestamp when only normal message, return -1`() { - TestMms.insertMmsMessage(db, threadId = 1, sentTimeMillis = 1, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT) + TestMms.insert(db, threadId = 1, sentTimeMillis = 1, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT) assertEquals(-1, mmsDatabase.getLatestGroupQuitTimestamp(1, 4)) } @Test fun `getLatestGroupQuitTimestamp when legacy quit, return message timestamp`() { - TestMms.insertMmsMessage(db, threadId = 1, sentTimeMillis = 2, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT) + TestMms.insert(db, threadId = 1, sentTimeMillis = 2, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT) assertEquals(2, mmsDatabase.getLatestGroupQuitTimestamp(1, 4)) } @Test fun `getLatestGroupQuitTimestamp when GV2 leave update message, return -1`() { - TestMms.insertMmsMessage(db, threadId = 1, sentTimeMillis = 3, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT or Types.GROUP_V2_BIT or Types.GROUP_UPDATE_BIT) + TestMms.insert(db, threadId = 1, sentTimeMillis = 3, type = Types.BASE_SENDING_TYPE or Types.SECURE_MESSAGE_BIT or Types.PUSH_MESSAGE_BIT or Types.GROUP_LEAVE_BIT or Types.GROUP_V2_BIT or Types.GROUP_UPDATE_BIT) assertEquals(-1, mmsDatabase.getLatestGroupQuitTimestamp(1, 4)) } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/MmsSmsDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/MmsSmsDatabaseTest.kt index 6c46f1a38..5242c9987 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/MmsSmsDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/MmsSmsDatabaseTest.kt @@ -40,7 +40,7 @@ class MmsSmsDatabaseTest { @Test fun `getConversationSnippet when single normal SMS, return SMS message id and transport as false`() { - TestSms.insertSmsMessage(db) + TestSms.insert(db) mmsSmsDatabase.getConversationSnippetCursor(1).use { cursor -> cursor.moveToFirst() assertEquals(1, CursorUtil.requireLong(cursor, MmsSmsColumns.ID)) @@ -50,7 +50,7 @@ class MmsSmsDatabaseTest { @Test fun `getConversationSnippet when single normal MMS, return MMS message id and transport as true`() { - TestMms.insertMmsMessage(db) + TestMms.insert(db) mmsSmsDatabase.getConversationSnippetCursor(1).use { cursor -> cursor.moveToFirst() assertEquals(1, CursorUtil.requireLong(cursor, MmsSmsColumns.ID)) @@ -62,14 +62,14 @@ class MmsSmsDatabaseTest { fun `getConversationSnippet when single normal MMS then GV2 leave update message, return MMS message id and transport as true both times`() { val timestamp = System.currentTimeMillis() - TestMms.insertMmsMessage(db, receivedTimestampMillis = timestamp + 2) + TestMms.insert(db, receivedTimestampMillis = timestamp + 2) mmsSmsDatabase.getConversationSnippetCursor(1).use { cursor -> cursor.moveToFirst() assertEquals(1, CursorUtil.requireLong(cursor, MmsSmsColumns.ID)) assertTrue(CursorUtil.requireBoolean(cursor, MmsSmsDatabase.TRANSPORT)) } - TestSms.insertSmsMessage(db, receivedTimestampMillis = timestamp + 3, type = MmsSmsColumns.Types.BASE_SENDING_TYPE or MmsSmsColumns.Types.SECURE_MESSAGE_BIT or MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS) + TestSms.insert(db, receivedTimestampMillis = timestamp + 3, type = MmsSmsColumns.Types.BASE_SENDING_TYPE or MmsSmsColumns.Types.SECURE_MESSAGE_BIT or MmsSmsColumns.Types.PUSH_MESSAGE_BIT or MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS) mmsSmsDatabase.getConversationSnippetCursor(1).use { cursor -> cursor.moveToFirst() assertEquals(1, CursorUtil.requireLong(cursor, MmsSmsColumns.ID)) diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt index f3a7b9514..cffba81e5 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt @@ -44,7 +44,7 @@ class SmsDatabaseTest { @Test fun `getThreadIdForMessage when message present for id, return thread id`() { - TestSms.insertSmsMessage(db) + TestSms.insert(db) assertThat(smsDatabase.getThreadIdForMessage(1), isEqual(1)) } @@ -55,13 +55,13 @@ class SmsDatabaseTest { @Test fun `hasMeaningfulMessage when normal message, return true`() { - TestSms.insertSmsMessage(db) + TestSms.insert(db) assertTrue(smsDatabase.hasMeaningfulMessage(1)) } @Test fun `hasMeaningfulMessage when GV2 create message only, return true`() { - TestSms.insertSmsMessage(db, type = MmsSmsColumns.Types.BASE_INBOX_TYPE or MmsSmsColumns.Types.SECURE_MESSAGE_BIT or MmsSmsColumns.Types.GROUP_V2_BIT or MmsSmsColumns.Types.GROUP_UPDATE_BIT) + TestSms.insert(db, type = MmsSmsColumns.Types.BASE_INBOX_TYPE or MmsSmsColumns.Types.SECURE_MESSAGE_BIT or MmsSmsColumns.Types.GROUP_V2_BIT or MmsSmsColumns.Types.GROUP_UPDATE_BIT) assertTrue(smsDatabase.hasMeaningfulMessage(1)) } @@ -69,16 +69,16 @@ class SmsDatabaseTest { fun `hasMeaningfulMessage when empty and then with ignored types, always return false`() { assertFalse(smsDatabase.hasMeaningfulMessage(1)) - TestSms.insertSmsMessage(db, type = SmsDatabase.IGNORABLE_TYPESMASK_WHEN_COUNTING) + TestSms.insert(db, type = SmsDatabase.IGNORABLE_TYPESMASK_WHEN_COUNTING) assertFalse(smsDatabase.hasMeaningfulMessage(1)) - TestSms.insertSmsMessage(db, type = MmsSmsColumns.Types.PROFILE_CHANGE_TYPE) + TestSms.insert(db, type = MmsSmsColumns.Types.PROFILE_CHANGE_TYPE) assertFalse(smsDatabase.hasMeaningfulMessage(1)) - TestSms.insertSmsMessage(db, type = MmsSmsColumns.Types.CHANGE_NUMBER_TYPE) + TestSms.insert(db, type = MmsSmsColumns.Types.CHANGE_NUMBER_TYPE) assertFalse(smsDatabase.hasMeaningfulMessage(1)) - TestSms.insertSmsMessage(db, type = MmsSmsColumns.Types.BASE_INBOX_TYPE or MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS) + TestSms.insert(db, type = MmsSmsColumns.Types.BASE_INBOX_TYPE or MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS) assertFalse(smsDatabase.hasMeaningfulMessage(1)) } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/TestMms.kt b/app/src/test/java/org/thoughtcrime/securesms/database/TestMms.kt index d79fe7504..cdecbf2c6 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/TestMms.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/TestMms.kt @@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.recipients.Recipient */ object TestMms { - fun insertMmsMessage( + fun insert( db: SQLiteDatabase, recipient: Recipient = Recipient.UNKNOWN, body: String = "body", @@ -42,7 +42,7 @@ object TestMms { emptySet() ) - return insertMmsMessage( + return insert( db = db, message = message, body = body, @@ -53,7 +53,7 @@ object TestMms { ) } - fun insertMmsMessage( + fun insert( db: SQLiteDatabase, message: OutgoingMediaMessage, body: String = message.body, @@ -62,24 +62,25 @@ object TestMms { threadId: Long = 1, receivedTimestampMillis: Long = System.currentTimeMillis(), ): Long { - val contentValues = ContentValues() - contentValues.put(MmsDatabase.DATE_SENT, message.sentTimeMillis) - contentValues.put(MmsDatabase.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ) + val contentValues = ContentValues().apply { + put(MmsDatabase.DATE_SENT, message.sentTimeMillis) + put(MmsDatabase.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ) - contentValues.put(MmsDatabase.MESSAGE_BOX, type) - contentValues.put(MmsSmsColumns.THREAD_ID, threadId) - contentValues.put(MmsSmsColumns.READ, if (unread) 0 else 1) - contentValues.put(MmsDatabase.DATE_RECEIVED, receivedTimestampMillis) - contentValues.put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId) - contentValues.put(MmsSmsColumns.EXPIRES_IN, message.expiresIn) - contentValues.put(MmsDatabase.VIEW_ONCE, message.isViewOnce) - contentValues.put(MmsSmsColumns.RECIPIENT_ID, message.recipient.id.serialize()) - contentValues.put(MmsSmsColumns.DELIVERY_RECEIPT_COUNT, 0) - contentValues.put(MmsSmsColumns.RECEIPT_TIMESTAMP, 0) + put(MmsDatabase.MESSAGE_BOX, type) + put(MmsSmsColumns.THREAD_ID, threadId) + put(MmsSmsColumns.READ, if (unread) 0 else 1) + put(MmsDatabase.DATE_RECEIVED, receivedTimestampMillis) + put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId) + put(MmsSmsColumns.EXPIRES_IN, message.expiresIn) + put(MmsDatabase.VIEW_ONCE, message.isViewOnce) + put(MmsSmsColumns.RECIPIENT_ID, message.recipient.id.serialize()) + put(MmsSmsColumns.DELIVERY_RECEIPT_COUNT, 0) + put(MmsSmsColumns.RECEIPT_TIMESTAMP, 0) - contentValues.put(MmsSmsColumns.BODY, body) - contentValues.put(MmsDatabase.PART_COUNT, 0) - contentValues.put(MmsDatabase.MENTIONS_SELF, 0) + put(MmsSmsColumns.BODY, body) + put(MmsDatabase.PART_COUNT, 0) + put(MmsDatabase.MENTIONS_SELF, 0) + } return db.insert(MmsDatabase.TABLE_NAME, null, contentValues) } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/TestSms.kt b/app/src/test/java/org/thoughtcrime/securesms/database/TestSms.kt index 2cefcb53c..cdc34e73b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/TestSms.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/TestSms.kt @@ -14,7 +14,7 @@ import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase */ object TestSms { - fun insertSmsMessage( + fun insert( db: AndroidSQLiteDatabase, sender: RecipientId = RecipientId.from(1), senderDeviceId: Int = 1, @@ -43,7 +43,7 @@ object TestSms { serverGuid ) - return insertSmsMessage( + return insert( db = db, message = message, type = type, @@ -52,33 +52,36 @@ object TestSms { ) } - fun insertSmsMessage( + fun insert( db: AndroidSQLiteDatabase, message: IncomingTextMessage, type: Long = MmsSmsColumns.Types.BASE_INBOX_TYPE, unread: Boolean = false, threadId: Long = 1 ): Long { - val values = ContentValues() - values.put(MmsSmsColumns.RECIPIENT_ID, message.sender.serialize()) - values.put(MmsSmsColumns.ADDRESS_DEVICE_ID, message.senderDeviceId) - values.put(SmsDatabase.DATE_RECEIVED, message.receivedTimestampMillis) - values.put(SmsDatabase.DATE_SENT, message.sentTimestampMillis) - values.put(MmsSmsColumns.DATE_SERVER, message.serverTimestampMillis) - values.put(SmsDatabase.PROTOCOL, message.protocol) - values.put(MmsSmsColumns.READ, if (unread) 0 else 1) - values.put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId) - values.put(MmsSmsColumns.EXPIRES_IN, message.expiresIn) - values.put(MmsSmsColumns.UNIDENTIFIED, message.isUnidentified) + val values = ContentValues().apply { + put(MmsSmsColumns.RECIPIENT_ID, message.sender.serialize()) + put(MmsSmsColumns.ADDRESS_DEVICE_ID, message.senderDeviceId) + put(SmsDatabase.DATE_RECEIVED, message.receivedTimestampMillis) + put(SmsDatabase.DATE_SENT, message.sentTimestampMillis) + put(MmsSmsColumns.DATE_SERVER, message.serverTimestampMillis) + put(SmsDatabase.PROTOCOL, message.protocol) + put(MmsSmsColumns.READ, if (unread) 0 else 1) + put(MmsSmsColumns.SUBSCRIPTION_ID, message.subscriptionId) + put(MmsSmsColumns.EXPIRES_IN, message.expiresIn) + put(MmsSmsColumns.UNIDENTIFIED, message.isUnidentified) - if (!TextUtils.isEmpty(message.pseudoSubject)) values.put(SmsDatabase.SUBJECT, message.pseudoSubject) + if (!TextUtils.isEmpty(message.pseudoSubject)) { + put(SmsDatabase.SUBJECT, message.pseudoSubject) + } - values.put(SmsDatabase.REPLY_PATH_PRESENT, message.isReplyPathPresent) - values.put(SmsDatabase.SERVICE_CENTER, message.serviceCenterAddress) - values.put(MmsSmsColumns.BODY, message.messageBody) - values.put(SmsDatabase.TYPE, type) - values.put(MmsSmsColumns.THREAD_ID, threadId) - values.put(MmsSmsColumns.SERVER_GUID, message.serverGuid) + put(SmsDatabase.REPLY_PATH_PRESENT, message.isReplyPathPresent) + put(SmsDatabase.SERVICE_CENTER, message.serviceCenterAddress) + put(MmsSmsColumns.BODY, message.messageBody) + put(SmsDatabase.TYPE, type) + put(MmsSmsColumns.THREAD_ID, threadId) + put(MmsSmsColumns.SERVER_GUID, message.serverGuid) + } return db.insert(SmsDatabase.TABLE_NAME, null, values) }