Skip to content

Commit

Permalink
Fix #5399: Fix tests in LoggingIdentifierControllerTest (#5409)
Browse files Browse the repository at this point in the history
## Explanation

Fixes #5399

This PR addresses the problems outlined in #5399 by:
- Ensuring that
``testGetInstallationId_secondAppOpen_providerReturnsSameInstallationIdValue``
and
``testFetchInstallationId_secondAppOpen_returnsSameInstallationIdValue``
are correctly run in a _new_ ``TestApplication`` (as inspired by
``SplashActivityTest``).
- Adding a new test to verify that session ID correctly regenerates in a
new app instance (``testGetSessionId_secondAppOpen_returnsNewRandomId``)
which also has the added benefit of providing confidence that the
earlier tests are correctly verifying that the installation ID _isn't_
changing, as expected, on a new app instance.
- Improved the tests around cache corruption (via emptying) and deletion
to ensure they correctly demonstrate the failure fallbacks (empty/null
for corruption and reinitialization for deletion in the same way as a
new app install).

## Essential Checklist
- [x] The PR title and explanation each start with "Fix #bugnum: " (If
this PR fixes part of an issue, prefix the title with "Fix part of
#bugnum: ...".)
- [x] Any changes to
[scripts/assets](https://github.com/oppia/oppia-android/tree/develop/scripts/assets)
files have their rationale included in the PR explanation.
- [x] The PR follows the [style
guide](https://github.com/oppia/oppia-android/wiki/Coding-style-guide).
- [x] The PR does not contain any unnecessary code changes from Android
Studio
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#undo-unnecessary-changes)).
- [x] The PR is made from a branch that's **not** called "develop" and
is up-to-date with "develop".
- [x] The PR is **assigned** to the appropriate reviewers
([reference](https://github.com/oppia/oppia-android/wiki/Guidance-on-submitting-a-PR#clarification-regarding-assignees-and-reviewers-section)).

## For UI-specific PRs only
N/A -- This is fixing a specific test and has no impact on UI behaviors.

---------

Co-authored-by: Adhiambo Peres <[email protected]>
  • Loading branch information
BenHenning and adhiamboperes authored May 30, 2024
1 parent 18fa030 commit 3c7713d
Showing 1 changed file with 84 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.oppia.android.domain.oppialogger

import android.app.Application
import android.app.Instrumentation
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.google.protobuf.MessageLite
import dagger.BindsInstance
Expand Down Expand Up @@ -73,6 +75,7 @@ class LoggingIdentifierControllerTest {

@Before
fun setUp() {
TestLoggingIdentifierModule.applicationIdSeed = INITIAL_APPLICATION_ID
setUpTestApplicationComponent()
}

Expand All @@ -99,7 +102,8 @@ class LoggingIdentifierControllerTest {
@Test
fun testGetInstallationId_secondAppOpen_providerReturnsSameInstallationIdValue() {
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
setUpTestApplicationComponent() // Simulate an app re-open.
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val installationId =
monitorFactory.waitForNextSuccessfulResult(loggingIdentifierController.getInstallationId())
Expand All @@ -110,15 +114,35 @@ class LoggingIdentifierControllerTest {

@Test
fun testGetInstallationId_secondAppOpen_emptiedDatabase_providerReturnsEmptyString() {
// Simulate initing the installation ID, then emptying/corrupting it, then reopening the app.
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
writeFileCache("device_context_database", DeviceContextDatabase.getDefaultInstance())
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val installationId =
monitorFactory.waitForNextSuccessfulResult(loggingIdentifierController.getInstallationId())

// The installation ID is empty since the database was overwritten.
// If the file was emptied, no installation ID can be loaded (this is a critical failure case).
assertThat(installationId).isEmpty()
}

@Test
fun testGetInstallationId_secondAppOpen_deletedDatabase_providerReturnsNewInstallationIdValue() {
// Simulate initing the installation ID, then deleting it, then reopening the app.
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
deleteCacheFile("device_context_database")
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val installationId =
monitorFactory.waitForNextSuccessfulResult(loggingIdentifierController.getInstallationId())

// It should seem like a reinstallation since the app's data has been cleared after restarting.
assertThat(installationId).isEqualTo("a52e69fcfedc")
assertThat(installationId.length).isEqualTo(12)
}

@Test
fun testFetchInstallationId_initialAppState_returnsNewInstallationIdValue() {
val installationId = fetchSuccessfulAsyncValue(loggingIdentifierController::fetchInstallationId)
Expand All @@ -130,7 +154,8 @@ class LoggingIdentifierControllerTest {
@Test
fun testFetchInstallationId_secondAppOpen_returnsSameInstallationIdValue() {
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
setUpTestApplicationComponent() // Simulate an app re-open.
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val installationId = fetchSuccessfulAsyncValue(loggingIdentifierController::fetchInstallationId)

Expand All @@ -140,14 +165,34 @@ class LoggingIdentifierControllerTest {

@Test
fun testFetchInstallationId_secondAppOpen_emptiedDatabase_returnsNull() {
// Simulate initing the installation ID, then emptying/corrupting it, then reopening the app.
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
writeFileCache("device_context_database", DeviceContextDatabase.getDefaultInstance())
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val installationId = fetchSuccessfulAsyncValue(loggingIdentifierController::fetchInstallationId)

// The installation ID is null since the database was overwritten.
// If the file was emptied, no installation ID can be loaded (this is a critical failure case).
assertThat(installationId).isNull()
}

@Test
fun testFetchInstallationId_secondAppOpen_deletedDatabase_returnsNewInstallationIdValue() {
// Simulate initing the installation ID, then deleting it, then reopening the app.
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())
deleteCacheFile("device_context_database")
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getInstallationId())

val installationId = fetchSuccessfulAsyncValue(loggingIdentifierController::fetchInstallationId)

// The installation ID is null since the database was overwritten.
assertThat(installationId).isEqualTo("a52e69fcfedc")
assertThat(installationId?.length).isEqualTo(12)
}

@Test
fun testGetSessionId_initialState_returnsRandomId() {
val sessionIdProvider = loggingIdentifierController.getSessionId()
Expand All @@ -167,6 +212,19 @@ class LoggingIdentifierControllerTest {
assertThat(sessionId).isEqualTo("4d0a66f3-82b6-3aa9-8f61-140bdd5f49d3")
}

@Test
fun testGetSessionId_secondAppOpen_returnsNewRandomId() {
monitorFactory.ensureDataProviderExecutes(loggingIdentifierController.getSessionId())
TestLoggingIdentifierModule.applicationIdSeed = SECOND_APP_OPEN_APPLICATION_ID
setUpNewTestApplicationComponent() // Simulate an app re-open with a new app ID.

val sessionIdProvider = loggingIdentifierController.getSessionId()

// The second call should return the same ID (since the ID doesn't automatically change).
val sessionId = monitorFactory.waitForNextSuccessfulResult(sessionIdProvider)
assertThat(sessionId).isEqualTo("18c2816d-f7ad-312f-b696-d3fdd51f2e92")
}

@Test
fun testGetSessionIdFlow_initialState_returnsFlowWithRandomId() {
val sessionIdFlow = loggingIdentifierController.getSessionIdFlow()
Expand Down Expand Up @@ -255,6 +313,10 @@ class LoggingIdentifierControllerTest {
getCacheFile(cacheName).writeBytes(value.toByteArray())
}

private fun deleteCacheFile(cacheName: String) {
check(getCacheFile(cacheName).delete()) { "Failed to delete: $cacheName." }
}

private fun getCacheFile(cacheName: String) = File(context.filesDir, "$cacheName.cache")

private fun File.writeBytes(data: ByteArray) {
Expand Down Expand Up @@ -292,6 +354,17 @@ class LoggingIdentifierControllerTest {
ApplicationProvider.getApplicationContext<TestApplication>().inject(this)
}

private fun setUpNewTestApplicationComponent() {
createNewTestApplication().inject(this)
}

private fun createNewTestApplication(): TestApplication {
return Instrumentation.newApplication(
TestApplication::class.java,
InstrumentationRegistry.getInstrumentation().targetContext
) as TestApplication
}

// TODO(#89): Move this to a common test application component.
@Module
class TestModule {
Expand Down Expand Up @@ -327,12 +400,12 @@ class LoggingIdentifierControllerTest {
@Module
class TestLoggingIdentifierModule {
companion object {
internal const val applicationIdSeed = 1L
internal var applicationIdSeed: Long? = null
}

@Provides
@ApplicationIdSeed
fun provideApplicationIdSeed(): Long = applicationIdSeed
fun provideApplicationIdSeed(): Long = applicationIdSeed!! // Fail if not initialized.
}

@Module
Expand Down Expand Up @@ -398,4 +471,9 @@ class LoggingIdentifierControllerTest {

override fun getDataProvidersInjector(): DataProvidersInjector = component
}

companion object {
private const val INITIAL_APPLICATION_ID = 1L
private const val SECOND_APP_OPEN_APPLICATION_ID = 2L
}
}

0 comments on commit 3c7713d

Please sign in to comment.