From 7d24bff134dad03ad985e2cfd8c55ec93580f8fc Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 22 Nov 2024 10:18:56 -0500 Subject: [PATCH] Add unit test for RegistrationUtil. --- .../components/emoji/EmojiUtilTest_isEmoji.kt | 12 +- .../account/export/ExportAccountDataTest.kt | 16 +- .../securesms/crash/CrashConfigTest.kt | 12 +- .../NotificationProfileDatabaseTest.kt | 11 +- .../MockApplicationDependencyProvider.kt | 88 +++++------ .../groups/GroupManagerV2Test_edit.kt | 11 +- .../processing/GroupsV2StateProcessorTest.kt | 11 +- .../securesms/keyvalue/PaymentsValuesTest.kt | 12 +- .../RemoteMegaphoneRepositoryTest.kt | 14 +- .../notifications/MarkReadReceiverTest.kt | 14 +- .../profiles/NotificationProfilesTest.kt | 12 +- .../registration/util/RegistrationUtilTest.kt | 143 ++++++++++++++++++ .../storage/ContactRecordProcessorTest.kt | 12 +- .../testutil/MockAppDependenciesRule.kt | 50 ++++++ .../securesms/testutil/MockSignalStoreRule.kt | 60 ++++++++ .../SignalMeUtilText_parseE164FromLink.kt | 12 +- 16 files changed, 356 insertions(+), 134 deletions(-) create mode 100644 app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt create mode 100644 app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt create mode 100644 app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt index d2b850f17c..da99a8d085 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/emoji/EmojiUtilTest_isEmoji.kt @@ -1,29 +1,27 @@ package org.thoughtcrime.securesms.components.emoji import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import org.junit.Assert +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.emoji.EmojiSource +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule @RunWith(ParameterizedRobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class EmojiUtilTest_isEmoji(private val input: String?, private val output: Boolean) { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Throws(Exception::class) @Test fun isEmoji() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - val source = EmojiSource.loadAssetBasedEmojis() mockkObject(EmojiSource) { diff --git a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt index 98469ed27e..9341e7830d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/components/settings/app/account/export/ExportAccountDataTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.components.settings.app.account.export import android.app.Application -import androidx.test.core.app.ApplicationProvider import com.fasterxml.jackson.core.JsonParseException import io.mockk.every import io.mockk.mockk @@ -12,14 +11,13 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue -import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.providers.BlobProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.JsonUtils import org.whispersystems.signalservice.api.SignalServiceAccountManager import java.io.IOException @@ -28,6 +26,9 @@ import java.io.IOException @Config(manifest = Config.NONE, application = Application::class) class ExportAccountDataTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val mockJson: String = """ { "reportId": "4c0ca2aa-151b-4e9e-8bf4-ea2c64345a22", @@ -64,13 +65,6 @@ class ExportAccountDataTest { } """ - @Before - fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - } - @Test fun `Export json without text field`() { val scheduler = TestScheduler() diff --git a/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt b/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt index 0a6b113a2c..e8a7581f80 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/crash/CrashConfigTest.kt @@ -1,20 +1,19 @@ package org.thoughtcrime.securesms.crash import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.unmockkAll import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.thoughtcrime.securesms.assertIs -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.RemoteConfig import org.whispersystems.signalservice.api.push.ServiceId import java.util.UUID @@ -23,14 +22,13 @@ import java.util.UUID @Config(manifest = Config.NONE, application = Application::class) class CrashConfigTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Before fun setup() { mockkObject(RemoteConfig) - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.aci } returns ServiceId.ACI.from(UUID.randomUUID()) } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt index 94a5c0e05e..1d531f33e6 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/NotificationProfileDatabaseTest.kt @@ -10,22 +10,25 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.thoughtcrime.securesms.conversation.colors.AvatarColor -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.testing.TestDatabaseUtil +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import java.time.DayOfWeek @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class NotificationProfileDatabaseTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var db: SQLiteDatabase private lateinit var database: NotificationProfileDatabase @@ -42,10 +45,6 @@ class NotificationProfileDatabaseTest { } } - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - db = sqlCipher.writableDatabase database = NotificationProfileDatabase(ApplicationProvider.getApplicationContext(), sqlCipher) } diff --git a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt index f800ac79e5..4ed3a383b1 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/dependencies/MockApplicationDependencyProvider.kt @@ -54,83 +54,83 @@ import java.util.function.Supplier class MockApplicationDependencyProvider : AppDependencies.Provider { override fun providePushServiceSocket(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): PushServiceSocket { - return mockk() + return mockk(relaxed = true) } override fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceAccountManager(pushServiceSocket: PushServiceSocket, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceMessageSender(signalWebSocket: SignalWebSocket, protocolStore: SignalServiceDataStore, pushServiceSocket: PushServiceSocket): SignalServiceMessageSender { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceMessageReceiver(pushServiceSocket: PushServiceSocket): SignalServiceMessageReceiver { - return mockk() + return mockk(relaxed = true) } override fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess { - return mockk() + return mockk(relaxed = true) } override fun provideRecipientCache(): LiveRecipientCache { - return mockk() + return mockk(relaxed = true) } override fun provideJobManager(): JobManager { - return mockk() + return mockk(relaxed = true) } override fun provideFrameRateTracker(): FrameRateTracker { - return mockk() + return mockk(relaxed = true) } override fun provideMegaphoneRepository(): MegaphoneRepository { - return mockk() + return mockk(relaxed = true) } override fun provideEarlyMessageCache(): EarlyMessageCache { - return mockk() + return mockk(relaxed = true) } override fun provideMessageNotifier(): MessageNotifier { - return mockk() + return mockk(relaxed = true) } override fun provideIncomingMessageObserver(): IncomingMessageObserver { - return mockk() + return mockk(relaxed = true) } override fun provideTrimThreadsByDateManager(): TrimThreadsByDateManager { - return mockk() + return mockk(relaxed = true) } override fun provideViewOnceMessageManager(): ViewOnceMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideExpiringStoriesManager(): ExpiringStoriesManager { - return mockk() + return mockk(relaxed = true) } override fun provideExpiringMessageManager(): ExpiringMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideDeletedCallEventManager(): DeletedCallEventManager { - return mockk() + return mockk(relaxed = true) } override fun provideTypingStatusRepository(): TypingStatusRepository { - return mockk() + return mockk(relaxed = true) } override fun provideTypingStatusSender(): TypingStatusSender { - return mockk() + return mockk(relaxed = true) } override fun provideDatabaseObserver(): DatabaseObserver { @@ -138,98 +138,98 @@ class MockApplicationDependencyProvider : AppDependencies.Provider { } override fun providePayments(signalServiceAccountManager: SignalServiceAccountManager): Payments { - return mockk() + return mockk(relaxed = true) } override fun provideShakeToReport(): ShakeToReport { - return mockk() + return mockk(relaxed = true) } override fun provideSignalCallManager(): SignalCallManager { - return mockk() + return mockk(relaxed = true) } override fun providePendingRetryReceiptManager(): PendingRetryReceiptManager { - return mockk() + return mockk(relaxed = true) } override fun providePendingRetryReceiptCache(): PendingRetryReceiptCache { - return mockk() + return mockk(relaxed = true) } override fun provideSignalWebSocket(signalServiceConfigurationSupplier: Supplier, libSignalNetworkSupplier: Supplier): SignalWebSocket { - return mockk() + return mockk(relaxed = true) } override fun provideProtocolStore(): SignalServiceDataStoreImpl { - return mockk() + return mockk(relaxed = true) } override fun provideGiphyMp4Cache(): GiphyMp4Cache { - return mockk() + return mockk(relaxed = true) } override fun provideExoPlayerPool(): SimpleExoPlayerPool { - return mockk() + return mockk(relaxed = true) } override fun provideAndroidCallAudioManager(): AudioManagerCompat { - return mockk() + return mockk(relaxed = true) } override fun provideDonationsService(pushServiceSocket: PushServiceSocket): DonationsService { - return mockk() + return mockk(relaxed = true) } override fun provideCallLinksService(pushServiceSocket: PushServiceSocket): CallLinksService { - return mockk() + return mockk(relaxed = true) } override fun provideProfileService(profileOperations: ClientZkProfileOperations, signalServiceMessageReceiver: SignalServiceMessageReceiver, signalWebSocket: SignalWebSocket): ProfileService { - return mockk() + return mockk(relaxed = true) } override fun provideDeadlockDetector(): DeadlockDetector { - return mockk() + return mockk(relaxed = true) } override fun provideClientZkReceiptOperations(signalServiceConfiguration: SignalServiceConfiguration): ClientZkReceiptOperations { - return mockk() + return mockk(relaxed = true) } override fun provideScheduledMessageManager(): ScheduledMessageManager { - return mockk() + return mockk(relaxed = true) } override fun provideLibsignalNetwork(config: SignalServiceConfiguration): Network { - return mockk() + return mockk(relaxed = true) } override fun provideBillingApi(): BillingApi { - return mockk() + return mockk(relaxed = true) } override fun provideArchiveApi(pushServiceSocket: PushServiceSocket): ArchiveApi { - return mockk() + return mockk(relaxed = true) } override fun provideKeysApi(pushServiceSocket: PushServiceSocket): KeysApi { - return mockk() + return mockk(relaxed = true) } override fun provideAttachmentApi(signalWebSocket: SignalWebSocket, pushServiceSocket: PushServiceSocket): AttachmentApi { - return mockk() + return mockk(relaxed = true) } override fun provideLinkDeviceApi(pushServiceSocket: PushServiceSocket): LinkDeviceApi { - return mockk() + return mockk(relaxed = true) } override fun provideRegistrationApi(pushServiceSocket: PushServiceSocket): RegistrationApi { - return mockk() + return mockk(relaxed = true) } override fun provideStorageServiceApi(pushServiceSocket: PushServiceSocket): StorageServiceApi { - return mockk() + return mockk(relaxed = true) } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt index 182ebdf3e4..aadf5f757f 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt @@ -15,6 +15,7 @@ import org.hamcrest.Matchers import org.hamcrest.Matchers.`is` import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -34,12 +35,11 @@ import org.thoughtcrime.securesms.TestZkGroupServer import org.thoughtcrime.securesms.database.GroupStateTestData import org.thoughtcrime.securesms.database.GroupTable import org.thoughtcrime.securesms.database.model.databaseprotos.member -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.testutil.SystemOutLogger import org.thoughtcrime.securesms.util.RemoteConfig import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations @@ -68,6 +68,9 @@ class GroupManagerV2Test_edit { val others: List = listOf(member(otherAci)) } + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var groupTable: GroupTable private lateinit var groupsV2API: GroupsV2Api private lateinit var groupsV2Operations: GroupsV2Operations @@ -81,10 +84,6 @@ class GroupManagerV2Test_edit { @Suppress("UsePropertyAccessSyntax") @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(RemoteConfig) mockkObject(SignalStore) every { RemoteConfig.internalUser } returns false diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt index a24f940247..87cce35512 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/v2/processing/GroupsV2StateProcessorTest.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.groups.v2.processing import android.annotation.SuppressLint import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.justRun import io.mockk.mockk @@ -17,6 +16,7 @@ import org.hamcrest.Matchers.hasItem import org.hamcrest.Matchers.`is` import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -45,7 +45,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.requestingMember import org.thoughtcrime.securesms.database.setNewDescription import org.thoughtcrime.securesms.database.setNewTitle import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.groups.GroupNotAMemberException import org.thoughtcrime.securesms.groups.GroupsV2Authorization @@ -57,6 +56,7 @@ import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.testutil.SystemOutLogger import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupResponse @@ -90,6 +90,9 @@ class GroupsV2StateProcessorTest { private val others: List = listOf(member(otherAci)) } + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var groupTable: GroupTable private lateinit var recipientTable: RecipientTable private lateinit var threadTable: ThreadTable @@ -103,10 +106,6 @@ class GroupsV2StateProcessorTest { @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.internal.gv2IgnoreP2PChanges } returns false diff --git a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt index 4d6c7ed7d2..0460bef5c8 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/keyvalue/PaymentsValuesTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.keyvalue import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -9,26 +8,25 @@ import io.mockk.unmockkAll import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.RemoteConfig @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class PaymentsValuesTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private lateinit var paymentValues: PaymentsValues @Before fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(RemoteConfig) mockkObject(SignalStore) diff --git a/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt b/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt index 948bcff8f7..7ad119773b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/megaphone/RemoteMegaphoneRepositoryTest.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.megaphone import android.app.Application import android.net.Uri -import androidx.test.core.app.ApplicationProvider import io.mockk.clearMocks import io.mockk.every import io.mockk.mockk @@ -14,8 +13,8 @@ import org.hamcrest.Matchers.nullValue import org.json.JSONObject import org.junit.After import org.junit.AfterClass -import org.junit.Before import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -23,8 +22,7 @@ import org.robolectric.annotation.Config import org.thoughtcrime.securesms.database.RemoteMegaphoneTable import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.toMillis import java.time.LocalDateTime import java.util.UUID @@ -37,12 +35,8 @@ import java.util.UUID @Config(manifest = Config.NONE, application = Application::class) class RemoteMegaphoneRepositoryTest { - @Before - fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - } + @get:Rule + val appDependencies = MockAppDependenciesRule() @After fun tearDown() { diff --git a/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt b/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt index f0dad25090..56b5239710 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/notifications/MarkReadReceiverTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.notifications import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.mockkStatic @@ -9,6 +8,7 @@ import io.mockk.unmockkAll import org.junit.After import org.junit.Assert import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -20,13 +20,13 @@ import org.thoughtcrime.securesms.database.MessageTable.SyncMessageId import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.StoryType import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.JobManager import org.thoughtcrime.securesms.jobmanager.JsonJobData import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.TextSecurePreferences import java.util.LinkedList @@ -34,17 +34,13 @@ import java.util.LinkedList @Config(manifest = Config.NONE, application = Application::class) class MarkReadReceiverTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val jobs: MutableList = LinkedList() @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init( - ApplicationProvider.getApplicationContext(), - MockApplicationDependencyProvider() - ) - } - val jobManager: JobManager = AppDependencies.jobManager every { jobManager.add(capture(jobs)) } returns Unit diff --git a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt index dcca57fbc1..597661a40b 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.notifications.profiles import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -11,14 +10,14 @@ import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.nullValue import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.NotificationProfileValues import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.toMillis import java.time.DayOfWeek import java.time.LocalDateTime @@ -29,6 +28,9 @@ import java.time.ZoneOffset @Config(manifest = Config.NONE, application = Application::class) class NotificationProfilesTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + private val sunday830am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 8, 30, 0) private val sunday9am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 9, 0, 0) private val sunday930am: LocalDateTime = LocalDateTime.of(2021, 7, 4, 9, 30, 0) @@ -55,10 +57,6 @@ class NotificationProfilesTest { @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - notificationProfileValues = mockk() every { notificationProfileValues.manuallyEnabledUntil } returns 0 every { notificationProfileValues.manuallyDisabledAt } returns 0 diff --git a/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt b/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt new file mode 100644 index 0000000000..b135c1253a --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/registration/util/RegistrationUtilTest.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.registration.util + +import android.app.Application +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockkObject +import io.mockk.unmockkAll +import io.mockk.verify +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.signal.core.util.logging.Log.initialize +import org.thoughtcrime.securesms.assertIs +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.profiles.ProfileName +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testutil.LogRecorder +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule +import org.thoughtcrime.securesms.testutil.MockSignalStoreRule +import org.thoughtcrime.securesms.util.RemoteConfig + +@RunWith(RobolectricTestRunner::class) +@Config(application = Application::class, manifest = Config.NONE) +class RegistrationUtilTest { + + @get:Rule + val signalStore = MockSignalStoreRule(relaxed = setOf(PhoneNumberPrivacyValues::class)) + + @get:Rule + val appDependencies = MockAppDependenciesRule() + + private lateinit var logRecorder: LogRecorder + + @Before + fun setup() { + mockkObject(Recipient) + mockkObject(RemoteConfig) + every { RemoteConfig.init() } just Runs + + logRecorder = LogRecorder() + initialize(logRecorder) + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun maybeMarkRegistrationComplete_allValidNoRestoreOption() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_allValidNoRestoreOptionSvrOptOut() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns false + every { signalStore.svr.hasOptedOut() } returns true + every { RemoteConfig.restoreAfterRegistration } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_allValidWithRestoreOption() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns true + every { signalStore.registration.hasSkippedTransferOrRestore() } returns true + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify { signalStore.registration.markRegistrationComplete() } + } + + @Test + fun maybeMarkRegistrationComplete_missingData() { + every { signalStore.registration.isRegistrationComplete } returns false + every { signalStore.account.isRegistered } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { signalStore.account.isRegistered } returns true + every { Recipient.self() } returns Recipient(profileName = ProfileName.EMPTY) + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { Recipient.self() } returns Recipient(profileName = ProfileName.fromParts("Dark", "Helmet")) + every { signalStore.svr.hasOptedInWithAccess() } returns false + every { signalStore.svr.hasOptedOut() } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + every { signalStore.svr.hasOptedInWithAccess() } returns true + every { RemoteConfig.restoreAfterRegistration } returns true + every { signalStore.registration.hasSkippedTransferOrRestore() } returns false + every { signalStore.registration.hasCompletedRestore() } returns false + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify(exactly = 0) { signalStore.registration.markRegistrationComplete() } + + val regUtilLogs = logRecorder.information.filter { it.tag == "RegistrationUtil" } + regUtilLogs.size assertIs 4 + regUtilLogs.all { it.message == "Registration is not yet complete." } assertIs true + } + + @Test + fun maybeMarkRegistrationComplete_alreadyMarked() { + every { signalStore.registration.isRegistrationComplete } returns true + + RegistrationUtil.maybeMarkRegistrationComplete() + + verify(exactly = 0) { signalStore.registration.markRegistrationComplete() } + + val regUtilLogs = logRecorder.information.filter { it.tag == "RegistrationUtil" } + regUtilLogs.size assertIs 0 + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt index ac07974d84..463ac955ec 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/storage/ContactRecordProcessorTest.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.storage import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject @@ -12,16 +11,16 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.RecipientTable -import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.testutil.EmptyLogger +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.whispersystems.signalservice.api.push.ServiceId.ACI import org.whispersystems.signalservice.api.push.ServiceId.PNI import org.whispersystems.signalservice.api.storage.SignalContactRecord @@ -33,14 +32,13 @@ import java.util.UUID @Config(application = Application::class) class ContactRecordProcessorTest { + @get:Rule + val appDependencies = MockAppDependenciesRule() + lateinit var recipientTable: RecipientTable @Before fun setup() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.isPrimaryDevice } returns true diff --git a/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt new file mode 100644 index 0000000000..386c16bf8d --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockAppDependenciesRule.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.testutil + +import androidx.test.core.app.ApplicationProvider +import io.mockk.clearMocks +import org.junit.rules.ExternalResource +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider +import kotlin.reflect.KVisibility +import kotlin.reflect.full.memberProperties + +/** + * Facilitates mocking and clearing components of [AppDependencies]. Clearing is particularly important as the + * mocks will be reused since [AppDependencies] is scoped to the entire test suite and stays initialized with prior + * test runs leading to unpredictable results based on how tests are run. + */ +class MockAppDependenciesRule : ExternalResource() { + + private val skipList = setOf( + "application", + "databaseObserver", + "groupsV2Authorization", + "isInitialized", + "okHttpClient", + "signalOkHttpClient", + "webSocketObserver" + ) + + private val properties = AppDependencies::class + .memberProperties + .filter { it.visibility == KVisibility.PUBLIC } + .filterNot { skipList.contains(it.name) } + + override fun before() { + if (!AppDependencies.isInitialized) { + AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) + } + } + + override fun after() { + properties + .forEach { property -> + property.get(AppDependencies)?.let { clearMocks(it) } + } + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt new file mode 100644 index 0000000000..a0d82e6891 --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/testutil/MockSignalStoreRule.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.testutil + +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import io.mockk.unmockkObject +import org.junit.rules.ExternalResource +import org.thoughtcrime.securesms.keyvalue.AccountValues +import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues +import org.thoughtcrime.securesms.keyvalue.RegistrationValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.keyvalue.SvrValues +import kotlin.reflect.KClass + +/** + * Mocks [SignalStore] to return mock versions of the various values. Mocks will default to not be relaxed (each + * method call on them will need to be mocked) except for unit functions which will do nothing. + * + * Expand mocked values as necessary when needed. + * + * @param relaxed Set of value classes that should default to relaxed thus defaulting all methods. Useful + * when value is not part of the input state under test but called within the under test code. + */ +@Suppress("MemberVisibilityCanBePrivate") +class MockSignalStoreRule(private val relaxed: Set> = emptySet()) : ExternalResource() { + + lateinit var account: AccountValues + private set + + lateinit var phoneNumberPrivacy: PhoneNumberPrivacyValues + private set + + lateinit var registration: RegistrationValues + private set + + lateinit var svr: SvrValues + private set + + override fun before() { + account = mockk(relaxed = relaxed.contains(AccountValues::class), relaxUnitFun = true) + phoneNumberPrivacy = mockk(relaxed = relaxed.contains(PhoneNumberPrivacyValues::class), relaxUnitFun = true) + registration = mockk(relaxed = relaxed.contains(RegistrationValues::class), relaxUnitFun = true) + svr = mockk(relaxed = relaxed.contains(SvrValues::class), relaxUnitFun = true) + + mockkObject(SignalStore) + every { SignalStore.account } returns account + every { SignalStore.phoneNumberPrivacy } returns phoneNumberPrivacy + every { SignalStore.registration } returns registration + every { SignalStore.svr } returns svr + } + + override fun after() { + unmockkObject(SignalStore) + } +} diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt index eff351652a..df4de4de3d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/util/SignalMeUtilText_parseE164FromLink.kt @@ -1,33 +1,31 @@ package org.thoughtcrime.securesms.util import android.app.Application -import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockkObject import io.mockk.unmockkAll import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config -import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.AppDependencies.application -import org.thoughtcrime.securesms.dependencies.MockApplicationDependencyProvider import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule import org.thoughtcrime.securesms.util.SignalMeUtil.parseE164FromLink @RunWith(ParameterizedRobolectricTestRunner::class) @Config(manifest = Config.NONE, application = Application::class) class SignalMeUtilText_parseE164FromLink(private val input: String?, private val output: String?) { + @get:Rule + val appDependencies = MockAppDependenciesRule() + @Before fun setUp() { - if (!AppDependencies.isInitialized) { - AppDependencies.init(ApplicationProvider.getApplicationContext(), MockApplicationDependencyProvider()) - } - mockkObject(SignalStore) every { SignalStore.account.e164 } returns "+15555555555" }