diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 0c77e9bcc31..a59584e77f2 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -125,8 +125,8 @@ config/kitkat_main_dex_class_list.txt @BenHenning
# Databinding adapters.
/app/src/main/java/org/oppia/android/app/databinding/ @BenHenning
-# App deprecation functionality.
-/app/src/*/java/org/oppia/android/app/deprecation/ @BenHenning
+# App notices functionality (such as for deprecations).
+/app/src/*/java/org/oppia/android/app/notice/ @BenHenning
# Parsing functionality needed for interactions.
/app/src/*/java/org/oppia/android/app/parser/ @BenHenning
diff --git a/.github/workflows/build_tests.yml b/.github/workflows/build_tests.yml
index b95d906cbc3..fdaebd4af7b 100644
--- a/.github/workflows/build_tests.yml
+++ b/.github/workflows/build_tests.yml
@@ -476,3 +476,263 @@ jobs:
with:
name: oppia_alpha_kenya.aab
path: /home/runner/work/oppia-android/oppia-android/oppia_alpha_kenya.aab
+
+ build_oppia_beta_aab:
+ name: Build Oppia AAB (beta flavor)
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-18.04]
+ env:
+ ENABLE_CACHING: false
+ CACHE_DIRECTORY: ~/.bazel_cache
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 9
+ uses: actions/setup-java@v1
+ with:
+ java-version: 9
+
+ - name: Set up Bazel
+ uses: abhinavsingh/setup-bazel@v3
+ with:
+ version: 4.0.0
+
+ - name: Set up build environment
+ uses: ./.github/actions/set-up-android-bazel-build-environment
+
+ # For reference on this & the later cache actions, see:
+ # https://github.com/actions/cache/issues/239#issuecomment-606950711 &
+ # https://github.com/actions/cache/issues/109#issuecomment-558771281. Note that these work
+ # with Bazel since Bazel can share the most recent cache from an unrelated build and still
+ # benefit from incremental build performance (assuming that actions/cache aggressively removes
+ # older caches due to the 5GB cache limit size & Bazel's large cache size).
+ - uses: actions/cache@v2
+ id: cache
+ with:
+ path: ${{ env.CACHE_DIRECTORY }}
+ key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-tests-
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-
+
+ # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
+ # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
+ # thereby only ever using the last successful cache version. This solution will result in a
+ # few slower CI actions around the time cache is detected to be too large, but it should
+ # incrementally improve thereafter.
+ - name: Ensure cache size
+ env:
+ BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
+ run: |
+ # See https://stackoverflow.com/a/27485157 for reference.
+ EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
+ CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
+ echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
+ # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
+ # to only increase by a few hundred megabytes across changes for unrelated branches. This
+ # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
+ # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
+ # compressed cache).
+ if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
+ echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
+ rm -rf $EXPANDED_BAZEL_CACHE_PATH
+ fi
+
+ - name: Configure Bazel to use a local cache
+ env:
+ BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
+ run: |
+ EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
+ echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
+ echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
+ shell: bash
+
+ - name: Check Bazel environment
+ run: bazel info
+
+ # See https://git-secret.io/installation for details on installing git-secret. Note that the
+ # apt-get method isn't used since it's much slower to update & upgrade apt before installation
+ # versus just directly cloning & installing the project. Further, the specific version
+ # shouldn't matter since git-secret relies on a future-proof storage mechanism for secrets.
+ # This also uses a different directory to install git-secret to avoid requiring root access
+ # when running the git secret command.
+ - name: Install git-secret (non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ shell: bash
+ run: |
+ cd $HOME
+ mkdir -p $HOME/gitsecret
+ git clone https://github.com/sobolevn/git-secret.git git-secret
+ cd git-secret && make build
+ PREFIX="$HOME/gitsecret" make install
+ echo "$HOME/gitsecret" >> $GITHUB_PATH
+ echo "$HOME/gitsecret/bin" >> $GITHUB_PATH
+
+ - name: Decrypt secrets (non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ env:
+ GIT_SECRET_GPG_PRIVATE_KEY: ${{ secrets.GIT_SECRET_GPG_PRIVATE_KEY }}
+ run: |
+ cd $HOME
+ # NOTE TO DEVELOPERS: Make sure to never print this key directly to stdout!
+ echo $GIT_SECRET_GPG_PRIVATE_KEY | base64 --decode > ./git_secret_private_key.gpg
+ gpg --import ./git_secret_private_key.gpg
+ cd $GITHUB_WORKSPACE
+ git secret reveal
+
+ # Note that caching only works on non-forks.
+ - name: Build Oppia beta AAB (with caching, non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ env:
+ BAZEL_REMOTE_CACHE_URL: ${{ secrets.BAZEL_REMOTE_CACHE_URL }}
+ run: |
+ bazel build --compilation_mode=opt --remote_http_cache=$BAZEL_REMOTE_CACHE_URL --google_credentials=./config/oppia-dev-workflow-remote-cache-credentials.json -- //:oppia_beta
+
+ - name: Build Oppia beta AAB (without caching, or on a fork)
+ if: ${{ env.ENABLE_CACHING == 'false' || github.event.pull_request.head.repo.full_name != 'oppia/oppia-android' }}
+ run: |
+ bazel build --compilation_mode=opt -- //:oppia_beta
+
+ - name: Copy Oppia beta AAB for uploading
+ run: |
+ cp $GITHUB_WORKSPACE/bazel-bin/oppia_beta.aab /home/runner/work/oppia-android/oppia-android/
+
+ - uses: actions/upload-artifact@v2
+ with:
+ name: oppia_beta.aab
+ path: /home/runner/work/oppia-android/oppia-android/oppia_beta.aab
+
+ build_oppia_ga_aab:
+ name: Build Oppia AAB (GA flavor)
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-18.04]
+ env:
+ ENABLE_CACHING: false
+ CACHE_DIRECTORY: ~/.bazel_cache
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 9
+ uses: actions/setup-java@v1
+ with:
+ java-version: 9
+
+ - name: Set up Bazel
+ uses: abhinavsingh/setup-bazel@v3
+ with:
+ version: 4.0.0
+
+ - name: Set up build environment
+ uses: ./.github/actions/set-up-android-bazel-build-environment
+
+ # For reference on this & the later cache actions, see:
+ # https://github.com/actions/cache/issues/239#issuecomment-606950711 &
+ # https://github.com/actions/cache/issues/109#issuecomment-558771281. Note that these work
+ # with Bazel since Bazel can share the most recent cache from an unrelated build and still
+ # benefit from incremental build performance (assuming that actions/cache aggressively removes
+ # older caches due to the 5GB cache limit size & Bazel's large cache size).
+ - uses: actions/cache@v2
+ id: cache
+ with:
+ path: ${{ env.CACHE_DIRECTORY }}
+ key: ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-${{ github.sha }}
+ restore-keys: |
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-binary-
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-tests-
+ ${{ runner.os }}-${{ env.CACHE_DIRECTORY }}-bazel-
+
+ # This check is needed to ensure that Bazel's unbounded cache growth doesn't result in a
+ # situation where the cache never updates (e.g. due to exceeding GitHub's cache size limit)
+ # thereby only ever using the last successful cache version. This solution will result in a
+ # few slower CI actions around the time cache is detected to be too large, but it should
+ # incrementally improve thereafter.
+ - name: Ensure cache size
+ env:
+ BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
+ run: |
+ # See https://stackoverflow.com/a/27485157 for reference.
+ EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
+ CACHE_SIZE_MB=$(du -smc $EXPANDED_BAZEL_CACHE_PATH | grep total | cut -f1)
+ echo "Total size of Bazel cache (rounded up to MBs): $CACHE_SIZE_MB"
+ # Use a 4.5GB threshold since actions/cache compresses the results, and Bazel caches seem
+ # to only increase by a few hundred megabytes across changes for unrelated branches. This
+ # is also a reasonable upper-bound (local tests as of 2021-03-31 suggest that a full build
+ # of the codebase (e.g. //...) from scratch only requires a ~2.1GB uncompressed/~900MB
+ # compressed cache).
+ if [[ "$CACHE_SIZE_MB" -gt 4500 ]]; then
+ echo "Cache exceeds cut-off; resetting it (will result in a slow build)"
+ rm -rf $EXPANDED_BAZEL_CACHE_PATH
+ fi
+
+ - name: Configure Bazel to use a local cache
+ env:
+ BAZEL_CACHE_DIR: ${{ env.CACHE_DIRECTORY }}
+ run: |
+ EXPANDED_BAZEL_CACHE_PATH="${BAZEL_CACHE_DIR/#\~/$HOME}"
+ echo "Using $EXPANDED_BAZEL_CACHE_PATH as Bazel's cache path"
+ echo "build --disk_cache=$EXPANDED_BAZEL_CACHE_PATH" >> $HOME/.bazelrc
+ shell: bash
+
+ - name: Check Bazel environment
+ run: bazel info
+
+ # See https://git-secret.io/installation for details on installing git-secret. Note that the
+ # apt-get method isn't used since it's much slower to update & upgrade apt before installation
+ # versus just directly cloning & installing the project. Further, the specific version
+ # shouldn't matter since git-secret relies on a future-proof storage mechanism for secrets.
+ # This also uses a different directory to install git-secret to avoid requiring root access
+ # when running the git secret command.
+ - name: Install git-secret (non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ shell: bash
+ run: |
+ cd $HOME
+ mkdir -p $HOME/gitsecret
+ git clone https://github.com/sobolevn/git-secret.git git-secret
+ cd git-secret && make build
+ PREFIX="$HOME/gitsecret" make install
+ echo "$HOME/gitsecret" >> $GITHUB_PATH
+ echo "$HOME/gitsecret/bin" >> $GITHUB_PATH
+
+ - name: Decrypt secrets (non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ env:
+ GIT_SECRET_GPG_PRIVATE_KEY: ${{ secrets.GIT_SECRET_GPG_PRIVATE_KEY }}
+ run: |
+ cd $HOME
+ # NOTE TO DEVELOPERS: Make sure to never print this key directly to stdout!
+ echo $GIT_SECRET_GPG_PRIVATE_KEY | base64 --decode > ./git_secret_private_key.gpg
+ gpg --import ./git_secret_private_key.gpg
+ cd $GITHUB_WORKSPACE
+ git secret reveal
+
+ # Note that caching only works on non-forks.
+ - name: Build Oppia GA AAB (with caching, non-fork only)
+ if: ${{ env.ENABLE_CACHING == 'true' && github.event.pull_request.head.repo.full_name == 'oppia/oppia-android' }}
+ env:
+ BAZEL_REMOTE_CACHE_URL: ${{ secrets.BAZEL_REMOTE_CACHE_URL }}
+ run: |
+ bazel build --compilation_mode=opt --remote_http_cache=$BAZEL_REMOTE_CACHE_URL --google_credentials=./config/oppia-dev-workflow-remote-cache-credentials.json -- //:oppia_ga
+
+ - name: Build Oppia GA AAB (without caching, or on a fork)
+ if: ${{ env.ENABLE_CACHING == 'false' || github.event.pull_request.head.repo.full_name != 'oppia/oppia-android' }}
+ run: |
+ bazel build --compilation_mode=opt -- //:oppia_ga
+
+ - name: Copy Oppia GA AAB for uploading
+ run: |
+ cp $GITHUB_WORKSPACE/bazel-bin/oppia_ga.aab /home/runner/work/oppia-android/oppia-android/
+
+ - uses: actions/upload-artifact@v2
+ with:
+ name: oppia_ga.aab
+ path: /home/runner/work/oppia-android/oppia-android/oppia_ga.aab
diff --git a/app/BUILD.bazel b/app/BUILD.bazel
index 749d0b4e908..d20fc5793ea 100644
--- a/app/BUILD.bazel
+++ b/app/BUILD.bazel
@@ -575,6 +575,7 @@ android_library(
"//app/src/main/java/org/oppia/android/app/translation:app_language_resource_handler",
"//model/src/main/proto:interaction_object_java_proto_lite",
"//model/src/main/proto:thumbnail_java_proto_lite",
+ "//model/src/main/proto:version_java_proto_lite",
"//third_party:androidx_annotation_annotation",
"//third_party:androidx_constraintlayout_constraintlayout",
"//third_party:androidx_core_core",
@@ -824,6 +825,7 @@ TEST_DEPS = [
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/testing/activity:test_activity",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//app/src/main/java/org/oppia/android/app/utility/math:math_expression_accessibility_util",
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 186e5830ae5..233b4595399 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -85,6 +85,14 @@
android:label="@string/my_downloads_activity_title"
android:screenOrientation="portrait"
android:theme="@style/OppiaThemeWithoutActionBar" />
+
+
(R.id.beta_notice_dialog_preference_checkbox)
+ return AlertDialog.Builder(activity)
+ .setTitle(R.string.beta_notice_dialog_title)
+ .setView(contentView)
+ .setPositiveButton(R.string.beta_notice_dialog_close_button_text) { _, _ ->
+ betaNoticeClosedListener.onBetaNoticeOkayButtonClicked(preferenceCheckbox.isChecked)
+ }
+ .setCancelable(false)
+ .create()
+ .also { it.setCanceledOnTouchOutside(false) }
+ }
+}
diff --git a/app/src/main/java/org/oppia/android/app/deprecation/DeprecationNoticeExitAppListener.kt b/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeExitAppListener.kt
similarity index 82%
rename from app/src/main/java/org/oppia/android/app/deprecation/DeprecationNoticeExitAppListener.kt
rename to app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeExitAppListener.kt
index 020ac27ef8e..ff3d7dd1671 100644
--- a/app/src/main/java/org/oppia/android/app/deprecation/DeprecationNoticeExitAppListener.kt
+++ b/app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeExitAppListener.kt
@@ -1,4 +1,4 @@
-package org.oppia.android.app.deprecation
+package org.oppia.android.app.notice
/** Listener for when the app deprecation dialog is closed. */
interface DeprecationNoticeExitAppListener {
diff --git a/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeClosedListener.kt b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeClosedListener.kt
new file mode 100644
index 00000000000..715b7d06e6a
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeClosedListener.kt
@@ -0,0 +1,11 @@
+package org.oppia.android.app.notice
+
+/** Listener for when the general availability update dialog is closed. */
+interface GeneralAvailabilityUpgradeNoticeClosedListener {
+ /**
+ * Called when the notice dialog was closed.
+ *
+ * @param permanentlyDismiss whether the user never wants to see this notice again
+ */
+ fun onGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss: Boolean)
+}
diff --git a/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragment.kt b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragment.kt
new file mode 100644
index 00000000000..30ca45e7d1c
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragment.kt
@@ -0,0 +1,31 @@
+package org.oppia.android.app.notice
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import org.oppia.android.app.fragment.FragmentComponentImpl
+import org.oppia.android.app.fragment.InjectableDialogFragment
+import javax.inject.Inject
+
+/**
+ * Dialog fragment to be shown when the user may be unaware that they've updated from a pre-release
+ * version of the app to general availability.
+ */
+class GeneralAvailabilityUpgradeNoticeDialogFragment : InjectableDialogFragment() {
+ companion object {
+ /** Returns a new instance of [GeneralAvailabilityUpgradeNoticeDialogFragment]. */
+ fun newInstance(): GeneralAvailabilityUpgradeNoticeDialogFragment =
+ GeneralAvailabilityUpgradeNoticeDialogFragment()
+ }
+
+ @Inject lateinit var presenter: GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ (fragmentComponent as FragmentComponentImpl).inject(this)
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return presenter.handleOnCreateDialog()
+ }
+}
diff --git a/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt
new file mode 100644
index 00000000000..74cdca94c9d
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt
@@ -0,0 +1,42 @@
+package org.oppia.android.app.notice
+
+import android.app.Dialog
+import android.view.View
+import android.widget.CheckBox
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import org.oppia.android.R
+import javax.inject.Inject
+
+/**
+ * Presenter for the dialog that shows when the user has updated to the general availability version
+ * app is being used.
+ */
+class GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter @Inject constructor(
+ private val activity: AppCompatActivity
+) {
+ private val gaUpgradeNoticeClosedListener by lazy {
+ activity as GeneralAvailabilityUpgradeNoticeClosedListener
+ }
+
+ /** Handles dialog creation for the general availability update notice. */
+ fun handleOnCreateDialog(): Dialog {
+ val contentView =
+ View.inflate(
+ activity, R.layout.general_availability_upgrade_notice_dialog_content, /* root= */ null
+ )
+ val preferenceCheckbox =
+ contentView.findViewById(R.id.ga_update_notice_dialog_preference_checkbox)
+ return AlertDialog.Builder(activity)
+ .setTitle(R.string.general_availability_notice_dialog_title)
+ .setView(contentView)
+ .setPositiveButton(R.string.general_availability_notice_dialog_close_button_text) { _, _ ->
+ gaUpgradeNoticeClosedListener.onGaUpgradeNoticeOkayButtonClicked(
+ preferenceCheckbox.isChecked
+ )
+ }
+ .setCancelable(false)
+ .create()
+ .also { it.setCanceledOnTouchOutside(false) }
+ }
+}
diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/BUILD.bazel b/app/src/main/java/org/oppia/android/app/notice/testing/BUILD.bazel
new file mode 100644
index 00000000000..92f1a0ae62b
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/testing/BUILD.bazel
@@ -0,0 +1,34 @@
+"""
+Test-only utilities corresponding to app notices.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library")
+
+kt_android_library(
+ name = "beta_notice_dialog_frgment_test_activity",
+ testonly = True,
+ srcs = [
+ "BetaNoticeDialogFragmentTestActivity.kt",
+ ],
+ visibility = ["//app:app_testing_visibility"],
+ deps = [
+ "//app",
+ "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity",
+ ],
+)
+
+kt_android_library(
+ name = "general_availability_upgrade_notice_dialog_fragment_test_activity",
+ testonly = True,
+ srcs = [
+ "GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt",
+ ],
+ visibility = ["//app:app_testing_visibility"],
+ deps = [
+ "//app",
+ "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt
new file mode 100644
index 00000000000..7d0e612bb26
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt
@@ -0,0 +1,26 @@
+package org.oppia.android.app.notice.testing
+
+import android.os.Bundle
+import org.oppia.android.app.notice.BetaNoticeClosedListener
+import org.oppia.android.app.notice.BetaNoticeDialogFragment
+import org.oppia.android.app.testing.activity.TestActivity
+
+/** [TestActivity] for setting up a test environment for testing the beta notice dialog. */
+class BetaNoticeDialogFragmentTestActivity : TestActivity(), BetaNoticeClosedListener {
+ /**
+ * [BetaNoticeClosedListener] that must be initialized by the test, and is presumed to be a
+ * Mockito mock (though this is not, strictly speaking, required).
+ *
+ * This listener will be used as the callback for the dialog in response to UI operations.
+ */
+ lateinit var mockCallbackListener: BetaNoticeClosedListener
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ BetaNoticeDialogFragment.newInstance().showNow(supportFragmentManager, "beta_notice_dialog")
+ }
+
+ override fun onBetaNoticeOkayButtonClicked(permanentlyDismiss: Boolean) {
+ mockCallbackListener.onBetaNoticeOkayButtonClicked(permanentlyDismiss)
+ }
+}
diff --git a/app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt
new file mode 100644
index 00000000000..b378a876e41
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt
@@ -0,0 +1,28 @@
+package org.oppia.android.app.notice.testing
+
+import android.os.Bundle
+import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeClosedListener
+import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragment
+import org.oppia.android.app.testing.activity.TestActivity
+
+/** [TestActivity] for setting up a test environment for testing the GA upgrade notice dialog. */
+class GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity :
+ TestActivity(), GeneralAvailabilityUpgradeNoticeClosedListener {
+ /**
+ * [GeneralAvailabilityUpgradeNoticeClosedListener] that must be initialized by the test, and is
+ * presumed to be a Mockito mock (though this is not, strictly speaking, required).
+ *
+ * This listener will be used as the callback for the dialog in response to UI operations.
+ */
+ lateinit var mockCallbackListener: GeneralAvailabilityUpgradeNoticeClosedListener
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ GeneralAvailabilityUpgradeNoticeDialogFragment.newInstance()
+ .showNow(supportFragmentManager, "ga_upgrade_notice_dialog")
+ }
+
+ override fun onGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss: Boolean) {
+ mockCallbackListener.onGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss)
+ }
+}
diff --git a/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt
index 83859936edf..e0840ebdf83 100644
--- a/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/splash/SplashActivity.kt
@@ -6,10 +6,12 @@ import androidx.fragment.app.Fragment
import org.oppia.android.app.activity.ActivityComponent
import org.oppia.android.app.activity.ActivityComponentFactory
import org.oppia.android.app.activity.ActivityComponentImpl
-import org.oppia.android.app.deprecation.DeprecationNoticeExitAppListener
import org.oppia.android.app.fragment.FragmentComponent
import org.oppia.android.app.fragment.FragmentComponentBuilderInjector
import org.oppia.android.app.fragment.FragmentComponentFactory
+import org.oppia.android.app.notice.BetaNoticeClosedListener
+import org.oppia.android.app.notice.DeprecationNoticeExitAppListener
+import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeClosedListener
import javax.inject.Inject
/**
@@ -21,7 +23,12 @@ import javax.inject.Inject
* through their intents).
*/
class SplashActivity :
- AppCompatActivity(), FragmentComponentFactory, DeprecationNoticeExitAppListener {
+ AppCompatActivity(),
+ FragmentComponentFactory,
+ DeprecationNoticeExitAppListener,
+ BetaNoticeClosedListener,
+ GeneralAvailabilityUpgradeNoticeClosedListener {
+
private lateinit var activityComponent: ActivityComponent
@Inject
@@ -35,10 +42,16 @@ class SplashActivity :
splashActivityPresenter.handleOnCreate()
}
- override fun onCloseAppButtonClicked() = splashActivityPresenter.handleOnCloseAppButtonClicked()
-
override fun createFragmentComponent(fragment: Fragment): FragmentComponent {
val builderInjector = activityComponent as FragmentComponentBuilderInjector
return builderInjector.getFragmentComponentBuilderProvider().get().setFragment(fragment).build()
}
+
+ override fun onCloseAppButtonClicked() = splashActivityPresenter.handleOnCloseAppButtonClicked()
+
+ override fun onBetaNoticeOkayButtonClicked(permanentlyDismiss: Boolean) =
+ splashActivityPresenter.handleOnBetaNoticeOkayButtonClicked(permanentlyDismiss)
+
+ override fun onGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss: Boolean) =
+ splashActivityPresenter.handleOnGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss)
}
diff --git a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt
index fd02c1ea32b..5cbbc7df08c 100644
--- a/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt
@@ -1,18 +1,23 @@
package org.oppia.android.app.splash
-import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
-import androidx.lifecycle.LiveData
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Observer
-import androidx.lifecycle.Transformations
import org.oppia.android.R
import org.oppia.android.app.activity.ActivityScope
-import org.oppia.android.app.deprecation.AutomaticAppDeprecationNoticeDialogFragment
import org.oppia.android.app.model.AppStartupState
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode
import org.oppia.android.app.model.AppStartupState.StartupMode
+import org.oppia.android.app.model.BuildFlavor
+import org.oppia.android.app.notice.AutomaticAppDeprecationNoticeDialogFragment
+import org.oppia.android.app.notice.BetaNoticeDialogFragment
+import org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragment
import org.oppia.android.app.onboarding.OnboardingActivity
import org.oppia.android.app.profile.ProfileChooserActivity
import org.oppia.android.app.translation.AppLanguageLocaleHandler
+import org.oppia.android.app.utility.LifecycleSafeTimerFactory
+import org.oppia.android.databinding.SplashActivityBinding
import org.oppia.android.domain.locale.LocaleController
import org.oppia.android.domain.onboarding.AppStartupStateController
import org.oppia.android.domain.oppialogger.OppiaLogger
@@ -26,6 +31,8 @@ import org.oppia.android.util.locale.OppiaLocale
import javax.inject.Inject
private const val AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG = "auto_deprecation_notice_dialog"
+private const val BETA_NOTICE_DIALOG_FRAGMENT_TAG = "beta_notice_dialog"
+private const val GA_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG = "general_availability_update_notice_dialog"
private const val SPLASH_INIT_STATE_DATA_PROVIDER_ID = "splash_init_state_data_provider"
/** The presenter for [SplashActivity]. */
@@ -37,15 +44,21 @@ class SplashActivityPresenter @Inject constructor(
private val primeTopicAssetsController: PrimeTopicAssetsController,
private val translationController: TranslationController,
private val localeController: LocaleController,
- private val appLanguageLocaleHandler: AppLanguageLocaleHandler
+ private val appLanguageLocaleHandler: AppLanguageLocaleHandler,
+ private val lifecycleSafeTimerFactory: LifecycleSafeTimerFactory,
+ private val currentBuildFlavor: BuildFlavor
) {
+ lateinit var startupMode: StartupMode
fun handleOnCreate() {
- activity.setContentView(R.layout.splash_activity)
- activity.window.setFlags(
- WindowManager.LayoutParams.FLAG_FULLSCREEN,
- WindowManager.LayoutParams.FLAG_FULLSCREEN
- )
+ DataBindingUtil.setContentView(
+ activity, R.layout.splash_activity
+ ).apply {
+ isOnDeveloperFlavor = currentBuildFlavor == BuildFlavor.DEVELOPER
+ isOnAlphaFlavor = currentBuildFlavor == BuildFlavor.ALPHA
+ isOnBetaFlavor = currentBuildFlavor == BuildFlavor.BETA
+ }
+
// Initiate download support before any additional processing begins.
primeTopicAssetsController.downloadAssets(R.style.OppiaAlertDialogTheme)
subscribeToOnboardingFlow()
@@ -57,44 +70,49 @@ class SplashActivityPresenter @Inject constructor(
activity.finish()
}
+ /** Handles cases when the user dismisses the beta notice dialog. */
+ fun handleOnBetaNoticeOkayButtonClicked(permanentlyDismiss: Boolean) {
+ if (permanentlyDismiss) {
+ appStartupStateController.dismissBetaNoticesPermanently()
+ }
+ processStartupMode()
+ }
+
+ /** Handles cases when the user dismisses the general availability update notice dialog. */
+ fun handleOnGaUpgradeNoticeOkayButtonClicked(permanentlyDismiss: Boolean) {
+ if (permanentlyDismiss) {
+ appStartupStateController.dismissGaUpgradeNoticesPermanently()
+ }
+ processStartupMode()
+ }
+
private fun subscribeToOnboardingFlow() {
- val liveData = computeInitStateLiveData()
+ val liveData = computeInitStateDataProvider().toLiveData()
liveData.observe(
activity,
- object : Observer {
- override fun onChanged(initState: SplashInitState) {
- // It's possible for the observer to still be active & change due to the next activity
- // causing a notification to be posted. That's always invalid to process here: the splash
- // activity should never do anything after its initial state since it always finishes (or
- // in the case of the deprecation dialog, blocks) the activity.
- liveData.removeObserver(this)
-
- // First, initialize the app's initial locale. Note that since the activity can be
- // reopened, it's possible for this to be initialized more than once.
- if (!appLanguageLocaleHandler.isInitialized()) {
- appLanguageLocaleHandler.initializeLocale(initState.displayLocale)
- }
-
- // Second, route the user to the correct destination.
- when (initState.startupMode) {
- StartupMode.USER_IS_ONBOARDED -> {
- activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity))
- activity.finish()
- }
- StartupMode.APP_IS_DEPRECATED -> {
- if (getDeprecationNoticeDialogFragment() == null) {
- activity.supportFragmentManager.beginTransaction()
- .add(
- AutomaticAppDeprecationNoticeDialogFragment.newInstance(),
- AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG
- ).commitNow()
+ object : Observer> {
+ override fun onChanged(initStateResult: AsyncResult) {
+ when (initStateResult) {
+ is AsyncResult.Pending -> {
+ // Ensure that pending states last no longer than 5 seconds. In cases where the app
+ // enters a bad state, this ensures that the user doesn't become stuck on the splash
+ // screen.
+ lifecycleSafeTimerFactory.createTimer(timeoutMillis = 5000).observe(activity) {
+ processInitState(SplashInitState.computeDefault(localeController))
}
}
- else -> {
- // In all other cases (including errors when the startup state fails to load or is
- // defaulted), assume the user needs to be onboarded.
- activity.startActivity(OnboardingActivity.createOnboardingActivity(activity))
- activity.finish()
+ is AsyncResult.Failure -> {
+ oppiaLogger.e(
+ "SplashActivity", "Failed to compute initial state", initStateResult.error
+ )
+ }
+ is AsyncResult.Success -> {
+ // It's possible for the observer to still be active & change due to the next activity
+ // causing a notification to be posted. That's always invalid to process here: the
+ // splash activity should never do anything after its initial state since it always
+ // finishes (or in the case of the deprecation dialog, blocks) the activity.
+ liveData.removeObserver(this)
+ processInitState(initStateResult.value)
}
}
}
@@ -102,47 +120,93 @@ class SplashActivityPresenter @Inject constructor(
)
}
+ private fun processInitState(initState: SplashInitState) {
+ // First, initialize the app's initial locale. Note that since the activity can be
+ // reopened, it's possible for this to be initialized more than once.
+ if (!appLanguageLocaleHandler.isInitialized()) {
+ appLanguageLocaleHandler.initializeLocale(initState.displayLocale)
+ }
+
+ // Second, prepare to route the user to the correct destination.
+ startupMode = initState.appStartupState.startupMode
+
+ // Third, show any dismissible notices (if the app isn't deprecated).
+ if (startupMode != StartupMode.APP_IS_DEPRECATED) {
+ when (initState.appStartupState.buildFlavorNoticeMode) {
+ BuildFlavorNoticeMode.FLAVOR_NOTICE_MODE_UNSPECIFIED, BuildFlavorNoticeMode.NO_NOTICE,
+ BuildFlavorNoticeMode.UNRECOGNIZED, null -> {
+ // No notice should be shown. However, when a pre-release version of the app is active
+ // that changes the splash screen have it wait a bit longer so that the build flavor can
+ // be clearly seen. The developer build isn't part of the wait to ensure fast startup
+ // times (for development purposes).
+ when (currentBuildFlavor) {
+ BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED,
+ BuildFlavor.TESTING, BuildFlavor.DEVELOPER, BuildFlavor.GENERAL_AVAILABILITY ->
+ processStartupMode()
+ BuildFlavor.ALPHA, BuildFlavor.BETA -> {
+ lifecycleSafeTimerFactory.createTimer(timeoutMillis = 2000).observe(activity) {
+ processStartupMode()
+ }
+ }
+ }
+ }
+ BuildFlavorNoticeMode.SHOW_BETA_NOTICE ->
+ showDialog(BETA_NOTICE_DIALOG_FRAGMENT_TAG, BetaNoticeDialogFragment::newInstance)
+ BuildFlavorNoticeMode.SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE -> {
+ showDialog(
+ GA_UPDATE_NOTICE_DIALOG_FRAGMENT_TAG,
+ GeneralAvailabilityUpgradeNoticeDialogFragment::newInstance
+ )
+ }
+ }
+ } else processStartupMode()
+ }
+
+ private fun processStartupMode() {
+ when (startupMode) {
+ StartupMode.USER_IS_ONBOARDED -> {
+ activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity))
+ activity.finish()
+ }
+ StartupMode.APP_IS_DEPRECATED -> {
+ showDialog(
+ AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG,
+ AutomaticAppDeprecationNoticeDialogFragment::newInstance
+ )
+ }
+ else -> {
+ // In all other cases (including errors when the startup state fails to load or is
+ // defaulted), assume the user needs to be onboarded.
+ activity.startActivity(OnboardingActivity.createOnboardingActivity(activity))
+ activity.finish()
+ }
+ }
+ }
+
private fun computeInitStateDataProvider(): DataProvider {
val startupStateDataProvider = appStartupStateController.getAppStartupState()
val systemAppLanguageLocaleDataProvider = translationController.getSystemLanguageLocale()
return startupStateDataProvider.combineWith(
systemAppLanguageLocaleDataProvider, SPLASH_INIT_STATE_DATA_PROVIDER_ID
) { startupState, systemAppLanguageLocale ->
- SplashInitState(startupState.startupMode, systemAppLanguageLocale)
+ SplashInitState(startupState, systemAppLanguageLocale)
}
}
- private fun computeInitStateLiveData(): LiveData =
- Transformations.map(computeInitStateDataProvider().toLiveData(), ::processInitState)
-
- private fun processInitState(
- initStateResult: AsyncResult
- ): SplashInitState {
- // If there's an error loading the data, assume the default.
- return when (initStateResult) {
- is AsyncResult.Failure -> {
- oppiaLogger.e("SplashActivity", "Failed to compute initial state", initStateResult.error)
- SplashInitState.computeDefault(localeController)
- }
- is AsyncResult.Pending -> SplashInitState.computeDefault(localeController)
- is AsyncResult.Success -> initStateResult.value
+ private inline fun showDialog(tag: String, createFragment: () -> T) {
+ if (activity.supportFragmentManager.findFragmentByTag(tag) as? T == null) {
+ activity.supportFragmentManager.beginTransaction().add(createFragment(), tag).commitNow()
}
}
- private fun getDeprecationNoticeDialogFragment(): AutomaticAppDeprecationNoticeDialogFragment? {
- return activity.supportFragmentManager.findFragmentByTag(
- AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG
- ) as? AutomaticAppDeprecationNoticeDialogFragment
- }
-
private data class SplashInitState(
- val startupMode: StartupMode,
+ val appStartupState: AppStartupState,
val displayLocale: OppiaLocale.DisplayLocale
) {
companion object {
fun computeDefault(localeController: LocaleController): SplashInitState {
return SplashInitState(
- startupMode = AppStartupState.getDefaultInstance().startupMode,
+ appStartupState = AppStartupState.getDefaultInstance(),
displayLocale = localeController.reconstituteDisplayLocale(
localeController.getLikelyDefaultAppStringLocaleContext()
)
diff --git a/app/src/main/res/drawable/full_oppia_logo.xml b/app/src/main/res/drawable/full_oppia_logo.xml
new file mode 100644
index 00000000000..f99bc2df503
--- /dev/null
+++ b/app/src/main/res/drawable/full_oppia_logo.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/beta_notice_dialog_content.xml b/app/src/main/res/layout/beta_notice_dialog_content.xml
new file mode 100755
index 00000000000..b456257a2b6
--- /dev/null
+++ b/app/src/main/res/layout/beta_notice_dialog_content.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/general_availability_upgrade_notice_dialog_content.xml b/app/src/main/res/layout/general_availability_upgrade_notice_dialog_content.xml
new file mode 100755
index 00000000000..f7ecc971fd7
--- /dev/null
+++ b/app/src/main/res/layout/general_availability_upgrade_notice_dialog_content.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/splash_activity.xml b/app/src/main/res/layout/splash_activity.xml
index c6a3978120e..f8d6bb317d5 100644
--- a/app/src/main/res/layout/splash_activity.xml
+++ b/app/src/main/res/layout/splash_activity.xml
@@ -1,7 +1,54 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/untranslated_strings.xml b/app/src/main/res/values/untranslated_strings.xml
index a0482e4ba72..ca0ce552a2c 100644
--- a/app/src/main/res/values/untranslated_strings.xml
+++ b/app/src/main/res/values/untranslated_strings.xml
@@ -79,4 +79,6 @@
Please connect to a WiFi or Cellular network in order to upload profile data.
%s installation ID
%s\'s learner ID
+
+ Test-only activity
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt
index 5918ad0614f..80fbbbbf0ec 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt
@@ -56,6 +56,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -835,7 +836,7 @@ class AdministratorControlsActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt
index ae58e53a313..95ee91fd9ef 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt
@@ -47,6 +47,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -613,7 +614,7 @@ class AdministratorControlsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt
index 4d3875e7a30..ea6982c549f 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -291,7 +292,7 @@ class AppVersionActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/BUILD.bazel
index 32688f31e7b..b64b7f60e82 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/BUILD.bazel
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/BUILD.bazel
@@ -14,6 +14,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//testing",
"//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule",
@@ -45,6 +46,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//testing",
"//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule",
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityTest.kt
index e0b44a0a5fa..37fbe33d836 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivityTest.kt
@@ -28,6 +28,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -199,7 +200,8 @@ class ProfileAndDeviceIdActivityTest {
AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
SyncStatusModule::class, SplitScreenInteractionModule::class,
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
- MathEquationInputModule::class, MetricLogSchedulerModule::class
+ MathEquationInputModule::class, MetricLogSchedulerModule::class,
+ TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentTest.kt
index 1c99c20e542..4f5b16a23e1 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragmentTest.kt
@@ -42,6 +42,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -625,7 +626,8 @@ class ProfileAndDeviceIdFragmentTest {
AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
SyncStatusModule::class, SplitScreenInteractionModule::class,
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
- MathEquationInputModule::class, MetricLogSchedulerModule::class
+ MathEquationInputModule::class, MetricLogSchedulerModule::class,
+ TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityTest.kt
index dc1c7fd5a82..f50c687c987 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -514,7 +515,7 @@ class CompletedStoryListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt
index e9fd803c1e3..c4082c8733a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.LessonThumbnail
@@ -172,7 +173,7 @@ class LessonThumbnailImageViewTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/BUILD.bazel
index d7571e7f1ac..37a21163500 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/BUILD.bazel
+++ b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/BUILD.bazel
@@ -17,6 +17,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//testing",
"//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action",
diff --git a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt
index 100c5a437f8..f5a54d6e7d1 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt
@@ -26,6 +26,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.CustomSchemaValue
@@ -1774,7 +1775,7 @@ class MathExpressionInteractionsViewTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/databinding/BUILD.bazel
index e6c820172aa..019f1ff1983 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/BUILD.bazel
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/BUILD.bazel
@@ -77,6 +77,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -113,6 +114,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -149,6 +151,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -185,6 +188,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -221,6 +225,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -257,6 +262,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
@@ -293,6 +299,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/DrawableBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/DrawableBindingAdaptersTest.kt
index 1bee02bb647..da2853a4422 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/DrawableBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/DrawableBindingAdaptersTest.kt
@@ -26,6 +26,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.DrawableBindingAdapters.setBackgroundColor
import org.oppia.android.app.databinding.DrawableBindingAdapters.setBackgroundDrawable
import org.oppia.android.app.databinding.DrawableBindingAdapters.setTopBackgroundDrawable
@@ -183,7 +184,7 @@ class DrawableBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt
index 6559b4e211b..fe003c940f8 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.ImageViewBindingAdapters.setPlayStateDrawable
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -225,7 +226,7 @@ class ImageViewBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** Create a TestApplicationComponent. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/MarginBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/MarginBindingAdaptersTest.kt
index 51af7e5e519..212323d5fdb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/MarginBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/MarginBindingAdaptersTest.kt
@@ -32,6 +32,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.MarginBindingAdapters.setLayoutMarginEnd
import org.oppia.android.app.databinding.MarginBindingAdapters.setLayoutMarginStart
import org.oppia.android.app.devoptions.DeveloperOptionsModule
@@ -313,7 +314,7 @@ class MarginBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** Create a TestApplicationComponent. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerMarginBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerMarginBindingAdaptersTest.kt
index 8f24ad52f76..e9603c8d4c0 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerMarginBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerMarginBindingAdaptersTest.kt
@@ -32,6 +32,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.StateAssemblerMarginBindingAdapters.setExplorationSplitViewMargin
import org.oppia.android.app.databinding.StateAssemblerMarginBindingAdapters.setExplorationViewMargin
import org.oppia.android.app.databinding.StateAssemblerMarginBindingAdapters.setQuestionSplitViewMargin
@@ -501,7 +502,7 @@ class StateAssemblerMarginBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** Create a TestApplicationComponent. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerPaddingBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerPaddingBindingAdaptersTest.kt
index d4c2448a4db..ce12765a684 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerPaddingBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/StateAssemblerPaddingBindingAdaptersTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.StateAssemblerPaddingBindingAdapters.setExplorationSplitViewPadding
import org.oppia.android.app.databinding.StateAssemblerPaddingBindingAdapters.setExplorationViewPadding
import org.oppia.android.app.databinding.StateAssemblerPaddingBindingAdapters.setQuestionSplitViewPadding
@@ -499,7 +500,7 @@ class StateAssemblerPaddingBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/TextViewBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/TextViewBindingAdaptersTest.kt
index ebd73e65eda..aff64f8daa1 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/TextViewBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/TextViewBindingAdaptersTest.kt
@@ -24,6 +24,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.TextViewBindingAdapters.setDrawableEndCompat
import org.oppia.android.app.databinding.TextViewBindingAdapters.setProfileDataText
import org.oppia.android.app.databinding.TextViewBindingAdapters.setProfileLastVisitedText
@@ -332,7 +333,7 @@ class TextViewBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt
index 5127d9aec73..2275048ed95 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.databinding.ViewBindingAdapters.setRotationAnimation
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -233,7 +234,7 @@ class ViewBindingAdaptersTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** Create a TestApplicationComponent. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityTest.kt
index f5be1badb63..78e2f796262 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityTest.kt
@@ -49,6 +49,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.mathexpressionparser.MathExpressionParserActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView
@@ -325,7 +326,7 @@ class DeveloperOptionsActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt
index 98130794bda..b1e1c4215b4 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt
@@ -38,6 +38,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.forcenetworktype.ForceNetworkTypeActivity
import org.oppia.android.app.devoptions.markchapterscompleted.MarkChaptersCompletedActivity
import org.oppia.android.app.devoptions.markstoriescompleted.MarkStoriesCompletedActivity
@@ -656,7 +657,7 @@ class DeveloperOptionsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt
index 1f3326161e7..932a971c13a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.markchapterscompleted.MarkChaptersCompletedActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.shim.ViewBindingShimModule
@@ -187,7 +188,7 @@ class MarkChaptersCompletedActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt
index 62272000673..b2ba91623d0 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.markchapterscompleted.testing.MarkChaptersCompletedTestActivity
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -915,7 +916,7 @@ class MarkChaptersCompletedFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt
index b4b998b9f7c..5978a29ce69 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.markstoriescompleted.MarkStoriesCompletedActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.shim.ViewBindingShimModule
@@ -187,7 +188,7 @@ class MarkStoriesCompletedActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt
index 9613d6d6729..e2ab546bb6b 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.markstoriescompleted.testing.MarkStoriesCompletedTestActivity
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -592,7 +593,7 @@ class MarkStoriesCompletedFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt
index 5107bd10697..13a792f1c21 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.marktopicscompleted.MarkTopicsCompletedActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.shim.ViewBindingShimModule
@@ -187,7 +188,7 @@ class MarkTopicsCompletedActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt
index 7ddd3a668ac..a13f699941d 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.marktopicscompleted.testing.MarkTopicsCompletedTestActivity
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -562,7 +563,7 @@ class MarkTopicsCompletedFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt
index 2104922adec..d76f14aabf0 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt
@@ -28,6 +28,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.vieweventlogs.ViewEventLogsActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.shim.ViewBindingShimModule
@@ -178,7 +179,8 @@ class ViewEventLogsActivityTest {
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
SyncStatusModule::class, MetricLogSchedulerModule::class,
- PerformanceMetricsAssessorModule::class, PerformanceMetricsConfigurationsModule::class
+ PerformanceMetricsAssessorModule::class, PerformanceMetricsConfigurationsModule::class,
+ TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt
index e10ba410943..907042b27ac 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.vieweventlogs.testing.ViewEventLogsTestActivity
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView
@@ -578,7 +579,7 @@ class ViewEventLogsFragmentTest {
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
MetricLogSchedulerModule::class, PerformanceMetricsAssessorModule::class,
- PerformanceMetricsConfigurationsModule::class
+ PerformanceMetricsConfigurationsModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityTest.kt
index 1647b6b5dee..1acc5d39820 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivityTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -179,7 +180,7 @@ class ForceNetworkTypeActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** [ApplicationComponent] for [ForceNetworkTypeActivityTest]. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentTest.kt
index d6c30067abe..0b63f3edacb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragmentTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.devoptions.forcenetworktype.testing.ForceNetworkTypeTestActivity
@@ -395,7 +396,7 @@ class ForceNetworkTypeFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
/** [ApplicationComponent] for [ForceNetworkTypeFragmentTest]. */
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/BUILD.bazel
index b0d8810bf89..e20913f64aa 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/BUILD.bazel
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/BUILD.bazel
@@ -17,6 +17,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//testing",
"//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action",
@@ -47,6 +48,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/testing/activity:test_activity",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//testing",
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityTest.kt
index e9a86da55ae..6a207e03bd8 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivityTest.kt
@@ -25,6 +25,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -163,7 +164,7 @@ class MathExpressionParserActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt
index 6bf88c29bf3..4f3a4f62600 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -1413,7 +1414,7 @@ class MathExpressionParserFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt
index e6f86c7cbbf..33fd5eeddb9 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.faq.FAQListActivity
@@ -247,7 +248,7 @@ class FAQListFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt
index 79c2ab467fb..1c354ef9c98 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt
@@ -32,6 +32,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.faq.faqsingle.FAQSingleActivity
@@ -225,7 +226,7 @@ class FAQSingleActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt
index 66b90ab902a..a9238755789 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.faq.FAQListActivity
@@ -152,7 +153,7 @@ class FaqListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt
index 659279b6035..69c195e66e7 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -154,7 +155,7 @@ class HelpActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt
index 085d91bda44..1c7cd066ce9 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt
@@ -44,6 +44,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.faq.FAQListActivity
@@ -1442,7 +1443,7 @@ class HelpFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
index 99e47450a8a..b229732f377 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
@@ -50,6 +50,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedActivity
@@ -1811,7 +1812,7 @@ class HomeActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt
index 672069768fd..f086e9920d2 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt
@@ -46,6 +46,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedActivity
@@ -1506,7 +1507,7 @@ class RecentlyPlayedFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt
index 7e6cedb6445..f910210e0ac 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt
@@ -19,6 +19,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.topiclist.TopicSummaryViewModel
@@ -373,7 +374,7 @@ class TopicSummaryViewModelTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt
index 7505e4d774c..95c188b9600 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt
@@ -20,6 +20,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -357,7 +358,7 @@ class WelcomeViewModelTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt
index aee3ce36441..15988155b7a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt
@@ -19,6 +19,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.PromotedActivityList
@@ -369,7 +370,7 @@ class PromotedStoryListViewModelTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt
index 6208ea3fbec..0e4f82c366e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt
@@ -19,6 +19,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.PromotedStory
@@ -379,7 +380,7 @@ class PromotedStoryViewModelTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt
index a9b9c815a49..d1b682cbb0b 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt
@@ -20,6 +20,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -142,7 +143,7 @@ class MyDownloadsActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt
index a1aa44be743..330171bab0c 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -233,7 +234,7 @@ class MyDownloadsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel
new file mode 100644
index 00000000000..a58c530be9f
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel
@@ -0,0 +1,81 @@
+"""
+Tests for notices shown in the app.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//app:app_test.bzl", "app_test")
+load("//app:test_with_resources.bzl", "test_with_resources")
+
+app_test(
+ name = "BetaNoticeDialogFragmentTest",
+ processed_src = test_with_resources("BetaNoticeDialogFragmentTest.kt"),
+ test_class = "org.oppia.android.app.notice.BetaNoticeDialogFragmentTest",
+ deps = [
+ ":dagger",
+ "//app",
+ "//app/src/main/java/org/oppia/android/app/application:application_component",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
+ "//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
+ "//app/src/main/java/org/oppia/android/app/notice/testing:beta_notice_dialog_frgment_test_activity",
+ "//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
+ "//data/src/main/java/org/oppia/android/data/backends/gae:prod_module",
+ "//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
+ "//testing",
+ "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule",
+ "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/threading:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/time:test_module",
+ "//third_party:androidx_test_espresso_espresso-core",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ "//utility/src/main/java/org/oppia/android/util/accessibility:test_module",
+ "//utility/src/main/java/org/oppia/android/util/caching:asset_prod_module",
+ "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module",
+ "//utility/src/main/java/org/oppia/android/util/locale/testing:test_module",
+ "//utility/src/main/java/org/oppia/android/util/logging:prod_module",
+ "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module",
+ ],
+)
+
+app_test(
+ name = "GeneralAvailabilityUpgradeNoticeDialogFragmentTest",
+ processed_src = test_with_resources("GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt"),
+ test_class = "org.oppia.android.app.notice.GeneralAvailabilityUpgradeNoticeDialogFragmentTest",
+ deps = [
+ ":dagger",
+ "//app",
+ "//app/src/main/java/org/oppia/android/app/application:application_component",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
+ "//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
+ "//app/src/main/java/org/oppia/android/app/notice/testing:general_availability_upgrade_notice_dialog_fragment_test_activity",
+ "//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
+ "//data/src/main/java/org/oppia/android/data/backends/gae:prod_module",
+ "//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
+ "//testing",
+ "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule",
+ "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/threading:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/time:test_module",
+ "//third_party:androidx_test_espresso_espresso-core",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ "//utility/src/main/java/org/oppia/android/util/accessibility:test_module",
+ "//utility/src/main/java/org/oppia/android/util/caching:asset_prod_module",
+ "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module",
+ "//utility/src/main/java/org/oppia/android/util/locale/testing:test_module",
+ "//utility/src/main/java/org/oppia/android/util/logging:prod_module",
+ "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt
new file mode 100644
index 00000000000..0a694c49b2b
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt
@@ -0,0 +1,278 @@
+package org.oppia.android.app.notice
+
+import android.app.Application
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.PositionAssertions.isCompletelyBelow
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.RootMatchers.isDialog
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import org.hamcrest.Matcher
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.oppia.android.R
+import org.oppia.android.app.activity.ActivityComponent
+import org.oppia.android.app.activity.ActivityComponentFactory
+import org.oppia.android.app.application.ApplicationComponent
+import org.oppia.android.app.application.ApplicationInjector
+import org.oppia.android.app.application.ApplicationInjectorProvider
+import org.oppia.android.app.application.ApplicationModule
+import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
+import org.oppia.android.app.devoptions.DeveloperOptionsModule
+import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.notice.testing.BetaNoticeDialogFragmentTestActivity
+import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
+import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.topic.PracticeTabModule
+import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
+import org.oppia.android.data.backends.gae.NetworkConfigProdModule
+import org.oppia.android.data.backends.gae.NetworkModule
+import org.oppia.android.domain.classify.InteractionsModule
+import org.oppia.android.domain.classify.rules.algebraicexpressioninput.AlgebraicExpressionInputModule
+import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule
+import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule
+import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule
+import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule
+import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule
+import org.oppia.android.domain.classify.rules.mathequationinput.MathEquationInputModule
+import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule
+import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule
+import org.oppia.android.domain.classify.rules.numericexpressioninput.NumericExpressionInputModule
+import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule
+import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule
+import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule
+import org.oppia.android.domain.exploration.lightweightcheckpointing.ExplorationStorageModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionConfigFastShowTestModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionProdModule
+import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule
+import org.oppia.android.domain.oppialogger.LogStorageModule
+import org.oppia.android.domain.oppialogger.LoggingIdentifierModule
+import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule
+import org.oppia.android.domain.oppialogger.logscheduler.MetricLogSchedulerModule
+import org.oppia.android.domain.oppialogger.loguploader.LogReportWorkerModule
+import org.oppia.android.domain.platformparameter.PlatformParameterModule
+import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule
+import org.oppia.android.domain.question.QuestionModule
+import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.TestImageLoaderModule
+import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
+import org.oppia.android.testing.robolectric.RobolectricModule
+import org.oppia.android.testing.threading.TestCoroutineDispatchers
+import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.testing.time.FakeOppiaClockModule
+import org.oppia.android.util.accessibility.AccessibilityTestModule
+import org.oppia.android.util.caching.AssetModule
+import org.oppia.android.util.caching.testing.CachingTestModule
+import org.oppia.android.util.gcsresource.GcsResourceModule
+import org.oppia.android.util.locale.LocaleProdModule
+import org.oppia.android.util.logging.LoggerModule
+import org.oppia.android.util.logging.SyncStatusModule
+import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule
+import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule
+import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
+import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
+import org.oppia.android.util.parser.image.ImageParsingModule
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [BetaNoticeDialogFragment]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@Config(
+ application = BetaNoticeDialogFragmentTest.TestApplication::class, qualifiers = "port-xxhdpi"
+)
+@LooperMode(LooperMode.Mode.PAUSED)
+class BetaNoticeDialogFragmentTest {
+ @get:Rule
+ val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
+ @field:[Rule JvmField] val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Mock
+ lateinit var mockBetaNoticeClosedListener: BetaNoticeClosedListener
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testFragment_hasExpectedTitle() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ onDialogView(withText(R.string.beta_notice_dialog_title)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testFragment_hasExpectedContentMessageTextUnderTitle() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ onDialogView(withId(R.id.beta_notice_dialog_message)).check(matches(isDisplayed()))
+ onDialogView(withId(R.id.beta_notice_dialog_message))
+ .check(matches(withText(R.string.beta_notice_dialog_message)))
+ onDialogView(withId(R.id.beta_notice_dialog_message))
+ .check(isCompletelyBelow(withText(R.string.beta_notice_dialog_title)))
+ }
+ }
+
+ @Test
+ fun testFragment_hasDoNotShowAgainCheckboxUnderContentMessage() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+ .check(matches(isDisplayed()))
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+ .check(matches(withText(R.string.beta_notice_dialog_do_not_show_again_text)))
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+ .check(isCompletelyBelow(withId(R.id.beta_notice_dialog_message)))
+ }
+ }
+
+ @Test
+ fun testFragment_hasExpectedAcknowledgementButtonUnderDoNotShowAgainCheckbox() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text))
+ .check(matches(isDisplayed()))
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text))
+ .check(isCompletelyBelow(withId(R.id.beta_notice_dialog_preference_checkbox)))
+ }
+ }
+
+ @Test
+ fun testFragment_clickAcknowledgeButton_callsCallbackListenerWithFalse() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ clickOnDialogView(withText(R.string.beta_notice_dialog_close_button_text))
+
+ verify(mockBetaNoticeClosedListener).onBetaNoticeOkayButtonClicked(false)
+ }
+ }
+
+ @Test
+ fun testFragment_clickDoNotShowCheckbox_clickAcknowledgeButton_callsCallbackListenerWithTrue() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ clickOnDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+
+ clickOnDialogView(withText(R.string.beta_notice_dialog_close_button_text))
+
+ verify(mockBetaNoticeClosedListener).onBetaNoticeOkayButtonClicked(true)
+ }
+ }
+
+ @Test
+ fun testFragment_toggleCheckbox_clickAcknowledgeButton_callsCallbackListenerWithFalse() {
+ launchBetaNoticeDialogFragmentTestActivity {
+ // Select, then deselect, the checkbox before closing the dialog.
+ clickOnDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+ clickOnDialogView(withId(R.id.beta_notice_dialog_preference_checkbox))
+
+ clickOnDialogView(withText(R.string.beta_notice_dialog_close_button_text))
+
+ verify(mockBetaNoticeClosedListener).onBetaNoticeOkayButtonClicked(false)
+ }
+ }
+
+ private fun launchBetaNoticeDialogFragmentTestActivity(testBlock: () -> Unit) {
+ // Launch the test activity, but make sure that it's properly set up & time is given for it to
+ // initialize.
+ ActivityScenario.launch(BetaNoticeDialogFragmentTestActivity::class.java).use { scenario ->
+ scenario.onActivity { it.mockCallbackListener = mockBetaNoticeClosedListener }
+ testCoroutineDispatchers.runCurrent()
+ testBlock()
+ }
+ }
+
+ private fun clickOnDialogView(matcher: Matcher) {
+ onDialogView(matcher).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ private companion object {
+ private fun onDialogView(matcher: Matcher) = onView(matcher).inRoot(isDialog())
+ }
+
+ @Singleton
+ @Component(
+ modules = [
+ RobolectricModule::class, PlatformParameterModule::class,
+ TestDispatcherModule::class, ApplicationModule::class, LoggerModule::class,
+ ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class,
+ MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class,
+ NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class,
+ ImageClickInputModule::class, InteractionsModule::class, GcsResourceModule::class,
+ TestImageLoaderModule::class, ImageParsingModule::class, HtmlParserEntityTypeModule::class,
+ QuestionModule::class, TestLogReportingModule::class, AccessibilityTestModule::class,
+ LogStorageModule::class, PrimeTopicAssetsControllerModule::class,
+ ExpirationMetaDataRetrieverModule::class, ViewBindingShimModule::class,
+ RatioInputModule::class, ApplicationStartupListenerModule::class,
+ HintsAndSolutionConfigFastShowTestModule::class, HintsAndSolutionProdModule::class,
+ WorkManagerConfigurationModule::class, LogReportWorkerModule::class,
+ FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class,
+ DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class,
+ ExplorationStorageModule::class, NetworkConnectionUtilDebugModule::class,
+ NetworkConnectionDebugUtilModule::class, NetworkModule::class, NetworkConfigProdModule::class,
+ AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
+ PlatformParameterSingletonModule::class, NumericExpressionInputModule::class,
+ AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
+ SplitScreenInteractionModule::class, LoggingIdentifierModule::class,
+ ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class,
+ CachingTestModule::class, MetricLogSchedulerModule::class
+ ]
+ )
+ interface TestApplicationComponent : ApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: BetaNoticeDialogFragmentTest)
+ }
+
+ class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
+ private val component: TestApplicationComponent by lazy {
+ DaggerBetaNoticeDialogFragmentTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: BetaNoticeDialogFragmentTest) = component.inject(test)
+
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
+ return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
+ }
+
+ override fun getApplicationInjector(): ApplicationInjector = component
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt
new file mode 100644
index 00000000000..4207a1d5e78
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt
@@ -0,0 +1,285 @@
+package org.oppia.android.app.notice
+
+import android.app.Application
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.PositionAssertions.isCompletelyBelow
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.RootMatchers.isDialog
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import org.hamcrest.Matcher
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.oppia.android.R
+import org.oppia.android.app.activity.ActivityComponent
+import org.oppia.android.app.activity.ActivityComponentFactory
+import org.oppia.android.app.application.ApplicationComponent
+import org.oppia.android.app.application.ApplicationInjector
+import org.oppia.android.app.application.ApplicationInjectorProvider
+import org.oppia.android.app.application.ApplicationModule
+import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
+import org.oppia.android.app.devoptions.DeveloperOptionsModule
+import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.notice.testing.GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity
+import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
+import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.topic.PracticeTabModule
+import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
+import org.oppia.android.data.backends.gae.NetworkConfigProdModule
+import org.oppia.android.data.backends.gae.NetworkModule
+import org.oppia.android.domain.classify.InteractionsModule
+import org.oppia.android.domain.classify.rules.algebraicexpressioninput.AlgebraicExpressionInputModule
+import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule
+import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule
+import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule
+import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule
+import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule
+import org.oppia.android.domain.classify.rules.mathequationinput.MathEquationInputModule
+import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule
+import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule
+import org.oppia.android.domain.classify.rules.numericexpressioninput.NumericExpressionInputModule
+import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule
+import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule
+import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule
+import org.oppia.android.domain.exploration.lightweightcheckpointing.ExplorationStorageModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionConfigFastShowTestModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionProdModule
+import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule
+import org.oppia.android.domain.oppialogger.LogStorageModule
+import org.oppia.android.domain.oppialogger.LoggingIdentifierModule
+import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule
+import org.oppia.android.domain.oppialogger.logscheduler.MetricLogSchedulerModule
+import org.oppia.android.domain.oppialogger.loguploader.LogReportWorkerModule
+import org.oppia.android.domain.platformparameter.PlatformParameterModule
+import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule
+import org.oppia.android.domain.question.QuestionModule
+import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.TestImageLoaderModule
+import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
+import org.oppia.android.testing.robolectric.RobolectricModule
+import org.oppia.android.testing.threading.TestCoroutineDispatchers
+import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.testing.time.FakeOppiaClockModule
+import org.oppia.android.util.accessibility.AccessibilityTestModule
+import org.oppia.android.util.caching.AssetModule
+import org.oppia.android.util.caching.testing.CachingTestModule
+import org.oppia.android.util.gcsresource.GcsResourceModule
+import org.oppia.android.util.locale.LocaleProdModule
+import org.oppia.android.util.logging.LoggerModule
+import org.oppia.android.util.logging.SyncStatusModule
+import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule
+import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule
+import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
+import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
+import org.oppia.android.util.parser.image.ImageParsingModule
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [GeneralAvailabilityUpgradeNoticeDialogFragment]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@Config(
+ application = GeneralAvailabilityUpgradeNoticeDialogFragmentTest.TestApplication::class,
+ qualifiers = "port-xxhdpi"
+)
+@LooperMode(LooperMode.Mode.PAUSED)
+class GeneralAvailabilityUpgradeNoticeDialogFragmentTest {
+ @get:Rule
+ val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
+ @field:[Rule JvmField] val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Mock
+ lateinit var mockNoticeClosedListener: GeneralAvailabilityUpgradeNoticeClosedListener
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testFragment_hasExpectedTitle() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ onDialogView(withText(R.string.general_availability_notice_dialog_title))
+ .check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testFragment_hasExpectedContentMessageTextUnderTitle() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ onDialogView(withId(R.id.ga_update_notice_dialog_message)).check(matches(isDisplayed()))
+ onDialogView(withId(R.id.ga_update_notice_dialog_message))
+ .check(matches(withText(R.string.general_availability_notice_dialog_message)))
+ onDialogView(withId(R.id.ga_update_notice_dialog_message))
+ .check(isCompletelyBelow(withText(R.string.general_availability_notice_dialog_title)))
+ }
+ }
+
+ @Test
+ fun testFragment_hasDoNotShowAgainCheckboxUnderContentMessage() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox))
+ .check(matches(isDisplayed()))
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox)).check(
+ matches(withText(R.string.general_availability_notice_dialog_do_not_show_again_text))
+ )
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox))
+ .check(isCompletelyBelow(withId(R.id.ga_update_notice_dialog_message)))
+ }
+ }
+
+ @Test
+ fun testFragment_hasExpectedAcknowledgementButtonUnderDoNotShowAgainCheckbox() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .check(matches(isDisplayed()))
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .check(isCompletelyBelow(withId(R.id.ga_update_notice_dialog_preference_checkbox)))
+ }
+ }
+
+ @Test
+ fun testFragment_clickAcknowledgeButton_callsCallbackListenerWithFalse() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ clickOnDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+
+ verify(mockNoticeClosedListener).onGaUpgradeNoticeOkayButtonClicked(false)
+ }
+ }
+
+ @Test
+ fun testFragment_clickDoNotShowCheckbox_clickAcknowledgeButton_callsCallbackListenerWithTrue() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ clickOnDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox))
+
+ clickOnDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+
+ verify(mockNoticeClosedListener).onGaUpgradeNoticeOkayButtonClicked(true)
+ }
+ }
+
+ @Test
+ fun testFragment_toggleCheckbox_clickAcknowledgeButton_callsCallbackListenerWithFalse() {
+ launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity {
+ // Select, then deselect, the checkbox before closing the dialog.
+ clickOnDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox))
+ clickOnDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox))
+
+ clickOnDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+
+ verify(mockNoticeClosedListener).onGaUpgradeNoticeOkayButtonClicked(false)
+ }
+ }
+
+ private fun launchGeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity(
+ testBlock: () -> Unit
+ ) {
+ // Launch the test activity, but make sure that it's properly set up & time is given for it to
+ // initialize.
+ ActivityScenario.launch(
+ GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity::class.java
+ ).use { scenario ->
+ scenario.onActivity { it.mockCallbackListener = mockNoticeClosedListener }
+ testCoroutineDispatchers.runCurrent()
+ testBlock()
+ }
+ }
+
+ private fun clickOnDialogView(matcher: Matcher) {
+ onDialogView(matcher).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ private companion object {
+ private fun onDialogView(matcher: Matcher) = onView(matcher).inRoot(isDialog())
+ }
+
+ @Singleton
+ @Component(
+ modules = [
+ RobolectricModule::class, PlatformParameterModule::class,
+ TestDispatcherModule::class, ApplicationModule::class, LoggerModule::class,
+ ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class,
+ MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class,
+ NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class,
+ ImageClickInputModule::class, InteractionsModule::class, GcsResourceModule::class,
+ TestImageLoaderModule::class, ImageParsingModule::class, HtmlParserEntityTypeModule::class,
+ QuestionModule::class, TestLogReportingModule::class, AccessibilityTestModule::class,
+ LogStorageModule::class, PrimeTopicAssetsControllerModule::class,
+ ExpirationMetaDataRetrieverModule::class, ViewBindingShimModule::class,
+ RatioInputModule::class, ApplicationStartupListenerModule::class,
+ HintsAndSolutionConfigFastShowTestModule::class, HintsAndSolutionProdModule::class,
+ WorkManagerConfigurationModule::class, LogReportWorkerModule::class,
+ FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class,
+ DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class,
+ ExplorationStorageModule::class, NetworkConnectionUtilDebugModule::class,
+ NetworkConnectionDebugUtilModule::class, NetworkModule::class, NetworkConfigProdModule::class,
+ AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
+ PlatformParameterSingletonModule::class, NumericExpressionInputModule::class,
+ AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
+ SplitScreenInteractionModule::class, LoggingIdentifierModule::class,
+ ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class,
+ CachingTestModule::class, MetricLogSchedulerModule::class
+ ]
+ )
+ interface TestApplicationComponent : ApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: GeneralAvailabilityUpgradeNoticeDialogFragmentTest)
+ }
+
+ class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
+ private val component: TestApplicationComponent by lazy {
+ DaggerGeneralAvailabilityUpgradeNoticeDialogFragmentTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: GeneralAvailabilityUpgradeNoticeDialogFragmentTest) = component.inject(test)
+
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
+ return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
+ }
+
+ override fun getApplicationInjector(): ApplicationInjector = component
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt
index 412a7674a49..3757c17d7cf 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -151,7 +152,7 @@ class OnboardingActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt
index 5b3d03d8824..4fea9798161 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt
@@ -46,6 +46,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -723,7 +724,7 @@ class OnboardingFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt
index c9d0f8ff1d1..44948180740 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt
@@ -36,6 +36,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -458,7 +459,7 @@ class OngoingTopicListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt
index 62965d09a72..1bda24c4f48 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -161,7 +162,7 @@ class AppLanguageActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt
index 78966f2e217..3f4aa8f3a31 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt
@@ -28,6 +28,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -256,7 +257,7 @@ class AppLanguageFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt
index eb69586b13c..2bc4922f9a4 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -161,7 +162,7 @@ class AudioLanguageActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt
index dbb338d76e2..69bf7e01e8a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -249,7 +250,7 @@ class AudioLanguageFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt
index b90e05fd338..8839bf88eb7 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -153,7 +154,7 @@ class OptionsActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt
index 50701da0b58..5e17d4b0877 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt
@@ -39,6 +39,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -653,7 +654,7 @@ class OptionsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt
index 40fd59f6fb8..2d11f2d65cc 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -161,7 +162,7 @@ class ReadingTextSizeActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt
index 46284607705..bb3b3d23a7b 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -315,7 +316,7 @@ class ReadingTextSizeFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/parser/HtmlParserTest.kt b/app/src/sharedTest/java/org/oppia/android/app/parser/HtmlParserTest.kt
index 6cc3bbcb4a0..83640c04d2f 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/parser/HtmlParserTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/parser/HtmlParserTest.kt
@@ -54,6 +54,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.LanguageSupportDefinition
@@ -907,7 +908,7 @@ class HtmlParserTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt
index 30186b1cc96..ecafe75284e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt
@@ -41,6 +41,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -478,7 +479,7 @@ class AudioFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
index 2020e38b87b..053cddc373a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
@@ -65,6 +65,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.HelpActivity
@@ -1958,7 +1959,7 @@ class ExplorationActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel
index 457225f4fe4..1430f73fa51 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel
+++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel
@@ -18,6 +18,7 @@ app_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//data/src/main/java/org/oppia/android/data/backends/gae:prod_module",
"//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt
index f1898d255d3..ed0a0125b0d 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt
@@ -70,6 +70,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -4319,7 +4320,7 @@ class StateFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesActivityTest.kt
index 40789301bbd..26724b47f42 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.PolicyPage
@@ -184,7 +185,7 @@ class PoliciesActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesFragmentTest.kt
index f2ffe23dd5c..69b5e5ddbdf 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/policies/PoliciesFragmentTest.kt
@@ -43,6 +43,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.PolicyPage
@@ -337,7 +338,7 @@ class PoliciesFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt
index 4cd82b31973..4595e72857e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt
@@ -54,6 +54,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -1732,7 +1733,7 @@ class AddProfileActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt
index c4934f2611c..23949bf6b7e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt
@@ -40,6 +40,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -672,7 +673,7 @@ class AdminAuthActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt
index 4f66269badb..5b92a040c25 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt
@@ -49,6 +49,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -1090,7 +1091,7 @@ class AdminPinActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt
index e8770e57d44..c4b9a510724 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt
@@ -41,6 +41,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.HomeActivity
@@ -1152,7 +1153,7 @@ class PinPasswordActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt
index cdcee36d429..cbb45e201cf 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt
@@ -39,6 +39,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.drawer.NAVIGATION_PROFILE_ID_ARGUMENT_KEY
@@ -529,7 +530,7 @@ class ProfileChooserFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt
index f1e9541bb3c..51a4440dc1f 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt
@@ -29,6 +29,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -204,7 +205,7 @@ class ProfilePictureActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt
index 4716612c00e..a47d30bd5a0 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -155,7 +156,7 @@ class ProfileProgressActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt
index 3593c3a83b7..b01743cdfcb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt
@@ -54,6 +54,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.completedstorylist.CompletedStoryListActivity
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -856,7 +857,7 @@ class ProfileProgressFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt
index e8a168ee1af..0a8321945bb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationComponent
import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.fragment.FragmentComponent
@@ -767,7 +768,7 @@ class BindableAdapterTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt
index e76a4ac0ab6..d3a0c8073bd 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ExplorationCheckpoint
@@ -225,7 +226,7 @@ class ResumeLessonActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt
index 1bf70dde862..4760439db99 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ExplorationCheckpoint
@@ -314,7 +315,7 @@ class ResumeLessonFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt
index f950c6f152c..0e0450b08a2 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt
@@ -35,6 +35,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -378,7 +379,7 @@ class ProfileEditActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt
index 84d523c5200..dcc7285bf51 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt
@@ -35,6 +35,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -355,7 +356,7 @@ class ProfileEditFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListActivityTest.kt
index ea0e29aaac2..fc6b3cc972e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -151,7 +152,7 @@ class ProfileListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt
index 6e8ac36d94f..37feb72c90d 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -389,7 +390,7 @@ class ProfileListFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt
index 55d6e85a511..026dede3753 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -174,7 +175,7 @@ class ProfileRenameActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt
index 9744a3daead..7efe91a973e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -463,7 +464,7 @@ class ProfileRenameFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt
index 38c6df5615d..766f0ffc3ec 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt
@@ -23,6 +23,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -185,7 +186,7 @@ class ProfileResetPinActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt
index dc210acaa36..c82448b3733 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt
@@ -38,6 +38,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -1027,7 +1028,7 @@ class ProfileResetPinFragmentTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel
new file mode 100644
index 00000000000..8a15fa58892
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel
@@ -0,0 +1,53 @@
+"""
+Tests for the splash/app UI initialization process.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//app:app_test.bzl", "app_test")
+load("//app:test_with_resources.bzl", "test_with_resources")
+
+app_test(
+ name = "SplashActivityTest",
+ processed_src = test_with_resources("SplashActivityTest.kt"),
+ test_class = "org.oppia.android.app.splash.SplashActivityTest",
+ deps = [
+ ":dagger",
+ "//app",
+ "//app:test_deps",
+ "//app/src/main/java/org/oppia/android/app/application:application_component",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector",
+ "//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
+ "//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
+ "//data/src/main/java/org/oppia/android/data/backends/gae:prod_module",
+ "//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
+ "//domain/src/main/java/org/oppia/android/domain/translation:translation_controller",
+ "//testing",
+ "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor",
+ "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action",
+ "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule",
+ "//testing/src/main/java/org/oppia/android/testing/junit:oppia_parameterized_test_runner",
+ "//testing/src/main/java/org/oppia/android/testing/junit:parameterized_auto_android_test_runner",
+ "//testing/src/main/java/org/oppia/android/testing/junit:parameterized_robolectric_test_runner",
+ "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/threading:coroutine_executor_service",
+ "//testing/src/main/java/org/oppia/android/testing/threading:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/time:test_module",
+ "//third_party:com_github_bumptech_glide_mocks",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ "//utility/src/main/java/org/oppia/android/util/accessibility:test_module",
+ "//utility/src/main/java/org/oppia/android/util/caching:asset_prod_module",
+ "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_test_module",
+ "//utility/src/main/java/org/oppia/android/util/data:data_providers",
+ "//utility/src/main/java/org/oppia/android/util/locale/testing:test_module",
+ "//utility/src/main/java/org/oppia/android/util/logging:prod_module",
+ "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
index e554c9df2e1..9c1da667d90 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
@@ -3,23 +3,30 @@ package org.oppia.android.app.splash
import android.app.Application
import android.app.Instrumentation
import android.content.Context
+import android.content.Intent
+import android.view.View
import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.rule.ActivityTestRule
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.hamcrest.Matcher
+import org.hamcrest.Matchers.not
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -35,6 +42,7 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.BuildFlavor
import org.oppia.android.app.model.OppiaLanguage.ARABIC
import org.oppia.android.app.model.OppiaLanguage.BRAZILIAN_PORTUGUESE
import org.oppia.android.app.model.OppiaLanguage.ENGLISH
@@ -85,6 +93,13 @@ import org.oppia.android.testing.OppiaTestRule
import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.Iteration
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.Parameter
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.RunParameterized
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.SelectRunnerPlatform
+import org.oppia.android.testing.junit.ParameterizedAutoAndroidTestRunner
import org.oppia.android.testing.robolectric.RobolectricModule
import org.oppia.android.testing.threading.TestCoroutineDispatchers
import org.oppia.android.testing.threading.TestDispatcherModule
@@ -110,6 +125,7 @@ import java.time.Duration
import java.time.Instant
import java.util.Date
import java.util.Locale
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -117,29 +133,33 @@ import javax.inject.Singleton
* Tests for [SplashActivity]. For context on the activity test rule setup see:
* https://jabknowsnothing.wordpress.com/2015/11/05/activitytestrule-espressos-test-lifecycle/.
*/
-@RunWith(AndroidJUnit4::class)
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(OppiaParameterizedTestRunner::class)
+@SelectRunnerPlatform(ParameterizedAutoAndroidTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
@Config(application = SplashActivityTest.TestApplication::class, qualifiers = "port-xxhdpi")
class SplashActivityTest {
@get:Rule
val oppiaTestRule = OppiaTestRule()
- @Inject
- lateinit var context: Context
+ @Inject lateinit var context: Context
+ @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+ @Inject lateinit var fakeMetaDataRetriever: FakeExpirationMetaDataRetriever
+ @Inject lateinit var appLanguageLocaleHandler: AppLanguageLocaleHandler
+ @Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory
+ @Inject lateinit var appStartupStateController: AppStartupStateController
- @Inject
- lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
-
- @Inject
- lateinit var fakeMetaDataRetriever: FakeExpirationMetaDataRetriever
-
- @Inject
- lateinit var appLanguageLocaleHandler: AppLanguageLocaleHandler
+ @Parameter lateinit var firstOpen: String
+ @Parameter lateinit var secondOpen: String
private val expirationDateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) }
+ private val firstOpenFlavor by lazy { BuildFlavor.valueOf(firstOpen) }
+ private val secondOpenFlavor by lazy { BuildFlavor.valueOf(secondOpen) }
@Before
fun setUp() {
+ TestModule.buildFlavor = BuildFlavor.BUILD_FLAVOR_UNSPECIFIED
Intents.init()
}
@@ -149,23 +169,13 @@ class SplashActivityTest {
Intents.release()
}
- // The initialTouchMode enables the activity to be launched in touch mode. The launchActivity is
- // disabled to launch Activity explicitly within each test case.
- @get:Rule
- var activityTestRule: ActivityTestRule = ActivityTestRule(
- SplashActivity::class.java,
- /* initialTouchMode= */ true,
- /* launchActivity= */ false
- )
-
@Test
fun testSplashActivity_initialOpen_routesToOnboardingActivity() {
initializeTestApplication()
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- intended(hasComponent(OnboardingActivity::class.java.name))
+ launchSplashActivityFully {
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
}
@Test
@@ -173,10 +183,9 @@ class SplashActivityTest {
simulateAppAlreadyOnboarded()
initializeTestApplication()
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- intended(hasComponent(ProfileChooserActivity::class.java.name))
+ launchSplashActivityFully {
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
}
@Test
@@ -185,26 +194,24 @@ class SplashActivityTest {
setAutoAppExpirationEnabled(enabled = true)
setAutoAppExpirationDate(dateStringAfterToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // App deprecation is enabled, but this app hasn't yet expired.
- intended(hasComponent(OnboardingActivity::class.java.name))
+ launchSplashActivityFully {
+ // App deprecation is enabled, but this app hasn't yet expired.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
}
@Test
- fun testOpenApp_initial_expirationEnabled_afterExpDate_intentsToDeprecationDialog() {
+ fun testOpenApp_initial_expirationEnabled_afterExpDate_showsDeprecationDialog() {
initializeTestApplication()
setAutoAppExpirationEnabled(enabled = true)
setAutoAppExpirationDate(dateStringBeforeToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // The current app is expired.
- onView(withText(R.string.unsupported_app_version_dialog_title))
- .inRoot(isDialog())
- .check(matches(isDisplayed()))
+ launchSplashActivityFully {
+ // The current app is expired.
+ onView(withText(R.string.unsupported_app_version_dialog_title))
+ .inRoot(isDialog())
+ .check(matches(isDisplayed()))
+ }
}
@Test
@@ -212,61 +219,60 @@ class SplashActivityTest {
initializeTestApplication()
setAutoAppExpirationEnabled(enabled = true)
setAutoAppExpirationDate(dateStringBeforeToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
- onView(withText(R.string.unsupported_app_version_dialog_close_button_text))
- .inRoot(isDialog())
- .perform(click())
- testCoroutineDispatchers.advanceUntilIdle()
+ launchSplashActivityFully { scenario ->
+ onView(withText(R.string.unsupported_app_version_dialog_close_button_text))
+ .inRoot(isDialog())
+ .perform(click())
+ testCoroutineDispatchers.advanceUntilIdle()
- // Closing the dialog should close the activity (and thus, the app).
- assertThat(activityTestRule.activity.isFinishing).isTrue()
+ scenario.onActivity { activity ->
+ // Closing the dialog should close the activity (and thus, the app).
+ assertThat(activity.isFinishing).isTrue()
+ }
+ }
}
@Test
- fun testOpenApp_initial_expirationDisabled_afterExpDate_intentsToOnboardingFlow() {
+ fun testOpenApp_initial_expirationDisabled_afterExpDate_showsOnboardingFlow() {
initializeTestApplication()
setAutoAppExpirationEnabled(enabled = false)
setAutoAppExpirationDate(dateStringBeforeToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // The app is technically deprecated, but because the deprecation check is disabled the
- // onboarding flow should be shown, instead.
- intended(hasComponent(OnboardingActivity::class.java.name))
+ launchSplashActivityFully {
+ // The app is technically deprecated, but because the deprecation check is disabled the
+ // onboarding flow should be shown, instead.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
}
@Test
- fun testOpenApp_reopen_onboarded_expirationEnabled_beforeExpDate_intentsToProfileChooser() {
+ fun testOpenApp_reopen_onboarded_expirationEnabled_beforeExpDate_routesToProfileChooser() {
simulateAppAlreadyOnboarded()
initializeTestApplication()
setAutoAppExpirationEnabled(enabled = true)
setAutoAppExpirationDate(dateStringAfterToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Reopening the app before it's expired should result in the profile activity showing since the
- // user has already been onboarded.
- intended(hasComponent(ProfileChooserActivity::class.java.name))
+ launchSplashActivityFully {
+ // Reopening the app before it's expired should result in the profile activity showing since
+ // the user has already been onboarded.
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
}
@Test
- fun testOpenApp_reopen_onboarded_expirationEnabled_afterExpDate_intentsToDeprecationDialog() {
+ fun testOpenApp_reopen_onboarded_expirationEnabled_afterExpDate_showsToDeprecationDialog() {
simulateAppAlreadyOnboarded()
initializeTestApplication()
setAutoAppExpirationEnabled(enabled = true)
setAutoAppExpirationDate(dateStringBeforeToday())
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Reopening the app after it expires should prevent further access.
- onView(withText(R.string.unsupported_app_version_dialog_title))
- .inRoot(isDialog())
- .check(matches(isDisplayed()))
+ launchSplashActivityFully {
+ // Reopening the app after it expires should prevent further access.
+ onView(withText(R.string.unsupported_app_version_dialog_title))
+ .inRoot(isDialog())
+ .check(matches(isDisplayed()))
+ }
}
@Test
@@ -275,20 +281,19 @@ class SplashActivityTest {
initializeTestApplication()
forceDefaultLocale(Locale.ENGLISH)
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
- // that the correct display locale is defined per the system locale.
- val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
- val context = displayLocale.localeContext
- assertThat(context.languageDefinition.language).isEqualTo(ENGLISH)
- assertThat(context.languageDefinition.minAndroidSdkVersion).isEqualTo(1)
- assertThat(context.languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("en")
- assertThat(context.hasFallbackLanguageDefinition()).isFalse()
- assertThat(context.regionDefinition.region).isEqualTo(OppiaRegion.REGION_UNSPECIFIED)
- assertThat(context.regionDefinition.regionId.ietfRegionTag).isEqualTo("")
- assertThat(context.usageMode).isEqualTo(OppiaLocaleContext.LanguageUsageMode.APP_STRINGS)
+ launchSplashActivityFully {
+ // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
+ // that the correct display locale is defined per the system locale.
+ val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
+ val context = displayLocale.localeContext
+ assertThat(context.languageDefinition.language).isEqualTo(ENGLISH)
+ assertThat(context.languageDefinition.minAndroidSdkVersion).isEqualTo(1)
+ assertThat(context.languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("en")
+ assertThat(context.hasFallbackLanguageDefinition()).isFalse()
+ assertThat(context.regionDefinition.region).isEqualTo(OppiaRegion.REGION_UNSPECIFIED)
+ assertThat(context.regionDefinition.regionId.ietfRegionTag).isEqualTo("")
+ assertThat(context.usageMode).isEqualTo(OppiaLocaleContext.LanguageUsageMode.APP_STRINGS)
+ }
}
@Test
@@ -297,14 +302,13 @@ class SplashActivityTest {
initializeTestApplication()
forceDefaultLocale(EGYPT_ARABIC_LOCALE)
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
- // that the correct display locale is defined per the system locale.
- val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
- val context = displayLocale.localeContext
- assertThat(context.languageDefinition.language).isEqualTo(ARABIC)
+ launchSplashActivityFully {
+ // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
+ // that the correct display locale is defined per the system locale.
+ val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
+ val context = displayLocale.localeContext
+ assertThat(context.languageDefinition.language).isEqualTo(ARABIC)
+ }
}
@Test
@@ -313,14 +317,13 @@ class SplashActivityTest {
initializeTestApplication()
forceDefaultLocale(BRAZIL_PORTUGUESE_LOCALE)
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
- // that the correct display locale is defined per the system locale.
- val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
- val context = displayLocale.localeContext
- assertThat(context.languageDefinition.language).isEqualTo(BRAZILIAN_PORTUGUESE)
+ launchSplashActivityFully {
+ // Verify that the locale is initialized (i.e. getDisplayLocale doesn't throw an exception) &
+ // that the correct display locale is defined per the system locale.
+ val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
+ val context = displayLocale.localeContext
+ assertThat(context.languageDefinition.language).isEqualTo(BRAZILIAN_PORTUGUESE)
+ }
}
@Test
@@ -328,73 +331,701 @@ class SplashActivityTest {
initializeTestApplication()
forceDefaultLocale(TURKEY_TURKISH_LOCALE)
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Verify that the context is the default state (due to the unsupported locale).
- val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
- val languageDefinition = displayLocale.localeContext.languageDefinition
- assertThat(languageDefinition.language).isEqualTo(LANGUAGE_UNSPECIFIED)
- assertThat(languageDefinition.minAndroidSdkVersion).isEqualTo(1)
- assertThat(languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("tr-TR")
+ launchSplashActivityFully {
+ // Verify that the context is the default state (due to the unsupported locale).
+ val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
+ val languageDefinition = displayLocale.localeContext.languageDefinition
+ assertThat(languageDefinition.language).isEqualTo(LANGUAGE_UNSPECIFIED)
+ assertThat(languageDefinition.minAndroidSdkVersion).isEqualTo(1)
+ assertThat(languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("tr-TR")
+ }
}
@Test
- @RunOn(TestPlatform.ROBOLECTRIC)
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
fun testSplashActivity_initializationFailure_initializesLocaleHandlerWithDefaultContext() {
corruptCacheFile()
initializeTestApplication()
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Verify that the context is the default state (due to the unsupported locale).
- val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
- val context = displayLocale.localeContext
- assertThat(context.languageDefinition.language).isEqualTo(ENGLISH)
- assertThat(context.languageDefinition.minAndroidSdkVersion).isEqualTo(1)
- assertThat(context.languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("en")
- assertThat(context.hasFallbackLanguageDefinition()).isFalse()
- assertThat(context.regionDefinition.region).isEqualTo(OppiaRegion.UNITED_STATES)
- assertThat(context.regionDefinition.regionId.ietfRegionTag).isEqualTo("US")
- assertThat(context.usageMode).isEqualTo(OppiaLocaleContext.LanguageUsageMode.APP_STRINGS)
+ launchSplashActivityFully {
+ // Verify that the context is the default state (due to the unsupported locale).
+ val displayLocale = appLanguageLocaleHandler.getDisplayLocale()
+ val context = displayLocale.localeContext
+ assertThat(context.languageDefinition.language).isEqualTo(ENGLISH)
+ assertThat(context.languageDefinition.minAndroidSdkVersion).isEqualTo(1)
+ assertThat(context.languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("en")
+ assertThat(context.hasFallbackLanguageDefinition()).isFalse()
+ assertThat(context.regionDefinition.region).isEqualTo(OppiaRegion.UNITED_STATES)
+ assertThat(context.regionDefinition.regionId.ietfRegionTag).isEqualTo("US")
+ assertThat(context.usageMode).isEqualTo(OppiaLocaleContext.LanguageUsageMode.APP_STRINGS)
+ }
}
@Test
- @RunOn(TestPlatform.ROBOLECTRIC)
- fun testSplashActivity_initializationFailure_logsError() {
- // Simulate a corrupted cache file to trigger an initialization failure.
+ fun testSplashActivity_initializationFailure_routesToOnboardingActivity() {
corruptCacheFile()
initializeTestApplication()
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
-
- val logs = getShadowLogsOnRobolectric()
- assertThat(logs.any { it.contains("Failed to compute initial state") }).isTrue()
+ launchSplashActivityFully {
+ // Verify that an initialization failure leads to the onboarding activity by default.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
}
@Test
- fun testSplashActivity_initializationFailure_routesToOnboardingActivity() {
- corruptCacheFile()
+ fun testSplashActivity_hasCorrectActivityLabel() {
initializeTestApplication()
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
+ launchSplashActivityFully { scenario ->
+ scenario.onActivity { activity ->
+ val title = activity.title
- // Verify that an initialization failure leads to the onboarding activity by default.
- intended(hasComponent(OnboardingActivity::class.java.name))
+ assertThat(title).isEqualTo(context.getString(R.string.app_name))
+ }
+ }
}
@Test
- fun testSplashActivity_hasCorrectActivityLabel() {
- initializeTestApplication()
+ fun testSplashActivity_newUser_firstTimeOpeningBetaFlavor_doesNotShowBetaNotice() {
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
- activityTestRule.launchActivity(null)
- testCoroutineDispatchers.advanceUntilIdle()
+ launchSplashActivityFully {
+ // Verify that the beta notice does not open (since there wasn't a version change).
+ onView(withId(R.id.beta_notice_dialog_message)).check(doesNotExist())
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_beta", "firstOpen=TESTING", "secondOpen=BETA"),
+ Iteration("dev_to_beta", "firstOpen=DEVELOPER", "secondOpen=BETA"),
+ Iteration("alpha_to_beta", "firstOpen=ALPHA", "secondOpen=BETA"),
+ Iteration("ga_to_beta", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=BETA")
+ )
+ fun testSplashActivity_newUser_betaFlavorTransitions_showsBetaNotice() {
+ simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor)
+
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.beta_notice_dialog_title)).check(matches(isDisplayed()))
+ onDialogView(withId(R.id.beta_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_beta", "firstOpen=TESTING", "secondOpen=BETA"),
+ Iteration("dev_to_beta", "firstOpen=DEVELOPER", "secondOpen=BETA"),
+ Iteration("alpha_to_beta", "firstOpen=ALPHA", "secondOpen=BETA"),
+ Iteration("ga_to_beta", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=BETA")
+ )
+ fun testSplashActivity_newUser_betaFlavorTransitions_closeNotice_routesToOnboardingFlow() {
+ simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor)
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // Close the notice.
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // The user should be routed to the onboarding flow after seeing the beta notice.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_beta", "firstOpen=TESTING", "secondOpen=BETA"),
+ Iteration("dev_to_beta", "firstOpen=DEVELOPER", "secondOpen=BETA"),
+ Iteration("alpha_to_beta", "firstOpen=ALPHA", "secondOpen=BETA"),
+ Iteration("ga_to_beta", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=BETA")
+ )
+ fun testSplashActivity_newUser_betaFlavorTransitions_doNotShowAgain_routesToOnboardingFlow() {
+ simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor)
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // Close the notice after selecting to never show it again.
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // The user should be routed to the onboarding flow after seeing the beta notice.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_dismissBetaNotice_reopenApp_doesNotShowNotice() {
+ // Open the app in beta notice mode, then dismiss the notice.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // Note this is a different "recreation" than other tests since the same instrumentation
+ // process needs to be preserved for Espresso to work correctly.
+ recreateExistingApplication()
+
+ launchSplashActivityFully {
+ // The user should be routed to the onboarding flow after seeing the beta notice.
+ onView(withId(R.id.beta_notice_dialog_message)).check(doesNotExist())
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_dismissBetaNotice_retriggerNotice_showsBetaNotice() {
+ // Open the app in beta notice mode, then dismiss the notice.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // "Retrigger" the notice by switching flavors again, then "recreate" the existing application
+ // so that new states can be observed.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ recreateExistingApplicationWithFlavor(BuildFlavor.BETA)
+
+ launchSplashActivityFully {
+ // The user should see the beta notice again despite dismissing it since the beta notice
+ // condition again occurred.
+ onDialogView(withText(R.string.beta_notice_dialog_title)).check(matches(isDisplayed()))
+ onDialogView(withId(R.id.beta_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_dismissBetaNoticeForever_retriggerNotice_doesNotShowNotice() {
+ // Open the app in beta notice mode, then dismiss the notice permanently.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+ launchSplashActivityFully {
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // "Retrigger" the notice by switching flavors again, then "recreate" the existing application
+ // so that new states can be observed.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ recreateExistingApplicationWithFlavor(BuildFlavor.BETA)
+
+ launchSplashActivityFully {
+ // The user should not see the beta notice again even though they changed flavors since they
+ // opted to permanently disable the notice.
+ onView(withId(R.id.beta_notice_dialog_message)).check(doesNotExist())
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_firstTimeOpeningGaFlavor_doesNotShowGaUpgradeNotice() {
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ launchSplashActivityFully {
+ // Verify that the GA notice does not open (since there wasn't an upgrade).
+ onView(withId(R.id.ga_update_notice_dialog_message)).check(doesNotExist())
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("alpha_to_ga", "firstOpen=ALPHA", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("beta_to_ga", "firstOpen=BETA", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_onboarded_gaFlavorTransitions_showsGaUpgradeNotice() {
+ simulateAppAlreadyOnboardedWithFlavor(firstOpenFlavor)
+
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.general_availability_notice_dialog_title))
+ .check(matches(isDisplayed()))
+ onDialogView(withId(R.id.ga_update_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("alpha_to_ga", "firstOpen=ALPHA", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("beta_to_ga", "firstOpen=BETA", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_onboarded_gaFlavorTransitions_closeNotice_routesToProfileChooser() {
+ simulateAppAlreadyOnboardedWithFlavor(firstOpenFlavor)
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // Close the notice.
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // The user should be routed to the profile chooser after seeing the GA upgrade notice.
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("alpha_to_ga", "firstOpen=ALPHA", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("beta_to_ga", "firstOpen=BETA", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_onboarded_gaFlavorTransitions_doNotShowAgain_routesToProfileChooser() {
+ simulateAppAlreadyOnboardedWithFlavor(firstOpenFlavor)
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // Close the notice after selecting to never show it again.
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // The user should be routed to the profile chooser after seeing the GA upgrade notice.
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_dismissGaNotice_reopenApp_doesNotShowNotice() {
+ // Open the app in GA upgrade mode, then dismiss the notice.
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.BETA)
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // Note this is a different "recreation" than other tests since the same instrumentation
+ // process needs to be preserved for Espresso to work correctly.
+ recreateExistingApplication()
+
+ launchSplashActivityFully {
+ // The user should be routed to the profile chooser after seeing the GA upgrade notice.
+ onView(withId(R.id.ga_update_notice_dialog_message)).check(doesNotExist())
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_dismissGaNotice_retriggerNotice_showsGaNotice() {
+ // Open the app in GA upgrade mode, then dismiss the notice.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // "Retrigger" the notice by switching flavors again, then "recreate" the existing application
+ // so that new states can be observed.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ recreateExistingApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ launchSplashActivityFully {
+ // The user should see the GA upgrade notice again despite dismissing it since the notice
+ // condition again occurred.
+ onDialogView(withText(R.string.general_availability_notice_dialog_title))
+ .check(matches(isDisplayed()))
+ onDialogView(withId(R.id.ga_update_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_dismissGaNoticeForever_retriggerNotice_doesNotShowNotice() {
+ // Open the app in GA upgrade mode, then dismiss the notice permanently.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.BETA)
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+ launchSplashActivityFully {
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // "Retrigger" the notice by switching flavors again, then "recreate" the existing application
+ // so that new states can be observed.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ recreateExistingApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ launchSplashActivityFully {
+ // The user should not see the GA upgrade notice again even though they changed flavors since
+ // they opted to permanently disable the notice.
+ onView(withId(R.id.ga_update_notice_dialog_message)).check(doesNotExist())
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_betaNoticeConditionsThenGa_showsGaNotice() {
+ // Simulate a beta notice first.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.BETA)
+
+ // Then simulate the GA upgrade notice.
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ // The GA upgrade notice should be the one to show since it's more recent.
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.general_availability_notice_dialog_title))
+ .check(matches(isDisplayed()))
+ onDialogView(withId(R.id.ga_update_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_betaNoticeConditionsThenGa_gaDisabled_showsNoNotice() {
+ // First, disable the GA notice, then trigger a beta notice.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+ launchSplashActivityFully {
+ onDialogView(withId(R.id.ga_update_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.general_availability_notice_dialog_close_button_text))
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+ reopenAppWithNewFlavor(BuildFlavor.BETA)
+
+ // Then simulate the GA upgrade notice.
+ reopenAppWithNewFlavor(BuildFlavor.GENERAL_AVAILABILITY)
- val title = activityTestRule.activity.title
- assertThat(title).isEqualTo(context.getString(R.string.app_name))
+ // No notice should show since the GA upgrade notice would normally show, but it's been
+ // permanently disabled.
+ launchSplashActivityFully {
+ onView(withId(R.id.beta_notice_dialog_message)).check(doesNotExist())
+ onView(withId(R.id.ga_update_notice_dialog_message)).check(doesNotExist())
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_gaNoticeConditionsThenBeta_showsBetaNotice() {
+ // Simulate a GA upgrade notice first.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.BETA)
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ // Then simulate the beta notice.
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+
+ // The beta notice should be the one to show since it's more recent.
+ launchSplashActivityFully {
+ onDialogView(withText(R.string.beta_notice_dialog_title)).check(matches(isDisplayed()))
+ onDialogView(withId(R.id.beta_notice_dialog_message)).check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_newUser_gaNoticeConditionsThenBeta_betaDisabled_showsNoNotice() {
+ // First, disable the beta notice, then trigger a GA upgrade notice.
+ simulateAppAlreadyOpenedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+ launchSplashActivityFully {
+ onDialogView(withId(R.id.beta_notice_dialog_preference_checkbox)).perform(click())
+ onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+ reopenAppWithNewFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ // Then simulate the beta notice.
+ reopenAppWithNewFlavor(BuildFlavor.BETA)
+
+ // No notice should show since the beta notice would normally show, but it's been permanently
+ // disabled.
+ launchSplashActivityFully {
+ onView(withId(R.id.beta_notice_dialog_message)).check(doesNotExist())
+ onView(withId(R.id.ga_update_notice_dialog_message)).check(doesNotExist())
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_testing", "firstOpen=TESTING", "secondOpen=TESTING"),
+ Iteration("testing_to_dev", "firstOpen=TESTING", "secondOpen=DEVELOPER"),
+ Iteration("testing_to_alpha", "firstOpen=TESTING", "secondOpen=ALPHA"),
+ Iteration("testing_to_ga", "firstOpen=TESTING", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("dev_to_testing", "firstOpen=DEVELOPER", "secondOpen=TESTING"),
+ Iteration("dev_to_dev", "firstOpen=DEVELOPER", "secondOpen=DEVELOPER"),
+ Iteration("dev_to_alpha", "firstOpen=DEVELOPER", "secondOpen=ALPHA"),
+ Iteration("dev_to_ga", "firstOpen=DEVELOPER", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("alpha_to_testing", "firstOpen=ALPHA", "secondOpen=TESTING"),
+ Iteration("alpha_to_dev", "firstOpen=ALPHA", "secondOpen=DEVELOPER"),
+ Iteration("alpha_to_alpha", "firstOpen=ALPHA", "secondOpen=ALPHA"),
+ Iteration("beta_to_testing", "firstOpen=BETA", "secondOpen=TESTING"),
+ Iteration("beta_to_dev", "firstOpen=BETA", "secondOpen=DEVELOPER"),
+ Iteration("beta_to_alpha", "firstOpen=BETA", "secondOpen=ALPHA"),
+ Iteration("beta_to_beta", "firstOpen=BETA", "secondOpen=BETA"),
+ Iteration("ga_to_testing", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=TESTING"),
+ Iteration("ga_to_dev", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=DEVELOPER"),
+ Iteration("ga_to_alpha", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=ALPHA"),
+ Iteration("ga_to_ga", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_newUser_ignoredFlavorTransitions_routesToOnboardingFlow() {
+ simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor)
+
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // The user should be immediately routed to the onboarding flow since this flavor transition
+ // does not trigger a notice.
+ intended(hasComponent(OnboardingActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_testing", "firstOpen=TESTING", "secondOpen=TESTING"),
+ Iteration("testing_to_dev", "firstOpen=TESTING", "secondOpen=DEVELOPER"),
+ Iteration("testing_to_alpha", "firstOpen=TESTING", "secondOpen=ALPHA"),
+ Iteration("testing_to_ga", "firstOpen=TESTING", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("dev_to_testing", "firstOpen=DEVELOPER", "secondOpen=TESTING"),
+ Iteration("dev_to_dev", "firstOpen=DEVELOPER", "secondOpen=DEVELOPER"),
+ Iteration("dev_to_alpha", "firstOpen=DEVELOPER", "secondOpen=ALPHA"),
+ Iteration("dev_to_ga", "firstOpen=DEVELOPER", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("alpha_to_testing", "firstOpen=ALPHA", "secondOpen=TESTING"),
+ Iteration("alpha_to_dev", "firstOpen=ALPHA", "secondOpen=DEVELOPER"),
+ Iteration("alpha_to_alpha", "firstOpen=ALPHA", "secondOpen=ALPHA"),
+ Iteration("beta_to_testing", "firstOpen=BETA", "secondOpen=TESTING"),
+ Iteration("beta_to_dev", "firstOpen=BETA", "secondOpen=DEVELOPER"),
+ Iteration("beta_to_alpha", "firstOpen=BETA", "secondOpen=ALPHA"),
+ Iteration("beta_to_beta", "firstOpen=BETA", "secondOpen=BETA"),
+ Iteration("ga_to_testing", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=TESTING"),
+ Iteration("ga_to_dev", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=DEVELOPER"),
+ Iteration("ga_to_alpha", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=ALPHA"),
+ Iteration("ga_to_ga", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_onboarded_ignoredFlavorTransitions_routesToProfileChooser() {
+ simulateAppAlreadyOnboardedWithFlavor(firstOpenFlavor)
+
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+
+ launchSplashActivityFully {
+ // The user should be immediately routed to the profile chooser since this flavor transition
+ // does not trigger a notice.
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_testing", "firstOpen=TESTING", "secondOpen=TESTING"),
+ Iteration("testing_to_dev", "firstOpen=TESTING", "secondOpen=DEVELOPER"),
+ Iteration("testing_to_alpha", "firstOpen=TESTING", "secondOpen=ALPHA"),
+ Iteration("testing_to_beta", "firstOpen=TESTING", "secondOpen=BETA"),
+ Iteration("testing_to_ga", "firstOpen=TESTING", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("dev_to_testing", "firstOpen=DEVELOPER", "secondOpen=TESTING"),
+ Iteration("dev_to_dev", "firstOpen=DEVELOPER", "secondOpen=DEVELOPER"),
+ Iteration("dev_to_alpha", "firstOpen=DEVELOPER", "secondOpen=ALPHA"),
+ Iteration("dev_to_beta", "firstOpen=DEVELOPER", "secondOpen=BETA"),
+ Iteration("dev_to_ga", "firstOpen=DEVELOPER", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("alpha_to_testing", "firstOpen=ALPHA", "secondOpen=TESTING"),
+ Iteration("alpha_to_dev", "firstOpen=ALPHA", "secondOpen=DEVELOPER"),
+ Iteration("alpha_to_alpha", "firstOpen=ALPHA", "secondOpen=ALPHA"),
+ Iteration("alpha_to_beta", "firstOpen=ALPHA", "secondOpen=BETA"),
+ Iteration("alpha_to_ga", "firstOpen=ALPHA", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("beta_to_testing", "firstOpen=BETA", "secondOpen=TESTING"),
+ Iteration("beta_to_dev", "firstOpen=BETA", "secondOpen=DEVELOPER"),
+ Iteration("beta_to_alpha", "firstOpen=BETA", "secondOpen=ALPHA"),
+ Iteration("beta_to_beta", "firstOpen=BETA", "secondOpen=BETA"),
+ Iteration("beta_to_ga", "firstOpen=BETA", "secondOpen=GENERAL_AVAILABILITY"),
+ Iteration("ga_to_testing", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=TESTING"),
+ Iteration("ga_to_dev", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=DEVELOPER"),
+ Iteration("ga_to_alpha", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=ALPHA"),
+ Iteration("ga_to_beta", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=BETA"),
+ Iteration("ga_to_ga", "firstOpen=GENERAL_AVAILABILITY", "secondOpen=GENERAL_AVAILABILITY")
+ )
+ fun testSplashActivity_appDeprecated_allFlavorTransitions_showsDeprecationNotice() {
+ simulateAppAlreadyOnboardedWithFlavor(firstOpenFlavor)
+
+ initializeTestApplicationWithFlavor(secondOpenFlavor)
+ setAutoAppExpirationEnabled(enabled = true)
+ setAutoAppExpirationDate(dateStringBeforeToday())
+
+ // The current app is expired, so the deprecation notice should show regardless of the build
+ // flavor notices that would normally show.
+ launchSplashActivityFully {
+ onView(withText(R.string.unsupported_app_version_dialog_title))
+ .inRoot(isDialog())
+ .check(matches(isDisplayed()))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_testingFlavor_showsNoFlavorText() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.TESTING)
+
+ initializeTestApplicationWithFlavor(BuildFlavor.TESTING)
+
+ // No label should show for this version of the app since it's meant to simulate the GA flavor.
+ launchSplashActivityFully {
+ onView(withId(R.id.build_flavor_label)).check(matches(not(isDisplayed())))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_devFlavor_showsDevFlavorText() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.DEVELOPER)
+
+ initializeTestApplicationWithFlavor(BuildFlavor.DEVELOPER)
+
+ // The developer label should be showing.
+ launchSplashActivityFully {
+ onView(withId(R.id.build_flavor_label)).check(matches(isDisplayed()))
+ onView(withId(R.id.build_flavor_label))
+ .check(matches(withText(R.string.splash_screen_developer_label)))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_alphaFlavor_showsAlphaFlavorText() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.ALPHA)
+
+ initializeTestApplicationWithFlavor(BuildFlavor.ALPHA)
+
+ // The alpha label should be showing.
+ launchSplashActivityFully {
+ onView(withId(R.id.build_flavor_label)).check(matches(isDisplayed()))
+ onView(withId(R.id.build_flavor_label))
+ .check(matches(withText(R.string.splash_screen_alpha_label)))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_betaFlavor_showsBetaFlavorText() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.BETA)
+
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+
+ // The beta label should be showing.
+ launchSplashActivityFully {
+ onView(withId(R.id.build_flavor_label)).check(matches(isDisplayed()))
+ onView(withId(R.id.build_flavor_label))
+ .check(matches(withText(R.string.splash_screen_beta_label)))
+ }
+ }
+
+ @Test
+ fun testSplashActivity_onboarded_generalAvailabilityFlavor_showsNoFlavorText() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ // No label should show for this version of the app.
+ launchSplashActivityFully {
+ onView(withId(R.id.build_flavor_label)).check(matches(not(isDisplayed())))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_testingFlavor_doesNotWaitToStart() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.TESTING)
+ initializeTestApplicationWithFlavor(BuildFlavor.TESTING)
+
+ // The profile chooser opens immediately for the testing flavor since it has no delay.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.runCurrent()
+
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_devFlavor_doesNotWaitToStart() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.DEVELOPER)
+ initializeTestApplicationWithFlavor(BuildFlavor.DEVELOPER)
+
+ // The profile chooser opens immediately for the developer flavor since it has no delay.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.runCurrent()
+
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_alphaFlavor_doNotWait_doesNotStart() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.ALPHA)
+
+ // Nothing opens without waiting for the alpha startup notice to finish.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.runCurrent()
+
+ Intents.assertNoUnverifiedIntents()
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_alphaFlavor_waitTwoSeconds_intentsToProfileChooser() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.ALPHA)
+ initializeTestApplicationWithFlavor(BuildFlavor.ALPHA)
+
+ // The profile chooser should appear after the 2 seconds wait for the alpha splash screen.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(2))
+
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_betaFlavor_doNotWait_doesNotStart() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.BETA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+
+ // Nothing opens without waiting for the beta startup notice to finish.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.runCurrent()
+
+ Intents.assertNoUnverifiedIntents()
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_betaFlavor_waitTwoSeconds_intentsToProfileChooser() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.BETA)
+ initializeTestApplicationWithFlavor(BuildFlavor.BETA)
+
+ // The profile chooser should appear after the 2 seconds wait for the beta splash screen.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.advanceTimeBy(TimeUnit.SECONDS.toMillis(2))
+
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC)
+ fun testSplashActivity_onboarded_gaFlavor_doesNotWaitToStart() {
+ simulateAppAlreadyOnboardedWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+ initializeTestApplicationWithFlavor(BuildFlavor.GENERAL_AVAILABILITY)
+
+ // The profile chooser opens immediately for the GA flavor since it has no delay.
+ launchSplashActivityPartially {
+ testCoroutineDispatchers.runCurrent()
+
+ intended(hasComponent(ProfileChooserActivity::class.java.name))
+ }
+ }
+
+ private fun simulateAppAlreadyOpened() {
+ runInNewTestApplication {
+ val monitor = monitorFactory.createMonitor(appStartupStateController.getAppStartupState())
+ testCoroutineDispatchers.advanceUntilIdle()
+ monitor.ensureNextResultIsSuccess()
+ }
}
private fun simulateAppAlreadyOnboarded() {
@@ -403,12 +1034,63 @@ class SplashActivityTest {
// to be done in an isolated test application since the test application of this class shares
// state with production code under test. The isolated test application must be created through
// Instrumentation to ensure it's properly attached.
- val testApplication = Instrumentation.newApplication(
+ runInNewTestApplication {
+ appStartupStateController.markOnboardingFlowCompleted()
+ testCoroutineDispatchers.advanceUntilIdle()
+ }
+ }
+
+ private fun runInNewTestApplication(block: TestApplication.() -> Unit) {
+ val newApplication = Instrumentation.newApplication(
TestApplication::class.java,
InstrumentationRegistry.getInstrumentation().targetContext
) as TestApplication
- testApplication.getAppStartupStateController().markOnboardingFlowCompleted()
- testApplication.getTestCoroutineDispatchers().advanceUntilIdle()
+ newApplication.testCoroutineDispatchers.registerIdlingResource()
+ newApplication.block()
+ newApplication.testCoroutineDispatchers.unregisterIdlingResource()
+ }
+
+ /**
+ * Allows the existing [TestApplication] to be treated as though it was recreated.
+ *
+ * This should only be used when Espresso needs to run operations in a "previous" application
+ * instance (since Espresso can only use the current instrumentation and not a new one). For all
+ * other cases, prefer [runInNewTestApplication] since it actually creates an entirely new
+ * application in isolation, and is closer to a separate app instance than what this method
+ * produces.
+ */
+ private fun recreateExistingApplication() {
+ testCoroutineDispatchers.unregisterIdlingResource()
+ ApplicationProvider.getApplicationContext().recreateDaggerGraph()
+ initializeTestApplication()
+
+ // Reset any intents previously recorded.
+ Intents.release()
+ Intents.init()
+ }
+
+ private fun recreateExistingApplicationWithFlavor(buildFlavor: BuildFlavor) {
+ TestModule.buildFlavor = buildFlavor
+ recreateExistingApplication()
+ }
+
+ /** See [recreateExistingApplication] for when to use this. */
+ private fun reopenAppWithNewFlavor(buildFlavor: BuildFlavor) {
+ TestModule.buildFlavor = buildFlavor
+ recreateExistingApplication()
+ val monitor = monitorFactory.createMonitor(appStartupStateController.getAppStartupState())
+ testCoroutineDispatchers.advanceUntilIdle()
+ monitor.ensureNextResultIsSuccess()
+ }
+
+ private fun simulateAppAlreadyOpenedWithFlavor(buildFlavor: BuildFlavor) {
+ TestModule.buildFlavor = buildFlavor
+ simulateAppAlreadyOpened()
+ }
+
+ private fun simulateAppAlreadyOnboardedWithFlavor(buildFlavor: BuildFlavor) {
+ TestModule.buildFlavor = buildFlavor
+ simulateAppAlreadyOnboarded()
}
private fun initializeTestApplication() {
@@ -417,6 +1099,38 @@ class SplashActivityTest {
setAutoAppExpirationEnabled(enabled = false) // Default to disabled.
}
+ private fun initializeTestApplicationWithFlavor(buildFlavor: BuildFlavor) {
+ TestModule.buildFlavor = buildFlavor
+ initializeTestApplication()
+ }
+
+ /**
+ * Launches [SplashActivity] and waits for all initial time-based operations to complete before
+ * executing [testBlock].
+ */
+ private fun launchSplashActivityFully(testBlock: (ActivityScenario) -> Unit) {
+ launchSplashActivity {
+ testCoroutineDispatchers.advanceUntilIdle()
+ testBlock(it)
+ }
+ }
+
+ /** Launches [SplashActivity] and waits for initial time-based operations to start. */
+ private fun launchSplashActivityPartially(testBlock: (ActivityScenario) -> Unit) {
+ launchSplashActivity {
+ testCoroutineDispatchers.runCurrent()
+ testBlock(it)
+ }
+ }
+
+ private fun launchSplashActivity(testBlock: (ActivityScenario) -> Unit) {
+ val openFromLauncher = Intent(context, SplashActivity::class.java).also {
+ it.action = Intent.ACTION_MAIN
+ it.addCategory(Intent.CATEGORY_LAUNCHER)
+ }
+ ActivityScenario.launch(openFromLauncher).use(testBlock)
+ }
+
private fun setAutoAppExpirationEnabled(enabled: Boolean) {
fakeMetaDataRetriever.putMetaDataBoolean("automatic_app_expiration_enabled", enabled)
}
@@ -448,26 +1162,26 @@ class SplashActivityTest {
Locale.setDefault(locale)
}
- private fun getShadowLogsOnRobolectric(): List {
- val shadowLogClass = Class.forName("org.robolectric.shadows.ShadowLog")
- val shadowLogItem = Class.forName("org.robolectric.shadows.ShadowLog\$LogItem")
- val msgField = shadowLogItem.getDeclaredField("msg")
- val logItems = shadowLogClass.getDeclaredMethod("getLogs").invoke(/* obj= */ null) as? List<*>
- return logItems?.map { logItem ->
- msgField.get(logItem) as String
- } ?: listOf()
- }
-
private fun corruptCacheFile() {
// Statically retrieve the application context since injection may not have yet occurred.
val applicationContext = ApplicationProvider.getApplicationContext()
File(applicationContext.filesDir, "on_boarding_flow.cache").writeText("broken")
}
+ @Module
+ class TestModule {
+ companion object {
+ var buildFlavor = BuildFlavor.BUILD_FLAVOR_UNSPECIFIED
+ }
+
+ @Provides
+ fun provideTestingBuildFlavor(): BuildFlavor = buildFlavor
+ }
+
@Singleton
@Component(
modules = [
- RobolectricModule::class,
+ TestModule::class, RobolectricModule::class,
TestDispatcherModule::class, ApplicationModule::class, PlatformParameterModule::class,
LoggerModule::class, ContinueModule::class, FractionInputModule::class,
ItemSelectionInputModule::class, MultipleChoiceInputModule::class,
@@ -505,34 +1219,47 @@ class SplashActivityTest {
fun getTestCoroutineDispatchers(): TestCoroutineDispatchers
+ fun getMonitorFactory(): DataProviderTestMonitor.Factory
+
fun inject(splashActivityTest: SplashActivityTest)
}
class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
- private val component: TestApplicationComponent by lazy {
- DaggerSplashActivityTest_TestApplicationComponent.builder()
- .setApplication(this)
- .build()
- }
+ private var component: TestApplicationComponent = createTestApplicationComponent()
+
+ val appStartupStateController: AppStartupStateController
+ get() = component.getAppStartupStateController()
+ val testCoroutineDispatchers: TestCoroutineDispatchers
+ get() = component.getTestCoroutineDispatchers()
+ val monitorFactory: DataProviderTestMonitor.Factory
+ get() = component.getMonitorFactory()
fun inject(splashActivityTest: SplashActivityTest) {
component.inject(splashActivityTest)
}
- fun getAppStartupStateController() = component.getAppStartupStateController()
-
- fun getTestCoroutineDispatchers() = component.getTestCoroutineDispatchers()
-
override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
}
override fun getApplicationInjector(): ApplicationInjector = component
+
+ fun recreateDaggerGraph() {
+ component = createTestApplicationComponent()
+ }
+
+ private fun createTestApplicationComponent(): TestApplicationComponent {
+ return DaggerSplashActivityTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
}
private companion object {
private val EGYPT_ARABIC_LOCALE = Locale("ar", "EG")
private val BRAZIL_PORTUGUESE_LOCALE = Locale("pt", "BR")
private val TURKEY_TURKISH_LOCALE = Locale("tr", "TR")
+
+ private fun onDialogView(matcher: Matcher) = onView(matcher).inRoot(isDialog())
}
}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt
index 54df28345a1..2fa6bda9d46 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.exploration.ExplorationActivity
@@ -235,7 +236,7 @@ class StoryActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt
index 74fae89ca56..e1b9859c69c 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt
@@ -61,6 +61,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.customview.LessonThumbnailImageView
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -903,7 +904,7 @@ class StoryFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt
index fc866ddceb1..3e79cccd1b6 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt
@@ -24,6 +24,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -228,7 +229,7 @@ class DragDropTestActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt
index 20d315e0475..15ced77dc59 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt
@@ -36,6 +36,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.ImageRegionSelectionInteractionView
@@ -415,7 +416,7 @@ class ImageRegionSelectionInteractionViewTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
index da4d3031acd..97d4326ceaf 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
@@ -36,6 +36,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.customview.interaction.RatioInputInteractionView
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -1126,7 +1127,7 @@ class InputInteractionViewTestActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt
index 69a7cfcc0ac..6096a49752e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt
@@ -55,6 +55,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsActivity
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
@@ -439,7 +440,7 @@ class NavigationDrawerActivityDebugTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt
index c7a0fc12355..e9c6b2a98ac 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt
@@ -60,6 +60,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.drawer.NavigationDrawerItem
import org.oppia.android.app.help.HelpActivity
@@ -988,7 +989,7 @@ class NavigationDrawerActivityProdTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/TestFontScaleConfigurationUtilActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/TestFontScaleConfigurationUtilActivityTest.kt
index 7488560d8d9..5cfe02d9bdc 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/TestFontScaleConfigurationUtilActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/TestFontScaleConfigurationUtilActivityTest.kt
@@ -25,6 +25,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ReadingTextSize
@@ -206,7 +207,7 @@ class TestFontScaleConfigurationUtilActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt
index 5aebaae32fa..329648407b1 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt
@@ -28,6 +28,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -203,7 +204,7 @@ class TopicTestActivityForStoryTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt
index efb85e232b1..32993ac8369 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.LicenseListActivity
@@ -163,7 +164,7 @@ class LicenseListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListFragmentTest.kt
index 4fd305d3ce0..0b5f2eae1ab 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.LicenseListActivity
@@ -373,7 +374,7 @@ class LicenseListFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerActivityTest.kt
index a751f1b52de..198ef7c20f5 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.LicenseListActivity
@@ -172,7 +173,7 @@ class LicenseTextViewerActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerFragmentTest.kt
index bd5f229906f..b12abd0898a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseTextViewerFragmentTest.kt
@@ -26,6 +26,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.LicenseTextViewerActivity
@@ -351,7 +352,7 @@ class LicenseTextViewerFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListActivityTest.kt
index 2298212d383..b6a7a2093b7 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.ThirdPartyDependencyListActivity
@@ -160,7 +161,7 @@ class ThirdPartyDependencyListActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt
index f9dd5befd2e..cd7839f3fdb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/ThirdPartyDependencyListFragmentTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.thirdparty.LicenseListActivity
@@ -483,7 +484,7 @@ class ThirdPartyDependencyListFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class,
- MetricLogSchedulerModule::class
+ MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
index 15c80f80eca..9cbbb6dd857 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -209,7 +210,7 @@ class TopicActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt
index 87fb560776c..a83ee19558f 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt
@@ -43,6 +43,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -664,7 +665,7 @@ class TopicFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
index 6a3c9d7be97..3af01c379e0 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
@@ -38,6 +38,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -435,7 +436,7 @@ class ConceptCardFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt
index e743d17c05d..ab908ad0315 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt
@@ -43,6 +43,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -491,7 +492,7 @@ class TopicInfoFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt
index 2e12f85d07a..6cccfd1f84a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt
@@ -43,6 +43,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -1020,7 +1021,7 @@ class TopicLessonsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
index 98ae842aa2d..be3f067135a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -436,7 +437,7 @@ class TopicPracticeFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
index 8fbee49074d..13e515ebefc 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
@@ -54,6 +54,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -725,7 +726,7 @@ class QuestionPlayerActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt
index 86da92fe690..01054765d12 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -332,7 +333,7 @@ class TopicRevisionFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
index 6d8fbf9be40..894dec9cb73 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
@@ -32,6 +32,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -286,7 +287,7 @@ class RevisionCardActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
index 84f04139d51..85c1e26ef3d 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
@@ -45,6 +45,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.HelpActivity
@@ -602,7 +603,7 @@ class RevisionCardFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt b/app/src/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt
index 3596c8533d1..8ea214e120a 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.RatioExpression
@@ -159,7 +160,7 @@ class RatioExtensionsTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt
index cd49b90d400..9d977f37a02 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt
@@ -31,6 +31,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -237,7 +238,7 @@ class WalkthroughActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt
index 7bdea47cd8c..d27197c2cad 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -294,7 +295,7 @@ class WalkthroughFinalFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt
index f334d5e7080..ec3ab607a23 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt
@@ -34,6 +34,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -320,7 +321,7 @@ class WalkthroughTopicListFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt
index 59b4f9f4ca8..d8b48d6dbdb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt
@@ -29,6 +29,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -217,7 +218,7 @@ class WalkthroughWelcomeFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/activity/ActivityIntentFactoriesTest.kt b/app/src/test/java/org/oppia/android/app/activity/ActivityIntentFactoriesTest.kt
index f0491f38b65..80145e188cc 100644
--- a/app/src/test/java/org/oppia/android/app/activity/ActivityIntentFactoriesTest.kt
+++ b/app/src/test/java/org/oppia/android/app/activity/ActivityIntentFactoriesTest.kt
@@ -19,6 +19,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedActivity
@@ -182,7 +183,7 @@ class ActivityIntentFactoriesTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/activity/BUILD.bazel b/app/src/test/java/org/oppia/android/app/activity/BUILD.bazel
index a3c4e616851..ec052a7416a 100644
--- a/app/src/test/java/org/oppia/android/app/activity/BUILD.bazel
+++ b/app/src/test/java/org/oppia/android/app/activity/BUILD.bazel
@@ -19,6 +19,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//domain/src/main/java/org/oppia/android/domain/classify:interactions_module",
diff --git a/app/src/test/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModuleTest.kt b/app/src/test/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModuleTest.kt
new file mode 100644
index 00000000000..f40c984f7b4
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModuleTest.kt
@@ -0,0 +1,81 @@
+package org.oppia.android.app.application.alpha
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.app.model.BuildFlavor
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [AlphaBuildFlavorModule]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(application = AlphaBuildFlavorModuleTest.TestApplication::class)
+class AlphaBuildFlavorModuleTest {
+ @Inject
+ lateinit var buildFlavor: BuildFlavor
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testBuildFlavor_isAlphaBuildFlavor() {
+ assertThat(buildFlavor).isEqualTo(BuildFlavor.ALPHA)
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Singleton
+ @Component(modules = [TestModule::class, AlphaBuildFlavorModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: AlphaBuildFlavorModuleTest)
+ }
+
+ class TestApplication : Application() {
+ private val component: TestApplicationComponent by lazy {
+ DaggerAlphaBuildFlavorModuleTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: AlphaBuildFlavorModuleTest) {
+ component.inject(test)
+ }
+ }
+}
diff --git a/app/src/test/java/org/oppia/android/app/application/alpha/BUILD.bazel b/app/src/test/java/org/oppia/android/app/application/alpha/BUILD.bazel
new file mode 100644
index 00000000000..900715a50e6
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/alpha/BUILD.bazel
@@ -0,0 +1,26 @@
+"""
+Tests for alpha-specific top-level application configurations.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "AlphaBuildFlavorModuleTest",
+ srcs = ["AlphaBuildFlavorModuleTest.kt"],
+ custom_package = "org.oppia.android.app.application.alpha",
+ test_class = "org.oppia.android.app.application.alpha.AlphaBuildFlavorModuleTest",
+ test_manifest = "//app:test_manifest",
+ deps = [
+ ":dagger",
+ "//app/src/main/java/org/oppia/android/app/application/alpha:alpha_build_flavor_module",
+ "//model/src/main/proto:version_java_proto_lite",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/test/java/org/oppia/android/app/application/beta/BUILD.bazel b/app/src/test/java/org/oppia/android/app/application/beta/BUILD.bazel
new file mode 100644
index 00000000000..4706b193b26
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/beta/BUILD.bazel
@@ -0,0 +1,26 @@
+"""
+Tests for beta-specific top-level application configurations.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "BetaBuildFlavorModuleTest",
+ srcs = ["BetaBuildFlavorModuleTest.kt"],
+ custom_package = "org.oppia.android.app.application.beta",
+ test_class = "org.oppia.android.app.application.beta.BetaBuildFlavorModuleTest",
+ test_manifest = "//app:test_manifest",
+ deps = [
+ ":dagger",
+ "//app/src/main/java/org/oppia/android/app/application/beta:beta_application",
+ "//model/src/main/proto:version_java_proto_lite",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/test/java/org/oppia/android/app/application/beta/BetaBuildFlavorModuleTest.kt b/app/src/test/java/org/oppia/android/app/application/beta/BetaBuildFlavorModuleTest.kt
new file mode 100644
index 00000000000..efbe6eab3e7
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/beta/BetaBuildFlavorModuleTest.kt
@@ -0,0 +1,81 @@
+package org.oppia.android.app.application.beta
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.app.model.BuildFlavor
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [BetaBuildFlavorModule]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(application = BetaBuildFlavorModuleTest.TestApplication::class)
+class BetaBuildFlavorModuleTest {
+ @Inject
+ lateinit var buildFlavor: BuildFlavor
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testBuildFlavor_isBetaBuildFlavor() {
+ assertThat(buildFlavor).isEqualTo(BuildFlavor.BETA)
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Singleton
+ @Component(modules = [TestModule::class, BetaBuildFlavorModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: BetaBuildFlavorModuleTest)
+ }
+
+ class TestApplication : Application() {
+ private val component: TestApplicationComponent by lazy {
+ DaggerBetaBuildFlavorModuleTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: BetaBuildFlavorModuleTest) {
+ component.inject(test)
+ }
+ }
+}
diff --git a/app/src/test/java/org/oppia/android/app/application/dev/BUILD.bazel b/app/src/test/java/org/oppia/android/app/application/dev/BUILD.bazel
new file mode 100644
index 00000000000..c6566c6f2a6
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/dev/BUILD.bazel
@@ -0,0 +1,26 @@
+"""
+Tests for developer-specific top-level application configurations.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "DeveloperBuildFlavorModuleTest",
+ srcs = ["DeveloperBuildFlavorModuleTest.kt"],
+ custom_package = "org.oppia.android.app.application.dev",
+ test_class = "org.oppia.android.app.application.dev.DeveloperBuildFlavorModuleTest",
+ test_manifest = "//app:test_manifest",
+ deps = [
+ ":dagger",
+ "//app/src/main/java/org/oppia/android/app/application/dev:developer_application",
+ "//model/src/main/proto:version_java_proto_lite",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/test/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModuleTest.kt b/app/src/test/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModuleTest.kt
new file mode 100644
index 00000000000..9f5a1c6e7ee
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModuleTest.kt
@@ -0,0 +1,81 @@
+package org.oppia.android.app.application.dev
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.app.model.BuildFlavor
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [DeveloperBuildFlavorModule]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(application = DeveloperBuildFlavorModuleTest.TestApplication::class)
+class DeveloperBuildFlavorModuleTest {
+ @Inject
+ lateinit var buildFlavor: BuildFlavor
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testBuildFlavor_isDeveloperBuildFlavor() {
+ assertThat(buildFlavor).isEqualTo(BuildFlavor.DEVELOPER)
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Singleton
+ @Component(modules = [TestModule::class, DeveloperBuildFlavorModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: DeveloperBuildFlavorModuleTest)
+ }
+
+ class TestApplication : Application() {
+ private val component: TestApplicationComponent by lazy {
+ DaggerDeveloperBuildFlavorModuleTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: DeveloperBuildFlavorModuleTest) {
+ component.inject(test)
+ }
+ }
+}
diff --git a/app/src/test/java/org/oppia/android/app/application/ga/BUILD.bazel b/app/src/test/java/org/oppia/android/app/application/ga/BUILD.bazel
new file mode 100644
index 00000000000..c3ce6168c22
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/ga/BUILD.bazel
@@ -0,0 +1,26 @@
+"""
+Tests for general availability-specific top-level application configurations.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "GaBuildFlavorModuleTest",
+ srcs = ["GaBuildFlavorModuleTest.kt"],
+ custom_package = "org.oppia.android.app.application.ga",
+ test_class = "org.oppia.android.app.application.ga.GaBuildFlavorModuleTest",
+ test_manifest = "//app:test_manifest",
+ deps = [
+ ":dagger",
+ "//app/src/main/java/org/oppia/android/app/application/ga:general_availability_application",
+ "//model/src/main/proto:version_java_proto_lite",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/test/java/org/oppia/android/app/application/ga/GaBuildFlavorModuleTest.kt b/app/src/test/java/org/oppia/android/app/application/ga/GaBuildFlavorModuleTest.kt
new file mode 100644
index 00000000000..b8a848a432e
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/ga/GaBuildFlavorModuleTest.kt
@@ -0,0 +1,81 @@
+package org.oppia.android.app.application.ga
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.app.model.BuildFlavor
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [GaBuildFlavorModule]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(application = GaBuildFlavorModuleTest.TestApplication::class)
+class GaBuildFlavorModuleTest {
+ @Inject
+ lateinit var buildFlavor: BuildFlavor
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testBuildFlavor_isGeneralAvailabilityBuildFlavor() {
+ assertThat(buildFlavor).isEqualTo(BuildFlavor.GENERAL_AVAILABILITY)
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Singleton
+ @Component(modules = [TestModule::class, GaBuildFlavorModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: GaBuildFlavorModuleTest)
+ }
+
+ class TestApplication : Application() {
+ private val component: TestApplicationComponent by lazy {
+ DaggerGaBuildFlavorModuleTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: GaBuildFlavorModuleTest) {
+ component.inject(test)
+ }
+ }
+}
diff --git a/app/src/test/java/org/oppia/android/app/application/testing/BUILD.bazel b/app/src/test/java/org/oppia/android/app/application/testing/BUILD.bazel
new file mode 100644
index 00000000000..2168366aeb4
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/testing/BUILD.bazel
@@ -0,0 +1,26 @@
+"""
+Tests for testing-specific top-level application configurations.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "TestingBuildFlavorModuleTest",
+ srcs = ["TestingBuildFlavorModuleTest.kt"],
+ custom_package = "org.oppia.android.app.application.testing",
+ test_class = "org.oppia.android.app.application.testing.TestingBuildFlavorModuleTest",
+ test_manifest = "//app:test_manifest",
+ deps = [
+ ":dagger",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
+ "//model/src/main/proto:version_java_proto_lite",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/app/src/test/java/org/oppia/android/app/application/testing/TestingBuildFlavorModuleTest.kt b/app/src/test/java/org/oppia/android/app/application/testing/TestingBuildFlavorModuleTest.kt
new file mode 100644
index 00000000000..f1b3136ccb0
--- /dev/null
+++ b/app/src/test/java/org/oppia/android/app/application/testing/TestingBuildFlavorModuleTest.kt
@@ -0,0 +1,81 @@
+package org.oppia.android.app.application.testing
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.app.model.BuildFlavor
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [TestingBuildFlavorModule]. */
+// FunctionName: test names are conventionally named with underscores.
+@Suppress("FunctionName")
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(application = TestingBuildFlavorModuleTest.TestApplication::class)
+class TestingBuildFlavorModuleTest {
+ @Inject
+ lateinit var buildFlavor: BuildFlavor
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ }
+
+ @Test
+ fun testBuildFlavor_isTestingBuildFlavor() {
+ assertThat(buildFlavor).isEqualTo(BuildFlavor.TESTING)
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+ }
+
+ // TODO(#89): Move this to a common test application component.
+ @Singleton
+ @Component(modules = [TestModule::class, TestingBuildFlavorModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+
+ fun inject(test: TestingBuildFlavorModuleTest)
+ }
+
+ class TestApplication : Application() {
+ private val component: TestApplicationComponent by lazy {
+ DaggerTestingBuildFlavorModuleTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build()
+ }
+
+ fun inject(test: TestingBuildFlavorModuleTest) {
+ component.inject(test)
+ }
+ }
+}
diff --git a/app/src/test/java/org/oppia/android/app/home/HomeActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/home/HomeActivityLocalTest.kt
index 445649d393b..810f35b9475 100644
--- a/app/src/test/java/org/oppia/android/app/home/HomeActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/home/HomeActivityLocalTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -154,7 +155,7 @@ class HomeActivityLocalTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/parser/FractionParsingUiErrorTest.kt b/app/src/test/java/org/oppia/android/app/parser/FractionParsingUiErrorTest.kt
index d6cb209d2c1..c0216f6e8c6 100644
--- a/app/src/test/java/org/oppia/android/app/parser/FractionParsingUiErrorTest.kt
+++ b/app/src/test/java/org/oppia/android/app/parser/FractionParsingUiErrorTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -264,7 +265,7 @@ class FractionParsingUiErrorTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt b/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt
index 1ccba698308..8c3fc424a40 100644
--- a/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt
@@ -36,6 +36,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.LanguageSupportDefinition
@@ -1106,7 +1107,7 @@ class ListItemLeadingMarginSpanTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
diff --git a/app/src/test/java/org/oppia/android/app/parser/StringToRatioParserTest.kt b/app/src/test/java/org/oppia/android/app/parser/StringToRatioParserTest.kt
index 73ab39d434f..0c038ea5d11 100644
--- a/app/src/test/java/org/oppia/android/app/parser/StringToRatioParserTest.kt
+++ b/app/src/test/java/org/oppia/android/app/parser/StringToRatioParserTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.RatioExpression
@@ -268,7 +269,7 @@ class StringToRatioParserTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt
index 1c875e82863..25d38fd6333 100644
--- a/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt
@@ -20,6 +20,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -215,7 +216,7 @@ class ExplorationActivityLocalTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
index 30849532780..0a6b6a31e01 100644
--- a/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
@@ -58,6 +58,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.hintsandsolution.TAG_REVEAL_SOLUTION_DIALOG
@@ -2297,7 +2298,7 @@ class StateFragmentLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/profile/ProfileChooserFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/profile/ProfileChooserFragmentLocalTest.kt
index df5ab8f58d0..db5a73a3add 100644
--- a/app/src/test/java/org/oppia/android/app/profile/ProfileChooserFragmentLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/profile/ProfileChooserFragmentLocalTest.kt
@@ -19,6 +19,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog.Context.ActivityContextCase.OPEN_PROFILE_CHOOSER
@@ -143,7 +144,7 @@ class ProfileChooserFragmentLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/story/StoryActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/story/StoryActivityLocalTest.kt
index 5d9cc5e3e75..dbe875db082 100644
--- a/app/src/test/java/org/oppia/android/app/story/StoryActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/story/StoryActivityLocalTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -168,7 +169,7 @@ class StoryActivityLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/CompletedStoryListSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/CompletedStoryListSpanTest.kt
index 86a05a175dc..e2874312b64 100644
--- a/app/src/test/java/org/oppia/android/app/testing/CompletedStoryListSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/CompletedStoryListSpanTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.completedstorylist.CompletedStoryListActivity
import org.oppia.android.app.completedstorylist.CompletedStoryListFragment.Companion.COMPLETED_STORY_LIST_FRAGMENT_TAG
import org.oppia.android.app.devoptions.DeveloperOptionsModule
@@ -177,7 +178,7 @@ class CompletedStoryListSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/HomeSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/HomeSpanTest.kt
index 83e920a77bd..d34e7fa7a19 100644
--- a/app/src/test/java/org/oppia/android/app/testing/HomeSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/HomeSpanTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.HomeActivity
@@ -191,7 +192,7 @@ class HomeSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/OngoingTopicListSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/OngoingTopicListSpanTest.kt
index fc6c6aff74e..ae741ccd5b8 100644
--- a/app/src/test/java/org/oppia/android/app/testing/OngoingTopicListSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/OngoingTopicListSpanTest.kt
@@ -23,6 +23,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.ongoingtopiclist.OngoingTopicListActivity
@@ -188,7 +189,7 @@ class OngoingTopicListSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/PlatformParameterIntegrationTest.kt b/app/src/test/java/org/oppia/android/app/testing/PlatformParameterIntegrationTest.kt
index 5d30677deb7..abb5758dc05 100644
--- a/app/src/test/java/org/oppia/android/app/testing/PlatformParameterIntegrationTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/PlatformParameterIntegrationTest.kt
@@ -33,6 +33,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.PlatformParameter
@@ -361,7 +362,7 @@ class PlatformParameterIntegrationTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/ProfileChooserSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/ProfileChooserSpanTest.kt
index ac18c7ff49f..07fa5fc3e8a 100644
--- a/app/src/test/java/org/oppia/android/app/testing/ProfileChooserSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/ProfileChooserSpanTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -390,7 +391,7 @@ class ProfileChooserSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/ProfileProgressSpanCountTest.kt b/app/src/test/java/org/oppia/android/app/testing/ProfileProgressSpanCountTest.kt
index 7b515278346..a51118c53c2 100644
--- a/app/src/test/java/org/oppia/android/app/testing/ProfileProgressSpanCountTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/ProfileProgressSpanCountTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -174,7 +175,7 @@ class ProfileProgressSpanCountTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt
index 4571ab23451..d922623fe3f 100644
--- a/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt
@@ -23,6 +23,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedActivity
@@ -309,7 +310,7 @@ class RecentlyPlayedSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/TopicRevisionSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/TopicRevisionSpanTest.kt
index 9ea7e810383..24822b4c5f2 100644
--- a/app/src/test/java/org/oppia/android/app/testing/TopicRevisionSpanTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/TopicRevisionSpanTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -174,7 +175,7 @@ class TopicRevisionSpanTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/activity/BUILD.bazel b/app/src/test/java/org/oppia/android/app/testing/activity/BUILD.bazel
index e56a257b618..6b3c8185dd7 100644
--- a/app/src/test/java/org/oppia/android/app/testing/activity/BUILD.bazel
+++ b/app/src/test/java/org/oppia/android/app/testing/activity/BUILD.bazel
@@ -18,6 +18,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/testing/activity:test_activity",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
diff --git a/app/src/test/java/org/oppia/android/app/testing/activity/TestActivityTest.kt b/app/src/test/java/org/oppia/android/app/testing/activity/TestActivityTest.kt
index bb510bd60c1..a2b18b6500a 100644
--- a/app/src/test/java/org/oppia/android/app/testing/activity/TestActivityTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/activity/TestActivityTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -199,7 +200,7 @@ class TestActivityTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/administratorcontrols/AdministratorControlsFragmentTest.kt b/app/src/test/java/org/oppia/android/app/testing/administratorcontrols/AdministratorControlsFragmentTest.kt
index 1f95d05540e..9f0d0a5e4a3 100644
--- a/app/src/test/java/org/oppia/android/app/testing/administratorcontrols/AdministratorControlsFragmentTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/administratorcontrols/AdministratorControlsFragmentTest.kt
@@ -30,6 +30,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -198,7 +199,7 @@ class AdministratorControlsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/options/OptionsFragmentTest.kt b/app/src/test/java/org/oppia/android/app/testing/options/OptionsFragmentTest.kt
index 3ebe6f52678..b0481e189ec 100644
--- a/app/src/test/java/org/oppia/android/app/testing/options/OptionsFragmentTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/options/OptionsFragmentTest.kt
@@ -27,6 +27,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.options.AppLanguageFragment
@@ -252,7 +253,7 @@ class OptionsFragmentTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/player/split/PlayerSplitScreenTest.kt b/app/src/test/java/org/oppia/android/app/testing/player/split/PlayerSplitScreenTest.kt
index a7d14d4a6c2..1b7ff73b2c3 100644
--- a/app/src/test/java/org/oppia/android/app/testing/player/split/PlayerSplitScreenTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/player/split/PlayerSplitScreenTest.kt
@@ -20,6 +20,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -207,7 +208,7 @@ class PlayerSplitScreenTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/testing/player/state/StateFragmentAccessibilityTest.kt b/app/src/test/java/org/oppia/android/app/testing/player/state/StateFragmentAccessibilityTest.kt
index 11c88ca340c..369862684ef 100644
--- a/app/src/test/java/org/oppia/android/app/testing/player/state/StateFragmentAccessibilityTest.kt
+++ b/app/src/test/java/org/oppia/android/app/testing/player/state/StateFragmentAccessibilityTest.kt
@@ -24,6 +24,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.StateFragment
@@ -214,7 +215,7 @@ class StateFragmentAccessibilityTest {
AlgebraicExpressionInputModule::class, MathEquationInputModule::class,
SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/topic/info/TopicInfoFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/info/TopicInfoFragmentLocalTest.kt
index 68b9da3603a..2ce810912e1 100644
--- a/app/src/test/java/org/oppia/android/app/topic/info/TopicInfoFragmentLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/topic/info/TopicInfoFragmentLocalTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -156,7 +157,7 @@ class TopicInfoFragmentLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentLocalTest.kt
index cd09a71eb8b..34e74865716 100644
--- a/app/src/test/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentLocalTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -159,7 +160,7 @@ class TopicLessonsFragmentLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
index aedeb8e43cc..f095af739b5 100644
--- a/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
@@ -35,6 +35,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.ProfileId
@@ -507,7 +508,7 @@ class QuestionPlayerActivityLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityLocalTest.kt
index 510f56bd197..df63ed67dbb 100644
--- a/app/src/test/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityLocalTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.EventLog
@@ -148,7 +149,7 @@ class RevisionCardActivityLocalTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/translation/AppLanguageResourceHandlerTest.kt b/app/src/test/java/org/oppia/android/app/translation/AppLanguageResourceHandlerTest.kt
index 3ef2af12404..12c7ed9d8c3 100644
--- a/app/src/test/java/org/oppia/android/app/translation/AppLanguageResourceHandlerTest.kt
+++ b/app/src/test/java/org/oppia/android/app/translation/AppLanguageResourceHandlerTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.AppLanguageSelection
@@ -557,7 +558,7 @@ class AppLanguageResourceHandlerTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt b/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
index 21ac94faac3..5b4a6ede4fb 100644
--- a/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
+++ b/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
@@ -23,6 +23,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.AppLanguageSelection
@@ -272,7 +273,7 @@ class AppLanguageWatcherMixinTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/translation/BUILD.bazel b/app/src/test/java/org/oppia/android/app/translation/BUILD.bazel
index 54853ed376f..171d07f5602 100644
--- a/app/src/test/java/org/oppia/android/app/translation/BUILD.bazel
+++ b/app/src/test/java/org/oppia/android/app/translation/BUILD.bazel
@@ -53,6 +53,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation:app_language_locale_handler",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
@@ -99,6 +100,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//data/src/main/java/org/oppia/android/data/backends/gae:prod_module",
"//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
diff --git a/app/src/test/java/org/oppia/android/app/utility/datetime/DateTimeUtilTest.kt b/app/src/test/java/org/oppia/android/app/utility/datetime/DateTimeUtilTest.kt
index 67d0b06add7..95ba0d375ff 100644
--- a/app/src/test/java/org/oppia/android/app/utility/datetime/DateTimeUtilTest.kt
+++ b/app/src/test/java/org/oppia/android/app/utility/datetime/DateTimeUtilTest.kt
@@ -22,6 +22,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -185,7 +186,7 @@ class DateTimeUtilTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class
+ SyncStatusModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/app/src/test/java/org/oppia/android/app/utility/math/BUILD.bazel b/app/src/test/java/org/oppia/android/app/utility/math/BUILD.bazel
index b50532b834d..24a84596c68 100644
--- a/app/src/test/java/org/oppia/android/app/utility/math/BUILD.bazel
+++ b/app/src/test/java/org/oppia/android/app/utility/math/BUILD.bazel
@@ -18,6 +18,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//app/src/main/java/org/oppia/android/app/utility/math:math_expression_accessibility_util",
"//domain/src/main/java/org/oppia/android/domain/onboarding/testing:retriever_test_module",
diff --git a/app/src/test/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtilTest.kt b/app/src/test/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtilTest.kt
index d742b20a586..293f8b0e94a 100644
--- a/app/src/test/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtilTest.kt
+++ b/app/src/test/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtilTest.kt
@@ -21,6 +21,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.MathBinaryOperation
@@ -1331,7 +1332,7 @@ class MathExpressionAccessibilityUtilTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/build_flavors.bzl b/build_flavors.bzl
index a5ca841a737..ec541fe8046 100644
--- a/build_flavors.bzl
+++ b/build_flavors.bzl
@@ -3,7 +3,7 @@ Macros & definitions corresponding to Oppia binary build flavors.
"""
load("//:oppia_android_application.bzl", "declare_deployable_application", "oppia_android_application")
-load("//:version.bzl", "MAJOR_VERSION", "MINOR_VERSION", "OPPIA_ALPHA_KENYA_VERSION_CODE", "OPPIA_ALPHA_KITKAT_VERSION_CODE", "OPPIA_ALPHA_VERSION_CODE", "OPPIA_DEV_KITKAT_VERSION_CODE", "OPPIA_DEV_VERSION_CODE")
+load("//:version.bzl", "MAJOR_VERSION", "MINOR_VERSION", "OPPIA_ALPHA_KENYA_VERSION_CODE", "OPPIA_ALPHA_KITKAT_VERSION_CODE", "OPPIA_ALPHA_VERSION_CODE", "OPPIA_BETA_VERSION_CODE", "OPPIA_DEV_KITKAT_VERSION_CODE", "OPPIA_DEV_VERSION_CODE", "OPPIA_GA_VERSION_CODE")
# Defines the list of flavors available to build the Oppia app in. Note to developers: this list
# should be ordered by the development pipeline (i.e. features go through dev first, then other
@@ -14,6 +14,8 @@ AVAILABLE_FLAVORS = [
"alpha",
"alpha_kitkat",
"alpha_kenya",
+ "beta",
+ "ga",
]
# This file contains the list of classes that must be in the main dex list for the legacy multidex
@@ -108,6 +110,32 @@ _FLAVOR_METADATA = {
"version_code": OPPIA_ALPHA_KENYA_VERSION_CODE,
"application_class": ".app.application.alphakenya.AlphaKenyaOppiaApplication",
},
+ "beta": {
+ "manifest": "//app:src/main/AndroidManifest.xml",
+ "min_sdk_version": 21,
+ "target_sdk_version": 30,
+ "multidex": "native",
+ "proguard_specs": _PRODUCTION_PROGUARD_SPECS,
+ "production_release": True,
+ "deps": [
+ "//app/src/main/java/org/oppia/android/app/application/beta:beta_application",
+ ],
+ "version_code": OPPIA_BETA_VERSION_CODE,
+ "application_class": ".app.application.beta.BetaOppiaApplication",
+ },
+ "ga": {
+ "manifest": "//app:src/main/AndroidManifest.xml",
+ "min_sdk_version": 21,
+ "target_sdk_version": 30,
+ "multidex": "native",
+ "proguard_specs": _PRODUCTION_PROGUARD_SPECS,
+ "production_release": True,
+ "deps": [
+ "//app/src/main/java/org/oppia/android/app/application/ga:general_availability_application",
+ ],
+ "version_code": OPPIA_GA_VERSION_CODE,
+ "application_class": ".app.application.ga.GaOppiaApplication",
+ },
}
def _transform_android_manifest_impl(ctx):
diff --git a/data/BUILD.bazel b/data/BUILD.bazel
index aecd0697a62..8189af63a2f 100644
--- a/data/BUILD.bazel
+++ b/data/BUILD.bazel
@@ -6,6 +6,12 @@ This library provides data to the rest of the application.
load("@dagger//:workspace_defs.bzl", "dagger_rules")
load("//data:data_test.bzl", "data_test")
+filegroup(
+ name = "test_manifest",
+ srcs = ["src/test/AndroidManifest.xml"],
+ visibility = ["//:oppia_testing_visibility"],
+)
+
# keep sorted
TEST_DEPS = [
":dagger",
diff --git a/data/src/main/java/org/oppia/android/data/persistence/PersistentCacheStore.kt b/data/src/main/java/org/oppia/android/data/persistence/PersistentCacheStore.kt
index daacb9b8822..e7c081e6834 100644
--- a/data/src/main/java/org/oppia/android/data/persistence/PersistentCacheStore.kt
+++ b/data/src/main/java/org/oppia/android/data/persistence/PersistentCacheStore.kt
@@ -1,6 +1,6 @@
package org.oppia.android.data.persistence
-import android.content.Context
+import android.app.Application
import androidx.annotation.GuardedBy
import com.google.protobuf.MessageLite
import kotlinx.coroutines.Deferred
@@ -21,24 +21,27 @@ import kotlin.concurrent.withLock
/**
* An on-disk persistent cache for proto messages that ensures reads and writes happen in a
- * well-defined order. Note that if this cache is used like a [DataProvider], there is a race
- * condition between the initial store's data being retrieved and any early writes to the store
- * (writes generally win). If this is not ideal, callers should use [primeInMemoryCacheAsync] to
- * synchronously kick-off a read update to the store that is guaranteed to complete before any
- * writes. This will be reflected in the first time the store's state is delivered to a subscriber
- * to a LiveData version of this data provider.
+ * well-defined order.
*
- * Note that this is a fast-response data provider, meaning it will provide a pending [AsyncResult]
- * to subscribers immediately until the actual store is retrieved from disk.
+ * Note that if this cache is used like a [DataProvider], there is a race condition between the
+ * initial store's data being retrieved and any early writes to the store (writes generally win). If
+ * this is not ideal, callers should use [primeInMemoryAndDiskCacheAsync] to synchronously kick-off
+ * a read update to the store that is guaranteed to complete before any writes. This will be
+ * reflected in the first time the store's state is delivered to a subscriber to a LiveData version
+ * of this data provider. Note that this priming will always complete before any updates if it's
+ * called before updates/reads.
+ *
+ * Note that this is a fast-response data provider, meaning it will provide a [AsyncResult.Pending]
+ * result to subscribers immediately until the actual store is retrieved from disk.
*/
class PersistentCacheStore private constructor(
- context: Context,
+ application: Application,
cacheFactory: InMemoryBlockingCache.Factory,
private val asyncDataSubscriptionManager: AsyncDataSubscriptionManager,
cacheName: String,
private val initialValue: T,
- directory: File = context.filesDir
-) : DataProvider(context) {
+ directory: File = application.filesDir
+) : DataProvider(application) {
private val cacheFileName = "$cacheName.cache"
private val providerId = PersistentCacheStoreId(cacheFileName)
private val failureLock = ReentrantLock()
@@ -50,14 +53,21 @@ class PersistentCacheStore private constructor(
cacheFactory.create(CachePayload(state = CacheState.UNLOADED, value = initialValue))
init {
- cache.observeChanges {
- asyncDataSubscriptionManager.notifyChange(providerId)
+ cache.observeChanges { oldValue, newValue ->
+ // Only notice subscribers if the in-memory version of the cache actually changed (not just
+ // its load state). This extra check ensures that priming the cache does not unnecessarily
+ // trigger a notification which would result in an unnecessary retrieveData() call. The
+ // exception is that changing from an UNLOADED state always results in a notification (since
+ // UNLOADED is treated as 'pending' by default).
+ val wasPending = oldValue?.state == CacheState.UNLOADED
+ val nowPending = newValue?.state == CacheState.UNLOADED
+ if ((wasPending && !nowPending) || oldValue?.value != newValue?.value) {
+ asyncDataSubscriptionManager.notifyChange(providerId)
+ }
}
}
- override fun getId(): Any {
- return providerId
- }
+ override fun getId(): Any = providerId
override suspend fun retrieveData(): AsyncResult {
cache.readIfPresentAsync().await().let { cachePayload ->
@@ -85,69 +95,81 @@ class PersistentCacheStore private constructor(
}
/**
- * Kicks off a read operation to update the in-memory cache. This operation blocks against calls
- * to [storeDataAsync] and deferred calls to [retrieveData].
+ * Primes the current cache such that certain guarantees can be assured for both the in-memory and
+ * on-disk version of this cache, depending on which policies are selected.
*
- * @param forceUpdate indicates whether to force a reset of the in-memory cache. Note that this
- * only forces a load; if the load fails then the store will remain in its same state. If this
- * value is false (the default), it will only perform file I/O if the cache is not already
- * loaded into memory.
- * @returns a [Deferred] that completes upon the completion of priming the cache, or failure to do
- * so with the failed exception. Note that the failure reason will not be propagated to a
- * LiveData-converted version of this data provider, so it must be handled at the callsite for
- * this method.
- */
- fun primeInMemoryCacheAsync(forceUpdate: Boolean = false): Deferred {
- return cache.updateIfPresentAsync { cachePayload ->
- if (forceUpdate || cachePayload.state == CacheState.UNLOADED) {
- // Store the retrieved on-disk cache, if it's present (otherwise set up state such that
- // retrieveData() does not attempt to load the file from disk again since the attempt was
- // made here).
- loadFileCache(cachePayload)
- } else {
- // Otherwise, keep the cache the same.
- cachePayload
- }
- }
- }
-
- /**
- * Primes the current cache such that both the in-memory and on-disk versions of this cache are
- * guaranteed to be in sync, returning a [Deferred] that completes only after the operation is
- * finished.
+ * Note that the value of the returned [Deferred] is not useful. The state of the cache should
+ * monitored by treating this provider as a [DataProvider]. This method may result in an update
+ * notification to observers of this [DataProvider].
*
- * The provided [initialize] initializer will only ever be called if the on-disk cache is not yet
- * initialized, and it will be passed the initial value used to create this cache store. The value
- * it returns will be used to initialize both the in-memory and on-disk copies of the cache.
+ * Note also that this method is particularly useful in two specific cases:
+ * 1. When an instance of this cache needs to be loaded from disk before an update operation
+ * occurs (otherwise update() will likely complete before a load, overwriting the current
+ * on-disk cache state).
+ * 2. When the cache needs to be initialized exactly once to a specific value (such as the case
+ * when an ID that cannot change after initialization needs to be generated and stored exactly
+ * once).
*
- * The value of the returned [Deferred] is not useful. The state of the cache should monitored by
- * treating this provider as a [DataProvider]. This method may result in multiple update
- * notifications to observers of this [DataProvider], but the latest value will be the source of
- * truth.
+ * Each of the above states are possible using different combinations of the provided [UpdateMode]
+ * and [PublishMode] parameters.
*
- * Where [primeInMemoryCacheAsync] is useful to ensure any on-disk cache is properly loaded into
- * memory prior to using a cache store, this method is useful when a disk cache has a
- * contextually-sensitive initialization routine (such as an ID that cannot change after
- * initialization) as it ensures a reliable, initial clean state for the cache store that will be
- * consistent with future runs of the app.
+ * Finally, this method succeeding more or less guarantees that the cache store is now in a good
+ * state (i.e. it will even recover from a corrupted or invalid disk cache file).
+ *
+ * @param updateMode how the cache should be changed (depending on whether it's been loaded yet,
+ * and whether it has an on-disk cache)
+ * @param publishMode whether changes to the cache's in-memory copy during priming should be kept
+ * in-memory and sent to observers (otherwise, only store the results on-disk and do not
+ * notify changes). Note that the in-memory cache *will* be updated if it hasn't yet been
+ * initialized (which may mean saving a result from [update]).
+ * @param update an optional function to transform the cache's current (in-memory if loaded, or
+ * from-disk if not) state to a new state, and then update the on-disk cache (and potentially
+ * the in-memory cache based on [publishMode]). Note that if the cache has not been loaded yet
+ * and has no on-disk copy then the cache's [initialValue] will be passed, instead. Omitting
+ * this transformation will just ensure the in-memory and/or on-disk cache are appropriately
+ * initialized.
+ * @return a [Deferred] tracking the success/failure of priming this cache store
*/
- fun primeInMemoryAndDiskCacheAsync(initialize: (T) -> T): Deferred {
+ fun primeInMemoryAndDiskCacheAsync(
+ updateMode: UpdateMode,
+ publishMode: PublishMode,
+ update: (T) -> T = { it }
+ ): Deferred {
return cache.updateIfPresentAsync { cachePayload ->
- when (cachePayload.state) {
+ // It's expected 'oldState' to match 'cachePayload' unless the cache hasn't yet been read
+ // (since then 'cachePayload' will be based on the store's default value).
+ val (oldState, newState) = when (cachePayload.state) {
CacheState.UNLOADED -> {
val loadedPayload = loadFileCache(cachePayload)
when (loadedPayload.state) {
// The state should never stay as UNLOADED.
CacheState.UNLOADED ->
error("Something went wrong loading the cache during priming: $cacheFile")
- CacheState.IN_MEMORY_ONLY -> storeFileCache(loadedPayload, initialize) // Needs saving.
- CacheState.IN_MEMORY_AND_ON_DISK -> loadedPayload // Loaded from disk successfully.
+ CacheState.IN_MEMORY_ONLY -> {
+ // Needs saving. In this case, there is no "old" value since the cache was never
+ // initialized.
+ val storedPayload = storeFileCache(loadedPayload, update)
+ storedPayload to storedPayload
+ }
+ CacheState.IN_MEMORY_AND_ON_DISK -> // Loaded from disk successfully.
+ loadedPayload to loadedPayload.maybeReprimePayload(updateMode, update)
}
}
- // This generally indicates that something went wrong reading the on-disk cache, so make
- // sure it's properly initialized.
- CacheState.IN_MEMORY_ONLY -> storeFileCache(cachePayload, initialize)
- CacheState.IN_MEMORY_AND_ON_DISK -> cachePayload
+ // Generally indicates that the cache was loaded but never written.
+ CacheState.IN_MEMORY_ONLY -> cachePayload to storeFileCache(cachePayload, update)
+ CacheState.IN_MEMORY_AND_ON_DISK ->
+ cachePayload to cachePayload.maybeReprimePayload(updateMode, update)
+ }
+
+ // The returned payload is always expected to be IN_MEMORY_AND_ON_DISK, but the in-memory copy
+ // may be intentionally kept out-of-date so that cache reads pick up the original version
+ // rather than the new one stored on-disk. Furthermore, this method guarantees by this point
+ // that the cache is in a good, non-error state (so the error is cleared in case one occurred
+ // during early priming).
+ failureLock.withLock { deferredLoadCacheFailure = null }
+ return@updateIfPresentAsync when (publishMode) {
+ PublishMode.PUBLISH_TO_IN_MEMORY_CACHE -> newState
+ PublishMode.DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE -> newState.copy(value = oldState.value)
}
}
}
@@ -290,6 +312,16 @@ class PersistentCacheStore private constructor(
)
}
+ private fun CachePayload.maybeReprimePayload(
+ updateMode: UpdateMode,
+ initialize: (T) -> T
+ ): CachePayload {
+ return when (updateMode) {
+ UpdateMode.UPDATE_IF_NEW_CACHE -> this // Nothing extra to do.
+ UpdateMode.UPDATE_ALWAYS -> storeFileCache(this, initialize) // Recompute the payload.
+ }
+ }
+
private data class PersistentCacheStoreId(private val id: String)
/** Represents different states the cache store can be in. */
@@ -306,13 +338,46 @@ class PersistentCacheStore private constructor(
private data class CachePayload(val state: CacheState, val value: T)
+ /**
+ * The mode of on-disk data updating that can be configured for specific operations like cache
+ * priming.
+ *
+ * This mode only configures on-disk data changes, not in-memory (see [PublishMode] for that).
+ */
+ enum class UpdateMode {
+ /** Indicates that the on-disk cache should only be changed if it doesn't already exist. */
+ UPDATE_IF_NEW_CACHE,
+
+ /**
+ * Indicates that the on-disk cache should always be changed regardless of if it already exists.
+ */
+ UPDATE_ALWAYS
+ }
+
+ /**
+ * The mode of in-memory data updating that can be configured for specific operations like cache
+ * priming.
+ *
+ * This mode only configures in-memory data changes, not on-disk (see [UpdateMode] for that).
+ */
+ enum class PublishMode {
+ /**
+ * Indicates that data changes should update the in-memory cache and be broadcast to
+ * subscribers.
+ */
+ PUBLISH_TO_IN_MEMORY_CACHE,
+
+ /** Indicates that data changes should not change the in-memory cache. */
+ DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ }
+
/**
* An injectable factory for [PersistentCacheStore]s. The stores themselves should be retrievable
* from central controllers since they can't be placed directly in the Dagger graph.
*/
@Singleton
class Factory @Inject constructor(
- private val context: Context,
+ private val application: Application,
private val cacheFactory: InMemoryBlockingCache.Factory,
private val asyncDataSubscriptionManager: AsyncDataSubscriptionManager,
private val directoryManagementUtil: DirectoryManagementUtil
@@ -325,7 +390,7 @@ class PersistentCacheStore private constructor(
*/
fun create(cacheName: String, initialValue: T): PersistentCacheStore {
return PersistentCacheStore(
- context,
+ application,
cacheFactory,
asyncDataSubscriptionManager,
cacheName,
@@ -344,7 +409,7 @@ class PersistentCacheStore private constructor(
): PersistentCacheStore {
val profileDirectory = directoryManagementUtil.getOrCreateDir(profileId.internalId.toString())
return PersistentCacheStore(
- context,
+ application,
cacheFactory,
asyncDataSubscriptionManager,
cacheName,
diff --git a/data/src/test/java/org/oppia/android/data/persistence/BUILD.bazel b/data/src/test/java/org/oppia/android/data/persistence/BUILD.bazel
new file mode 100644
index 00000000000..9a0686181ab
--- /dev/null
+++ b/data/src/test/java/org/oppia/android/data/persistence/BUILD.bazel
@@ -0,0 +1,32 @@
+"""
+Tests for infrastructure that provides data persistence support for the rest of the app.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "PersistentCacheStoreTest",
+ srcs = ["PersistentCacheStoreTest.kt"],
+ custom_package = "org.oppia.android.data.persistence",
+ test_class = "org.oppia.android.data.persistence.PersistentCacheStoreTest",
+ test_manifest = "//data:test_manifest",
+ deps = [
+ ":dagger",
+ "//data/src/main/java/org/oppia/android/data/persistence:cache_store",
+ "//model/src/main/proto:test_models",
+ "//testing",
+ "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor",
+ "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/threading:test_module",
+ "//third_party:androidx_test_ext_junit",
+ "//third_party:com_google_truth_extensions_truth-liteproto-extension",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_mockito_mockito-core",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ ],
+)
+
+dagger_rules()
diff --git a/data/src/test/java/org/oppia/android/data/persistence/PersistentCacheStoreTest.kt b/data/src/test/java/org/oppia/android/data/persistence/PersistentCacheStoreTest.kt
index c14643c3baa..6143cf71b39 100644
--- a/data/src/test/java/org/oppia/android/data/persistence/PersistentCacheStoreTest.kt
+++ b/data/src/test/java/org/oppia/android/data/persistence/PersistentCacheStoreTest.kt
@@ -19,19 +19,32 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
import org.oppia.android.app.model.TestMessage
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode.DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode.PUBLISH_TO_IN_MEMORY_CACHE
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode.UPDATE_ALWAYS
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode.UPDATE_IF_NEW_CACHE
+import org.oppia.android.data.persistence.PersistentCacheStoreTest.SubscriptionCallback.Companion.toAsyncChange
import org.oppia.android.testing.TestLogReportingModule
import org.oppia.android.testing.data.AsyncResultSubject.Companion.assertThat
import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.robolectric.RobolectricModule
import org.oppia.android.testing.threading.TestCoroutineDispatchers
import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.util.data.AsyncDataSubscriptionManager
import org.oppia.android.util.data.AsyncResult
import org.oppia.android.util.data.DataProviders
import org.oppia.android.util.data.DataProvidersInjector
import org.oppia.android.util.data.DataProvidersInjectorProvider
+import org.oppia.android.util.data.ObserveAsyncChange
import org.oppia.android.util.threading.BackgroundDispatcher
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
@@ -42,6 +55,7 @@ import java.io.IOException
import java.lang.IllegalStateException
import javax.inject.Inject
import javax.inject.Singleton
+import kotlin.reflect.full.staticFunctions
private const val CACHE_NAME_1 = "test_cache_1"
private const val CACHE_NAME_2 = "test_cache_2"
@@ -55,16 +69,38 @@ private const val CACHE_NAME_2 = "test_cache_2"
@Config(application = PersistentCacheStoreTest.TestApplication::class)
class PersistentCacheStoreTest {
private companion object {
- private val TEST_MESSAGE_V1 = TestMessage.newBuilder().setIntValue(1).build()
- private val TEST_MESSAGE_V2 = TestMessage.newBuilder().setIntValue(2).build()
+ private const val TEST_INT_V1 = 1
+ private const val TEST_INT_V2 = 2
+ private const val TEST_STR_V1 = "test string"
+ private val DEFAULT_TEST_MESSAGE = TestMessage.getDefaultInstance()
+ private val TEST_INT_MESSAGE_V1 = createTestMessage(intValue = TEST_INT_V1)
+ private val TEST_INT_MESSAGE_V2 = createTestMessage(intValue = TEST_INT_V2)
+
+ private fun TestMessage.addString(strValue: String) = toBuilder().apply {
+ this.strValue = strValue
+ }.build()
+
+ private fun createTestMessage(
+ intValue: Int = DEFAULT_TEST_MESSAGE.intValue,
+ strValue: String = DEFAULT_TEST_MESSAGE.strValue
+ ) = TestMessage.newBuilder().apply {
+ this.intValue = intValue
+ this.strValue = strValue
+ }.build()
}
+ @Rule
+ @JvmField
+ val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
@Inject lateinit var context: Context
@Inject lateinit var cacheFactory: PersistentCacheStore.Factory
@Inject lateinit var dataProviders: DataProviders
@Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
@field:[Inject BackgroundDispatcher] lateinit var backgroundDispatcher: CoroutineDispatcher
@Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory
+ @Inject lateinit var asyncDataSubscriptionManager: AsyncDataSubscriptionManager
+ @Mock lateinit var mockSubscriptionCallback: SubscriptionCallback
private val backgroundDispatcherScope by lazy { CoroutineScope(backgroundDispatcher) }
@@ -107,12 +143,12 @@ class PersistentCacheStoreTest {
@Test
fun testCache_nonDefaultInitialState_loaded_providesCorrectInitialVal() {
- val cacheStore = cacheFactory.create(CACHE_NAME_1, TEST_MESSAGE_V1)
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
val value = monitorFactory.waitForNextSuccessfulResult(cacheStore)
// Caches can have non-default initial states.
- assertThat(value).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(value).isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
@@ -120,27 +156,28 @@ class PersistentCacheStoreTest {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
monitorFactory.waitForNextSuccessfulResult(cacheStore)
- val storeOp = cacheStore.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// The store operation should be completed, and the observer should be notified of the changed
// value.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
fun testCache_registerObserver_updateBefore_observesUpdatedStateInitially() {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// The store operation should be completed, and the observer's only call should be the updated
// state.
val value = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(storeOp.isCompleted).isTrue()
- assertThat(value).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(value).isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
@@ -149,7 +186,7 @@ class PersistentCacheStoreTest {
val monitor = monitorFactory.createMonitor(cacheStore)
monitor.waitForNextSuccessResult()
- val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// The store operation should be completed, but the observe will not be notified of changes
@@ -162,7 +199,7 @@ class PersistentCacheStoreTest {
fun testCache_noMemoryCacheUpdate_updateBeforeReg_observesUpdatedState() {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// The store operation should be completed, but the observer will receive the updated state
@@ -170,13 +207,14 @@ class PersistentCacheStoreTest {
// NB: This may not be ideal behavior long-term; the store may need to be updated to be more
// resilient to these types of scenarios.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
fun testCache_updated_newCache_newObserver_observesNewValue() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// Create a new cache with the same name.
@@ -187,13 +225,14 @@ class PersistentCacheStoreTest {
// refresh since UI components should share the same cache instance via an application-bound
// controller object.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
fun testCache_updated_noInMemoryCacheUpdate_newCache_newObserver_observesNewVal() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// Create a new cache with the same name.
@@ -205,108 +244,28 @@ class PersistentCacheStoreTest {
// Dagger component refresh since UI components should share the same cache instance via an
// application-bound controller object.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
fun testExistingDiskCache_newCacheObject_updateNoMemThenRead_receivesNewValue() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
val storeOp1 =
- cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
+ cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// Create a new cache with the same name and update it, then observe it.
val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp2 = cacheStore2.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V2 }
+ val storeOp2 = cacheStore2.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V2 }
testCoroutineDispatchers.advanceUntilIdle()
// Both operations should be complete, and the observer will receive the latest value since the
// update was posted before the read occurred.
assertThat(storeOp1.isCompleted).isTrue()
assertThat(storeOp2.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V2)
- }
-
- @Test
- fun testExistingDiskCache_newObject_updateNoMemThenRead_primed_receivesPrevVal() {
- val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp1 =
- cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Create a new cache with the same name and update it, then observe it. However, first prime
- // it.
- val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val primeOp = cacheStore2.primeInMemoryCacheAsync()
- testCoroutineDispatchers.advanceUntilIdle()
- val storeOp2 = cacheStore2.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V2 }
- testCoroutineDispatchers.advanceUntilIdle()
-
- // All operations should be complete, but the observer will receive the previous update rather
- // than the latest since it wasn't updated in memory and the cache was pre-primed.
- assertThat(storeOp1.isCompleted).isTrue()
- assertThat(storeOp2.isCompleted).isTrue()
- assertThat(primeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V1)
- }
-
- @Test
- fun testExistingDiskCache_newObject_updateMemThenRead_primed_receivesNewVal() {
- val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp1 =
- cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Create a new cache with the same name and update it, then observe it. However, first prime
- // it.
- val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val primeOp = cacheStore2.primeInMemoryCacheAsync()
- testCoroutineDispatchers.advanceUntilIdle()
- val storeOp2 = cacheStore2.storeDataAsync { TEST_MESSAGE_V2 }
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Similar to the previous test, except due to the in-memory update the observer will receive
- // the latest result regardless of the cache priming.
- assertThat(storeOp1.isCompleted).isTrue()
- assertThat(storeOp2.isCompleted).isTrue()
- assertThat(primeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V2)
- }
-
- @Test
- fun testCache_primed_afterStoreUpdateWithoutMemUpdate_notForced_observesOldValue() {
- val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- // Force initializing the store's in-memory cache
- monitorFactory.waitForNextSuccessfulResult(cacheStore)
-
- val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
- testCoroutineDispatchers.advanceUntilIdle()
- val primeOp = cacheStore.primeInMemoryCacheAsync(forceUpdate = false)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // Both ops will succeed, and the observer will receive the old value due to the update not
- // changing the in-memory cache, and the prime no-oping due to the cache already being
- // initialized.
- assertThat(storeOp.isCompleted).isTrue()
- assertThat(primeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualToDefaultInstance()
- }
-
- @Test
- fun testCache_primed_afterStoreUpdateWithoutMemoryUpdate_forced_observesNewValue() {
- val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- monitorFactory.waitForNextSuccessfulResult(cacheStore)
-
- val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_MESSAGE_V1 }
- testCoroutineDispatchers.advanceUntilIdle()
- val primeOp = cacheStore.primeInMemoryCacheAsync(forceUpdate = true)
- testCoroutineDispatchers.advanceUntilIdle()
-
- // The observer will receive the new value because the prime was forced. This ensures the
- // store's in-memory cache is now up-to-date with the on-disk representation.
- assertThat(storeOp.isCompleted).isTrue()
- assertThat(primeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V2)
}
@Test
@@ -325,7 +284,7 @@ class PersistentCacheStoreTest {
@Test
fun testCache_update_clear_resetsCacheToInitialState() {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
val clearOp = cacheStore.clearCacheAsync()
@@ -340,7 +299,7 @@ class PersistentCacheStoreTest {
@Test
fun testCache_update_existingObserver_clear_isNotifiedOfClear() {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
val monitor = monitorFactory.createMonitor(cacheStore)
@@ -357,34 +316,36 @@ class PersistentCacheStoreTest {
@Test
fun testCache_update_newCache_observesInitialState() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
val clearOp = cacheStore1.clearCacheAsync()
testCoroutineDispatchers.advanceUntilIdle()
- val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TEST_MESSAGE_V2)
+ val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TEST_INT_MESSAGE_V2)
// The new observer should observe that there's no persisted on-disk store since it has a
// different default value that would only be used if there wasn't already on-disk storage.
assertThat(storeOp.isCompleted).isTrue()
assertThat(clearOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V2)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V2)
}
@Test
fun testMultipleCaches_oneUpdates_newCacheSameNameDiffInit_observesUpdatedValue() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
- val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TEST_MESSAGE_V2)
+ val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TEST_INT_MESSAGE_V2)
// The new cache should observe the updated on-disk value rather than its new default since an
// on-disk value exists. This isn't a very realistic test since all caches should use default
// proto instances for initialization, but it's a possible edge case that should at least have
// established behavior that can be adjusted later if it isn't desirable in some circumstances.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
}
@Test
@@ -394,7 +355,7 @@ class PersistentCacheStoreTest {
val monitor1 = monitorFactory.createMonitor(cacheStore1)
val monitor2 = monitorFactory.createMonitor(cacheStore2)
- val storeOp = cacheStore1.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// The observer of the second store will be not notified of the change to the first store since
@@ -408,7 +369,7 @@ class PersistentCacheStoreTest {
fun testMultipleCaches_diffNames_oneUpdates_cachesRecreated_onlyOneObservesVal() {
val cacheStore1a = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
cacheFactory.create(CACHE_NAME_2, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1a.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1a.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// Recreate the stores and observe them.
@@ -418,14 +379,15 @@ class PersistentCacheStoreTest {
// Only the observer of the first cache should notice the update since they are different
// caches.
assertThat(storeOp.isCompleted).isTrue()
- assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore1b)).isEqualTo(TEST_MESSAGE_V1)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore1b))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2b)).isEqualToDefaultInstance()
}
@Test
fun testNewCache_fileCorrupted_providesError() {
val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val storeOp = cacheStore1.storeDataAsync { TEST_MESSAGE_V1 }
+ val storeOp = cacheStore1.storeDataAsync { TEST_INT_MESSAGE_V1 }
testCoroutineDispatchers.advanceUntilIdle()
// Simulate the file being corrupted & reopen the file in a new store.
@@ -470,17 +432,1000 @@ class PersistentCacheStoreTest {
assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore).strValue).isEqualTo("initial")
}
+ @Test
+ fun testNoPrime_noDiskCache_unloadedCache_noNotifyOrLoadedOrDisk() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ // Verify that a default cache store performs no notifications, nor does it create a disk cache.
+ // This helps to establish the baseline assumed in later testPrime_* tests.
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ assertThat(getCacheFile(CACHE_NAME_1).exists()).isFalse()
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateIfNew_publish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateIfNew_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateIfNew_noPublish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ // The cache will notify in this case since the in-memory cache wasn't yet initialized.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateIfNew_noPublish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The cache will notify in this case since the in-memory cache wasn't yet initialized.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateAlways_publish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateAlways_noPublish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // The cache will notify in this case since the in-memory cache wasn't yet initialized.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_unloadedCache_updateAlways_noPublish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The cache will notify in this case since the in-memory cache wasn't yet initialized.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateIfNew_publish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // No notification is sent since the in-memory cache is already initialized ahead of priming.
+ // The on-disk cache still needs to be initialized.
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateIfNew_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateIfNew_noPublish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateIfNew_noPublish_withXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The in-memory cache will not be updated in this situation since it was already loaded ahead
+ // of priming the cache (and thus established).
+ val expectedDiskMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateAlways_publish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Despite the update & publish policies, no notification will be sent here since priming
+ // doesn't change the cache store and the store has already be initialized into memory.
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateAlways_noPublish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_noDiskCache_cacheFromMem_updateAlways_noPublish_withXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The in-memory cache will not be updated in this situation since it was already loaded ahead
+ // of priming the cache (and thus established).
+ val expectedDiskMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateIfNew_publish_noXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Effectively, only the in-memory version of the cache changes.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateIfNew_publish_withXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The in-memory version should be initialized, but not updated (since it already exists). Plus,
+ // the disk cache should not be read or updated.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateIfNew_noPublish_noXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ // Effectively, only the in-memory version of the cache changes, which in turn still results in
+ // a notification being sent.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateIfNew_noPublish_withXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The in-memory version should be initialized, but not updated (since it already exists). Plus,
+ // the disk cache should not be read or updated. However, this will result in a notification
+ // since the in-memory cache was unloaded.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateAlways_publish_noXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Effectively, only the in-memory version of the cache changes.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Both the in-memory and on-disk variants should change per the UPDATE_ALWAYS policy.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1, strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateAlways_noPublish_noXform_initsMemOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Effectively, only the in-memory version of the cache changes, which in turn still results in
+ // a notification being sent.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_unloadedCache_updateAlways_noPublish_withXform_initsDiskOnlyNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Only the on-disk variant should actually change (per UPDATE_ALWAYS), since the publish policy
+ // indicates that the in-memory cache shouldn't reflect the new value (only the existing disk
+ // cache). Furthermore, a notification will be sent anyway since the in-memory cache hasn't yet
+ // been initialized.
+ val expectedMemoryMessage = createTestMessage(intValue = TEST_INT_V1)
+ val expectedDiskMessage = createTestMessage(intValue = TEST_INT_V1, strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMemoryMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateIfNew_publish_noXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory (other initing the cache).
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateIfNew_publish_withXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory per UPDATE_IF_NEW_CACHE.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateIfNew_noPublish_noXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory (other initing the cache).
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateIfNew_noPublish_withXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory per UPDATE_IF_NEW_CACHE.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateAlways_publish_noXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory (other initing the cache).
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Since the policy is to always update the cache, the in-memory & on-disk versions should be
+ // updated plus a notification sent.
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1, strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateAlways_noPublish_noXform_noChangeOrNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Nothing should be notified, or change on-disk or in-memory (other initing the cache).
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrime_diskCache_cacheFromDisk_updateAlways_noPublish_withXform_initsMemDiskNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ writeFileCache(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Since the policy is to always update the cache, the on-disk version should be updated.
+ // However, the in-memory cache will not be updated since it was already loaded ahead of priming
+ // (and hence established). Furthermore, no notification sent per the publish policy.
+ val expectedMemoryMessage = createTestMessage(intValue = TEST_INT_V1)
+ val expectedDiskMessage = createTestMessage(intValue = TEST_INT_V1, strValue = TEST_STR_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMemoryMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateIfNew_publish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateIfNew_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateIfNew_noPublish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache. Because of that, a notification
+ // is actually sent in this case.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateIfNew_noPublish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache. Because of that, a notification
+ // is actually sent in this case.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateAlways_publish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateAlways_noPublish_noXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache. Because of that, a notification
+ // is actually sent in this case.
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_unloadedCache_updateAlways_noPublish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // A corrupted disk cache is treated as a completely new cache. Because of that, a notification
+ // is actually sent in this case.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateIfNew_publish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // The corrupted cache should have failed to be read, so it will be treated as a new cache.
+ // However, only the disk cache actually needs to be updated since the in-memory one has already
+ // been established prior to priming (which is also why no notification is sent in this case).
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateIfNew_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_IF_NEW_CACHE, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The corrupted cache should have failed to be read, so it will be treated as a new cache.
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateIfNew_noPublish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ primeDeferred.waitForSuccessfulResult()
+
+ // The corrupted cache should have failed to be read, so it will be treated as a new cache.
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateIfNew_noPublish_withXform_initsMemDiskNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.addString(TEST_STR_V1) }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The corrupted cache should have failed to be read, so it will be treated as a new cache. Note
+ // though that the initial load operation will result in the in-memory cache becoming the
+ // default cache value (and thus different from the on-disk version) per the publish policy.
+ val expectedOnDiskMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedOnDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateAlways_publish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ // Despite the update & publish policies, no notification will be sent here since priming
+ // doesn't change the cache store and the store has already be initialized into memory.
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateAlways_publish_withXform_initsMemDiskNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ val expectedMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreSentDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateAlways_noPublish_noXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE)
+ primeDeferred.waitForSuccessfulResult()
+
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_updateAlways_noPublish_withXform_initsDiskOnlyNoNotify() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, DEFAULT_TEST_MESSAGE)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+ subscribeToCacheStoreChanges(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it.addString(TEST_STR_V1)
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // The in-memory cache will not be updated in this situation since it was already loaded ahead
+ // of priming the cache (and thus established).
+ val expectedDiskMessage = createTestMessage(strValue = TEST_STR_V1)
+ verifyCacheStoreDidNotSendDataProviderNotification()
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedDiskMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, DEFAULT_TEST_MESSAGE)
+ }
+
+ @Test
+ fun testPrime_badDiskCache_cacheFromMem_passesDefaultMessageInToTransform() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, TEST_INT_MESSAGE_V1)
+ corruptFileCache(CACHE_NAME_1)
+ loadCacheIntoMemory(cacheStore)
+
+ val primeDeferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(UPDATE_ALWAYS, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE) {
+ it
+ }
+ primeDeferred.waitForSuccessfulResult()
+
+ // Verify that the proto passed into prime's update method is the default in this case (since
+ // the disk cache is corrupted).
+ val expectedMessage = createTestMessage(intValue = TEST_INT_V1)
+ verifyDiskCacheHasValue(CACHE_NAME_1, expectedMessage)
+ verifyCacheStoreHasInMemoryValue(CACHE_NAME_1, cacheStore, expectedMessage)
+ }
+
+ @Test
+ fun testPrimeInMemoryAndOnDisk_newObject_updateNoMemThenRead_primed_receivesPrevVal() {
+ val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val storeOp1 =
+ cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
+ testCoroutineDispatchers.advanceUntilIdle()
+
+ // Create a new cache with the same name and update it, then observe it. However, first prime
+ // it.
+ val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val primeOp =
+ cacheStore2.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ testCoroutineDispatchers.advanceUntilIdle()
+ val storeOp2 = cacheStore2.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V2 }
+ testCoroutineDispatchers.advanceUntilIdle()
+
+ // All operations should be complete, but the observer will receive the previous update rather
+ // than the latest since it wasn't updated in memory and the cache was pre-primed.
+ assertThat(storeOp1.isCompleted).isTrue()
+ assertThat(storeOp2.isCompleted).isTrue()
+ assertThat(primeOp.isCompleted).isTrue()
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V1)
+ }
+
+ @Test
+ fun testPrimeInMemoryAndOnDisk_onDisk_newObject_updateMemThenRead_primed_receivesNewVal() {
+ val cacheStore1 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val storeOp1 =
+ cacheStore1.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
+ testCoroutineDispatchers.advanceUntilIdle()
+
+ // Create a new cache with the same name and update it, then observe it. However, first prime
+ // it.
+ val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val primeOp =
+ cacheStore2.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ testCoroutineDispatchers.advanceUntilIdle()
+ val storeOp2 = cacheStore2.storeDataAsync { TEST_INT_MESSAGE_V2 }
+ testCoroutineDispatchers.advanceUntilIdle()
+
+ // Similar to the previous test, except due to the in-memory update the observer will receive
+ // the latest result regardless of the cache priming.
+ assertThat(storeOp1.isCompleted).isTrue()
+ assertThat(storeOp2.isCompleted).isTrue()
+ assertThat(primeOp.isCompleted).isTrue()
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2))
+ .isEqualTo(TEST_INT_MESSAGE_V2)
+ }
+
+ @Test
+ fun testPrimeInMemoryAndOnDisk_afterStoreUpdateWithoutMemUpdate_observesOldValue() {
+ val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ // Force initializing the store's in-memory cache
+ monitorFactory.waitForNextSuccessfulResult(cacheStore)
+
+ val storeOp = cacheStore.storeDataAsync(updateInMemoryCache = false) { TEST_INT_MESSAGE_V1 }
+ testCoroutineDispatchers.advanceUntilIdle()
+ val primeOp =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE,
+ publishMode = DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ )
+ testCoroutineDispatchers.advanceUntilIdle()
+
+ // Both ops will succeed, and the observer will receive the old value due to the update not
+ // changing the in-memory cache, and the prime no-oping due to the cache already being
+ // initialized.
+ assertThat(storeOp.isCompleted).isTrue()
+ assertThat(primeOp.isCompleted).isTrue()
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualToDefaultInstance()
+ }
+
@Test
fun testPrimeInMemoryAndOnDisk_newCache_notOnDisk_notInMem_writesFileAndRetsNewVal() {
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val deferred = cacheStore.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// The on-disk and in-memory values should change.
deferred.waitForSuccessfulResult()
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(cacheValue.strValue.trim()).isEqualTo("first transform")
@@ -491,14 +1436,15 @@ class PersistentCacheStoreTest {
writeFileCache(CACHE_NAME_1, TestMessage.newBuilder().apply { strValue = "initial" }.build())
val cacheStore = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
- val deferred = cacheStore.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// The on-disk value should be the same, and the in-memory value should become the on-disk
// value. The initializer shouldn't be used since the value is already on disk.
deferred.waitForSuccessfulResult()
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(cacheValue.strValue).isEqualTo("initial")
@@ -511,14 +1457,15 @@ class PersistentCacheStoreTest {
CACHE_NAME_1, TestMessage.newBuilder().apply { strValue = "different initial" }.build()
)
- val deferred = cacheStore.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// Priming should ignore both the on-disk and in-memory values of the cache store since only the
// initial value matters.
deferred.waitForSuccessfulResult()
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(cacheValue.strValue).isEqualTo("initial")
@@ -533,14 +1480,15 @@ class PersistentCacheStoreTest {
it.toBuilder().apply { strValue = "different update" }.build()
}.waitForResult()
- val deferred = cacheStore.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// Priming shouldn't really change much since the recent change to the cache store takes
// precedence.
deferred.waitForSuccessfulResult()
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(cacheValue.strValue).isEqualTo("different update")
@@ -556,14 +1504,15 @@ class PersistentCacheStoreTest {
it.toBuilder().apply { strValue = "different update" }.build()
}.waitForResult()
- val deferred = cacheStore.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// Priming shouldn't really change much since the recent change to the cache store takes
// precedence.
deferred.waitForSuccessfulResult()
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(cacheValue.strValue).isEqualTo("different update")
@@ -578,26 +1527,62 @@ class PersistentCacheStoreTest {
val cacheStore2 = cacheFactory.create(CACHE_NAME_1, TestMessage.getDefaultInstance())
monitorFactory.ensureDataProviderExecutes(cacheStore1)
- val deferred = cacheStore1.primeInMemoryAndDiskCacheAsync {
- it.toBuilder().apply { strValue += " first transform" }.build()
- }
+ val deferred =
+ cacheStore1.primeInMemoryAndDiskCacheAsync(
+ updateMode = UPDATE_IF_NEW_CACHE, publishMode = PUBLISH_TO_IN_MEMORY_CACHE
+ ) { it.toBuilder().apply { strValue += " first transform" }.build() }
// The corrupted cache will trigger an in-memory only state that will lead to the cache being
// overwritten (since it can't be determined whether the on-disk cache matches the expected
- // value). Note that the cache will be in a bad state since it failed to read its original
- // state, but the on-disk copy will still be updated. Note also that the second instance of the
+ // value). Note the on-disk cache will still be updated, and that the second instance of the
// cache wasn't yet primed until this step, so it's being used to validate that the in-memory
// copy is also correct after the on-disk value has been updated.
deferred.waitForSuccessfulResult()
- monitorFactory.waitForNextFailureResult(cacheStore1)
- val onDiskValue = readFileCache(CACHE_NAME_1, TestMessage.getDefaultInstance())
+ val onDiskValue = readFileCache(CACHE_NAME_1)
val cacheValue = monitorFactory.waitForNextSuccessfulResult(cacheStore2)
assertThat(cacheValue).isEqualTo(onDiskValue)
assertThat(onDiskValue.strValue).isEqualTo("different initial first transform")
}
+ private fun subscribeToCacheStoreChanges(cacheStore: PersistentCacheStore) {
+ asyncDataSubscriptionManager.subscribe(
+ cacheStore.getId(), mockSubscriptionCallback.toAsyncChange()
+ )
+ }
+
+ private fun loadCacheIntoMemory(cacheStore: PersistentCacheStore) {
+ // Attempt to load the cache store from disk--this ensures that the cache store is at least
+ // loaded into memory.
+ monitorFactory.ensureDataProviderExecutes(cacheStore)
+ }
+
+ private fun verifyCacheStoreHasInMemoryValue(
+ cacheName: String,
+ cacheStore: PersistentCacheStore,
+ value: T
+ ) {
+ // Delete the cache before reading from the store to verify that the read value is actually
+ // in-memory and not being read from disk.
+ deleteCacheFile(cacheName)
+ assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualTo(value)
+ }
+
+ private fun verifyDiskCacheHasValue(cacheName: String, value: T) {
+ assertThat(readFileCache(cacheName)).isEqualTo(value)
+ }
+
+ private fun verifyCacheStoreSentDataProviderNotification() {
+ verify(mockSubscriptionCallback).onDataProviderChanged()
+ }
+
+ private fun verifyCacheStoreDidNotSendDataProviderNotification() {
+ verify(mockSubscriptionCallback, never()).onDataProviderChanged()
+ }
+
private fun getCacheFile(cacheName: String) = File(context.filesDir, "$cacheName.cache")
+ private fun deleteCacheFile(cacheName: String) = getCacheFile(cacheName).delete()
+
private fun corruptFileCache(cacheName: String) {
// NB: This is unfortunately tied to the implementation details of PersistentCacheStore. If this
// ends up being an issue, the store should be updated to call into a file path provider that
@@ -615,9 +1600,12 @@ class PersistentCacheStoreTest {
FileOutputStream(this).use { it.write(data) }
}
- private inline fun readFileCache(cacheName: String, baseMessage: T): T {
+ private inline fun readFileCache(cacheName: String): T {
+ // Use reflection to simplify the test API.
+ val defaultInstance =
+ T::class.staticFunctions.find { it.name == "getDefaultInstance" }?.call() as? T?
return FileInputStream(getCacheFile(cacheName)).use {
- baseMessage.newBuilderForType().mergeFrom(it).build()
+ checkNotNull(defaultInstance).newBuilderForType().mergeFrom(it).build()
} as T
}
@@ -694,4 +1682,12 @@ class PersistentCacheStoreTest {
override fun getDataProvidersInjector(): DataProvidersInjector = component
}
+
+ interface SubscriptionCallback {
+ fun onDataProviderChanged()
+
+ companion object {
+ fun SubscriptionCallback.toAsyncChange(): ObserveAsyncChange = { onDataProviderChanged() }
+ }
+ }
}
diff --git a/domain/BUILD.bazel b/domain/BUILD.bazel
index e13cc36c9a6..db2919a4ff8 100755
--- a/domain/BUILD.bazel
+++ b/domain/BUILD.bazel
@@ -158,6 +158,7 @@ TEST_DEPS = [
":interaction_object_test_builder",
"//app:crashlytics",
"//app:crashlytics_deps",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//data/src/main/java/org/oppia/android/data/backends/gae:network_config_prod_module",
"//data/src/main/java/org/oppia/android/data/backends/gae/model",
"//data/src/main/java/org/oppia/android/data/persistence:cache_store",
diff --git a/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointController.kt b/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointController.kt
index b69e949a859..84b74a71211 100644
--- a/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/exploration/lightweightcheckpointing/ExplorationCheckpointController.kt
@@ -8,6 +8,8 @@ import org.oppia.android.app.model.ExplorationCheckpointDatabase
import org.oppia.android.app.model.ExplorationCheckpointDetails
import org.oppia.android.app.model.ProfileId
import org.oppia.android.data.persistence.PersistentCacheStore
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode
import org.oppia.android.domain.exploration.ExplorationRetriever
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.util.data.AsyncResult
@@ -282,7 +284,10 @@ class ExplorationCheckpointController @Inject constructor(
cacheStore
}
- cacheStore.primeInMemoryCacheAsync().invokeOnCompletion { throwable ->
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UpdateMode.UPDATE_IF_NEW_CACHE,
+ publishMode = PublishMode.PUBLISH_TO_IN_MEMORY_CACHE
+ ).invokeOnCompletion { throwable ->
throwable?.let {
oppiaLogger.e(
"ExplorationCheckpointController",
diff --git a/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt b/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt
index e6196c6174e..31b6b203ba7 100644
--- a/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt
@@ -1,7 +1,9 @@
package org.oppia.android.domain.onboarding
import org.oppia.android.app.model.AppStartupState
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode
import org.oppia.android.app.model.AppStartupState.StartupMode
+import org.oppia.android.app.model.BuildFlavor
import org.oppia.android.app.model.OnboardingState
import org.oppia.android.data.persistence.PersistentCacheStore
import org.oppia.android.domain.oppialogger.OppiaLogger
@@ -12,7 +14,7 @@ import org.oppia.android.util.locale.OppiaLocale
import javax.inject.Inject
import javax.inject.Singleton
-private const val APP_STARTUP_STATE_DATA_PROVIDER_ID = "app_startup_state_data_provider_id"
+private const val APP_STARTUP_STATE_PROVIDER_ID = "app_startup_state_data_provider_id"
/** Controller for persisting and retrieving the user's initial app state upon opening the app. */
@Singleton
@@ -20,44 +22,65 @@ class AppStartupStateController @Inject constructor(
cacheStoreFactory: PersistentCacheStore.Factory,
private val oppiaLogger: OppiaLogger,
private val expirationMetaDataRetriever: ExpirationMetaDataRetriever,
- private val machineLocale: OppiaLocale.MachineLocale
+ private val machineLocale: OppiaLocale.MachineLocale,
+ private val currentBuildFlavor: BuildFlavor
) {
- private val onboardingFlowStore =
+ private val onboardingFlowStore by lazy {
cacheStoreFactory.create("on_boarding_flow", OnboardingState.getDefaultInstance())
-
- private val appStartupStateDataProvider by lazy {
- onboardingFlowStore.transform(APP_STARTUP_STATE_DATA_PROVIDER_ID) {
- AppStartupState.newBuilder().setStartupMode(computeAppStartupMode(it)).build()
- }
}
+ private val appStartupStateDataProvider by lazy { computeAppStartupStateProvider() }
+
init {
// Prime the cache ahead of time so that any existing history is read prior to any calls to
- // markOnboardingFlowCompleted().
- onboardingFlowStore.primeInMemoryCacheAsync().invokeOnCompletion {
- it?.let {
+ // markOnboardingFlowCompleted(). Note that this also ensures that the on-disk cache contains
+ // the last used build flavor (but it doesn't update the in-memory copy as it's the *last* used
+ // flavor, and thus requires an app restart in order to observe).
+ onboardingFlowStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = PersistentCacheStore.UpdateMode.UPDATE_ALWAYS,
+ publishMode = PersistentCacheStore.PublishMode.DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE
+ ) { state ->
+ state.toBuilder().apply { lastUsedBuildFlavor = currentBuildFlavor }.build()
+ }.invokeOnCompletion { primeFailure ->
+ if (primeFailure != null) {
oppiaLogger.e(
- "DOMAIN", "Failed to prime cache ahead of data retrieval for user onboarding data.", it
+ "StartupController",
+ "Failed to prime cache ahead of data retrieval for user onboarding data.",
+ primeFailure
)
}
}
}
/**
- * Saves that the user has completed the app onboarding flow. Note that this does not notify
- * existing subscribers of the changed state, nor can future subscribers observe this state until
- * the app restarts.
+ * Saves that the user has completed the app onboarding flow.
+ *
+ * Note that this does not notify existing subscribers of the changed state, nor can future
+ * subscribers observe this state until the app restarts.
*/
fun markOnboardingFlowCompleted() {
- onboardingFlowStore.storeDataAsync(updateInMemoryCache = false) {
- it.toBuilder().setAlreadyOnboardedApp(true).build()
- }.invokeOnCompletion {
- it?.let {
- oppiaLogger.e(
- "DOMAIN", "Failed when storing that the user already onboarded the app.", it
- )
- }
- }
+ updateOnboardingState { alreadyOnboardedApp = true }
+ }
+
+ /**
+ * Saves that the user never wants to see beta notices again.
+ *
+ * Note that this does not notify existing subscribers of the changed state, nor can future
+ * subscribers observe this state until the app restarts.
+ */
+ fun dismissBetaNoticesPermanently() {
+ updateOnboardingState { permanentlyDismissedBetaNotice = true }
+ }
+
+ /**
+ * Saves that the user never wants to notices for cases when their app has updated from a
+ * pre-release version of the app to the general availability version.
+ *
+ * Note that this does not notify existing subscribers of the changed state, nor can future
+ * subscribers observe this state until the app restarts.
+ */
+ fun dismissGaUpgradeNoticesPermanently() {
+ updateOnboardingState { permanentlyDismissedGaUpgradeNotice = true }
}
/**
@@ -66,6 +89,32 @@ class AppStartupStateController @Inject constructor(
*/
fun getAppStartupState(): DataProvider = appStartupStateDataProvider
+ private fun computeAppStartupStateProvider(): DataProvider {
+ return onboardingFlowStore.transform(APP_STARTUP_STATE_PROVIDER_ID) { onboardingState ->
+ AppStartupState.newBuilder().apply {
+ startupMode = computeAppStartupMode(onboardingState)
+ buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState, startupMode)
+ }.build()
+ }
+ }
+
+ private fun updateOnboardingState(updateState: OnboardingState.Builder.() -> Unit) {
+ // Note that the flavor must be written here since it only gets updated on-disk and never
+ // in-memory (which means it will be inadvertently overwritten when updating onboarding state
+ // here).
+ val deferred = onboardingFlowStore.storeDataAsync(updateInMemoryCache = false) { state ->
+ state.toBuilder().apply {
+ updateState()
+ lastUsedBuildFlavor = currentBuildFlavor
+ }.build()
+ }
+ deferred.invokeOnCompletion { failure ->
+ if (failure != null) {
+ oppiaLogger.e("StartupController", "Failed to update onboarding state.", failure)
+ }
+ }
+ }
+
private fun computeAppStartupMode(onboardingState: OnboardingState): StartupMode {
return when {
hasAppExpired() -> StartupMode.APP_IS_DEPRECATED
@@ -74,6 +123,39 @@ class AppStartupStateController @Inject constructor(
}
}
+ private fun computeBuildNoticeMode(
+ onboardingState: OnboardingState,
+ startupMode: StartupMode
+ ): BuildFlavorNoticeMode {
+ return when (currentBuildFlavor) {
+ BuildFlavor.TESTING, BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED ->
+ BuildFlavorNoticeMode.FLAVOR_NOTICE_MODE_UNSPECIFIED
+ // No notice is shown for developer & alpha builds.
+ BuildFlavor.DEVELOPER, BuildFlavor.ALPHA -> BuildFlavorNoticeMode.NO_NOTICE
+ BuildFlavor.BETA -> {
+ // Only show the beta notice if the user hasn't permanently dismissed it, and when it's
+ // appropriate to show (i.e. they've recently changed to the beta flavor, and their app is
+ // not force-deprecated).
+ if (!onboardingState.permanentlyDismissedBetaNotice &&
+ onboardingState.lastUsedBuildFlavor != BuildFlavor.BETA &&
+ startupMode != StartupMode.APP_IS_DEPRECATED
+ ) {
+ BuildFlavorNoticeMode.SHOW_BETA_NOTICE
+ } else BuildFlavorNoticeMode.NO_NOTICE
+ }
+ BuildFlavor.GENERAL_AVAILABILITY -> when (onboardingState.lastUsedBuildFlavor) {
+ BuildFlavor.ALPHA, BuildFlavor.BETA, null -> {
+ if (!onboardingState.permanentlyDismissedGaUpgradeNotice) {
+ BuildFlavorNoticeMode.SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE
+ } else BuildFlavorNoticeMode.NO_NOTICE // The user doesn't want to see the notice again.
+ }
+ // A brand new install should result in no notice, or an update from a developer build.
+ BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED, BuildFlavor.TESTING,
+ BuildFlavor.DEVELOPER, BuildFlavor.GENERAL_AVAILABILITY -> BuildFlavorNoticeMode.NO_NOTICE
+ }
+ }
+ }
+
private fun hasAppExpired(): Boolean {
val applicationMetadata = expirationMetaDataRetriever.getMetaData()
val isAppExpirationEnabled =
diff --git a/domain/src/main/java/org/oppia/android/domain/oppialogger/LoggingIdentifierController.kt b/domain/src/main/java/org/oppia/android/domain/oppialogger/LoggingIdentifierController.kt
index 3440f62fe0f..ca4a741da2d 100644
--- a/domain/src/main/java/org/oppia/android/domain/oppialogger/LoggingIdentifierController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/oppialogger/LoggingIdentifierController.kt
@@ -4,6 +4,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.oppia.android.app.model.DeviceContextDatabase
import org.oppia.android.data.persistence.PersistentCacheStore
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode
import org.oppia.android.util.data.DataProvider
import org.oppia.android.util.data.DataProviders
import org.oppia.android.util.data.DataProviders.Companion.transform
@@ -36,10 +38,11 @@ class LoggingIdentifierController @Inject constructor(
persistentCacheStoreFactory.create(
cacheName = "device_context_database", DeviceContextDatabase.getDefaultInstance()
).also {
- it.primeInMemoryAndDiskCacheAsync { database ->
- database.toBuilder().apply {
- installationId = computeInstallationId()
- }.build()
+ it.primeInMemoryAndDiskCacheAsync(
+ updateMode = UpdateMode.UPDATE_IF_NEW_CACHE,
+ publishMode = PublishMode.PUBLISH_TO_IN_MEMORY_CACHE
+ ) { database ->
+ database.toBuilder().apply { installationId = computeInstallationId() }.build()
}.invokeOnCompletion { failure ->
if (failure != null) {
oppiaLogger.e(
diff --git a/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt b/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt
index dac62b05666..e2c15df2253 100644
--- a/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/profile/ProfileManagementController.kt
@@ -17,6 +17,8 @@ import org.oppia.android.app.model.ProfileDatabase
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.ReadingTextSize
import org.oppia.android.data.persistence.PersistentCacheStore
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode
import org.oppia.android.domain.oppialogger.LoggingIdentifierController
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.oppialogger.analytics.LearnerAnalyticsLogger
@@ -129,7 +131,10 @@ class ProfileManagementController @Inject constructor(
// TODO(#272): Remove init block when storeDataAsync is fixed
init {
- profileDataStore.primeInMemoryCacheAsync().invokeOnCompletion {
+ profileDataStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UpdateMode.UPDATE_IF_NEW_CACHE,
+ publishMode = PublishMode.PUBLISH_TO_IN_MEMORY_CACHE
+ ).invokeOnCompletion {
it?.let {
oppiaLogger.e(
"ProfileManagementController",
diff --git a/domain/src/main/java/org/oppia/android/domain/topic/PrimeTopicAssetsControllerImpl.kt b/domain/src/main/java/org/oppia/android/domain/topic/PrimeTopicAssetsControllerImpl.kt
index b1eddc72ddc..fa25ee6f163 100644
--- a/domain/src/main/java/org/oppia/android/domain/topic/PrimeTopicAssetsControllerImpl.kt
+++ b/domain/src/main/java/org/oppia/android/domain/topic/PrimeTopicAssetsControllerImpl.kt
@@ -3,7 +3,6 @@ package org.oppia.android.domain.topic
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
-import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.os.SystemClock
@@ -73,7 +72,7 @@ private const val REPLACE_IMG_FILE_PATH_ATTRIBUTE = "src"
*/
@Singleton
class PrimeTopicAssetsControllerImpl @Inject constructor(
- private val context: Context,
+ private val application: Application,
private val oppiaLogger: OppiaLogger,
private val assetRepository: AssetRepository,
private val topicController: TopicController,
@@ -197,7 +196,6 @@ class PrimeTopicAssetsControllerImpl @Inject constructor(
private fun prepareUiForDownloadStatusChanges(dialogStyleResId: Int) {
// Reference: https://stackoverflow.com/a/37713320.
- val application = context.applicationContext as Application
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
diff --git a/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt b/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt
index bba4660a16f..81b9d28e8d8 100644
--- a/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt
+++ b/domain/src/main/java/org/oppia/android/domain/topic/StoryProgressController.kt
@@ -8,6 +8,8 @@ import org.oppia.android.app.model.StoryProgress
import org.oppia.android.app.model.TopicProgress
import org.oppia.android.app.model.TopicProgressDatabase
import org.oppia.android.data.persistence.PersistentCacheStore
+import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode
+import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.util.data.AsyncResult
import org.oppia.android.util.data.DataProvider
@@ -371,8 +373,11 @@ class StoryProgressController @Inject constructor(
cacheStore
}
- cacheStore.primeInMemoryCacheAsync().invokeOnCompletion {
- it?.let { it ->
+ cacheStore.primeInMemoryAndDiskCacheAsync(
+ updateMode = UpdateMode.UPDATE_IF_NEW_CACHE,
+ publishMode = PublishMode.PUBLISH_TO_IN_MEMORY_CACHE
+ ).invokeOnCompletion {
+ if (it != null) {
oppiaLogger.e(
"StoryProgressController",
"Failed to prime cache ahead of data retrieval for StoryProgressController.",
diff --git a/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt
index c77a04df305..9f924f394dd 100644
--- a/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt
+++ b/domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt
@@ -6,17 +6,22 @@ import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import androidx.test.core.content.pm.ApplicationInfoBuilder
import androidx.test.core.content.pm.PackageInfoBuilder
-import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.FLAVOR_NOTICE_MODE_UNSPECIFIED
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.NO_NOTICE
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.SHOW_BETA_NOTICE
+import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode.SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE
import org.oppia.android.app.model.AppStartupState.StartupMode.APP_IS_DEPRECATED
import org.oppia.android.app.model.AppStartupState.StartupMode.USER_IS_ONBOARDED
import org.oppia.android.app.model.AppStartupState.StartupMode.USER_NOT_YET_ONBOARDED
+import org.oppia.android.app.model.BuildFlavor
import org.oppia.android.app.model.OnboardingState
import org.oppia.android.data.persistence.PersistentCacheStore
import org.oppia.android.domain.oppialogger.LogStorageModule
@@ -26,6 +31,12 @@ import org.oppia.android.domain.platformparameter.PlatformParameterModule
import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule
import org.oppia.android.testing.TestLogReportingModule
import org.oppia.android.testing.data.DataProviderTestMonitor
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.Iteration
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.Parameter
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.RunParameterized
+import org.oppia.android.testing.junit.OppiaParameterizedTestRunner.SelectRunnerPlatform
+import org.oppia.android.testing.junit.ParameterizedRobolectricTestRunner
import org.oppia.android.testing.robolectric.RobolectricModule
import org.oppia.android.testing.threading.TestCoroutineDispatchers
import org.oppia.android.testing.threading.TestDispatcherModule
@@ -52,18 +63,25 @@ import javax.inject.Singleton
/** Tests for [AppStartupStateController]. */
// FunctionName: test names are conventionally named with underscores.
@Suppress("FunctionName")
-@RunWith(AndroidJUnit4::class)
+@RunWith(OppiaParameterizedTestRunner::class)
+@SelectRunnerPlatform(ParameterizedRobolectricTestRunner::class)
@Config(application = AppStartupStateControllerTest.TestApplication::class)
class AppStartupStateControllerTest {
@Inject lateinit var context: Context
@Inject lateinit var appStartupStateController: AppStartupStateController
@Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
@Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory
+ @Parameter lateinit var initialFlavorName: String
// TODO(#3792): Remove this usage of Locale (probably by introducing a test utility in the locale
// package to generate these strings).
private val expirationDateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) }
+ @Before
+ fun setUp() {
+ TestModule.buildFlavor = BuildFlavor.BUILD_FLAVOR_UNSPECIFIED
+ }
+
@Test
fun testController_providesInitialState_indicatesUserHasNotOnboardedTheApp() {
setUpDefaultTestApplicationComponent()
@@ -91,7 +109,7 @@ class AppStartupStateControllerTest {
@Test
fun testController_settingAppOnboarded_observedNewController_userOnboardedApp() {
// Simulate the previous app already having completed onboarding.
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
testComponent.getTestCoroutineDispatchers().runCurrent()
}
@@ -100,8 +118,8 @@ class AppStartupStateControllerTest {
setUpDefaultTestApplicationComponent()
val appStartupState = appStartupStateController.getAppStartupState()
- // The app should be considered onboarded since a new DataProvider instance was observed after
- // marking the app as onboarded.
+ // The user should be considered onboarded since a new DataProvider instance was observed after
+ // marking the user as onboarded.
val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
assertThat(mode.startupMode).isEqualTo(USER_IS_ONBOARDED)
}
@@ -110,7 +128,7 @@ class AppStartupStateControllerTest {
@Suppress("DeferredResultUnused")
fun testController_onboardedApp_cleared_observeNewController_userDidNotOnboardApp() {
// Simulate the previous app already having completed onboarding, then cleared.
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
testComponent.getTestCoroutineDispatchers().runCurrent()
@@ -180,7 +198,7 @@ class AppStartupStateControllerTest {
@Test
fun testSecondAppOpen_onboardingFlowNotDone_deprecationEnabled_beforeDepDate_appNotDeprecated() {
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
setUpOppiaApplicationForContext(
context = testComponent.getContext(),
expirationEnabled = true,
@@ -198,7 +216,7 @@ class AppStartupStateControllerTest {
@Test
fun testSecondAppOpen_onboardingFlowNotDone_deprecationEnabled_afterDepDate_appIsDeprecated() {
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
setUpOppiaApplicationForContext(
context = testComponent.getContext(),
expirationEnabled = true,
@@ -216,7 +234,7 @@ class AppStartupStateControllerTest {
@Test
fun testSecondAppOpen_onboardingFlowCompleted_depEnabled_beforeDepDate_appNotDeprecated() {
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
setUpOppiaApplicationForContext(
context = testComponent.getContext(),
expirationEnabled = true,
@@ -238,7 +256,7 @@ class AppStartupStateControllerTest {
@Test
fun testSecondAppOpen_onboardingFlowCompleted_deprecationEnabled_afterDepDate_appIsDeprecated() {
- executeInPreviousApp { testComponent ->
+ executeInPreviousAppInstance { testComponent ->
setUpOppiaApplicationForContext(
context = testComponent.getContext(),
expirationEnabled = true,
@@ -258,6 +276,495 @@ class AppStartupStateControllerTest {
assertThat(mode.startupMode).isEqualTo(APP_IS_DEPRECATED)
}
+ /* Tests to verify that beta & no notices are shown at the expected times. */
+
+ @Test
+ fun testController_initialState_testingBuild_showsUnspecifiedNotice() {
+ TestModule.buildFlavor = BuildFlavor.TESTING
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Testing mode is specially handled as it's generally not expected to be encountered (but is
+ // still possible).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(FLAVOR_NOTICE_MODE_UNSPECIFIED)
+ }
+
+ @Test
+ fun testController_initialState_developerBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_initialState_alphaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_initialState_betaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // No notice is shown here since the 'prior' version is beta, and the notice is only shown if
+ // the build flavor changes for the user.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_initialState_gaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_appDeprecated_testingBuild_showsUnspecifiedNotice() {
+ TestModule.buildFlavor = BuildFlavor.TESTING
+ setUpDefaultTestApplicationComponent()
+ setUpOppiaApplication(expirationEnabled = true, expDate = dateStringForToday())
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Testing mode is specially handled as it's generally not expected to be encountered (but is
+ // still possible).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(FLAVOR_NOTICE_MODE_UNSPECIFIED)
+ }
+
+ @Test
+ fun testController_appDeprecated_developerBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ setUpDefaultTestApplicationComponent()
+ setUpOppiaApplication(expirationEnabled = true, expDate = dateStringForToday())
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_appDeprecated_alphaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ setUpDefaultTestApplicationComponent()
+ setUpOppiaApplication(expirationEnabled = true, expDate = dateStringForToday())
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_appDeprecated_betaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+ setUpOppiaApplication(expirationEnabled = true, expDate = dateStringForToday())
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // The beta notice is not shown in cases when the app is deprecated (since there's no point in
+ // showing it; the user can't actually use the app).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_appDeprecated_gaBuild_showNoNotice() {
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+ setUpOppiaApplication(expirationEnabled = true, expDate = dateStringForToday())
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_testingBuild_showsUnspecifiedNotice() {
+ // Simulate the previous app already having completed onboarding.
+ executeInPreviousAppInstance { testComponent ->
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.TESTING
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Testing mode is specially handled as it's generally not expected to be encountered (but is
+ // still possible).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(FLAVOR_NOTICE_MODE_UNSPECIFIED)
+ }
+
+ @Test
+ fun testController_userOnboarded_developerBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding.
+ executeInPreviousAppInstance { testComponent ->
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_alphaBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding.
+ executeInPreviousAppInstance { testComponent ->
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_betaBuild_showBetaNotice() {
+ // Simulate the previous app already having completed onboarding.
+ executeInPreviousAppInstance { testComponent ->
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Beta is shown when using beta mode.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(SHOW_BETA_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_gaBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding.
+ executeInPreviousAppInstance { testComponent ->
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ /* Tests to verify that changing from one build flavor to another can cause notices to show. */
+
+ @Test
+ fun testController_userOnboarded_changeToTestingBuild_showsUnspecifiedNotice() {
+ // Simulate the previous app already having completed onboarding in a non-testing build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.TESTING
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(FLAVOR_NOTICE_MODE_UNSPECIFIED)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToDevBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding in a non-dev build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.TESTING
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToAlphaBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding in a non-alpha build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToBetaBuild_fromAlpha_showBetaNotice() {
+ // Simulate the previous app already having completed onboarding in an alpha build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Changing from alpha to beta should result in the notice showing.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(SHOW_BETA_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToBetaBuild_fromBetaAgain_showNoNotice() {
+ // Simulate the previous app already having completed onboarding in a beta build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.BETA
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // The beta notice was shown on the last run; don't show it again.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToBetaBuild_fromGa_showBetaNotice() {
+ // Simulate the previous app already having completed onboarding in a generally available build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Changing from GA to beta should result in the notice showing.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(SHOW_BETA_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToGaBuild_fromDeveloperBuild_showNoNotice() {
+ // Simulate the previous app already having completed onboarding in an developer-only build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.DEVELOPER
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // The GA upgrade notice is only shown when changing from alpha or beta.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToGaBuild_fromAlpha_showGaNotice() {
+ // Simulate the previous app already having completed onboarding in an alpha build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.ALPHA
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Changing from alpha to GA should result in the GA upgrade notice showing.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToGaBuild_fromBeta_showGaNotice() {
+ // Simulate the previous app already having completed onboarding in a beta build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.BETA
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Changing from beta to GA should result in the GA upgrade notice showing.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE)
+ }
+
+ @Test
+ fun testController_userOnboarded_changeToGaBuild_fromGaAgain_showNoNotice() {
+ // Simulate the previous app already having completed onboarding in a generally available build.
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ testComponent.getAppStartupStateController().markOnboardingFlowCompleted()
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // The GA upgrade notice is only shown when changing from beta.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ /* Tests to verify that notices can be permanently dismissed. */
+
+ @Test
+ fun testController_dismissBetaNoticePermanently_scenariosWithoutBetaNotice_showNoNotice() {
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.BETA
+ testComponent.getAppStartupStateController().apply {
+ markOnboardingFlowCompleted()
+ // While this is technically impossible for this exact configuration, it could be done by
+ // permanently dismissing during an earlier time when the notice was shown before the user
+ // returned to the beta flavor of the app.
+ dismissBetaNoticesPermanently()
+ }
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // No notice is shown in this case (beta -> beta).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_beta", "initialFlavorName=TESTING"),
+ Iteration("dev_to_beta", "initialFlavorName=DEVELOPER"),
+ Iteration("alpha_to_beta", "initialFlavorName=ALPHA"),
+ Iteration("ga_to_beta", "initialFlavorName=GENERAL_AVAILABILITY")
+ )
+ fun testController_dismissBetaNoticePermanently_scenariosWhenBetaNoticeDoesShow_showNoNotice() {
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.valueOf(initialFlavorName)
+ testComponent.getAppStartupStateController().apply {
+ markOnboardingFlowCompleted()
+ dismissBetaNoticesPermanently()
+ }
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.BETA
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Despite a notice normally showing in this circumstance, it doesn't here since the beta notice
+ // was permanently disabled.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("testing_to_ga", "initialFlavorName=TESTING"),
+ Iteration("dev_to_ga", "initialFlavorName=DEVELOPER")
+ )
+ fun testController_dismissGaNoticePermanently_scenariosWhenGaNoticeDoesNotShow_showNoNotice() {
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.valueOf(initialFlavorName)
+ testComponent.getAppStartupStateController().apply {
+ markOnboardingFlowCompleted()
+ dismissGaUpgradeNoticesPermanently()
+ }
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // A notice does not show in these circumstances (though, it wouldn't be expected to regardless
+ // of whether the GA notice was permanently disabled).
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
+ @Test
+ @RunParameterized(
+ Iteration("alpha_to_ga", "initialFlavorName=ALPHA"),
+ Iteration("beta_to_ga", "initialFlavorName=BETA")
+ )
+ fun testController_dismissGaNoticePermanently_scenariosWhenGaNoticeDoesShow_showNoNotice() {
+ executeInPreviousAppInstance { testComponent ->
+ TestModule.buildFlavor = BuildFlavor.valueOf(initialFlavorName)
+ testComponent.getAppStartupStateController().apply {
+ markOnboardingFlowCompleted()
+ dismissGaUpgradeNoticesPermanently()
+ }
+ testComponent.getTestCoroutineDispatchers().runCurrent()
+ }
+ TestModule.buildFlavor = BuildFlavor.GENERAL_AVAILABILITY
+ setUpDefaultTestApplicationComponent()
+
+ val appStartupState = appStartupStateController.getAppStartupState()
+
+ // Despite a notice normally showing in this circumstance, it doesn't here since the GA upgrade
+ // notice was permanently disabled.
+ val mode = monitorFactory.waitForNextSuccessfulResult(appStartupState)
+ assertThat(mode.buildFlavorNoticeMode).isEqualTo(NO_NOTICE)
+ }
+
private fun setUpTestApplicationComponent() {
ApplicationProvider.getApplicationContext().inject(this)
}
@@ -277,7 +784,7 @@ class AppStartupStateControllerTest {
* Note that only dependencies fetched from the specified [TestApplicationComponent] should be
* used, not any class-level injected dependencies.
*/
- private fun executeInPreviousApp(block: (TestApplicationComponent) -> Unit) {
+ private fun executeInPreviousAppInstance(block: (TestApplicationComponent) -> Unit) {
val testApplication = TestApplication()
// The true application is hooked as a base context. This is to make sure the new application
// can behave like a real Android application class (per Robolectric) without having a shared
@@ -341,6 +848,10 @@ class AppStartupStateControllerTest {
// TODO(#89): Move this to a common test application component.
@Module
class TestModule {
+ companion object {
+ var buildFlavor = BuildFlavor.BUILD_FLAVOR_UNSPECIFIED
+ }
+
@Provides
@Singleton
fun provideContext(application: Application): Context {
@@ -360,6 +871,9 @@ class AppStartupStateControllerTest {
@GlobalLogLevel
@Provides
fun provideGlobalLogLevel(): LogLevel = LogLevel.VERBOSE
+
+ @Provides
+ fun provideTestingBuildFlavor(): BuildFlavor = buildFlavor
}
// TODO(#89): Move this to a common test application component.
diff --git a/domain/src/test/java/org/oppia/android/domain/onboarding/BUILD.bazel b/domain/src/test/java/org/oppia/android/domain/onboarding/BUILD.bazel
new file mode 100644
index 00000000000..85124495fd0
--- /dev/null
+++ b/domain/src/test/java/org/oppia/android/domain/onboarding/BUILD.bazel
@@ -0,0 +1,39 @@
+"""
+Tests for domain components pertaining to onboarding the user to the app.
+"""
+
+load("@dagger//:workspace_defs.bzl", "dagger_rules")
+load("//:oppia_android_test.bzl", "oppia_android_test")
+
+oppia_android_test(
+ name = "AppStartupStateControllerTest",
+ srcs = ["AppStartupStateControllerTest.kt"],
+ custom_package = "org.oppia.android.domain.onboarding",
+ test_class = "org.oppia.android.domain.onboarding.AppStartupStateControllerTest",
+ test_manifest = "//domain:test_manifest",
+ deps = [
+ ":dagger",
+ "//domain",
+ "//domain/src/main/java/org/oppia/android/domain/onboarding:retriever_prod_module",
+ "//domain/src/main/java/org/oppia/android/domain/onboarding:state_controller",
+ "//domain/src/main/java/org/oppia/android/domain/oppialogger:prod_module",
+ "//domain/src/main/java/org/oppia/android/domain/oppialogger/analytics:prod_module",
+ "//testing",
+ "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor",
+ "//testing/src/main/java/org/oppia/android/testing/junit:oppia_parameterized_test_runner",
+ "//testing/src/main/java/org/oppia/android/testing/junit:parameterized_robolectric_test_runner",
+ "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module",
+ "//testing/src/main/java/org/oppia/android/testing/threading:test_module",
+ "//third_party:com_google_truth_truth",
+ "//third_party:junit_junit",
+ "//third_party:org_mockito_mockito-core",
+ "//third_party:org_robolectric_robolectric",
+ "//third_party:robolectric_android-all",
+ "//utility/src/main/java/org/oppia/android/util/locale:prod_module",
+ "//utility/src/main/java/org/oppia/android/util/logging:prod_module",
+ "//utility/src/main/java/org/oppia/android/util/networking:debug_module",
+ "//utility/src/main/java/org/oppia/android/util/system:prod_module",
+ ],
+)
+
+dagger_rules()
diff --git a/instrumentation/src/java/org/oppia/android/instrumentation/application/BUILD.bazel b/instrumentation/src/java/org/oppia/android/instrumentation/application/BUILD.bazel
index 10e816007b1..78c15038969 100644
--- a/instrumentation/src/java/org/oppia/android/instrumentation/application/BUILD.bazel
+++ b/instrumentation/src/java/org/oppia/android/instrumentation/application/BUILD.bazel
@@ -22,6 +22,7 @@ kt_android_library(
"//app/src/main/java/org/oppia/android/app/application:abstract_application",
"//app/src/main/java/org/oppia/android/app/application:application_component",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//data/src/main/java/org/oppia/android/data/backends/gae:network_config_annotations",
"//domain",
"//utility",
diff --git a/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt b/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt
index b8f2ba6422a..7a95bcc6990 100644
--- a/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt
+++ b/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt
@@ -4,6 +4,7 @@ import dagger.Component
import org.oppia.android.app.application.ApplicationComponent
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -96,7 +97,8 @@ import javax.inject.Singleton
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
SyncStatusModule::class, NetworkConnectionDebugUtilModule::class,
MetricLogSchedulerModule::class, PerformanceMetricsLoggerModule::class,
- PerformanceMetricsAssessorModule::class, PerformanceMetricsConfigurationsModule::class
+ PerformanceMetricsAssessorModule::class, PerformanceMetricsConfigurationsModule::class,
+ TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/model/src/main/proto/BUILD.bazel b/model/src/main/proto/BUILD.bazel
index c6d19966e44..94372b448ff 100644
--- a/model/src/main/proto/BUILD.bazel
+++ b/model/src/main/proto/BUILD.bazel
@@ -112,6 +112,7 @@ java_lite_proto_library(
oppia_proto_library(
name = "onboarding_proto",
srcs = ["onboarding.proto"],
+ deps = [":version_proto"],
)
java_lite_proto_library(
@@ -191,6 +192,18 @@ java_lite_proto_library(
deps = [":translation_proto"],
)
+oppia_proto_library(
+ name = "version_proto",
+ srcs = ["version.proto"],
+ visibility = ["//:oppia_api_visibility"],
+)
+
+java_lite_proto_library(
+ name = "version_java_proto_lite",
+ visibility = ["//:oppia_api_visibility"],
+ deps = [":version_proto"],
+)
+
oppia_proto_library(
name = "voiceover_proto",
srcs = ["voiceover.proto"],
diff --git a/model/src/main/proto/onboarding.proto b/model/src/main/proto/onboarding.proto
index ca2027922ec..3ed97a65062 100644
--- a/model/src/main/proto/onboarding.proto
+++ b/model/src/main/proto/onboarding.proto
@@ -2,6 +2,8 @@ syntax = "proto3";
package model;
+import "version.proto";
+
option java_package = "org.oppia.android.app.model";
option java_multiple_files = true;
@@ -24,15 +26,51 @@ message AppStartupState {
APP_IS_DEPRECATED = 3;
}
+ // Describes different notices that may be shown to the user on startup depending on whether
+ // they're using or have used a pre-release version of the app.
+ enum BuildFlavorNoticeMode {
+ // Indicates that the current build flavor notice mode is unknown.
+ FLAVOR_NOTICE_MODE_UNSPECIFIED = 0;
+
+ // Indicates that the user is not in a situation where a notice should be shown (either because
+ // they aren't or haven't recently used a pre-release version of the app, or they've asked to
+ // not see those notices again).
+ NO_NOTICE = 1;
+
+ // Indicates that the user should be a shown a notice mentioning that they're currently using a
+ // beta version of the app.
+ SHOW_BETA_NOTICE = 2;
+
+ // Indicates that the user should be shown a notice mentioning that they've recently upgraded to
+ // the general availability version of the app.
+ SHOW_UPGRADE_TO_GENERAL_AVAILABILITY_NOTICE = 3;
+ }
+
// Contains the user's startup state upon opening the app. This may change from app open to app
// open, but should remain constant throughout the lifetime of a single process.
StartupMode startup_mode = 1;
+
// Indicates if the app is starting up again immediately after a crash has occurred.
bool is_from_crash = 2;
+
+ // Indicates whether the user should be shown a startup notice based on their recent usage of
+ // different build flavors of the app.
+ BuildFlavorNoticeMode build_flavor_notice_mode = 3;
}
// Stores the completion state of the user's progress through the app onboarding flow.
message OnboardingState {
// Indicates whether user has fully completed the onboarding flow.
bool already_onboarded_app = 1;
+
+ // Represents the build flavor of the app the last time the user used it.
+ BuildFlavor last_used_build_flavor = 2;
+
+ // Represents whether the user has seen, and asked to not see again, the notice that they're using
+ // a beta version of the app.
+ bool permanently_dismissed_beta_notice = 3;
+
+ // Represents whether the user has seen, and asked to not see again, the notice that they're using
+ // the general availability version of the app after having previously used a pre-release version.
+ bool permanently_dismissed_ga_upgrade_notice = 4;
}
diff --git a/model/src/main/proto/version.proto b/model/src/main/proto/version.proto
new file mode 100644
index 00000000000..a0878579e57
--- /dev/null
+++ b/model/src/main/proto/version.proto
@@ -0,0 +1,32 @@
+syntax = "proto3";
+
+package model;
+
+option java_package = "org.oppia.android.app.model";
+option java_multiple_files = true;
+
+// Represents different compile-time build flavors that the app may be built under.
+enum BuildFlavor {
+ // Indicates an unknown build flavor (in these situations, client code should fall back to a
+ // reasonable default).
+ BUILD_FLAVOR_UNSPECIFIED = 0;
+
+ // Corresponds to a testing environment-specific build of the app. Note that this is different
+ // than a 'test version' of the app for the purpose of quality assurance testing. The QA team will
+ // always be testing one of the user-facing flavors of the app (generally alpha, beta, and GA, but
+ // potentially the developer flavor on occasion). This flavor is never expected to be used for a
+ // version of the app used directly by a human.
+ TESTING = 1;
+
+ // Corresponds to a developer-only build of the app.
+ DEVELOPER = 2;
+
+ // Corresponds to an alpha (closed testing track) pre-release build of the app.
+ ALPHA = 3;
+
+ // Corresponds to a beta (open testing track) pre-release/early access build of the app.
+ BETA = 4;
+
+ // Corresponds to a generally available production build of the app.
+ GENERAL_AVAILABILITY = 5;
+}
diff --git a/scripts/assets/file_content_validation_checks.textproto b/scripts/assets/file_content_validation_checks.textproto
index ab76d9e3ddf..0c585bad36b 100644
--- a/scripts/assets/file_content_validation_checks.textproto
+++ b/scripts/assets/file_content_validation_checks.textproto
@@ -291,6 +291,7 @@ file_content_checks {
prohibited_content_regex: "OppiaParameterizedTestRunner"
failure_message: "To use OppiaParameterizedTestRunner, please add an exemption to file_content_validation_checks.textproto and add an explanation for your use case in your PR description. Note that parameterized tests should only be used in special circumstances where a single behavior can be tested across multiple inputs, or for especially large test suites that can be trivially reduced."
exempted_file_name: "app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt"
+ exempted_file_name: "app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt"
exempted_file_name: "app/src/test/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtilTest.kt"
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputIsEquivalentToRuleClassifierProviderTest.kt"
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput/AlgebraicExpressionInputMatchesExactlyWithRuleClassifierProviderTest.kt"
@@ -301,6 +302,7 @@ file_content_checks {
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/classify/rules/numericexpressioninput/NumericExpressionInputIsEquivalentToRuleClassifierProviderTest.kt"
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/classify/rules/numericexpressioninput/NumericExpressionInputMatchesExactlyWithRuleClassifierProviderTest.kt"
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/classify/rules/numericexpressioninput/NumericExpressionInputMatchesUpToTrivialManipulationsRuleClassifierProviderTest.kt"
+ exempted_file_name: "domain/src/test/java/org/oppia/android/domain/onboarding/AppStartupStateControllerTest.kt"
exempted_file_name: "domain/src/test/java/org/oppia/android/domain/oppialogger/analytics/LearnerAnalyticsLoggerTest.kt"
exempted_file_name: "scripts/src/javatests/org/oppia/android/scripts/regex/RegexPatternValidationCheckTest.kt"
exempted_file_name: "utility/src/test/java/org/oppia/android/util/math/ExpressionToComparableOperationConverterTest.kt"
diff --git a/scripts/assets/kdoc_validity_exemptions.textproto b/scripts/assets/kdoc_validity_exemptions.textproto
index edae7eeecd5..eb430e59c77 100644
--- a/scripts/assets/kdoc_validity_exemptions.textproto
+++ b/scripts/assets/kdoc_validity_exemptions.textproto
@@ -23,7 +23,7 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/completedstorylist/
exempted_file_path: "app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListViewModel.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/SegmentedCircularProgressView.kt"
-exempted_file_path: "app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt"
diff --git a/scripts/assets/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto
index 24119196db3..8018118b950 100644
--- a/scripts/assets/test_file_exemptions.textproto
+++ b/scripts/assets/test_file_exemptions.textproto
@@ -51,8 +51,12 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alpha/A
exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaApplicationComponent.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaBuildFlavorModule.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaOppiaApplication.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/application/beta/BetaOppiaApplication.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ga/GaOppiaApplication.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryItemViewModel.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivityPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListFragment.kt"
@@ -64,9 +68,13 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/interact
exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/interaction/NumericInputInteractionView.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/interaction/RatioInputInteractionView.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/customview/interaction/TextInputInteractionView.kt"
-exempted_file_path: "app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragment.kt"
-exempted_file_path: "app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt"
-exempted_file_path: "app/src/main/java/org/oppia/android/app/deprecation/DeprecationNoticeExitAppListener.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragment.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/BetaNoticeClosedListener.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentPresenter.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/DeprecationNoticeExitAppListener.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeClosedListener.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivityPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsModule.kt"
@@ -215,6 +223,8 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/mydownloads/MyDownl
exempted_file_path: "app/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsViewPagerAdapter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/mydownloads/UpdatesTabFragment.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/mydownloads/UpdatesTabFragmentPresenter.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt"
+exempted_file_path: "app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/OnboadingSlideViewModel.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/OnboardingActivityPresenter.kt"
exempted_file_path: "app/src/main/java/org/oppia/android/app/onboarding/OnboardingFragmentPresenter.kt"
diff --git a/testing/src/main/java/org/oppia/android/testing/OppiaTestRunner.kt b/testing/src/main/java/org/oppia/android/testing/OppiaTestRunner.kt
index 30a9a81d934..8ac79646368 100644
--- a/testing/src/main/java/org/oppia/android/testing/OppiaTestRunner.kt
+++ b/testing/src/main/java/org/oppia/android/testing/OppiaTestRunner.kt
@@ -27,7 +27,7 @@ class OppiaTestRunner : AndroidJUnitRunner() {
// Load a new application if it's different than the original.
val bindApplication = retrieveTestApplicationName(arguments?.getString("class"))?.let {
newApplication(applicationClassLoader, it, targetContext)
- } ?: targetContext.applicationContext as Application
+ } ?: targetContext as Application
// Ensure the bound application is forcibly overwritten in the target context, and used
// subsequently throughout the runner since it's replacing the previous application.
diff --git a/testing/src/test/java/org/oppia/android/testing/junit/BUILD.bazel b/testing/src/test/java/org/oppia/android/testing/junit/BUILD.bazel
index 6dcc99d23a8..cde8eeeb69a 100644
--- a/testing/src/test/java/org/oppia/android/testing/junit/BUILD.bazel
+++ b/testing/src/test/java/org/oppia/android/testing/junit/BUILD.bazel
@@ -18,6 +18,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation:app_language_locale_handler",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
@@ -71,6 +72,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation:app_language_locale_handler",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
@@ -123,6 +125,7 @@ oppia_android_test(
"//app/src/main/java/org/oppia/android/app/application:application_injector",
"//app/src/main/java/org/oppia/android/app/application:application_injector_provider",
"//app/src/main/java/org/oppia/android/app/application:common_application_modules",
+ "//app/src/main/java/org/oppia/android/app/application/testing:testing_build_flavor_module",
"//app/src/main/java/org/oppia/android/app/translation:app_language_locale_handler",
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
diff --git a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleCustomContextTest.kt b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleCustomContextTest.kt
index 51e520fb94b..0064df9d8cc 100644
--- a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleCustomContextTest.kt
+++ b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleCustomContextTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -265,7 +266,7 @@ class InitializeDefaultLocaleRuleCustomContextTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleOmissionTest.kt b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleOmissionTest.kt
index 65b3bc82a08..4eeb5333f5c 100644
--- a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleOmissionTest.kt
+++ b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleOmissionTest.kt
@@ -17,6 +17,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
@@ -141,7 +142,7 @@ class InitializeDefaultLocaleRuleOmissionTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleTest.kt b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleTest.kt
index a53e4b716e0..4cb60a9367a 100644
--- a/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleTest.kt
+++ b/testing/src/test/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRuleTest.kt
@@ -18,6 +18,7 @@ import org.oppia.android.app.application.ApplicationInjector
import org.oppia.android.app.application.ApplicationInjectorProvider
import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
+import org.oppia.android.app.application.testing.TestingBuildFlavorModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.OppiaLanguage
@@ -145,7 +146,7 @@ class InitializeDefaultLocaleRuleTest {
NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
MathEquationInputModule::class, SplitScreenInteractionModule::class,
LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
- SyncStatusModule::class, MetricLogSchedulerModule::class
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class
]
)
interface TestApplicationComponent : ApplicationComponent {
diff --git a/utility/src/main/java/org/oppia/android/util/data/DataProvider.kt b/utility/src/main/java/org/oppia/android/util/data/DataProvider.kt
index 5b940afa095..0cb2e1b97dd 100644
--- a/utility/src/main/java/org/oppia/android/util/data/DataProvider.kt
+++ b/utility/src/main/java/org/oppia/android/util/data/DataProvider.kt
@@ -1,14 +1,13 @@
package org.oppia.android.util.data
-import android.content.Context
+import android.app.Application
/**
* Represents a provider of data that can be delivered and changed asynchronously.
*
* @param The type of data being provided.
*/
-abstract class DataProvider(val context: Context) {
-
+abstract class DataProvider(val application: Application) {
/**
* Returns a unique identifier that corresponds to this data provider. This should be a trivially
* copyable and immutable object. This ID is used to determine which data provider subscribers
diff --git a/utility/src/main/java/org/oppia/android/util/data/DataProviders.kt b/utility/src/main/java/org/oppia/android/util/data/DataProviders.kt
index e86c139c122..93049848c3c 100644
--- a/utility/src/main/java/org/oppia/android/util/data/DataProviders.kt
+++ b/utility/src/main/java/org/oppia/android/util/data/DataProviders.kt
@@ -1,6 +1,6 @@
package org.oppia.android.util.data
-import android.content.Context
+import android.app.Application
import androidx.lifecycle.LiveData
import dagger.Reusable
import kotlinx.coroutines.CoroutineDispatcher
@@ -26,7 +26,7 @@ import javax.inject.Inject
*/
@Reusable // Since otherwise a new provider will be created for each companion object call.
class DataProviders @Inject constructor(
- private val context: Context,
+ private val application: Application,
@BackgroundDispatcher private val backgroundDispatcher: CoroutineDispatcher,
private val asyncDataSubscriptionManager: AsyncDataSubscriptionManager,
private val exceptionLogger: ExceptionLogger
@@ -46,7 +46,7 @@ class DataProviders @Inject constructor(
fun DataProvider.transform(newId: Any, function: (I) -> O): DataProvider {
val dataProviders = getDataProviders()
dataProviders.asyncDataSubscriptionManager.associateIds(newId, getId())
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any = newId
override suspend fun retrieveData(): AsyncResult {
@@ -70,7 +70,7 @@ class DataProviders @Inject constructor(
): DataProvider {
val dataProviders = getDataProviders()
dataProviders.asyncDataSubscriptionManager.associateIds(newId, getId())
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any = newId
override suspend fun retrieveData(): AsyncResult {
@@ -90,7 +90,7 @@ class DataProviders @Inject constructor(
function: suspend (I) -> AsyncResult
): NestedTransformedDataProvider {
return NestedTransformedDataProvider.createNestedTransformedDataProvider(
- context, newId, this, function, getDataProviders().asyncDataSubscriptionManager
+ application, newId, this, function, getDataProviders().asyncDataSubscriptionManager
)
}
@@ -113,7 +113,7 @@ class DataProviders @Inject constructor(
val dataProviders = getDataProviders()
dataProviders.asyncDataSubscriptionManager.associateIds(newId, getId())
dataProviders.asyncDataSubscriptionManager.associateIds(newId, dataProvider.getId())
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any {
return newId
}
@@ -141,7 +141,7 @@ class DataProviders @Inject constructor(
val dataProviders = getDataProviders()
dataProviders.asyncDataSubscriptionManager.associateIds(newId, getId())
dataProviders.asyncDataSubscriptionManager.associateIds(newId, dataProvider.getId())
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any {
return newId
}
@@ -167,7 +167,7 @@ class DataProviders @Inject constructor(
}
private fun DataProvider.getDataProviders(): DataProviders {
- val injectorProvider = context.applicationContext as DataProvidersInjectorProvider
+ val injectorProvider = application as DataProvidersInjectorProvider
return injectorProvider.getDataProvidersInjector().getDataProviders()
}
}
@@ -184,7 +184,7 @@ class DataProviders @Inject constructor(
* [AsyncDataSubscriptionManager.notifyChange] with the in-memory provider's identifier.
*/
fun createInMemoryDataProvider(id: Any, loadFromMemory: () -> T): DataProvider {
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any {
return id
}
@@ -208,7 +208,7 @@ class DataProviders @Inject constructor(
id: Any,
loadFromMemoryAsync: suspend () -> AsyncResult
): DataProvider {
- return object : DataProvider(context) {
+ return object : DataProvider(application) {
override fun getId(): Any {
return id
}
@@ -277,12 +277,12 @@ class DataProviders @Inject constructor(
* provider can change.
*/
class NestedTransformedDataProvider private constructor(
- context: Context,
+ application: Application,
private val id: Any,
private var baseId: Any,
private val asyncDataSubscriptionManager: AsyncDataSubscriptionManager,
private var retrieveTransformedData: suspend () -> AsyncResult
- ) : DataProvider(context) {
+ ) : DataProvider(application) {
init {
initializeTransformer()
}
@@ -320,14 +320,14 @@ class DataProviders @Inject constructor(
companion object {
/** Returns a new [NestedTransformedDataProvider]. */
internal fun createNestedTransformedDataProvider(
- context: Context,
+ application: Application,
id: Any,
baseDataProvider: DataProvider,
transform: suspend (I) -> AsyncResult,
asyncDataSubscriptionManager: AsyncDataSubscriptionManager
): NestedTransformedDataProvider {
return NestedTransformedDataProvider(
- context, id, baseDataProvider.getId(), asyncDataSubscriptionManager
+ application, id, baseDataProvider.getId(), asyncDataSubscriptionManager
) {
baseDataProvider.retrieveData().transformAsync(transform)
}
diff --git a/utility/src/main/java/org/oppia/android/util/data/InMemoryBlockingCache.kt b/utility/src/main/java/org/oppia/android/util/data/InMemoryBlockingCache.kt
index 0229c013ea7..289a63b5324 100644
--- a/utility/src/main/java/org/oppia/android/util/data/InMemoryBlockingCache.kt
+++ b/utility/src/main/java/org/oppia/android/util/data/InMemoryBlockingCache.kt
@@ -28,10 +28,10 @@ class InMemoryBlockingCache private constructor(
*/
private var value: T? = initialValue
- private var changeObserver: suspend () -> Unit = {}
+ private var changeObserver: suspend (T?, T?) -> Unit = { _, _ -> }
/** Registers an observer that is called synchronously whenever this cache's contents are changed. */
- fun observeChanges(changeObserver: suspend () -> Unit) {
+ fun observeChanges(changeObserver: suspend (T?, T?) -> Unit) {
this.changeObserver = changeObserver
}
@@ -168,14 +168,16 @@ class InMemoryBlockingCache private constructor(
}
private suspend fun setCache(newValue: T): T {
+ val oldValue = value
value = newValue
- changeObserver()
+ changeObserver(oldValue, newValue)
return newValue
}
private suspend fun clearCache() {
+ val oldValue = value
value = null
- changeObserver()
+ changeObserver(oldValue, null)
}
/** An injectable factory for [InMemoryBlockingCache]es. */
diff --git a/utility/src/main/java/org/oppia/android/util/logging/firebase/FirebaseEventLogger.kt b/utility/src/main/java/org/oppia/android/util/logging/firebase/FirebaseEventLogger.kt
index eb285ed8864..93704c12dd6 100644
--- a/utility/src/main/java/org/oppia/android/util/logging/firebase/FirebaseEventLogger.kt
+++ b/utility/src/main/java/org/oppia/android/util/logging/firebase/FirebaseEventLogger.kt
@@ -1,7 +1,7 @@
package org.oppia.android.util.logging.firebase
import android.annotation.SuppressLint
-import android.content.Context
+import android.app.Application
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
import org.oppia.android.app.model.EventLog
@@ -65,13 +65,11 @@ class FirebaseEventLogger private constructor(
/** Application-scoped injectable factory for creating new [FirebaseEventLogger]s. */
@SuppressLint("MissingPermission") // This is a false warning probably due to the IJwB plugin.
class Factory @Inject constructor(
- private val context: Context,
+ private val application: Application,
private val networkConnectionUtil: NetworkConnectionUtil,
private val eventBundleCreator: EventBundleCreator
) {
- private val firebaseAnalytics by lazy {
- FirebaseAnalytics.getInstance(context.applicationContext)
- }
+ private val firebaseAnalytics by lazy { FirebaseAnalytics.getInstance(application) }
/**
* Returns a new [FirebaseEventLogger] for the current application context.
diff --git a/utility/src/main/java/org/oppia/android/util/parser/image/RepositoryGlideModule.kt b/utility/src/main/java/org/oppia/android/util/parser/image/RepositoryGlideModule.kt
index d2349ab3eec..bd6bcbb52e0 100644
--- a/utility/src/main/java/org/oppia/android/util/parser/image/RepositoryGlideModule.kt
+++ b/utility/src/main/java/org/oppia/android/util/parser/image/RepositoryGlideModule.kt
@@ -1,5 +1,6 @@
package org.oppia.android.util.parser.image
+import android.app.Application
import android.content.Context
import com.bumptech.glide.Glide
import com.bumptech.glide.Registry
@@ -44,7 +45,9 @@ class RepositoryGlideModule : AppGlideModule() {
registry.append(
MathModel::class.java,
ByteBuffer::class.java,
- MathBitmapModelLoader.Factory(context.applicationContext)
+ MathBitmapModelLoader.Factory(
+ context as? Application ?: context.applicationContext as Application
+ )
)
}
}
diff --git a/utility/src/main/java/org/oppia/android/util/parser/math/MathBitmapModelLoader.kt b/utility/src/main/java/org/oppia/android/util/parser/math/MathBitmapModelLoader.kt
index e53cd82e71b..73100116aea 100644
--- a/utility/src/main/java/org/oppia/android/util/parser/math/MathBitmapModelLoader.kt
+++ b/utility/src/main/java/org/oppia/android/util/parser/math/MathBitmapModelLoader.kt
@@ -1,6 +1,6 @@
package org.oppia.android.util.parser.math
-import android.content.Context
+import android.app.Application
import android.graphics.Bitmap
import android.graphics.Bitmap.Config.ARGB_8888
import android.graphics.Canvas
@@ -46,24 +46,24 @@ import kotlin.math.roundToInt
* and blocks the main thread).
*/
class MathBitmapModelLoader private constructor(
- private val applicationContext: Context
+ private val application: Application
) : ModelLoader {
// Ref: https://bumptech.github.io/glide/tut/custom-modelloader.html#writing-the-modelloader.
private val backgroundDispatcher by lazy {
- val injectorProvider = applicationContext.applicationContext as DispatcherInjectorProvider
+ val injectorProvider = application as DispatcherInjectorProvider
val injector = injectorProvider.getDispatcherInjector()
injector.getBackgroundDispatcher()
}
private val blockingDispatcher by lazy {
- val injectorProvider = applicationContext.applicationContext as DispatcherInjectorProvider
+ val injectorProvider = application as DispatcherInjectorProvider
val injector = injectorProvider.getDispatcherInjector()
injector.getBlockingDispatcher()
}
private val consoleLogger by lazy {
- val injectorProvider = applicationContext as ConsoleLoggerInjectorProvider
+ val injectorProvider = application as ConsoleLoggerInjectorProvider
val injector = injectorProvider.getConsoleLoggerInjector()
injector.getConsoleLogger()
}
@@ -77,7 +77,7 @@ class MathBitmapModelLoader private constructor(
return ModelLoader.LoadData(
model.toKeySignature(),
LatexModelDataFetcher(
- applicationContext,
+ application,
model,
width,
height,
@@ -91,7 +91,7 @@ class MathBitmapModelLoader private constructor(
override fun handles(model: MathModel): Boolean = true
private class LatexModelDataFetcher(
- private val applicationContext: Context,
+ private val application: Application,
private val model: MathModel,
private val targetWidth: Int,
private val targetHeight: Int,
@@ -109,7 +109,7 @@ class MathBitmapModelLoader private constructor(
// creation can still happen in parallel, and those are the more expensive steps.
val span = withContext(CoroutineScope(blockingDispatcher).coroutineContext) {
MathExpressionSpan(
- model.rawLatex, model.lineHeight, applicationContext.assets, !model.useInlineRendering
+ model.rawLatex, model.lineHeight, application.assets, !model.useInlineRendering
).also { it.ensureDrawable() }
}
val renderableText = SpannableStringBuilder("\uFFFC").apply {
@@ -385,10 +385,10 @@ class MathBitmapModelLoader private constructor(
/** [ModelLoaderFactory] for creating new [MathBitmapModelLoader]s. */
class Factory(
- private val applicationContext: Context
+ private val application: Application
) : ModelLoaderFactory {
override fun build(factory: MultiModelLoaderFactory): ModelLoader {
- return MathBitmapModelLoader(applicationContext)
+ return MathBitmapModelLoader(application)
}
override fun teardown() {}
diff --git a/utility/src/test/java/org/oppia/android/util/data/DataProvidersTest.kt b/utility/src/test/java/org/oppia/android/util/data/DataProvidersTest.kt
index b7b4832a180..d4697e891b7 100644
--- a/utility/src/test/java/org/oppia/android/util/data/DataProvidersTest.kt
+++ b/utility/src/test/java/org/oppia/android/util/data/DataProvidersTest.kt
@@ -74,7 +74,7 @@ private const val COMBINED_STR_VALUE_02 = "I used to be indecisive. At least I t
class DataProvidersTest {
@field:[Rule JvmField] val mockitoRule: MockitoRule = MockitoJUnit.rule()
- @Inject lateinit var context: Context
+ @Inject lateinit var application: Application
@Inject lateinit var dataProviders: DataProviders
@Inject lateinit var asyncDataSubscriptionManager: AsyncDataSubscriptionManager
@Inject lateinit var fakeExceptionLogger: FakeExceptionLogger
@@ -111,7 +111,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_fakeDataProvider_noObserver_doesNotCallRetrieve() {
- val fakeDataProvider = object : DataProvider(context) {
+ val fakeDataProvider = object : DataProvider(application) {
var hasRetrieveBeenCalled = false
override fun getId(): Any = "fake_data_provider"
@@ -130,7 +130,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_fakeDataProvider_withObserver_callsRetrieve() {
- val fakeDataProvider = object : DataProvider(context) {
+ val fakeDataProvider = object : DataProvider(application) {
var hasRetrieveBeenCalled = false
override fun getId(): Any = "fake_data_provider"
@@ -149,7 +149,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_trivialDataProvider_withObserver_observerReceivesValue() {
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
override fun getId(): Any = "simple_data_provider"
override suspend fun retrieveData(): AsyncResult = AsyncResult.Success(123)
@@ -165,7 +165,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_dataProviderChanges_withObserver_observerReceivesUpdatedValue() {
var providerValue = 123
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
override fun getId(): Any = "simple_data_provider"
override suspend fun retrieveData(): AsyncResult = AsyncResult.Success(providerValue)
@@ -183,7 +183,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_providerChanges_withoutObserver_newObserver_newObserverReceivesValue() {
var providerValue = 123
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
override fun getId(): Any = "simple_data_provider"
override suspend fun retrieveData(): AsyncResult = AsyncResult.Success(providerValue)
@@ -203,7 +203,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_dataProviderNotified_sameValue_withObserver_observerNotCalledAgain() {
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
override fun getId(): Any = "simple_data_provider"
override suspend fun retrieveData(): AsyncResult = AsyncResult.Success(123)
@@ -225,7 +225,7 @@ class DataProvidersTest {
val providerOldResult = AsyncResult.Success(123)
testCoroutineDispatchers.advanceTimeBy(10)
val providerNewResult = AsyncResult.Success(456)
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
var callCount = 0
override fun getId(): Any = "simple_data_provider"
@@ -257,7 +257,7 @@ class DataProvidersTest {
@Test
fun testConvertToLiveData_dataProvider_providesPendingResultTwice_doesNotRedeliver() {
- val simpleDataProvider = object : DataProvider(context) {
+ val simpleDataProvider = object : DataProvider(application) {
override fun getId(): Any = "simple_data_provider"
// Return a new pending result for each call.
diff --git a/version.bzl b/version.bzl
index 51dba48bd38..06510b95a88 100644
--- a/version.bzl
+++ b/version.bzl
@@ -16,3 +16,5 @@ OPPIA_DEV_VERSION_CODE = 28
OPPIA_ALPHA_KITKAT_VERSION_CODE = 29
OPPIA_ALPHA_VERSION_CODE = 30
OPPIA_ALPHA_KENYA_VERSION_CODE = 31
+OPPIA_BETA_VERSION_CODE = 32
+OPPIA_GA_VERSION_CODE = 33