From 5f4be0289011b70ac4eb3064be2a34dbf2de367e Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 1 Jul 2022 17:58:03 -0700 Subject: [PATCH 01/19] Create dedicated alpha application component. This simplifies application component management significantly and allows individual build flavors to have their own unique module lists. --- BUILD.bazel | 3 +- app/BUILD.bazel | 57 ++------ app/src/main/AndroidManifest.xml | 2 +- ...ication.kt => AbstractOppiaApplication.kt} | 12 +- .../app/application/ApplicationComponent.kt | 100 +------------ .../app/application/ApplicationContext.kt | 7 - .../app/application/ApplicationModule.kt | 20 +-- .../ApplicationStartupListenerModule.kt | 1 - .../oppia/android/app/application/BUILD.bazel | 135 ++++++++++++++++++ .../alpha/AlphaApplicationComponent.kt | 104 ++++++++++++++ .../alpha/AlphaOppiaApplication.kt | 6 + .../android/app/application/alpha/BUILD.bazel | 26 ++++ .../android/app/application/dev/BUILD.bazel | 30 ++++ .../dev/DeveloperApplicationComponent.kt | 105 ++++++++++++++ .../dev/DeveloperOppiaApplication.kt | 8 ++ .../oppia/android/app/translation/BUILD.bazel | 2 + .../learneranalytics/BUILD.bazel | 12 ++ .../app/customview/interaction/BUILD.bazel | 6 + .../oppia/android/app/databinding/BUILD.bazel | 42 ++++++ .../mathexpressionparser/BUILD.bazel | 12 ++ .../android/app/player/state/BUILD.bazel | 5 + .../app/recyclerview/BindableAdapterTest.kt | 7 +- .../oppia/android/app/activity/BUILD.bazel | 6 + .../devoptionsitemviewmodel/BUILD.bazel | 6 + .../player/state/StateFragmentLocalTest.kt | 2 - .../android/app/testing/activity/BUILD.bazel | 5 + .../state/StateFragmentAccessibilityTest.kt | 2 - .../QuestionPlayerActivityLocalTest.kt | 2 - .../oppia/android/app/translation/BUILD.bazel | 10 ++ .../android/app/utility/math/BUILD.bazel | 5 + build_flavors.bzl | 22 ++- .../data/persistence/PersistentCacheStore.kt | 2 - .../oppia/android/domain/locale/BUILD.bazel | 4 +- instrumentation/src/java/AndroidManifest.xml | 2 +- .../instrumentation/application/BUILD.bazel | 8 +- .../application/OppiaTestApplication.kt | 43 ------ .../application/TestApplicationComponent.kt | 27 +--- .../application/TestOppiaApplication.kt | 6 + scripts/assets/maven_dependencies.textproto | 66 ++------- scripts/assets/test_file_exemptions.textproto | 8 +- .../scripts/build/TransformAndroidManifest.kt | 31 +++- .../oppia/android/testing/junit/BUILD.bazel | 15 ++ third_party/maven_install.json | 6 +- third_party/versions.bzl | 1 + 44 files changed, 653 insertions(+), 328 deletions(-) rename app/src/main/java/org/oppia/android/app/application/{OppiaApplication.kt => AbstractOppiaApplication.kt} (89%) delete mode 100644 app/src/main/java/org/oppia/android/app/application/ApplicationContext.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt delete mode 100644 instrumentation/src/java/org/oppia/android/instrumentation/application/OppiaTestApplication.kt create mode 100644 instrumentation/src/java/org/oppia/android/instrumentation/application/TestOppiaApplication.kt diff --git a/BUILD.bazel b/BUILD.bazel index 4dccfd87153..81250077b03 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -75,6 +75,7 @@ package_group( [ transform_android_manifest( name = "oppia_apk_%s_transformed_manifest" % apk_flavor_metadata["flavor"], + application_relative_qualified_class = ".app.application.dev.DeveloperOppiaApplication", build_flavor = apk_flavor_metadata["flavor"], input_file = "//app:src/main/AndroidManifest.xml", major_version = MAJOR_VERSION, @@ -108,7 +109,7 @@ package_group( }, multidex = apk_flavor_metadata["multidex"], deps = [ - "//app", + "//app/src/main/java/org/oppia/android/app/application/dev:developer_application", ], ) for apk_flavor_metadata in [ diff --git a/app/BUILD.bazel b/app/BUILD.bazel index 7c9941d90a8..0b239f4a9d2 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -630,9 +630,6 @@ kt_android_library( "//third_party:androidx_databinding_databinding-runtime", "//third_party:circularimageview_circular_image_view", "//utility/src/main/java/org/oppia/android/util/accessibility", - "//utility/src/main/java/org/oppia/android/util/caching:caching_prod_module", - "//utility/src/main/java/org/oppia/android/util/logging:prod_module", - "//utility/src/main/java/org/oppia/android/util/logging/firebase:prod_module", "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser", "//utility/src/main/java/org/oppia/android/util/parser/image:image_loader", "//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_annonations", @@ -668,11 +665,11 @@ kt_android_library( ":listeners", ":resources", "//app/src/main/java/org/oppia/android/app/shim:intent_factory_shim", + "//app/src/main/java/org/oppia/android/app/utility/datetime:date_time_util", + "//app/src/main/java/org/oppia/android/app/utility/math:math_expression_accessibility_util", "//app/src/main/java/org/oppia/android/app/viewmodel:observable_array_list", "//app/src/main/java/org/oppia/android/app/viewmodel:observable_view_model", "//app/src/main/java/org/oppia/android/app/viewmodel:view_model_provider", - "//app/src/main/java/org/oppia/android/app/utility/datetime:date_time_util", - "//app/src/main/java/org/oppia/android/app/utility/math:math_expression_accessibility_util", "//domain", "//domain/src/main/java/org/oppia/android/domain/audio:audio_player_controller", "//domain/src/main/java/org/oppia/android/domain/clipboard:clipboard_controller", @@ -684,11 +681,8 @@ kt_android_library( "//utility", "//utility/src/main/java/org/oppia/android/util/extensions:context_extensions", "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_event_logger", - "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module", "//utility/src/main/java/org/oppia/android/util/math:fraction_parser", - # TODO(#59): Remove 'debug_util_module' once we completely migrate to Bazel from Gradle as - # we can then directly exclude debug files from the build and thus won't be requiring this module. - "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module", + "//utility/src/main/java/org/oppia/android/util/networking:network_connection_debug_util", "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser", ], ) @@ -757,34 +751,11 @@ kt_android_library( "//app/src/main/java/org/oppia/android/app/fragment:injectable_dialog_fragment", "//app/src/main/java/org/oppia/android/app/fragment:injectable_fragment", "//app/src/main/java/org/oppia/android/app/shim:prod_modules", - "//app/src/main/java/org/oppia/android/app/translation:prod_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:prod_module", - "//domain/src/main/java/org/oppia/android/domain/classify:interactions_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput:algebraic_expression_input_rule_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/continueinteraction:continue_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput:drag_and_drop_sort_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput:fraction_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput:image_click_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput:item_selection_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput:math_equation_input_rule_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/multiplechoiceinput:multiple_choice_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/numberwithunits:number_with_units_rule_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/numericexpressioninput:numeric_expression_input_rule_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/numericinput:numeric_input_rule_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/ratioinput:ratio_input_module", - "//domain/src/main/java/org/oppia/android/domain/classify/rules/textinput:text_input_rule_module", - "//domain/src/main/java/org/oppia/android/domain/locale:locale_application_injector", - "//domain/src/main/java/org/oppia/android/domain/locale:locale_application_injector_provider", - "//domain/src/main/java/org/oppia/android/domain/onboarding:retriever_prod_module", + "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity", "//domain/src/main/java/org/oppia/android/domain/onboarding:state_controller", "//domain/src/main/java/org/oppia/android/domain/oppialogger:startup_listener", - "//domain/src/main/java/org/oppia/android/domain/oppialogger/exceptions:logger_module", - "//domain/src/main/java/org/oppia/android/domain/oppialogger/loguploader:worker_module", - "//model/src/main/proto:arguments_java_proto_lite", "//domain/src/main/java/org/oppia/android/domain/profile:profile_management_controller", - "//domain/src/main/java/org/oppia/android/domain/oppialogger/analytics:prod_module", - "//app/src/main/java/org/oppia/android/app/testing/activity:test_activity", + "//model/src/main/proto:arguments_java_proto_lite", "//third_party:androidx_databinding_databinding-adapters", "//third_party:androidx_databinding_databinding-common", "//third_party:androidx_databinding_databinding-runtime", @@ -794,26 +765,14 @@ kt_android_library( "//third_party:androidx_multidex_multidex", "//third_party:androidx_viewpager2_viewpager2", "//third_party:androidx_viewpager_viewpager", - "//third_party:androidx_work_work-runtime-ktx", "//third_party:com_caverock_androidsvg", "//third_party:com_google_android_flexbox_flexbox", "//third_party:javax_annotation_javax_annotation-api_jar", "//utility", - "//utility/src/main/java/org/oppia/android/util/accessibility:prod_module", - "//utility/src/main/java/org/oppia/android/util/caching:asset_prod_module", "//utility/src/main/java/org/oppia/android/util/extensions:bundle_extensions", - "//utility/src/main/java/org/oppia/android/util/locale:prod_module", - "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser_entity_type_module", "//utility/src/main/java/org/oppia/android/util/parser/image:image_loader", - "//utility/src/main/java/org/oppia/android/util/parser/image:glide_image_loader_module", - "//utility/src/main/java/org/oppia/android/util/parser/image:repository_glide_module", "//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_annonations", - "//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_module", - # TODO(#2432): Replace debug_module with prod_module when building the app in prod mode. - "//utility/src/main/java/org/oppia/android/util/networking:debug_module", - "//utility/src/main/java/org/oppia/android/util/logging:console_logger_injector_provider", "//utility/src/main/java/org/oppia/android/util/statusbar:status_bar_color", - "//utility/src/main/java/org/oppia/android/util/threading:dispatcher_injector_provider", ], ) @@ -858,6 +817,10 @@ TEST_DEPS = [ ":dagger", ":resources", ":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/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", @@ -923,7 +886,9 @@ TEST_DEPS = [ "//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/logging/firebase:debug_module", "//utility/src/main/java/org/oppia/android/util/math:math_expression_parser", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", "//utility/src/main/java/org/oppia/android/util/parser/html:custom_bullet_span", "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser", "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser_entity_type_module", diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6515cf4329a..89dc7903f41 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ ApplicationComponent.Builder +) : MultiDexApplication(), ActivityComponentFactory, ApplicationInjectorProvider, Configuration.Provider { + /** The root [ApplicationComponent]. */ private val component: ApplicationComponent by lazy { - DaggerApplicationComponent.builder() - .setApplication(this) - .build() + createComponentBuilder().setApplication(this).build() } override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent { diff --git a/app/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt index f08f475b263..1c92ad5c6c6 100644 --- a/app/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt @@ -3,116 +3,22 @@ package org.oppia.android.app.application import android.app.Application import androidx.work.Configuration import dagger.BindsInstance -import dagger.Component import org.oppia.android.app.activity.ActivityComponentImpl -import org.oppia.android.app.devoptions.DeveloperOptionsModule -import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule -import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule -import org.oppia.android.app.shim.IntentFactoryShimModule -import org.oppia.android.app.shim.ViewBindingShimModule -import org.oppia.android.app.topic.PracticeTabModule -import org.oppia.android.app.translation.ActivityRecreatorProdModule -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.HintsAndSolutionConfigModule -import org.oppia.android.domain.hintsandsolution.HintsAndSolutionDebugModule -import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule import org.oppia.android.domain.oppialogger.ApplicationStartupListener -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.exceptions.UncaughtExceptionLoggerModule -import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule -import org.oppia.android.domain.platformparameter.PlatformParameterModule -import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule -import org.oppia.android.domain.platformparameter.syncup.PlatformParameterSyncUpWorkerModule -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.util.accessibility.AccessibilityProdModule -import org.oppia.android.util.caching.AssetModule -import org.oppia.android.util.caching.CachingModule -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.DebugLogReportingModule -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.GlideImageLoaderModule -import org.oppia.android.util.parser.image.ImageParsingModule -import org.oppia.android.util.system.OppiaClockModule -import org.oppia.android.util.threading.DispatcherModule import javax.inject.Provider -import javax.inject.Singleton /** * Root Dagger component for the application. All application-scoped modules should be included in * this component. * - * At the time of building the app in prod mode - - * Remove: [DeveloperOptionsStarterModule], [DebugLogReportingModule], - * [NetworkConnectionUtilDebugModule], [HintsAndSolutionDebugModule] - * Add: [LogReportingModule], [NetworkConnectionUtilProdModule], [HintsAndSolutionProdModule] - * - * When building with Bazel, please also refer to instructions in app/BUILD.bazel. + * This component will be subclasses for specific contexts (such as test builds, or specific build + * flavors of the app). */ -@Singleton -@Component( - modules = [ - ApplicationModule::class, DispatcherModule::class, - LoggerModule::class, OppiaClockModule::class, - ContinueModule::class, FractionInputModule::class, - ItemSelectionInputModule::class, MultipleChoiceInputModule::class, - NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, - TextInputRuleModule::class, DragDropSortInputModule::class, - InteractionsModule::class, GcsResourceModule::class, - GlideImageLoaderModule::class, ImageParsingModule::class, - HtmlParserEntityTypeModule::class, CachingModule::class, - QuestionModule::class, DebugLogReportingModule::class, - AccessibilityProdModule::class, ImageClickInputModule::class, - LogStorageModule::class, IntentFactoryShimModule::class, - ViewBindingShimModule::class, PrimeTopicAssetsControllerModule::class, - ExpirationMetaDataRetrieverModule::class, RatioInputModule::class, - UncaughtExceptionLoggerModule::class, ApplicationStartupListenerModule::class, - LogUploadWorkerModule::class, WorkManagerConfigurationModule::class, - HintsAndSolutionConfigModule::class, HintsAndSolutionDebugModule::class, - FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, - PlatformParameterModule::class, PlatformParameterSingletonModule::class, - ExplorationStorageModule::class, DeveloperOptionsStarterModule::class, - DeveloperOptionsModule::class, PlatformParameterSyncUpWorkerModule::class, - NetworkConnectionUtilDebugModule::class, NetworkConfigProdModule::class, AssetModule::class, - LocaleProdModule::class, ActivityRecreatorProdModule::class, - NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, - MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - // TODO(#59): Remove this module once we completely migrate to Bazel from Gradle as we can then - // directly exclude debug files from the build and thus won't be requiring this module. - NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class - ] -) interface ApplicationComponent : ApplicationInjector { - @Component.Builder interface Builder { @BindsInstance fun setApplication(application: Application): Builder + fun build(): ApplicationComponent } diff --git a/app/src/main/java/org/oppia/android/app/application/ApplicationContext.kt b/app/src/main/java/org/oppia/android/app/application/ApplicationContext.kt deleted file mode 100644 index d4aa0324272..00000000000 --- a/app/src/main/java/org/oppia/android/app/application/ApplicationContext.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.oppia.android.app.application - -import javax.inject.Qualifier - -/** Qualifier for injecting the application context. */ -@Qualifier -annotation class ApplicationContext diff --git a/app/src/main/java/org/oppia/android/app/application/ApplicationModule.kt b/app/src/main/java/org/oppia/android/app/application/ApplicationModule.kt index 00954abdc7d..9cd0698c05b 100644 --- a/app/src/main/java/org/oppia/android/app/application/ApplicationModule.kt +++ b/app/src/main/java/org/oppia/android/app/application/ApplicationModule.kt @@ -2,25 +2,13 @@ package org.oppia.android.app.application import android.app.Application import android.content.Context +import dagger.Binds import dagger.Module -import dagger.Provides import org.oppia.android.app.activity.ActivityComponentImpl -import javax.inject.Singleton /** Provides core infrastructure needed to support all other dependencies in the app. */ @Module(subcomponents = [ActivityComponentImpl::class]) -class ApplicationModule { - @Provides - @Singleton - @ApplicationContext - fun provideApplicationContext(application: Application): Context { - return application - } - - // TODO(#59): Remove this provider once all modules have access to the @ApplicationContext qualifier. - @Provides - @Singleton - fun provideContext(@ApplicationContext context: Context): Context { - return context - } +interface ApplicationModule { + @Binds + fun provideContext(application: Application): Context } diff --git a/app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt b/app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt index 239e3b0e9f8..77287f080d3 100644 --- a/app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt +++ b/app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt @@ -7,7 +7,6 @@ import org.oppia.android.domain.oppialogger.ApplicationStartupListener /** Binds multiple dependencies that implement [ApplicationStartupListener] into a set. */ @Module interface ApplicationStartupListenerModule { - @Multibinds fun bindStartupListenerSet(): Set } diff --git a/app/src/main/java/org/oppia/android/app/application/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/BUILD.bazel new file mode 100644 index 00000000000..9b2927ff303 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/BUILD.bazel @@ -0,0 +1,135 @@ +""" +This package contains all of the top-level infrastructure for the application entrypoint and root +Dagger graph. + +Specific application implementations can be found in subpackages. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "abstract_application", + srcs = [ + "AbstractOppiaApplication.kt", + ], + visibility = [ + "//app/src/main/java/org/oppia/android/app/application:__subpackages__", + "//instrumentation/src/java/org/oppia/android/instrumentation/application:__pkg__", + ], + deps = [ + ":application_component", + ":application_injector", + ":application_injector_provider", + "//domain/src/main/java/org/oppia/android/domain/oppialogger:startup_listener", + "//third_party:androidx_work_work-runtime-ktx", + "//third_party:com_google_firebase_firebase-common", + ], +) + +kt_android_library( + name = "application_component", + srcs = [ + "ApplicationComponent.kt", + ], + visibility = [ + "//:oppia_testing_visibility", + "//app/src/main/java/org/oppia/android/app/application:__subpackages__", + ], + deps = [ + ":application_injector", + "//app", + "//domain/src/main/java/org/oppia/android/domain/oppialogger:startup_listener", + "//third_party:androidx_work_work-runtime-ktx", + ], +) + +kt_android_library( + name = "application_injector", + srcs = [ + "ApplicationInjector.kt", + ], + visibility = ["//:oppia_testing_visibility"], + deps = [ + "//app/src/main/java/org/oppia/android/app/translation:app_language_application_injector", + "//domain/src/main/java/org/oppia/android/domain/locale:locale_application_injector", + "//utility/src/main/java/org/oppia/android/util/logging:console_logger_injector", + "//utility/src/main/java/org/oppia/android/util/system:oppia_clock_injector", + "//utility/src/main/java/org/oppia/android/util/threading:dispatcher_injector", + ], +) + +kt_android_library( + name = "application_injector_provider", + srcs = [ + "ApplicationInjectorProvider.kt", + ], + visibility = ["//:oppia_testing_visibility"], + deps = [ + ":application_injector", + "//app/src/main/java/org/oppia/android/app/translation:app_language_application_injector_provider", + "//domain/src/main/java/org/oppia/android/domain/locale:locale_application_injector_provider", + "//utility/src/main/java/org/oppia/android/util/logging:console_logger_injector_provider", + "//utility/src/main/java/org/oppia/android/util/system:oppia_clock_injector_provider", + "//utility/src/main/java/org/oppia/android/util/threading:dispatcher_injector_provider", + ], +) + +kt_android_library( + name = "modules", + srcs = [ + "ApplicationModule.kt", + "ApplicationStartupListenerModule.kt", + ], + deps = [ + ":dagger", + "//app", + "//domain/src/main/java/org/oppia/android/domain/oppialogger:startup_listener", + ], +) + +# TODO(#1720): Remove this once the list can be automatically determined from the build graph. +android_library( + name = "common_application_modules", + visibility = [ + "//:oppia_testing_visibility", + "//app/src/main/java/org/oppia/android/app/application:__subpackages__", + ], + exports = [ + ":modules", + "//app/src/main/java/org/oppia/android/app/translation:prod_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:prod_module", + "//domain/src/main/java/org/oppia/android/domain/classify:interactions_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/algebraicexpressioninput:algebraic_expression_input_rule_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/continueinteraction:continue_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/dragAndDropSortInput:drag_and_drop_sort_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/fractioninput:fraction_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/imageClickInput:image_click_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/itemselectioninput:item_selection_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/mathequationinput:math_equation_input_rule_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/multiplechoiceinput:multiple_choice_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/numberwithunits:number_with_units_rule_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/numericexpressioninput:numeric_expression_input_rule_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/numericinput:numeric_input_rule_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/ratioinput:ratio_input_module", + "//domain/src/main/java/org/oppia/android/domain/classify/rules/textinput:text_input_rule_module", + "//domain/src/main/java/org/oppia/android/domain/onboarding:retriever_prod_module", + "//domain/src/main/java/org/oppia/android/domain/oppialogger/analytics:prod_module", + "//domain/src/main/java/org/oppia/android/domain/oppialogger/exceptions:logger_module", + "//domain/src/main/java/org/oppia/android/domain/oppialogger/loguploader:worker_module", + "//utility/src/main/java/org/oppia/android/util/accessibility:prod_module", + "//utility/src/main/java/org/oppia/android/util/caching:asset_prod_module", + "//utility/src/main/java/org/oppia/android/util/caching:caching_prod_module", + "//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/logging/firebase:prod_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module", + "//utility/src/main/java/org/oppia/android/util/parser/html:html_parser_entity_type_module", + "//utility/src/main/java/org/oppia/android/util/parser/image:glide_image_loader_module", + "//utility/src/main/java/org/oppia/android/util/parser/image:image_parsing_module", + "//utility/src/main/java/org/oppia/android/util/parser/image:repository_glide_module", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt new file mode 100644 index 00000000000..8aa2c20f6bd --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt @@ -0,0 +1,104 @@ +package org.oppia.android.app.application.alpha + +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.devoptions.DeveloperOptionsModule +import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule +import org.oppia.android.app.shim.IntentFactoryShimModule +import org.oppia.android.app.shim.ViewBindingShimModule +import org.oppia.android.app.topic.PracticeTabModule +import org.oppia.android.app.translation.ActivityRecreatorProdModule +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.HintsAndSolutionConfigModule +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.exceptions.UncaughtExceptionLoggerModule +import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +import org.oppia.android.domain.platformparameter.PlatformParameterModule +import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule +import org.oppia.android.domain.platformparameter.syncup.PlatformParameterSyncUpWorkerModule +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.util.accessibility.AccessibilityProdModule +import org.oppia.android.util.caching.AssetModule +import org.oppia.android.util.caching.CachingModule +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.logging.firebase.LogReportingModule +import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule +import org.oppia.android.util.networking.NetworkConnectionUtilProdModule +import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule +import org.oppia.android.util.parser.image.GlideImageLoaderModule +import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.system.OppiaClockModule +import org.oppia.android.util.threading.DispatcherModule +import javax.inject.Singleton + +/** + * Root Dagger component for alpha versions of the application. + * + * All application-scoped modules should be included in this component. + */ +@Singleton +@Component( + modules = [ + ApplicationModule::class, DispatcherModule::class, LoggerModule::class, OppiaClockModule::class, + ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, + MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, + NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, + InteractionsModule::class, GcsResourceModule::class, GlideImageLoaderModule::class, + ImageParsingModule::class, HtmlParserEntityTypeModule::class, CachingModule::class, + QuestionModule::class, AccessibilityProdModule::class, ImageClickInputModule::class, + LogStorageModule::class, IntentFactoryShimModule::class, ViewBindingShimModule::class, + PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, + RatioInputModule::class, UncaughtExceptionLoggerModule::class, + ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, + WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, + FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, + PlatformParameterModule::class, PlatformParameterSingletonModule::class, + ExplorationStorageModule::class, DeveloperOptionsModule::class, + PlatformParameterSyncUpWorkerModule::class, NetworkConfigProdModule::class, AssetModule::class, + LocaleProdModule::class, ActivityRecreatorProdModule::class, + NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, + MathEquationInputModule::class, SplitScreenInteractionModule::class, + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, + NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, + SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, + HintsAndSolutionProdModule::class + ] +) +interface AlphaApplicationComponent : ApplicationComponent { + /** + * The [ApplicationComponent.Builder] for this component. Dagger will generate an implementation + * of this builder for use. + */ + @Component.Builder + interface Builder : ApplicationComponent.Builder { + override fun build(): AlphaApplicationComponent + } +} diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt new file mode 100644 index 00000000000..c37a271e8e1 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt @@ -0,0 +1,6 @@ +package org.oppia.android.app.application.alpha + +import org.oppia.android.app.application.AbstractOppiaApplication + +/** The root [AbstractOppiaApplication] for alpha builds of the Oppia app. */ +class AlphaOppiaApplication : AbstractOppiaApplication(DaggerAlphaApplicationComponent::builder) diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel new file mode 100644 index 00000000000..c0df9390ced --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel @@ -0,0 +1,26 @@ +""" +This package contains the root application definitions for alpha builds of the app. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "alpha_application", + srcs = [ + "AlphaApplicationComponent.kt", + "AlphaOppiaApplication.kt", + ], + visibility = ["//:oppia_binary_visibility"], + deps = [ + ":dagger", + "//app", + "//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", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:prod_module", + "//utility/src/main/java/org/oppia/android/util/networking:prod_module", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel new file mode 100644 index 00000000000..0b087d04fb9 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel @@ -0,0 +1,30 @@ +""" +This package contains the root application definitions for developer builds of the app. + +Note that this will be the application configuration used for Gradle builds of the app. For Bazel, +there are specially defined top-level build flavors which will select their corresponding +application configuration. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "developer_application", + srcs = [ + "DeveloperApplicationComponent.kt", + "DeveloperOppiaApplication.kt", + ], + visibility = ["//:oppia_binary_visibility"], + deps = [ + ":dagger", + "//app", + "//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", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt new file mode 100644 index 00000000000..aee630025d2 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt @@ -0,0 +1,105 @@ +package org.oppia.android.app.application.dev + +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.devoptions.DeveloperOptionsModule +import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule +import org.oppia.android.app.shim.IntentFactoryShimModule +import org.oppia.android.app.shim.ViewBindingShimModule +import org.oppia.android.app.topic.PracticeTabModule +import org.oppia.android.app.translation.ActivityRecreatorProdModule +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.HintsAndSolutionConfigModule +import org.oppia.android.domain.hintsandsolution.HintsAndSolutionDebugModule +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.exceptions.UncaughtExceptionLoggerModule +import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +import org.oppia.android.domain.platformparameter.PlatformParameterModule +import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule +import org.oppia.android.domain.platformparameter.syncup.PlatformParameterSyncUpWorkerModule +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.util.accessibility.AccessibilityProdModule +import org.oppia.android.util.caching.AssetModule +import org.oppia.android.util.caching.CachingModule +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.DebugLogReportingModule +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.GlideImageLoaderModule +import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.system.OppiaClockModule +import org.oppia.android.util.threading.DispatcherModule +import javax.inject.Singleton + +/** + * Root Dagger component for developer versions of the application. + * + * All application-scoped modules should be included in this component. + */ +@Singleton +@Component( + modules = [ + ApplicationModule::class, DispatcherModule::class, LoggerModule::class, OppiaClockModule::class, + ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, + MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, + NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, + InteractionsModule::class, GcsResourceModule::class, GlideImageLoaderModule::class, + ImageParsingModule::class, HtmlParserEntityTypeModule::class, CachingModule::class, + QuestionModule::class, DebugLogReportingModule::class, AccessibilityProdModule::class, + ImageClickInputModule::class, LogStorageModule::class, IntentFactoryShimModule::class, + ViewBindingShimModule::class, PrimeTopicAssetsControllerModule::class, + ExpirationMetaDataRetrieverModule::class, RatioInputModule::class, + UncaughtExceptionLoggerModule::class, ApplicationStartupListenerModule::class, + LogUploadWorkerModule::class, WorkManagerConfigurationModule::class, + HintsAndSolutionConfigModule::class, HintsAndSolutionDebugModule::class, + FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, + PlatformParameterModule::class, PlatformParameterSingletonModule::class, + ExplorationStorageModule::class, DeveloperOptionsStarterModule::class, + DeveloperOptionsModule::class, PlatformParameterSyncUpWorkerModule::class, + NetworkConnectionUtilDebugModule::class, NetworkConfigProdModule::class, AssetModule::class, + LocaleProdModule::class, ActivityRecreatorProdModule::class, + NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, + MathEquationInputModule::class, SplitScreenInteractionModule::class, + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, + NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class + ] +) +interface DeveloperApplicationComponent : ApplicationComponent { + /** + * The [ApplicationComponent.Builder] for this component. Dagger will generate an implementation + * of this builder for use. + */ + @Component.Builder + interface Builder : ApplicationComponent.Builder { + override fun build(): DeveloperApplicationComponent + } +} diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt new file mode 100644 index 00000000000..495a21d7a3e --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt @@ -0,0 +1,8 @@ +package org.oppia.android.app.application.dev + +import org.oppia.android.app.application.AbstractOppiaApplication + +/** The root [AbstractOppiaApplication] for developer builds of the Oppia app. */ +class DeveloperOppiaApplication : AbstractOppiaApplication( + DaggerDeveloperApplicationComponent::builder +) diff --git a/app/src/main/java/org/oppia/android/app/translation/BUILD.bazel b/app/src/main/java/org/oppia/android/app/translation/BUILD.bazel index 3fb8eb46d02..e5bc20b3d81 100644 --- a/app/src/main/java/org/oppia/android/app/translation/BUILD.bazel +++ b/app/src/main/java/org/oppia/android/app/translation/BUILD.bazel @@ -120,6 +120,7 @@ kt_android_library( ], visibility = [ "//app/src/main/java/org/oppia/android/app/activity:__pkg__", + "//app/src/main/java/org/oppia/android/app/application:__pkg__", ], deps = [ ":app_language_locale_handler", @@ -133,6 +134,7 @@ kt_android_library( ], visibility = [ "//app/src/main/java/org/oppia/android/app/activity:__pkg__", + "//app/src/main/java/org/oppia/android/app/application:__pkg__", ], deps = [ ":app_language_application_injector", 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 a62f39766e0..7b28b53da4a 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 @@ -10,6 +10,10 @@ app_test( ":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", "//testing", "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule", @@ -24,6 +28,8 @@ app_test( "//utility/src/main/java/org/oppia/android/util/accessibility:test_module", "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_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", ], ) @@ -35,6 +41,10 @@ app_test( ":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", "//testing", "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule", @@ -49,6 +59,8 @@ app_test( "//utility/src/main/java/org/oppia/android/util/accessibility:test_module", "//utility/src/main/java/org/oppia/android/util/caching/testing:caching_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", ], ) 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 f63fd4d52d8..d7571e7f1ac 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 @@ -13,6 +13,10 @@ app_test( 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/translation/testing:test_module", "//testing", "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action", @@ -30,6 +34,8 @@ app_test( "//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/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], ) 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 1279ddff613..e6c820172aa 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 @@ -73,6 +73,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -89,6 +93,8 @@ oppia_android_test( "//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/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", ], ) @@ -103,6 +109,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -119,6 +129,8 @@ oppia_android_test( "//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/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", ], ) @@ -133,6 +145,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -149,6 +165,8 @@ oppia_android_test( "//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/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", ], ) @@ -163,6 +181,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -179,6 +201,8 @@ oppia_android_test( "//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/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", ], ) @@ -193,6 +217,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -209,6 +237,8 @@ oppia_android_test( "//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/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", ], ) @@ -223,6 +253,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -239,6 +273,8 @@ oppia_android_test( "//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/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", ], ) @@ -253,6 +289,10 @@ oppia_android_test( "//app", "//app:test_deps", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//testing", @@ -269,6 +309,8 @@ oppia_android_test( "//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/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", ], ) 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 6329deeae6f..b0d8810bf89 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 @@ -13,6 +13,10 @@ app_test( 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/translation/testing:test_module", "//testing", "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action", @@ -27,6 +31,8 @@ app_test( "//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/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], ) @@ -37,6 +43,10 @@ app_test( 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/testing/activity:test_activity", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//testing", @@ -52,6 +62,8 @@ app_test( "//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/testing:caching_test_module", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:debug_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], ) 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 631bf8ec398..457225f4fe4 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 @@ -14,6 +14,10 @@ app_test( ":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", @@ -38,6 +42,7 @@ app_test( "//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", ], 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 a207a2e9a59..a0b1d3577a3 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 @@ -34,7 +34,6 @@ import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.ActivityIntentFactoriesModule import org.oppia.android.app.activity.ActivityScope import org.oppia.android.app.application.ApplicationComponent -import org.oppia.android.app.application.ApplicationContext import org.oppia.android.app.application.ApplicationInjector import org.oppia.android.app.application.ApplicationInjectorProvider import org.oppia.android.app.application.ApplicationStartupListenerModule @@ -732,11 +731,7 @@ class BindableAdapterTest { @Module(subcomponents = [TestActivityComponent::class]) interface TestApplicationModule { @Binds - @ApplicationContext - fun provideApplicationContext(application: Application): Context - - @Binds - fun provideContext(@ApplicationContext context: Context): Context + fun provideContext(application: Application): Context // Bridge the test & original ActivityComponent builders to properly hook up the :replacement // test subcomponent. 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 1b94e416f2b..a3c4e616851 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 @@ -15,6 +15,10 @@ oppia_android_test( ":dagger", "//app", "//app/src/main/java/org/oppia/android/app/activity:activity_intent_factories_shim", + "//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", "//domain", "//domain/src/main/java/org/oppia/android/domain/classify:interactions_module", @@ -33,6 +37,8 @@ oppia_android_test( "//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/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", ], ) diff --git a/app/src/test/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/BUILD.bazel b/app/src/test/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/BUILD.bazel index 5f9ce1e46d2..55d5000dbd8 100644 --- a/app/src/test/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/BUILD.bazel +++ b/app/src/test/java/org/oppia/android/app/devoptions/devoptionsitemviewmodel/BUILD.bazel @@ -12,9 +12,15 @@ oppia_android_test( test_manifest = "//app:test_manifest", deps = [ "//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", "//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/logging/firebase:debug_module", + "//utility/src/main/java/org/oppia/android/util/networking:debug_module", ], ) 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 4fe37db7c6f..7e5c339ef1b 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 @@ -54,7 +54,6 @@ 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.ApplicationContext import org.oppia.android.app.application.ApplicationInjector import org.oppia.android.app.application.ApplicationInjectorProvider import org.oppia.android.app.application.ApplicationModule @@ -191,7 +190,6 @@ class StateFragmentLocalTest { lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject - @field:ApplicationContext lateinit var context: Context @Inject 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 0b005e08fb7..e56a257b618 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 @@ -14,6 +14,10 @@ oppia_android_test( 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/testing/activity:test_activity", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//domain", @@ -35,6 +39,7 @@ oppia_android_test( "//utility/src/main/java/org/oppia/android/util/locale:prod_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", ], 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 24565f44dd7..8c22af7c2d9 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 @@ -20,7 +20,6 @@ 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.ApplicationContext import org.oppia.android.app.application.ApplicationInjector import org.oppia.android.app.application.ApplicationInjectorProvider import org.oppia.android.app.application.ApplicationModule @@ -108,7 +107,6 @@ class StateFragmentAccessibilityTest { lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject - @field:ApplicationContext lateinit var context: Context @Inject 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 3af85d95b99..35862612657 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 @@ -31,7 +31,6 @@ 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.ApplicationContext import org.oppia.android.app.application.ApplicationInjector import org.oppia.android.app.application.ApplicationInjectorProvider import org.oppia.android.app.application.ApplicationModule @@ -136,7 +135,6 @@ class QuestionPlayerActivityLocalTest { lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject - @field:ApplicationContext lateinit var context: Context @Inject 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 b0e7a26150e..5777d66c61e 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 @@ -49,6 +49,10 @@ oppia_android_test( 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/translation:app_language_locale_handler", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//domain", @@ -75,6 +79,7 @@ oppia_android_test( "//utility/src/main/java/org/oppia/android/util/locale/testing:test_module", "//utility/src/main/java/org/oppia/android/util/locale/testing:test_oppia_bidi_formatter", "//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", "//utility/src/main/java/org/oppia/android/util/system:prod_module", @@ -90,6 +95,10 @@ oppia_android_test( 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/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", @@ -113,6 +122,7 @@ oppia_android_test( "//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", "//utility/src/main/java/org/oppia/android/util/system:prod_module", 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 3f5ff80c424..b50532b834d 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 @@ -14,6 +14,10 @@ oppia_android_test( 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/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", @@ -39,6 +43,7 @@ oppia_android_test( "//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/math:math_expression_parser", "//utility/src/main/java/org/oppia/android/util/networking:debug_module", "//utility/src/main/java/org/oppia/android/util/networking:debug_util_module", diff --git a/build_flavors.bzl b/build_flavors.bzl index a42b3d916c3..e5bf54adb2f 100644 --- a/build_flavors.bzl +++ b/build_flavors.bzl @@ -47,9 +47,10 @@ _FLAVOR_METADATA = { "proguard_specs": [], # Developer builds are not optimized. "production_release": False, "deps": [ - "//app", + "//app/src/main/java/org/oppia/android/app/application/dev:developer_application", ], "version_code": OPPIA_DEV_VERSION_CODE, + "application_class": ".app.application.dev.DeveloperOppiaApplication", }, "dev_kitkat": { "manifest": "//app:src/main/AndroidManifest.xml", @@ -60,9 +61,10 @@ _FLAVOR_METADATA = { "proguard_specs": [], # Developer builds are not optimized. "production_release": False, "deps": [ - "//app", + "//app/src/main/java/org/oppia/android/app/application/dev:developer_application", ], "version_code": OPPIA_DEV_KITKAT_VERSION_CODE, + "application_class": ".app.application.dev.DeveloperOppiaApplication", }, "alpha": { "manifest": "//app:src/main/AndroidManifest.xml", @@ -72,9 +74,10 @@ _FLAVOR_METADATA = { "proguard_specs": _PRODUCTION_PROGUARD_SPECS, "production_release": True, "deps": [ - "//app", + "//app/src/main/java/org/oppia/android/app/application/alpha:alpha_application", ], "version_code": OPPIA_ALPHA_VERSION_CODE, + "application_class": ".app.application.alpha.AlphaOppiaApplication", }, "alpha_kitkat": { "manifest": "//app:src/main/AndroidManifest.xml", @@ -85,9 +88,10 @@ _FLAVOR_METADATA = { "proguard_specs": [], # TODO(#3886): Re-add Proguard support to alpha_kitkat. "production_release": True, "deps": [ - "//app", + "//app/src/main/java/org/oppia/android/app/application/alpha:alpha_application", ], "version_code": OPPIA_ALPHA_KITKAT_VERSION_CODE, + "application_class": ".app.application.alpha.AlphaOppiaApplication", }, } @@ -99,6 +103,7 @@ def _transform_android_manifest_impl(ctx): major_version = ctx.attr.major_version minor_version = ctx.attr.minor_version version_code = ctx.attr.version_code + application_relative_qualified_class = ctx.attr.application_relative_qualified_class # See corresponding transformation script for details on the passed arguments. arguments = [ @@ -109,6 +114,7 @@ def _transform_android_manifest_impl(ctx): "%s" % major_version, "%s" % minor_version, "%s" % version_code, + "%s" % application_relative_qualified_class, "origin/develop", # The base branch for computing the version name. ] @@ -144,6 +150,7 @@ _transform_android_manifest = rule( "major_version": attr.int(mandatory = True), "minor_version": attr.int(mandatory = True), "version_code": attr.int(mandatory = True), + "application_relative_qualified_class": attr.string(mandatory = True), "_transform_android_manifest_tool": attr.label( executable = True, cfg = "host", @@ -160,7 +167,8 @@ def transform_android_manifest( build_flavor, major_version, minor_version, - version_code): + version_code, + application_relative_qualified_class): """ Generates a new transformation of the specified AndroidManifest.xml. @@ -177,6 +185,8 @@ def transform_android_manifest( major_version: int. The major version of the app. minor_version: int. The minor version of the app. version_code: int. The version code of this flavor of the app. + application_relative_qualified_class: String. The relatively qualified main application + class of the app for this build flavor. """ _transform_android_manifest( name = name, @@ -187,6 +197,7 @@ def transform_android_manifest( major_version = major_version, minor_version = minor_version, version_code = version_code, + application_relative_qualified_class = application_relative_qualified_class, ) def define_oppia_aab_binary_flavor(flavor): @@ -206,6 +217,7 @@ def define_oppia_aab_binary_flavor(flavor): """ transform_android_manifest( name = "oppia_%s_transformed_manifest" % flavor, + application_relative_qualified_class = _FLAVOR_METADATA[flavor]["application_class"], input_file = _FLAVOR_METADATA[flavor]["manifest"], output_file = "AndroidManifest_transformed_%s.xml" % flavor, build_flavor = flavor, 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 b1c131bc498..daacb9b8822 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 @@ -306,8 +306,6 @@ class PersistentCacheStore private constructor( private data class CachePayload(val state: CacheState, val value: T) - // TODO(#59): Use @ApplicationContext instead of Context once package dependencies allow for - // cross-module circular ependencies. Currently, the data module cannot depend on the app module. /** * 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. diff --git a/domain/src/main/java/org/oppia/android/domain/locale/BUILD.bazel b/domain/src/main/java/org/oppia/android/domain/locale/BUILD.bazel index a62092b664e..392a52b317f 100644 --- a/domain/src/main/java/org/oppia/android/domain/locale/BUILD.bazel +++ b/domain/src/main/java/org/oppia/android/domain/locale/BUILD.bazel @@ -92,7 +92,7 @@ kt_android_library( "LocaleApplicationInjector.kt", ], visibility = [ - "//app:__pkg__", + "//app/src/main/java/org/oppia/android/app/application:__pkg__", "//testing/src/main/java/org/oppia/android/testing/junit:__pkg__", ], deps = [ @@ -106,7 +106,7 @@ kt_android_library( "LocaleApplicationInjectorProvider.kt", ], visibility = [ - "//app:__pkg__", + "//app/src/main/java/org/oppia/android/app/application:__pkg__", "//testing/src/main/java/org/oppia/android/testing/junit:__pkg__", ], deps = [ diff --git a/instrumentation/src/java/AndroidManifest.xml b/instrumentation/src/java/AndroidManifest.xml index 847486b0240..3db0b5f7c51 100644 --- a/instrumentation/src/java/AndroidManifest.xml +++ b/instrumentation/src/java/AndroidManifest.xml @@ -7,7 +7,7 @@ - - fun getApplicationStartupListeners(): Set - - fun getWorkManagerConfiguration(): Configuration } diff --git a/instrumentation/src/java/org/oppia/android/instrumentation/application/TestOppiaApplication.kt b/instrumentation/src/java/org/oppia/android/instrumentation/application/TestOppiaApplication.kt new file mode 100644 index 00000000000..9fbe3666675 --- /dev/null +++ b/instrumentation/src/java/org/oppia/android/instrumentation/application/TestOppiaApplication.kt @@ -0,0 +1,6 @@ +package org.oppia.android.instrumentation.application + +import org.oppia.android.app.application.AbstractOppiaApplication + +/** The root [Application] of the all end-to-end test apps. */ +class TestOppiaApplication : AbstractOppiaApplication(DaggerTestApplicationComponent::builder) diff --git a/scripts/assets/maven_dependencies.textproto b/scripts/assets/maven_dependencies.textproto index 63b0bca6b08..ddb692a40f3 100644 --- a/scripts/assets/maven_dependencies.textproto +++ b/scripts/assets/maven_dependencies.textproto @@ -503,61 +503,6 @@ maven_dependency { } } } -maven_dependency { - artifact_name: "androidx.annotation:annotation-experimental:1.0.0" - artifact_version: "1.0.0" - license { - license_name: "The Apache Software License, Version 2.0" - original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" - scrapable_link { - url: "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } -} -maven_dependency { - artifact_name: "androidx.cardview:cardview:1.0.0" - artifact_version: "1.0.0" - license { - license_name: "The Apache Software License, Version 2.0" - original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" - scrapable_link { - url: "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } -} -maven_dependency { - artifact_name: "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - artifact_version: "1.1.0" - license { - license_name: "The Apache Software License, Version 2.0" - original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" - scrapable_link { - url: "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } -} -maven_dependency { - artifact_name: "androidx.transition:transition:1.2.0" - artifact_version: "1.2.0" - license { - license_name: "The Apache Software License, Version 2.0" - original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" - scrapable_link { - url: "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } -} -maven_dependency { - artifact_name: "androidx.vectordrawable:vectordrawable:1.1.0" - artifact_version: "1.1.0" - license { - license_name: "The Apache Software License, Version 2.0" - original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" - scrapable_link { - url: "https://www.apache.org/licenses/LICENSE-2.0.txt" - } - } -} maven_dependency { artifact_name: "com.google.android.material:material:1.3.0" artifact_version: "1.3.0" @@ -633,6 +578,17 @@ maven_dependency { } } } +maven_dependency { + artifact_name: "com.google.firebase:firebase-common:19.3.0" + artifact_version: "19.3.0" + license { + license_name: "The Apache Software License, Version 2.0" + original_link: "https://www.apache.org/licenses/LICENSE-2.0.txt" + scrapable_link { + url: "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } +} maven_dependency { artifact_name: "com.google.firebase:firebase-crashlytics:17.1.1" artifact_version: "17.1.1" diff --git a/scripts/assets/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto index ad6b4398d00..71a0bc61c76 100644 --- a/scripts/assets/test_file_exemptions.textproto +++ b/scripts/assets/test_file_exemptions.textproto @@ -38,6 +38,7 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/administratorcontro exempted_file_path: "app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileLearnerIdItemViewModel.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileListViewModel.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/SyncStatusItemViewModel.kt" +exempted_file_path: "app/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ActivityComponentFactory.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationContext.kt" @@ -45,7 +46,10 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/application/Applica exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationInjectorProvider.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationModule.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt" -exempted_file_path: "app/src/main/java/org/oppia/android/app/application/OppiaApplication.kt" +exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt" +exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.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/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" @@ -646,8 +650,8 @@ exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/JsonExte exempted_file_path: "domain/src/main/java/org/oppia/android/domain/util/WorkDataExtensions.kt" exempted_file_path: "domain/src/main/java/org/oppia/android/domain/workmanager/WorkManagerConfigurationModule.kt" exempted_file_path: "domain/src/test/java/org/oppia/android/domain/classify/InteractionObjectTestBuilder.kt" -exempted_file_path: "instrumentation/src/java/org/oppia/android/instrumentation/application/OppiaTestApplication.kt" exempted_file_path: "instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt" +exempted_file_path: "instrumentation/src/java/org/oppia/android/instrumentation/application/TestOppiaApplication.kt" exempted_file_path: "instrumentation/src/java/org/oppia/android/instrumentation/testing/EndToEndTestHelper.kt" exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandExecutor.kt" exempted_file_path: "scripts/src/java/org/oppia/android/scripts/common/CommandResult.kt" diff --git a/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt b/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt index fc5ec90d7e0..51732748d68 100644 --- a/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt +++ b/scripts/src/java/org/oppia/android/scripts/build/TransformAndroidManifest.kt @@ -2,6 +2,7 @@ package org.oppia.android.scripts.build import org.oppia.android.scripts.common.GitClient import org.w3c.dom.Document +import org.w3c.dom.NodeList import java.io.File import java.io.StringWriter import javax.xml.parsers.DocumentBuilderFactory @@ -14,7 +15,7 @@ private const val USAGE_STRING = " " + " " + " " + - "" + " " /** * The main entrypoint for transforming an AndroidManifest to include both a version code and @@ -38,6 +39,7 @@ private const val USAGE_STRING = * \\ * \\ * \\ + * \\ * * * Arguments: @@ -54,10 +56,10 @@ private const val USAGE_STRING = * Example: * bazel run //scripts:transform_android_manifest -- $(pwd) \\ * $(pwd)/app/src/main/AndroidManifest.xml $(pwd)/TransformedAndroidManifest.xml alpha 0 6 6 \\ - * origin/develop + * .app.application.alpha.AlphaOppiaApplication origin/develop */ fun main(args: Array) { - check(args.size >= 8) { USAGE_STRING } + check(args.size >= 9) { USAGE_STRING } val repoRoot = File(args[0]).also { if (!it.exists()) error("File doesn't exist: ${args[0]}") } TransformAndroidManifest( @@ -72,7 +74,8 @@ fun main(args: Array) { majorVersion = args[4].toIntOrNull() ?: error(USAGE_STRING), minorVersion = args[5].toIntOrNull() ?: error(USAGE_STRING), versionCode = args[6].toIntOrNull() ?: error(USAGE_STRING), - baseDevelopBranchReference = args[7] + relativelyQualifiedApplicationClass = args[7], + baseDevelopBranchReference = args[8] ).generateAndOutputNewManifest() } @@ -84,6 +87,7 @@ private class TransformAndroidManifest( private val majorVersion: Int, private val minorVersion: Int, private val versionCode: Int, + private val relativelyQualifiedApplicationClass: String, private val baseDevelopBranchReference: String ) { private val gitClient by lazy { @@ -107,11 +111,26 @@ private class TransformAndroidManifest( buildFlavor, majorVersion, minorVersion, commitHash = gitClient.branchMergeBase ) } + val applicationNameAttribute = manifestDocument.createAttribute("android:name").apply { + value = relativelyQualifiedApplicationClass + } + val replaceNameAttribute = manifestDocument.createAttribute("tools:replace").apply { + // Other manifests may define duplicate names. Make sure the manifest merger knows to + // prioritize this name. + value = "android:name" + } val manifestNode = manifestDocument.childNodes.item(0) manifestNode.attributes.apply { setNamedItem(versionCodeAttribute) setNamedItem(versionNameAttribute) } + val applicationNode = + manifestNode.childNodes.asSequence().find { it.nodeName == "application" } + ?: error("Failed to find an 'application' tag in manifest.") + applicationNode.attributes.apply { + setNamedItem(applicationNameAttribute) + setNamedItem(replaceNameAttribute) + } // Output the new transformed manifest. outputManifestFile.writeText(manifestDocument.toSource()) @@ -135,4 +154,8 @@ private class TransformAndroidManifest( transformer.transform(DOMSource(this@toSource), StreamResult(this@apply)) }.toString() } + + private companion object { + private fun NodeList.asSequence() = (0 until length).asSequence().map { item(it) } + } } 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 afae4ea110d..6dcc99d23a8 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 @@ -14,6 +14,10 @@ oppia_android_test( 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/translation:app_language_locale_handler", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//domain", @@ -48,6 +52,7 @@ oppia_android_test( "//utility/src/main/java/org/oppia/android/util/locale:prod_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", ], @@ -62,6 +67,10 @@ oppia_android_test( 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/translation:app_language_locale_handler", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//domain", @@ -95,6 +104,7 @@ oppia_android_test( "//utility/src/main/java/org/oppia/android/util/locale:prod_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", ], @@ -109,6 +119,10 @@ oppia_android_test( 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/translation:app_language_locale_handler", "//app/src/main/java/org/oppia/android/app/translation/testing:test_module", "//domain", @@ -143,6 +157,7 @@ oppia_android_test( "//utility/src/main/java/org/oppia/android/util/locale:prod_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", ], diff --git a/third_party/maven_install.json b/third_party/maven_install.json index 57ed039a4bb..a25cf4753fa 100644 --- a/third_party/maven_install.json +++ b/third_party/maven_install.json @@ -1,8 +1,8 @@ { "dependency_tree": { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1952591953, - "__RESOLVED_ARTIFACTS_HASH": 804693445, + "__INPUT_ARTIFACTS_HASH": 134596076, + "__RESOLVED_ARTIFACTS_HASH": -1383535075, "conflict_resolution": { "androidx.constraintlayout:constraintlayout:1.1.3": "androidx.constraintlayout:constraintlayout:2.0.1", "androidx.core:core:1.0.1": "androidx.core:core:1.3.1", @@ -5859,7 +5859,7 @@ "url": "https://maven.google.com/com/google/firebase/firebase-analytics/17.5.0/firebase-analytics-17.5.0.aar" }, { - "coord": "com.google.firebase:firebase-common:aar:19.3.0", + "coord": "com.google.firebase:firebase-common:19.3.0", "dependencies": [ "androidx.collection:collection:jar:1.1.0", "com.google.android.gms:play-services-tasks:aar:17.0.0", diff --git a/third_party/versions.bzl b/third_party/versions.bzl index ac1b9198d81..289c58ad5a8 100644 --- a/third_party/versions.bzl +++ b/third_party/versions.bzl @@ -54,6 +54,7 @@ MAVEN_PRODUCTION_DEPENDENCY_VERSIONS = { "com.google.android.flexbox:flexbox": "3.0.0", "com.google.android.material:material": "1.3.0", "com.google.firebase:firebase-analytics": "17.5.0", + "com.google.firebase:firebase-common": "19.3.0", "com.google.firebase:firebase-crashlytics": "17.1.1", "com.google.gms:google-services": "4.3.3", "com.google.guava:guava": "28.1-android", From 6fdbe6c40247494ef85c67804b76033871f6a9b2 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Sat, 2 Jul 2022 22:06:31 -0700 Subject: [PATCH 02/19] Add beta & GA update notices. This also introduces dedicated beta & GA build flavors which is a necessary prerequisite. It also introduces an extra beta, alpha, and dev mode labels for the splash screen (the latter 2 were extra) with 2 second minimum wait timers for beta and alpha to ensure they are seen. A 5-second safety timer was added to ensure the splash screen can always be passed even if something goes wrong at the domain level (since there are now quite a few moving pieces to determine the user's current onboarding state). --- app/BUILD.bazel | 1 + .../alpha/AlphaApplicationComponent.kt | 2 +- .../alpha/AlphaBuildFlavorModule.kt | 12 ++ .../android/app/application/alpha/BUILD.bazel | 1 + .../android/app/application/beta/BUILD.bazel | 27 +++ .../beta/BetaApplicationComponent.kt | 104 ++++++++++ .../application/beta/BetaBuildFlavorModule.kt | 12 ++ .../application/beta/BetaOppiaApplication.kt | 6 + .../android/app/application/dev/BUILD.bazel | 1 + .../dev/DeveloperApplicationComponent.kt | 3 +- .../dev/DeveloperBuildFlavorModule.kt | 12 ++ .../android/app/application/ga/BUILD.bazel | 27 +++ .../application/ga/GaApplicationComponent.kt | 104 ++++++++++ .../app/application/ga/GaBuildFlavorModule.kt | 12 ++ .../app/application/ga/GaOppiaApplication.kt | 6 + .../app/fragment/FragmentComponentImpl.kt | 6 +- ...maticAppDeprecationNoticeDialogFragment.kt | 2 +- ...eprecationNoticeDialogFragmentPresenter.kt | 2 +- .../app/notice/BetaNoticeClosedListener.kt | 11 + .../app/notice/BetaNoticeDialogFragment.kt | 30 +++ .../BetaNoticeDialogFragmentPresenter.kt | 32 +++ .../DeprecationNoticeExitAppListener.kt | 2 +- ...AvailabilityUpgradeNoticeClosedListener.kt | 11 + ...AvailabilityUpgradeNoticeDialogFragment.kt | 31 +++ ...ityUpgradeNoticeDialogFragmentPresenter.kt | 42 ++++ .../android/app/splash/SplashActivity.kt | 21 +- .../app/splash/SplashActivityPresenter.kt | 193 ++++++++++++------ app/src/main/res/drawable/full_oppia_logo.xml | 24 +++ .../res/layout/beta_notice_dialog_content.xml | 33 +++ ...lability_upgrade_notice_dialog_content.xml | 33 +++ app/src/main/res/layout/splash_activity.xml | 58 +++++- app/src/main/res/values/strings.xml | 11 + build_flavors.bzl | 30 ++- .../onboarding/AppStartupStateController.kt | 155 ++++++++++++-- model/src/main/proto/BUILD.bazel | 13 ++ model/src/main/proto/onboarding.proto | 38 ++++ model/src/main/proto/version.proto | 25 +++ .../assets/kdoc_validity_exemptions.textproto | 2 +- scripts/assets/test_file_exemptions.textproto | 14 +- version.bzl | 2 + 40 files changed, 1045 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModule.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/beta/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/beta/BetaBuildFlavorModule.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/beta/BetaOppiaApplication.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModule.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/ga/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/ga/GaBuildFlavorModule.kt create mode 100644 app/src/main/java/org/oppia/android/app/application/ga/GaOppiaApplication.kt rename app/src/main/java/org/oppia/android/app/{deprecation => notice}/AutomaticAppDeprecationNoticeDialogFragment.kt (97%) rename app/src/main/java/org/oppia/android/app/{deprecation => notice}/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt (96%) create mode 100644 app/src/main/java/org/oppia/android/app/notice/BetaNoticeClosedListener.kt create mode 100644 app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragment.kt create mode 100644 app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentPresenter.kt rename app/src/main/java/org/oppia/android/app/{deprecation => notice}/DeprecationNoticeExitAppListener.kt (82%) create mode 100644 app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeClosedListener.kt create mode 100644 app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragment.kt create mode 100644 app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt create mode 100644 app/src/main/res/drawable/full_oppia_logo.xml create mode 100755 app/src/main/res/layout/beta_notice_dialog_content.xml create mode 100755 app/src/main/res/layout/general_availability_upgrade_notice_dialog_content.xml create mode 100644 model/src/main/proto/version.proto diff --git a/app/BUILD.bazel b/app/BUILD.bazel index 0b239f4a9d2..7e9ece6971a 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -573,6 +573,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", diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt index 8aa2c20f6bd..8ac4ee60ccb 100644 --- a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt @@ -89,7 +89,7 @@ import javax.inject.Singleton LoggingIdentifierModule::class, ApplicationLifecycleModule::class, NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, - HintsAndSolutionProdModule::class + HintsAndSolutionProdModule::class, AlphaBuildFlavorModule::class ] ) interface AlphaApplicationComponent : ApplicationComponent { diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModule.kt b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModule.kt new file mode 100644 index 00000000000..1830cc54291 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModule.kt @@ -0,0 +1,12 @@ +package org.oppia.android.app.application.alpha + +import dagger.Module +import dagger.Provides +import org.oppia.android.app.model.BuildFlavor + +/** Module for providing the compile-time [BuildFlavor] of alpha builds of the app. */ +@Module +class AlphaBuildFlavorModule { + @Provides + fun provideAlphaBuildFlavor(): BuildFlavor = BuildFlavor.ALPHA +} diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel index c0df9390ced..3b9f6d8b50f 100644 --- a/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel +++ b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel @@ -9,6 +9,7 @@ kt_android_library( name = "alpha_application", srcs = [ "AlphaApplicationComponent.kt", + "AlphaBuildFlavorModule.kt", "AlphaOppiaApplication.kt", ], visibility = ["//:oppia_binary_visibility"], diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/beta/BUILD.bazel new file mode 100644 index 00000000000..84b9e51b0b3 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/beta/BUILD.bazel @@ -0,0 +1,27 @@ +""" +This package contains the root application definitions for beta builds of the app. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "beta_application", + srcs = [ + "BetaApplicationComponent.kt", + "BetaBuildFlavorModule.kt", + "BetaOppiaApplication.kt", + ], + visibility = ["//:oppia_binary_visibility"], + deps = [ + ":dagger", + "//app", + "//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", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:prod_module", + "//utility/src/main/java/org/oppia/android/util/networking:prod_module", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt new file mode 100644 index 00000000000..4002d1a99de --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt @@ -0,0 +1,104 @@ +package org.oppia.android.app.application.beta + +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.devoptions.DeveloperOptionsModule +import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule +import org.oppia.android.app.shim.IntentFactoryShimModule +import org.oppia.android.app.shim.ViewBindingShimModule +import org.oppia.android.app.topic.PracticeTabModule +import org.oppia.android.app.translation.ActivityRecreatorProdModule +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.HintsAndSolutionConfigModule +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.exceptions.UncaughtExceptionLoggerModule +import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +import org.oppia.android.domain.platformparameter.PlatformParameterModule +import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule +import org.oppia.android.domain.platformparameter.syncup.PlatformParameterSyncUpWorkerModule +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.util.accessibility.AccessibilityProdModule +import org.oppia.android.util.caching.AssetModule +import org.oppia.android.util.caching.CachingModule +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.logging.firebase.LogReportingModule +import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule +import org.oppia.android.util.networking.NetworkConnectionUtilProdModule +import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule +import org.oppia.android.util.parser.image.GlideImageLoaderModule +import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.system.OppiaClockModule +import org.oppia.android.util.threading.DispatcherModule +import javax.inject.Singleton + +/** + * Root Dagger component for beta versions of the application. + * + * All application-scoped modules should be included in this component. + */ +@Singleton +@Component( + modules = [ + ApplicationModule::class, DispatcherModule::class, LoggerModule::class, OppiaClockModule::class, + ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, + MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, + NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, + InteractionsModule::class, GcsResourceModule::class, GlideImageLoaderModule::class, + ImageParsingModule::class, HtmlParserEntityTypeModule::class, CachingModule::class, + QuestionModule::class, AccessibilityProdModule::class, ImageClickInputModule::class, + LogStorageModule::class, IntentFactoryShimModule::class, ViewBindingShimModule::class, + PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, + RatioInputModule::class, UncaughtExceptionLoggerModule::class, + ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, + WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, + FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, + PlatformParameterModule::class, PlatformParameterSingletonModule::class, + ExplorationStorageModule::class, DeveloperOptionsModule::class, + PlatformParameterSyncUpWorkerModule::class, NetworkConfigProdModule::class, AssetModule::class, + LocaleProdModule::class, ActivityRecreatorProdModule::class, + NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, + MathEquationInputModule::class, SplitScreenInteractionModule::class, + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, + NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, + SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, + HintsAndSolutionProdModule::class, BetaBuildFlavorModule::class + ] +) +interface BetaApplicationComponent : ApplicationComponent { + /** + * The [ApplicationComponent.Builder] for this component. Dagger will generate an implementation + * of this builder for use. + */ + @Component.Builder + interface Builder : ApplicationComponent.Builder { + override fun build(): BetaApplicationComponent + } +} diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BetaBuildFlavorModule.kt b/app/src/main/java/org/oppia/android/app/application/beta/BetaBuildFlavorModule.kt new file mode 100644 index 00000000000..46e04d47ec7 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/beta/BetaBuildFlavorModule.kt @@ -0,0 +1,12 @@ +package org.oppia.android.app.application.beta + +import dagger.Module +import dagger.Provides +import org.oppia.android.app.model.BuildFlavor + +/** Module for providing the compile-time [BuildFlavor] of beta builds of the app. */ +@Module +class BetaBuildFlavorModule { + @Provides + fun provideBetaBuildFlavor(): BuildFlavor = BuildFlavor.BETA +} diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BetaOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/beta/BetaOppiaApplication.kt new file mode 100644 index 00000000000..39d33b818a2 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/beta/BetaOppiaApplication.kt @@ -0,0 +1,6 @@ +package org.oppia.android.app.application.beta + +import org.oppia.android.app.application.AbstractOppiaApplication + +/** The root [AbstractOppiaApplication] for beta builds of the Oppia app. */ +class BetaOppiaApplication : AbstractOppiaApplication(DaggerBetaApplicationComponent::builder) diff --git a/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel index 0b087d04fb9..e0a3891ed8c 100644 --- a/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel +++ b/app/src/main/java/org/oppia/android/app/application/dev/BUILD.bazel @@ -13,6 +13,7 @@ kt_android_library( name = "developer_application", srcs = [ "DeveloperApplicationComponent.kt", + "DeveloperBuildFlavorModule.kt", "DeveloperOppiaApplication.kt", ], visibility = ["//:oppia_binary_visibility"], diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt index aee630025d2..953f1a28bfb 100644 --- a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt @@ -90,7 +90,8 @@ import javax.inject.Singleton NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class + NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, + SyncStatusModule::class, DeveloperBuildFlavorModule::class ] ) interface DeveloperApplicationComponent : ApplicationComponent { diff --git a/app/src/main/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModule.kt b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModule.kt new file mode 100644 index 00000000000..20975a930db --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModule.kt @@ -0,0 +1,12 @@ +package org.oppia.android.app.application.dev + +import dagger.Module +import dagger.Provides +import org.oppia.android.app.model.BuildFlavor + +/** Module for providing the compile-time [BuildFlavor] of developer-only builds of the app. */ +@Module +class DeveloperBuildFlavorModule { + @Provides + fun provideDeveloperBuildFlavor(): BuildFlavor = BuildFlavor.DEVELOPER +} diff --git a/app/src/main/java/org/oppia/android/app/application/ga/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/ga/BUILD.bazel new file mode 100644 index 00000000000..ef7f6c80a79 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/ga/BUILD.bazel @@ -0,0 +1,27 @@ +""" +This package contains the root application definitions for general availability builds of the app. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "general_availability_application", + srcs = [ + "GaApplicationComponent.kt", + "GaBuildFlavorModule.kt", + "GaOppiaApplication.kt", + ], + visibility = ["//:oppia_binary_visibility"], + deps = [ + ":dagger", + "//app", + "//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", + "//utility/src/main/java/org/oppia/android/util/logging/firebase:prod_module", + "//utility/src/main/java/org/oppia/android/util/networking:prod_module", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt new file mode 100644 index 00000000000..8c6e98fb28c --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt @@ -0,0 +1,104 @@ +package org.oppia.android.app.application.ga + +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.devoptions.DeveloperOptionsModule +import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule +import org.oppia.android.app.shim.IntentFactoryShimModule +import org.oppia.android.app.shim.ViewBindingShimModule +import org.oppia.android.app.topic.PracticeTabModule +import org.oppia.android.app.translation.ActivityRecreatorProdModule +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.HintsAndSolutionConfigModule +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.exceptions.UncaughtExceptionLoggerModule +import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +import org.oppia.android.domain.platformparameter.PlatformParameterModule +import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule +import org.oppia.android.domain.platformparameter.syncup.PlatformParameterSyncUpWorkerModule +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.util.accessibility.AccessibilityProdModule +import org.oppia.android.util.caching.AssetModule +import org.oppia.android.util.caching.CachingModule +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.logging.firebase.LogReportingModule +import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule +import org.oppia.android.util.networking.NetworkConnectionUtilProdModule +import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule +import org.oppia.android.util.parser.image.GlideImageLoaderModule +import org.oppia.android.util.parser.image.ImageParsingModule +import org.oppia.android.util.system.OppiaClockModule +import org.oppia.android.util.threading.DispatcherModule +import javax.inject.Singleton + +/** + * Root Dagger component for general availability versions of the application. + * + * All application-scoped modules should be included in this component. + */ +@Singleton +@Component( + modules = [ + ApplicationModule::class, DispatcherModule::class, LoggerModule::class, OppiaClockModule::class, + ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, + MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, + NumericInputRuleModule::class, TextInputRuleModule::class, DragDropSortInputModule::class, + InteractionsModule::class, GcsResourceModule::class, GlideImageLoaderModule::class, + ImageParsingModule::class, HtmlParserEntityTypeModule::class, CachingModule::class, + QuestionModule::class, AccessibilityProdModule::class, ImageClickInputModule::class, + LogStorageModule::class, IntentFactoryShimModule::class, ViewBindingShimModule::class, + PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, + RatioInputModule::class, UncaughtExceptionLoggerModule::class, + ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, + WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, + FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, + PlatformParameterModule::class, PlatformParameterSingletonModule::class, + ExplorationStorageModule::class, DeveloperOptionsModule::class, + PlatformParameterSyncUpWorkerModule::class, NetworkConfigProdModule::class, AssetModule::class, + LocaleProdModule::class, ActivityRecreatorProdModule::class, + NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, + MathEquationInputModule::class, SplitScreenInteractionModule::class, + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, + NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, + SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, + HintsAndSolutionProdModule::class, GaBuildFlavorModule::class + ] +) +interface GaApplicationComponent : ApplicationComponent { + /** + * The [ApplicationComponent.Builder] for this component. Dagger will generate an implementation + * of this builder for use. + */ + @Component.Builder + interface Builder : ApplicationComponent.Builder { + override fun build(): GaApplicationComponent + } +} diff --git a/app/src/main/java/org/oppia/android/app/application/ga/GaBuildFlavorModule.kt b/app/src/main/java/org/oppia/android/app/application/ga/GaBuildFlavorModule.kt new file mode 100644 index 00000000000..816b4df088d --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/ga/GaBuildFlavorModule.kt @@ -0,0 +1,12 @@ +package org.oppia.android.app.application.ga + +import dagger.Module +import dagger.Provides +import org.oppia.android.app.model.BuildFlavor + +/** Module for providing the compile-time [BuildFlavor] of generally available builds of the app. */ +@Module +class GaBuildFlavorModule { + @Provides + fun provideGaBuildFlavor(): BuildFlavor = BuildFlavor.GENERAL_AVAILABILITY +} diff --git a/app/src/main/java/org/oppia/android/app/application/ga/GaOppiaApplication.kt b/app/src/main/java/org/oppia/android/app/application/ga/GaOppiaApplication.kt new file mode 100644 index 00000000000..001f10b032a --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/ga/GaOppiaApplication.kt @@ -0,0 +1,6 @@ +package org.oppia.android.app.application.ga + +import org.oppia.android.app.application.AbstractOppiaApplication + +/** The root [AbstractOppiaApplication] for general availability builds of the Oppia app. */ +class GaOppiaApplication : AbstractOppiaApplication(DaggerGaApplicationComponent::builder) diff --git a/app/src/main/java/org/oppia/android/app/fragment/FragmentComponentImpl.kt b/app/src/main/java/org/oppia/android/app/fragment/FragmentComponentImpl.kt index 5ec6f40e761..4afac3cd88b 100644 --- a/app/src/main/java/org/oppia/android/app/fragment/FragmentComponentImpl.kt +++ b/app/src/main/java/org/oppia/android/app/fragment/FragmentComponentImpl.kt @@ -8,7 +8,6 @@ import org.oppia.android.app.administratorcontrols.LogoutDialogFragment import org.oppia.android.app.administratorcontrols.appversion.AppVersionFragment import org.oppia.android.app.administratorcontrols.learneranalytics.ProfileAndDeviceIdFragment import org.oppia.android.app.completedstorylist.CompletedStoryListFragment -import org.oppia.android.app.deprecation.AutomaticAppDeprecationNoticeDialogFragment import org.oppia.android.app.devoptions.DeveloperOptionsFragment import org.oppia.android.app.devoptions.forcenetworktype.ForceNetworkTypeFragment import org.oppia.android.app.devoptions.markchapterscompleted.MarkChaptersCompletedFragment @@ -30,6 +29,9 @@ import org.oppia.android.app.home.recentlyplayed.RecentlyPlayedFragment import org.oppia.android.app.mydownloads.DownloadsTabFragment import org.oppia.android.app.mydownloads.MyDownloadsFragment import org.oppia.android.app.mydownloads.UpdatesTabFragment +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.OnboardingFragment import org.oppia.android.app.ongoingtopiclist.OngoingTopicListFragment import org.oppia.android.app.options.AppLanguageFragment @@ -105,6 +107,7 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto fun inject(audioFragment: AudioFragment) fun inject(audioLanguageFragment: AudioLanguageFragment) fun inject(autoAppDeprecationNoticeDialogFragment: AutomaticAppDeprecationNoticeDialogFragment) + fun inject(betaNoticeDialogFragment: BetaNoticeDialogFragment) fun inject(cellularAudioDialogFragment: CellularAudioDialogFragment) fun inject(completedStoryListFragment: CompletedStoryListFragment) fun inject(conceptCardFragment: ConceptCardFragment) @@ -116,6 +119,7 @@ interface FragmentComponentImpl : FragmentComponent, ViewComponentBuilderInjecto fun inject(explorationTestActivityTestFragment: ExplorationTestActivityPresenter.TestFragment) fun inject(faqListFragment: FAQListFragment) fun inject(forceNetworkTypeFragment: ForceNetworkTypeFragment) + fun inject(fragment: GeneralAvailabilityUpgradeNoticeDialogFragment) fun inject(helpFragment: HelpFragment) fun inject(hintsAndSolutionDialogFragment: HintsAndSolutionDialogFragment) fun inject(hintsAndSolutionExplorationManagerFragment: HintsAndSolutionExplorationManagerFragment) diff --git a/app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragment.kt b/app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragment.kt similarity index 97% rename from app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragment.kt rename to app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragment.kt index 5e9d58c7b0a..f4f8beed7fe 100644 --- a/app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragment.kt +++ b/app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragment.kt @@ -1,4 +1,4 @@ -package org.oppia.android.app.deprecation +package org.oppia.android.app.notice import android.app.Dialog import android.content.Context diff --git a/app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt similarity index 96% rename from app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt rename to app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt index 58a40354dd6..b587df03185 100644 --- a/app/src/main/java/org/oppia/android/app/deprecation/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/notice/AutomaticAppDeprecationNoticeDialogFragmentPresenter.kt @@ -1,4 +1,4 @@ -package org.oppia.android.app.deprecation +package org.oppia.android.app.notice import android.app.Dialog import androidx.appcompat.app.AlertDialog diff --git a/app/src/main/java/org/oppia/android/app/notice/BetaNoticeClosedListener.kt b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeClosedListener.kt new file mode 100644 index 00000000000..3eee34a7147 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeClosedListener.kt @@ -0,0 +1,11 @@ +package org.oppia.android.app.notice + +/** Listener for when the beta notice dialog is closed. */ +interface BetaNoticeClosedListener { + /** + * Called when the notice dialog was closed. + * + * @param permanentlyDismiss whether the user never wants to see this notice again + */ + fun onBetaNoticeOkayButtonClicked(permanentlyDismiss: Boolean) +} diff --git a/app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragment.kt b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragment.kt new file mode 100644 index 00000000000..5456f0e95ad --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragment.kt @@ -0,0 +1,30 @@ +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're using a beta pre-release + * version of the app. + */ +class BetaNoticeDialogFragment : InjectableDialogFragment() { + companion object { + /** Returns a new instance of [BetaNoticeDialogFragment]. */ + fun newInstance(): BetaNoticeDialogFragment = BetaNoticeDialogFragment() + } + + @Inject lateinit var presenter: BetaNoticeDialogFragmentPresenter + + 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/BetaNoticeDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentPresenter.kt new file mode 100644 index 00000000000..50c034720c2 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentPresenter.kt @@ -0,0 +1,32 @@ +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 beta version app is being used. */ +class BetaNoticeDialogFragmentPresenter @Inject constructor( + private val activity: AppCompatActivity +) { + private val betaNoticeClosedListener by lazy { activity as BetaNoticeClosedListener } + + /** Handles dialog creation for the beta notice. */ + fun handleOnCreateDialog(): Dialog { + val contentView = View.inflate(activity, R.layout.beta_notice_dialog_content, /* root= */ null) + val preferenceCheckbox = + contentView.findViewById(R.id.beta_notice_dialog_preference_checkbox) + return AlertDialog.Builder(activity) + .setTitle(R.string.beta_notice_dialog_title) + .setView(contentView) + .setNegativeButton(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..986bae14316 --- /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) + .setNegativeButton(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/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 84fe06a1197..f8b7acf3ec6 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,41 +70,52 @@ 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. - 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() + 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)) + } + ) } - StartupMode.APP_IS_DEPRECATED -> { - if (getDeprecationNoticeDialogFragment() == null) { - activity.supportFragmentManager.beginTransaction() - .add( - AutomaticAppDeprecationNoticeDialogFragment.newInstance(), - AUTO_DEPRECATION_NOTICE_DIALOG_FRAGMENT_TAG - ).commitNow() - } + is AsyncResult.Failure -> { + oppiaLogger.e( + "SplashActivity", "Failed to compute initial state", initStateResult.error + ) } - 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.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) } } } @@ -99,47 +123,90 @@ class SplashActivityPresenter @Inject constructor( ) } + private fun processInitState(initState: SplashInitState) { + // First, initialize the app's initial locale. + appLanguageLocaleHandler.initializeLocale(initState.displayLocale) + + // Second, prepare to route the user to the correct destination. + startupMode = initState.appStartupState.startupMode + + // Third, show any dismissible notices. + 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.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 + ) + } + } + } + + 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..3757a5c97d7 100644 --- a/app/src/main/res/layout/splash_activity.xml +++ b/app/src/main/res/layout/splash_activity.xml @@ -1,7 +1,53 @@ - + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d10c913c8ee..150b95fd395 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -482,6 +482,17 @@ Close app + Developer Build + Alpha + Beta + Beta Notice + Hello! Your app is now being updated to the Beta version. If you experience problems while using the app, or have questions, please contact us at android-feedback@oppia.org. + Don\'t show this message again + OK + General Availability Notice + Hello! Your app is now being updated to the General Availability version. If you experience problems while using the app, or have questions, please contact us at android-feedback@oppia.org. + Don\'t show this message again + OK \u0020to\u0020 diff --git a/build_flavors.bzl b/build_flavors.bzl index e5bf54adb2f..67d97dd6f48 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_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_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 @@ -13,6 +13,8 @@ AVAILABLE_FLAVORS = [ "dev_kitkat", "alpha", "alpha_kitkat", + "beta", + "ga", ] # This file contains the list of classes that must be in the main dex list for the legacy multidex @@ -93,6 +95,32 @@ _FLAVOR_METADATA = { "version_code": OPPIA_ALPHA_KITKAT_VERSION_CODE, "application_class": ".app.application.alpha.AlphaOppiaApplication", }, + "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/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..264e799327e 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,18 +1,30 @@ package org.oppia.android.domain.onboarding +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow 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 +import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProvider -import org.oppia.android.util.data.DataProviders.Companion.transform +import org.oppia.android.util.data.DataProviders +import org.oppia.android.util.data.DataProviders.Companion.combineWith import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.locale.OppiaLocale +import org.oppia.android.util.threading.BackgroundDispatcher 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 UPDATE_ONBOARDING_STATE_PROVIDER_ID = "update_onboarding_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,43 +32,70 @@ 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 dataProviders: DataProviders, + @BackgroundDispatcher private val backgroundDispatcher: CoroutineDispatcher, + private val currentBuildFlavor: BuildFlavor ) { private val onboardingFlowStore = 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 { + onboardingFlowStore.primeInMemoryCacheAsync().invokeOnCompletion { failure -> + if (failure != 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.", + failure ) } } } /** - * 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 { onboardingState -> + onboardingState.toBuilder().apply { + alreadyOnboardedApp = true + }.build() + } + } + + /** + * 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 { onboardingState -> + onboardingState.toBuilder().apply { + permanentlyDismissedBetaNotice = true + }.build() + } + } + + /** + * 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 { onboardingState -> + onboardingState.toBuilder().apply { + permanentlyDismissedGaUpgradeNotice = true + }.build() } } @@ -66,6 +105,34 @@ class AppStartupStateController @Inject constructor( */ fun getAppStartupState(): DataProvider = appStartupStateDataProvider + private fun computeAppStartupStateProvider(): DataProvider { + val updateProvider = dataProviders.run { + onboardingFlowStore.storeDataAsync(updateInMemoryCache = false) { state -> + state.toBuilder().apply { lastUsedBuildFlavor = currentBuildFlavor }.build() + }.toStateFlow().convertAsyncToSimpleDataProvider(UPDATE_ONBOARDING_STATE_PROVIDER_ID) + } + // Combining a cache read and update like this may seem like it could introduce a data race, + // however it won't because: (1) the update doesn't alter the in-memory cache, and (2) the + // in-memory cache should already be primed before this point. + return updateProvider.combineWith( + onboardingFlowStore, APP_STARTUP_STATE_PROVIDER_ID + ) { _, onboardingState -> + AppStartupState.newBuilder().apply { + startupMode = computeAppStartupMode(onboardingState) + buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState) + }.build() + } + } + + private fun updateOnboardingState(updateState: (OnboardingState) -> OnboardingState) { + val deferred = onboardingFlowStore.storeDataAsync(updateInMemoryCache = false, updateState) + 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 +141,32 @@ class AppStartupStateController @Inject constructor( } } + private fun computeBuildNoticeMode(onboardingState: OnboardingState): BuildFlavorNoticeMode { + return when (currentBuildFlavor) { + 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 -> { + if (!onboardingState.permanentlyDismissedBetaNotice && + onboardingState.lastUsedBuildFlavor != BuildFlavor.BETA + ) { + BuildFlavorNoticeMode.SHOW_BETA_NOTICE + } else BuildFlavorNoticeMode.NO_NOTICE // The user doesn't want to see the notice again. + } + 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.DEVELOPER, + BuildFlavor.GENERAL_AVAILABILITY -> BuildFlavorNoticeMode.NO_NOTICE + } + } + } + private fun hasAppExpired(): Boolean { val applicationMetadata = expirationMetaDataRetriever.getMetaData() val isAppExpirationEnabled = @@ -87,4 +180,22 @@ class AppStartupStateController @Inject constructor( expirationDate?.isBeforeToday() ?: true } else false } + + /** + * Converts a [Deferred] to a [StateFlow] in a way that avoids potentially deadlocking when + * asynchronously blocking on the [Deferred] since it leverages a background coroutine. + * + * The returned [StateFlow] will never be updated more than once, and will always start in a + * pending state. + */ + private fun Deferred.toStateFlow(): StateFlow> { + val deferred = this + return MutableStateFlow>(value = AsyncResult.Pending()).also { flow -> + CoroutineScope(backgroundDispatcher).async { + flow.emit(AsyncResult.Success(deferred.await())) + }.invokeOnCompletion { + it?.let { flow.tryEmit(AsyncResult.Failure(it)) } + } + } + } } diff --git a/model/src/main/proto/BUILD.bazel b/model/src/main/proto/BUILD.bazel index 02838f1c01c..b6823102ed9 100644 --- a/model/src/main/proto/BUILD.bazel +++ b/model/src/main/proto/BUILD.bazel @@ -101,6 +101,7 @@ java_lite_proto_library( oppia_proto_library( name = "onboarding_proto", srcs = ["onboarding.proto"], + deps = [":version_proto"], ) java_lite_proto_library( @@ -180,6 +181,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..cdac45eca15 --- /dev/null +++ b/model/src/main/proto/version.proto @@ -0,0 +1,25 @@ +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 developer-only build of the app. + DEVELOPER = 1; + + // Corresponds to an alpha (closed testing track) pre-release build of the app. + ALPHA = 2; + + // Corresponds to a beta (open testing track) pre-release/early access build of the app. + BETA = 3; + + // Corresponds to a generally available production build of the app. + GENERAL_AVAILABILITY = 4; +} diff --git a/scripts/assets/kdoc_validity_exemptions.textproto b/scripts/assets/kdoc_validity_exemptions.textproto index 72bb2e9c3de..f767318db8d 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 71a0bc61c76..97696a7b5eb 100644 --- a/scripts/assets/test_file_exemptions.textproto +++ b/scripts/assets/test_file_exemptions.textproto @@ -48,8 +48,12 @@ exempted_file_path: "app/src/main/java/org/oppia/android/app/application/Applica exempted_file_path: "app/src/main/java/org/oppia/android/app/application/ApplicationStartupListenerModule.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alpha/AlphaOppiaApplication.kt" exempted_file_path: "app/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.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" @@ -61,9 +65,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" diff --git a/version.bzl b/version.bzl index f2002a5f728..9aeed14364e 100644 --- a/version.bzl +++ b/version.bzl @@ -14,3 +14,5 @@ OPPIA_DEV_KITKAT_VERSION_CODE = 22 OPPIA_DEV_VERSION_CODE = 23 OPPIA_ALPHA_KITKAT_VERSION_CODE = 24 OPPIA_ALPHA_VERSION_CODE = 25 +OPPIA_BETA_VERSION_CODE = 26 +OPPIA_GA_VERSION_CODE = 27 From 01d5dfb867b8c1cdcc1d783f07b96c839809969d Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Sat, 2 Jul 2022 22:34:05 -0700 Subject: [PATCH 03/19] Add build tests for the new beta & GA flavors. --- .github/workflows/build_tests.yml | 260 ++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/.github/workflows/build_tests.yml b/.github/workflows/build_tests.yml index 4fa60d192e0..0fcc6f06076 100644 --- a/.github/workflows/build_tests.yml +++ b/.github/workflows/build_tests.yml @@ -457,3 +457,263 @@ jobs: with: name: oppia_alpha_kitkat.aab path: /home/runner/work/oppia-android/oppia-android/oppia_alpha_kitkat.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 From 08a547f635285e12e2e005ffa5ce7ef729531f09 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 22 Jul 2022 01:26:31 -0700 Subject: [PATCH 04/19] Fix broken test per earlier changes. --- .../build/TransformAndroidManifestTest.kt | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt index 02889cc08bd..4fa19a2faa2 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt @@ -26,22 +26,33 @@ class TransformAndroidManifestTest { " " + " " + " " + - "" + " " private val TEST_MANIFEST_FILE_NAME = "AndroidManifest.xml" private val TRANSFORMED_MANIFEST_FILE_NAME = "TransformedAndroidManifest.xml" - private val TEST_MANIFEST_CONTENT_WITHOUT_VERSIONS = + private val TEST_MANIFEST_CONTENT_WITHOUT_VERSIONS_AND_APPLICATION = """ """.trimIndent() + private val TEST_MANIFEST_CONTENT_WITHOUT_VERSIONS = + """ + + + + + """.trimIndent() private val BUILD_FLAVOR = "beta" private val MAJOR_VERSION = "1" private val MINOR_VERSION = "3" private val VERSION_CODE = "23" + private val APPLICATION_RELATIVE_QUALIFIED_CLASS = ".example.CustomApplication" @Rule @JvmField @@ -176,6 +187,27 @@ class TransformAndroidManifestTest { assertThat(exception).hasMessageThat().contains(USAGE_STRING) } + @Test + fun testUtility_eightAgs_failsWithUsageString() { + initializeEmptyGitRepository() + val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) + + val exception = assertThrows(IllegalStateException::class) { + runScript( + tempFolder.root.absolutePath, + manifestFile.absolutePath, + File(tempFolder.root, TRANSFORMED_MANIFEST_FILE_NAME).absolutePath, + BUILD_FLAVOR, + MAJOR_VERSION, + MINOR_VERSION, + VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS + ) + } + + assertThat(exception).hasMessageThat().contains(USAGE_STRING) + } + @Test fun testUtility_allArgs_nonIntMajorVersion_failsWithUsageString() { initializeEmptyGitRepository() @@ -190,6 +222,7 @@ class TransformAndroidManifestTest { "major_version", MINOR_VERSION, VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) } @@ -211,6 +244,7 @@ class TransformAndroidManifestTest { MAJOR_VERSION, "minor_version", VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) } @@ -232,6 +266,7 @@ class TransformAndroidManifestTest { MAJOR_VERSION, MINOR_VERSION, "version_code", + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) } @@ -253,6 +288,7 @@ class TransformAndroidManifestTest { MAJOR_VERSION, MINOR_VERSION, VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) } @@ -273,6 +309,7 @@ class TransformAndroidManifestTest { MAJOR_VERSION, MINOR_VERSION, VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) } @@ -281,7 +318,33 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_allArgsCorrect_outputsNewManifestWithVersionNameAndCode() { + fun testUtility_allArgsCorrect_manifestMissingApplicationTag_throwsException() { + initializeEmptyGitRepository() + val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME).apply { + writeText(TEST_MANIFEST_CONTENT_WITHOUT_VERSIONS_AND_APPLICATION) + } + + val exception = assertThrows(IllegalStateException::class) { + runScript( + tempFolder.root.absolutePath, + manifestFile.absolutePath, + File(tempFolder.root, TRANSFORMED_MANIFEST_FILE_NAME).absolutePath, + BUILD_FLAVOR, + MAJOR_VERSION, + MINOR_VERSION, + VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, + "develop" + ) + } + + assertThat(exception) + .hasMessageThat() + .contains("Failed to find an 'application' tag in manifest") + } + + @Test + fun testUtility_allArgsCorrect_outputsNewManifestWithVersionNameAndCodeAndCustomApplication() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME).apply { writeText(TEST_MANIFEST_CONTENT_WITHOUT_VERSIONS) @@ -295,6 +358,7 @@ class TransformAndroidManifestTest { MAJOR_VERSION, MINOR_VERSION, VERSION_CODE, + APPLICATION_RELATIVE_QUALIFIED_CLASS, "develop" ) @@ -305,6 +369,8 @@ class TransformAndroidManifestTest { "android:versionName=\"$MAJOR_VERSION\\.$MINOR_VERSION" + "-$BUILD_FLAVOR-[a-f0-9]{10}\"" ) + assertThat(transformedManifest) + .containsMatch(" Date: Fri, 22 Jul 2022 17:12:09 -0700 Subject: [PATCH 05/19] Fix general broken tests & builds. Tests broken due to changes to the app startup experience haven't yet been fixed. --- app/BUILD.bazel | 1 + .../app/application/testing/BUILD.bazel | 22 +++++++++++++++++++ .../testing/TestingBuildFlavorModule.kt | 15 +++++++++++++ .../AdministratorControlsActivityTest.kt | 3 ++- .../AdministratorControlsFragmentTest.kt | 3 ++- .../AppVersionActivityTest.kt | 3 ++- .../learneranalytics/BUILD.bazel | 2 ++ .../ProfileAndDeviceIdActivityTest.kt | 3 ++- .../ProfileAndDeviceIdFragmentTest.kt | 3 ++- .../CompletedStoryListActivityTest.kt | 3 ++- .../LessonThumbnailImageViewTest.kt | 3 ++- .../app/customview/interaction/BUILD.bazel | 1 + .../MathExpressionInteractionsViewTest.kt | 3 ++- .../oppia/android/app/databinding/BUILD.bazel | 7 ++++++ .../DrawableBindingAdaptersTest.kt | 3 ++- .../ImageViewBindingAdaptersTest.kt | 4 +++- .../databinding/MarginBindingAdaptersTest.kt | 4 +++- ...StateAssemblerMarginBindingAdaptersTest.kt | 4 +++- ...tateAssemblerPaddingBindingAdaptersTest.kt | 4 +++- .../TextViewBindingAdaptersTest.kt | 3 ++- .../databinding/ViewBindingAdaptersTest.kt | 4 +++- .../DeveloperOptionsActivityTest.kt | 3 ++- .../DeveloperOptionsFragmentTest.kt | 3 ++- .../MarkChaptersCompletedActivityTest.kt | 3 ++- .../MarkChaptersCompletedFragmentTest.kt | 3 ++- .../MarkStoriesCompletedActivityTest.kt | 3 ++- .../MarkStoriesCompletedFragmentTest.kt | 3 ++- .../MarkTopicsCompletedActivityTest.kt | 3 ++- .../MarkTopicsCompletedFragmentTest.kt | 3 ++- .../devoptions/ViewEventLogsActivityTest.kt | 3 ++- .../devoptions/ViewEventLogsFragmentTest.kt | 4 +++- .../ForceNetworkTypeActivityTest.kt | 4 +++- .../ForceNetworkTypeFragmentTest.kt | 4 +++- .../mathexpressionparser/BUILD.bazel | 2 ++ .../MathExpressionParserActivityTest.kt | 3 ++- .../MathExpressionParserFragmentTest.kt | 3 ++- .../android/app/faq/FAQListFragmentTest.kt | 3 ++- .../android/app/faq/FAQSingleActivityTest.kt | 3 ++- .../android/app/faq/FaqListActivityTest.kt | 3 ++- .../android/app/help/HelpActivityTest.kt | 3 ++- .../android/app/help/HelpFragmentTest.kt | 3 ++- .../android/app/home/HomeActivityTest.kt | 3 ++- .../app/home/RecentlyPlayedFragmentTest.kt | 3 ++- .../app/home/TopicSummaryViewModelTest.kt | 3 ++- .../android/app/home/WelcomeViewModelTest.kt | 3 ++- .../PromotedStoryListViewModelTest.kt | 3 ++- .../PromotedStoryViewModelTest.kt | 3 ++- .../mydownloads/MyDownloadsActivityTest.kt | 3 ++- .../mydownloads/MyDownloadsFragmentTest.kt | 3 ++- .../app/onboarding/OnboardingActivityTest.kt | 3 ++- .../app/onboarding/OnboardingFragmentTest.kt | 3 ++- .../OngoingTopicListActivityTest.kt | 3 ++- .../app/options/AppLanguageActivityTest.kt | 3 ++- .../app/options/AppLanguageFragmentTest.kt | 3 ++- .../app/options/AudioLanguageActivityTest.kt | 3 ++- .../app/options/AudioLanguageFragmentTest.kt | 3 ++- .../app/options/OptionsActivityTest.kt | 3 ++- .../app/options/OptionsFragmentTest.kt | 3 ++- .../options/ReadingTextSizeActivityTest.kt | 3 ++- .../options/ReadingTextSizeFragmentTest.kt | 3 ++- .../app/parser/CustomBulletSpanTest.kt | 3 ++- .../android/app/parser/HtmlParserTest.kt | 3 ++- .../app/player/audio/AudioFragmentTest.kt | 3 ++- .../exploration/ExplorationActivityTest.kt | 3 ++- .../android/app/player/state/BUILD.bazel | 1 + .../app/player/state/StateFragmentTest.kt | 3 ++- .../app/profile/AddProfileActivityTest.kt | 3 ++- .../app/profile/AdminAuthActivityTest.kt | 3 ++- .../app/profile/AdminPinActivityTest.kt | 3 ++- .../app/profile/PinPasswordActivityTest.kt | 3 ++- .../app/profile/ProfileChooserFragmentTest.kt | 3 ++- .../ProfilePictureActivityTest.kt | 3 ++- .../ProfileProgressActivityTest.kt | 3 ++- .../ProfileProgressFragmentTest.kt | 3 ++- .../app/recyclerview/BindableAdapterTest.kt | 3 ++- .../resumelesson/ResumeLessonActivityTest.kt | 3 ++- .../resumelesson/ResumeLessonFragmentTest.kt | 3 ++- .../profile/ProfileEditActivityTest.kt | 3 ++- .../profile/ProfileEditFragmentTest.kt | 3 ++- .../profile/ProfileListActivityTest.kt | 3 ++- .../profile/ProfileListFragmentTest.kt | 3 ++- .../profile/ProfileRenameActivityTest.kt | 3 ++- .../profile/ProfileRenameFragmentTest.kt | 3 ++- .../profile/ProfileResetPinActivityTest.kt | 3 ++- .../profile/ProfileResetPinFragmentTest.kt | 3 ++- .../android/app/splash/SplashActivityTest.kt | 3 ++- .../android/app/story/StoryActivityTest.kt | 3 ++- .../android/app/story/StoryFragmentTest.kt | 3 ++- .../app/testing/DragDropTestActivityTest.kt | 3 ++- ...ImageRegionSelectionInteractionViewTest.kt | 3 ++- .../InputInteractionViewTestActivityTest.kt | 3 ++- .../NavigationDrawerActivityDebugTest.kt | 3 ++- .../NavigationDrawerActivityProdTest.kt | 3 ++- ...tFontScaleConfigurationUtilActivityTest.kt | 3 ++- .../testing/TopicTestActivityForStoryTest.kt | 3 ++- .../app/thirdparty/LicenseListActivityTest.kt | 3 ++- .../app/thirdparty/LicenseListFragmentTest.kt | 3 ++- .../LicenseTextViewerActivityTest.kt | 3 ++- .../LicenseTextViewerFragmentTest.kt | 3 ++- .../ThirdPartyDependencyListActivityTest.kt | 3 ++- .../ThirdPartyDependencyListFragmentTest.kt | 3 ++- .../android/app/topic/TopicActivityTest.kt | 3 ++- .../android/app/topic/TopicFragmentTest.kt | 3 ++- .../conceptcard/ConceptCardFragmentTest.kt | 3 ++- .../app/topic/info/TopicInfoFragmentTest.kt | 3 ++- .../topic/lessons/TopicLessonsFragmentTest.kt | 3 ++- .../practice/TopicPracticeFragmentTest.kt | 3 ++- .../QuestionPlayerActivityTest.kt | 3 ++- .../revision/TopicRevisionFragmentTest.kt | 3 ++- .../revisioncard/RevisionCardActivityTest.kt | 3 ++- .../revisioncard/RevisionCardFragmentTest.kt | 3 ++- .../app/utility/RatioExtensionsTest.kt | 3 ++- .../walkthrough/WalkthroughActivityTest.kt | 3 ++- .../WalkthroughFinalFragmentTest.kt | 3 ++- .../WalkthroughTopicListFragmentTest.kt | 3 ++- .../WalkthroughWelcomeFragmentTest.kt | 3 ++- .../activity/ActivityIntentFactoriesTest.kt | 3 ++- .../oppia/android/app/activity/BUILD.bazel | 1 + .../android/app/home/HomeActivityLocalTest.kt | 3 ++- .../app/parser/FractionParsingUiErrorTest.kt | 3 ++- .../app/parser/StringToRatioParserTest.kt | 3 ++- .../ExplorationActivityLocalTest.kt | 3 ++- .../player/state/StateFragmentLocalTest.kt | 3 ++- .../ProfileChooserFragmentLocalTest.kt | 3 ++- .../app/story/StoryActivityLocalTest.kt | 3 ++- .../app/testing/CompletedStoryListSpanTest.kt | 3 ++- .../oppia/android/app/testing/HomeSpanTest.kt | 3 ++- .../app/testing/OngoingTopicListSpanTest.kt | 3 ++- .../PlatformParameterIntegrationTest.kt | 3 ++- .../app/testing/ProfileChooserSpanTest.kt | 3 ++- .../testing/ProfileProgressSpanCountTest.kt | 3 ++- .../app/testing/RecentlyPlayedSpanTest.kt | 3 ++- .../app/testing/TopicRevisionSpanTest.kt | 3 ++- .../android/app/testing/activity/BUILD.bazel | 1 + .../app/testing/activity/TestActivityTest.kt | 3 ++- .../AdministratorControlsFragmentTest.kt | 3 ++- .../testing/options/OptionsFragmentTest.kt | 3 ++- .../player/split/PlayerSplitScreenTest.kt | 3 ++- .../state/StateFragmentAccessibilityTest.kt | 3 ++- .../topic/info/TopicInfoFragmentLocalTest.kt | 3 ++- .../lessons/TopicLessonsFragmentLocalTest.kt | 3 ++- .../QuestionPlayerActivityLocalTest.kt | 3 ++- .../RevisionCardActivityLocalTest.kt | 3 ++- .../AppLanguageResourceHandlerTest.kt | 3 ++- .../AppLanguageWatcherMixinTest.kt | 3 ++- .../oppia/android/app/translation/BUILD.bazel | 2 ++ .../app/utility/datetime/DateTimeUtilTest.kt | 3 ++- .../android/app/utility/math/BUILD.bazel | 1 + .../MathExpressionAccessibilityUtilTest.kt | 3 ++- domain/BUILD.bazel | 1 + .../onboarding/AppStartupStateController.kt | 6 ++--- .../AppStartupStateControllerTest.kt | 3 ++- .../instrumentation/application/BUILD.bazel | 1 + .../application/TestApplicationComponent.kt | 4 +++- model/src/main/proto/version.proto | 15 +++++++++---- .../oppia/android/testing/junit/BUILD.bazel | 3 +++ ...alizeDefaultLocaleRuleCustomContextTest.kt | 3 ++- ...InitializeDefaultLocaleRuleOmissionTest.kt | 3 ++- .../junit/InitializeDefaultLocaleRuleTest.kt | 3 ++- 159 files changed, 368 insertions(+), 149 deletions(-) create mode 100644 app/src/main/java/org/oppia/android/app/application/testing/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/application/testing/TestingBuildFlavorModule.kt diff --git a/app/BUILD.bazel b/app/BUILD.bazel index 4e5cae497d4..c53cc0a5173 100644 --- a/app/BUILD.bazel +++ b/app/BUILD.bazel @@ -822,6 +822,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/java/org/oppia/android/app/application/testing/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/testing/BUILD.bazel new file mode 100644 index 00000000000..f55e5d15b86 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/testing/BUILD.bazel @@ -0,0 +1,22 @@ +""" +This package contains testing utilities that may be needed to set up the root application for +testing environments. +""" + +load("@dagger//:workspace_defs.bzl", "dagger_rules") +load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") + +kt_android_library( + name = "testing_build_flavor_module", + testonly = True, + srcs = [ + "TestingBuildFlavorModule.kt", + ], + visibility = ["//:oppia_testing_visibility"], + deps = [ + ":dagger", + "//model/src/main/proto:version_java_proto_lite", + ], +) + +dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/testing/TestingBuildFlavorModule.kt b/app/src/main/java/org/oppia/android/app/application/testing/TestingBuildFlavorModule.kt new file mode 100644 index 00000000000..e23384d275f --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/application/testing/TestingBuildFlavorModule.kt @@ -0,0 +1,15 @@ +package org.oppia.android.app.application.testing + +import dagger.Module +import dagger.Provides +import org.oppia.android.app.model.BuildFlavor + +/** + * Module for providing the compile-time [BuildFlavor] of test environment exclusive builds of the + * app. + */ +@Module +class TestingBuildFlavorModule { + @Provides + fun provideTestingBuildFlavor(): BuildFlavor = BuildFlavor.TESTING +} 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 1b46cb78cb4..11bfa7500aa 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 @@ -120,6 +120,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AdministratorControlsActivity]. */ @RunWith(AndroidJUnit4::class) @@ -834,7 +835,7 @@ class AdministratorControlsActivityTest { 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/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragmentTest.kt index 2f737fe499b..cb663d6ca78 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 @@ -111,6 +111,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -612,7 +613,7 @@ class AdministratorControlsFragmentTest { 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/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AppVersionActivityTest.kt index 894b70c8199..1904dc722e2 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 @@ -99,6 +99,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppVersionActivity]. */ @RunWith(AndroidJUnit4::class) @@ -290,7 +291,7 @@ class AppVersionActivityTest { 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/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 beb69438acf..01f9741c9c6 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 @@ -93,6 +93,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileAndDeviceIdActivity]. */ // Function name: test names are conventionally named with underscores. @@ -198,7 +199,7 @@ class ProfileAndDeviceIdActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, SyncStatusModule::class, SplitScreenInteractionModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, - MathEquationInputModule::class + MathEquationInputModule::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 affe4a867b5..d19bfbba1d7 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 @@ -113,6 +113,7 @@ import org.robolectric.annotation.LooperMode import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileAndDeviceIdFragment]. */ // Same parameter value: helpers reduce test context, even if they are used by 1 test. @@ -624,7 +625,7 @@ class ProfileAndDeviceIdFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, SyncStatusModule::class, SplitScreenInteractionModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, - MathEquationInputModule::class + MathEquationInputModule::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 df59509cf92..e526dcace98 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 @@ -104,6 +104,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [CompletedStoryListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -513,7 +514,7 @@ class CompletedStoryListActivityTest { 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/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/customview/LessonThumbnailImageViewTest.kt index e45061db40d..3a92a42d93a 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 @@ -81,6 +81,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LessonThumbnailImageView]. */ @RunWith(AndroidJUnit4::class) @@ -171,7 +172,7 @@ class LessonThumbnailImageViewTest { 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/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 453409d3d18..1a6c0b6d189 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 @@ -104,6 +104,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [MathExpressionInteractionsView] & its view model. @@ -1773,7 +1774,7 @@ class MathExpressionInteractionsViewTest { 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/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 0a9961aaa12..f77b30b26a1 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 @@ -86,6 +86,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DrawableBindingAdapters]. */ @RunWith(AndroidJUnit4::class) @@ -182,7 +183,7 @@ class DrawableBindingAdaptersTest { 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/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/ImageViewBindingAdaptersTest.kt index 6b36d3068dd..d68f92f78ce 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 @@ -91,6 +91,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ImageViewBindingAdapters]. */ @RunWith(AndroidJUnit4::class) @@ -223,7 +224,8 @@ class ImageViewBindingAdaptersTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 1153bd7ef6f..db48bb81df9 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 @@ -93,6 +93,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f @@ -311,7 +312,8 @@ class MarginBindingAdaptersTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 e297c8cf07f..f7360ae58e2 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 @@ -95,6 +95,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f @@ -499,7 +500,8 @@ class StateAssemblerMarginBindingAdaptersTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 1bf0b3191c5..3db1763442f 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 @@ -93,6 +93,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f @@ -497,7 +498,8 @@ class StateAssemblerPaddingBindingAdaptersTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 c20b2af3164..90bf776c495 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 @@ -86,6 +86,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TextViewBindingAdapters]. */ @RunWith(AndroidJUnit4::class) @@ -331,7 +332,7 @@ class TextViewBindingAdaptersTest { 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/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt b/app/src/sharedTest/java/org/oppia/android/app/databinding/ViewBindingAdaptersTest.kt index c1735ce7351..c7a00e4d917 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 @@ -90,6 +90,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Default value for float comparison. */ private const val TOLERANCE = 1e-5f @@ -231,7 +232,8 @@ class ViewBindingAdaptersTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 e5022299560..4f667f67296 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 @@ -109,6 +109,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DeveloperOptionsActivity]. */ @RunWith(AndroidJUnit4::class) @@ -324,7 +325,7 @@ class DeveloperOptionsActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/DeveloperOptionsFragmentTest.kt index 3c039beadf5..1493bc1b88f 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 @@ -106,6 +106,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DeveloperOptionsFragment]. */ @RunWith(AndroidJUnit4::class) @@ -655,7 +656,7 @@ class DeveloperOptionsFragmentTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedActivityTest.kt index 1244ee96e90..60258af2454 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 @@ -85,6 +85,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkChaptersCompletedActivity]. */ @RunWith(AndroidJUnit4::class) @@ -186,7 +187,7 @@ class MarkChaptersCompletedActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkChaptersCompletedFragmentTest.kt index 928e30a086a..04b6d65195e 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 @@ -97,6 +97,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkChaptersCompletedFragment]. */ @RunWith(AndroidJUnit4::class) @@ -914,7 +915,7 @@ class MarkChaptersCompletedFragmentTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedActivityTest.kt index 0deb989b06d..c87dc185a9c 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 @@ -85,6 +85,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkStoriesCompletedActivity]. */ @RunWith(AndroidJUnit4::class) @@ -186,7 +187,7 @@ class MarkStoriesCompletedActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkStoriesCompletedFragmentTest.kt index 5e8c2c5dc4f..f32970b7bf6 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 @@ -97,6 +97,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkStoriesCompletedFragment]. */ @RunWith(AndroidJUnit4::class) @@ -591,7 +592,7 @@ class MarkStoriesCompletedFragmentTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedActivityTest.kt index 0aedf6d8d4e..27ec49cf93f 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 @@ -85,6 +85,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkTopicsCompletedActivity]. */ @RunWith(AndroidJUnit4::class) @@ -186,7 +187,7 @@ class MarkTopicsCompletedActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/MarkTopicsCompletedFragmentTest.kt index 9f677943e57..d107d3fe78d 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 @@ -97,6 +97,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkTopicsCompletedFragment]. */ @RunWith(AndroidJUnit4::class) @@ -561,7 +562,7 @@ class MarkTopicsCompletedFragmentTest { 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/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsActivityTest.kt index 109e70e701c..0bcba4d30c4 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 @@ -86,6 +86,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ViewEventLogsActivity]. */ @RunWith(AndroidJUnit4::class) @@ -174,7 +175,7 @@ class ViewEventLogsActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/ViewEventLogsFragmentTest.kt index 8d6e26325a3..3486139f713 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 @@ -94,6 +94,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TIMESTAMP = 1624902815000 private const val TEST_TOPIC_ID = "test_topicId" @@ -574,7 +575,8 @@ class ViewEventLogsFragmentTest { PlatformParameterSingletonModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 dd0cdd29398..b5287175caf 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 @@ -86,6 +86,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ForceNetworkTypeActivity]. */ @RunWith(AndroidJUnit4::class) @@ -177,7 +178,8 @@ class ForceNetworkTypeActivityTest { PlatformParameterSingletonModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 defd8e3d336..00ea3b60861 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 @@ -92,6 +92,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ForceNetworkTypeFragment]. */ @RunWith(AndroidJUnit4::class) @@ -393,7 +394,8 @@ class ForceNetworkTypeFragmentTest { PlatformParameterSingletonModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 149e44b36d2..42c4c17b1ff 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 @@ -83,6 +83,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MathExpressionParserActivity] and its presenter. */ // FunctionName: test names are conventionally named with underscores. @@ -162,7 +163,7 @@ class MathExpressionParserActivityTest { 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/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragmentTest.kt index 39a65aa5273..d44adebd89c 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 @@ -95,6 +95,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MathExpressionParserFragment] and its presenter and view model. */ // FunctionName: test names are conventionally named with underscores. @@ -1412,7 +1413,7 @@ class MathExpressionParserFragmentTest { 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/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQListFragmentTest.kt index f5401ca0893..35cff72b8ed 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 @@ -96,6 +96,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQListFragment]. */ @RunWith(AndroidJUnit4::class) @@ -246,7 +247,7 @@ class FAQListFragmentTest { 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/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FAQSingleActivityTest.kt index 41e9dcb8eb2..6644bf537d4 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 @@ -93,6 +93,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQSingleActivity]. */ @RunWith(AndroidJUnit4::class) @@ -218,7 +219,7 @@ class FAQSingleActivityTest { 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/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/faq/FaqListActivityTest.kt index baad49bd787..4db6c70c826 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 @@ -80,6 +80,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -151,7 +152,7 @@ class FaqListActivityTest { 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/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/help/HelpActivityTest.kt index 7a284ed0d26..3343754e7d2 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [HelpActivity]. */ @RunWith(AndroidJUnit4::class) @@ -153,7 +154,7 @@ class HelpActivityTest { 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/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/help/HelpFragmentTest.kt index 2d3e25a941f..e371f7c2a0c 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 @@ -107,6 +107,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -1187,7 +1188,7 @@ class HelpFragmentTest { 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/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt index 647e2af2237..abd13ff7856 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 @@ -138,6 +138,7 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Tue Apr 23 2019 23:22:00 private const val EVENING_TIMESTAMP = 1556061720000 @@ -1752,7 +1753,7 @@ class HomeActivityTest { 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/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt index 157e4032e81..a0f919e98ac 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 @@ -123,6 +123,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_FRAGMENT_TAG = "recently_played_test_fragment" private const val TOLERANCE = 1e-5f @@ -1505,7 +1506,7 @@ class RecentlyPlayedFragmentTest { 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/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/TopicSummaryViewModelTest.kt index 4b6b1351d10..fbb03811eb1 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 @@ -81,6 +81,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_FRAGMENT_TAG = "topic_summary_view_model_test_fragment" @@ -372,7 +373,7 @@ class TopicSummaryViewModelTest { 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/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/WelcomeViewModelTest.kt index b041e1e92d1..edbaac93836 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 @@ -82,6 +82,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Wed Apr 24 2019 08:22:00 private const val MORNING_TIMESTAMP = 1556094120000 @@ -356,7 +357,7 @@ class WelcomeViewModelTest { 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/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModelTest.kt index 94ea2653fc9..957fbb3165f 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 @@ -82,6 +82,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [PromotedStoryListViewModel] data. */ @RunWith(AndroidJUnit4::class) @@ -368,7 +369,7 @@ class PromotedStoryListViewModelTest { 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/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModelTest.kt index 58f9a657ff1..661b1448693 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 @@ -80,6 +80,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [PromotedStoryViewModel] data. */ @RunWith(AndroidJUnit4::class) @@ -378,7 +379,7 @@ class PromotedStoryViewModelTest { 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/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsActivityTest.kt index 7c074f3ac7e..7b878ea25bb 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 @@ -78,6 +78,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -141,7 +142,7 @@ class MyDownloadsActivityTest { 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/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt index e00a159ed6a..87c930de876 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 @@ -88,6 +88,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MyDownloadsFragment]. */ @RunWith(AndroidJUnit4::class) @@ -232,7 +233,7 @@ class MyDownloadsFragmentTest { 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/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingActivityTest.kt index 4a75018850e..ab253c35f0b 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OnboardingActivity]. */ @RunWith(AndroidJUnit4::class) @@ -150,7 +151,7 @@ class OnboardingActivityTest { 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/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/onboarding/OnboardingFragmentTest.kt index a6dd984f1f4..c4fb0fc8c6f 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 @@ -107,6 +107,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OnboardingFragment]. */ @RunWith(AndroidJUnit4::class) @@ -693,7 +694,7 @@ class OnboardingFragmentTest { 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/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivityTest.kt index e5caa8c9196..959a50f2610 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 @@ -102,6 +102,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OngoingTopicListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -457,7 +458,7 @@ class OngoingTopicListActivityTest { 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/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageActivityTest.kt index 395f1bfa6d0..c746159890d 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppLanguageActivity]. */ @RunWith(AndroidJUnit4::class) @@ -160,7 +161,7 @@ class AppLanguageActivityTest { 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/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AppLanguageFragmentTest.kt index 1720dc9ebf2..889c18210cf 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 @@ -91,6 +91,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val ENGLISH = 0 private const val FRENCH = 1 @@ -255,7 +256,7 @@ class AppLanguageFragmentTest { 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/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageActivityTest.kt index fa2bed58f8b..3c5c15a3b8a 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AudioLanguageActivity]. */ @RunWith(AndroidJUnit4::class) @@ -160,7 +161,7 @@ class AudioLanguageActivityTest { 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/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/AudioLanguageFragmentTest.kt index 4376d4ebeb3..ac1346ca1d7 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 @@ -90,6 +90,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val ENGLISH = 1 private const val FRENCH = 2 @@ -248,7 +249,7 @@ class AudioLanguageFragmentTest { 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/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsActivityTest.kt index 51ef476ad81..43963c8108a 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OptionsActivity]. */ @RunWith(AndroidJUnit4::class) @@ -152,7 +153,7 @@ class OptionsActivityTest { 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/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/OptionsFragmentTest.kt index 8d8dd411b1b..465e297c24e 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 @@ -101,6 +101,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OptionsFragment]. */ @RunWith(AndroidJUnit4::class) @@ -652,7 +653,7 @@ class OptionsFragmentTest { 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/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeActivityTest.kt index ae37f1cea24..965b3785223 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ReadingTextSizeActivity]. */ @RunWith(AndroidJUnit4::class) @@ -160,7 +161,7 @@ class ReadingTextSizeActivityTest { 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/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/options/ReadingTextSizeFragmentTest.kt index 52e4e3e40fe..a957ab9a96d 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 @@ -97,6 +97,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // TODO(#1815): Remove these duplicate values once Screenshot tests are implemented. private const val SMALL_TEXT_SIZE_SCALE = 0.8f @@ -314,7 +315,7 @@ class ReadingTextSizeFragmentTest { 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/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt b/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt index 1fc440a2c5f..ae9ac70dbab 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt @@ -81,6 +81,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -255,7 +256,7 @@ class CustomBulletSpanTest { NetworkConfigProdModule::class, PlatformParameterSingletonModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class ] ) 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 29b83b1a068..c37716e37f5 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 @@ -112,6 +112,7 @@ import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton import kotlin.reflect.KClass +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // TODO(#277): Add tests for UrlImageParser. /** Tests for [HtmlParser]. */ @@ -710,7 +711,7 @@ class HtmlParserTest { 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/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/audio/AudioFragmentTest.kt index 026a0eb3a6a..8b89ef4ad00 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 @@ -110,6 +110,7 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * TODO(#59): Make this test work with Espresso. @@ -477,7 +478,7 @@ class AudioFragmentTest { 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/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt index 13ea34c89a2..fcd96eee7ef 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 @@ -160,6 +160,7 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ExplorationActivity]. */ @RunWith(AndroidJUnit4::class) @@ -1956,7 +1957,7 @@ class ExplorationActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 080310ccaf2..2278389ebb8 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 @@ -183,6 +183,7 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -4316,7 +4317,7 @@ class StateFragmentTest { 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/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AddProfileActivityTest.kt index 9629aa9a6c1..3c7d6c86fa1 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 @@ -118,6 +118,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -1731,7 +1732,7 @@ class AddProfileActivityTest { 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/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminAuthActivityTest.kt index 98236590291..1a935372aec 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 @@ -102,6 +102,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -671,7 +672,7 @@ class AdminAuthActivityTest { 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/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/AdminPinActivityTest.kt index 6159eddba5c..f773533cbdc 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 @@ -113,6 +113,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -1089,7 +1090,7 @@ class AdminPinActivityTest { 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/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/PinPasswordActivityTest.kt index 76f4a79eb69..3d69545a7ec 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 @@ -108,6 +108,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -1151,7 +1152,7 @@ class PinPasswordActivityTest { 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/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profile/ProfileChooserFragmentTest.kt index d90eee72e20..e6cf2a9dd6c 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 @@ -104,6 +104,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileChooserFragment]. */ @RunWith(AndroidJUnit4::class) @@ -528,7 +529,7 @@ class ProfileChooserFragmentTest { 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/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfilePictureActivityTest.kt index f0c957a2ea4..6fd6514989a 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 @@ -88,6 +88,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfilePictureActivity]. */ @RunWith(AndroidJUnit4::class) @@ -203,7 +204,7 @@ class ProfilePictureActivityTest { 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/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressActivityTest.kt index 6b05ea0bf15..b48fe5ae0ea 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileProgressActivity]. */ @RunWith(AndroidJUnit4::class) @@ -154,7 +155,7 @@ class ProfileProgressActivityTest { 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/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt index b2955dcb059..135ba2bf311 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 @@ -128,6 +128,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileProgressFragment]. */ @RunWith(AndroidJUnit4::class) @@ -855,7 +856,7 @@ class ProfileProgressFragmentTest { 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/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/BindableAdapterTest.kt index a0b1d3577a3..407fdcf0e70 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 @@ -120,6 +120,7 @@ import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [BindableAdapter]. */ @RunWith(AndroidJUnit4::class) @@ -766,7 +767,7 @@ class BindableAdapterTest { 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/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonActivityTest.kt index 636fe149c84..a25bd467199 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 @@ -98,6 +98,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ResumeLessonActivity]. */ @RunWith(AndroidJUnit4::class) @@ -224,7 +225,7 @@ class ResumeLessonActivityTest { 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/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/resumelesson/ResumeLessonFragmentTest.kt index 098001373d2..ce0f35d03b7 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 @@ -96,6 +96,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ResumeLessonFragment]. */ @RunWith(AndroidJUnit4::class) @@ -286,7 +287,7 @@ class ResumeLessonFragmentTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditActivityTest.kt index efdbb35eeff..8fe593278a7 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 @@ -98,6 +98,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileEditActivity]. */ @RunWith(AndroidJUnit4::class) @@ -377,7 +378,7 @@ class ProfileEditActivityTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileEditFragmentTest.kt index ddcda48de71..8406d9287fc 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 @@ -99,6 +99,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -354,7 +355,7 @@ class ProfileEditFragmentTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::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 4bf44638958..1b996fce065 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -150,7 +151,7 @@ class ProfileListActivityTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileListFragmentTest.kt index bbf88cff9dc..53d47ac6685 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 @@ -96,6 +96,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -388,7 +389,7 @@ class ProfileListFragmentTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameActivityTest.kt index a6a0d30c004..bd47ed010b4 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 @@ -83,6 +83,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ProfileRenameActivity]. */ @RunWith(AndroidJUnit4::class) @@ -173,7 +174,7 @@ class ProfileRenameActivityTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileRenameFragmentTest.kt index f88763417df..f45b2ca2c46 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 @@ -101,6 +101,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileRenameFragment]. */ @RunWith(AndroidJUnit4::class) @@ -462,7 +463,7 @@ class ProfileRenameFragmentTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinActivityTest.kt index 67a3fd83b5d..fb6aa153349 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 @@ -84,6 +84,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -184,7 +185,7 @@ class ProfileResetPinActivityTest { 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/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/settings/profile/ProfileResetPinFragmentTest.kt index 4a137a97356..b7fd278d594 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 @@ -102,6 +102,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -1026,7 +1027,7 @@ class ProfileResetPinFragmentTest { AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::class, TestingBuildFlavorModule::class ] ) interface TestApplicationComponent : ApplicationComponent { 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 3fe1604bb31..e9c16017336 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 @@ -111,6 +111,7 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [SplashActivity]. For context on the activity test rule setup see: @@ -488,7 +489,7 @@ class SplashActivityTest { 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/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/story/StoryActivityTest.kt index 9cca4e9781e..c87330ff0a7 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 @@ -98,6 +98,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StoryActivity]. */ @RunWith(AndroidJUnit4::class) @@ -234,7 +235,7 @@ class StoryActivityTest { 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/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/story/StoryFragmentTest.kt index f22f9aeab77..94fc3dcf4e1 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 @@ -141,6 +141,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StoryFragment]. */ @RunWith(AndroidJUnit4::class) @@ -902,7 +903,7 @@ class StoryFragmentTest { 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/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/DragDropTestActivityTest.kt index 0d3c55261f1..c24368297b2 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 @@ -89,6 +89,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -227,7 +228,7 @@ class DragDropTestActivityTest { 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/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/ImageRegionSelectionInteractionViewTest.kt index 0985f555568..24d4da0e884 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 @@ -104,6 +104,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -390,7 +391,7 @@ class ImageRegionSelectionInteractionViewTest { 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/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt index 68404f0feaa..4d03556eb52 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 @@ -99,6 +99,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [InputInteractionViewTestActivity]. */ @RunWith(AndroidJUnit4::class) @@ -1125,7 +1126,7 @@ class InputInteractionViewTestActivityTest { 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/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityDebugTest.kt index 017b47e5702..e5a025073d4 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 @@ -120,6 +120,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [NavigationDrawerTestActivity]. */ @RunWith(AndroidJUnit4::class) @@ -438,7 +439,7 @@ class NavigationDrawerActivityDebugTest { 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/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/NavigationDrawerActivityProdTest.kt index 8acb7046c00..d0d6d3396a6 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 @@ -130,6 +130,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [NavigationDrawerTestActivity]. */ @RunWith(AndroidJUnit4::class) @@ -987,7 +988,7 @@ class NavigationDrawerActivityProdTest { AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::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 42d893f3abb..bff8656f688 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 @@ -84,6 +84,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TestFontScaleConfigurationUtilActivity]. */ @RunWith(AndroidJUnit4::class) @@ -205,7 +206,7 @@ class TestFontScaleConfigurationUtilActivityTest { 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/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/TopicTestActivityForStoryTest.kt index 6311b17fa9a..63823251b7f 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 @@ -92,6 +92,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicTestActivityForStory]. */ @RunWith(AndroidJUnit4::class) @@ -202,7 +203,7 @@ class TopicTestActivityForStoryTest { 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/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/thirdparty/LicenseListActivityTest.kt index 9d2caf4de06..47897791ef3 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 @@ -80,6 +80,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -161,7 +162,7 @@ class LicenseListActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 31d05596c1c..d40e0777169 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 @@ -97,6 +97,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -371,7 +372,7 @@ class LicenseListFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 8f88e75d8bb..7b53b8de850 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 @@ -81,6 +81,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -170,7 +171,7 @@ class LicenseTextViewerActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 62406e2ba8d..0db699701c0 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 @@ -87,6 +87,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseTextViewerFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -349,7 +350,7 @@ class LicenseTextViewerFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 6f904e3c2f9..efd31b8e545 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 @@ -80,6 +80,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ThirdPartyDependencyListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -158,7 +159,7 @@ class ThirdPartyDependencyListActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 1109f817ac6..b4ca98196f1 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 @@ -96,6 +96,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ThirdPartyDependencyListFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -481,7 +482,7 @@ class ThirdPartyDependencyListFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 93e663a8a4a..e56de25d78c 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 @@ -96,6 +96,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicActivity]. */ @RunWith(AndroidJUnit4::class) @@ -208,7 +209,7 @@ class TopicActivityTest { 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/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicFragmentTest.kt index 48700e24d59..402587cc8e1 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 @@ -106,6 +106,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val INFO_TAB_POSITION = 0 private const val LESSON_TAB_POSITION = 1 @@ -663,7 +664,7 @@ class TopicFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt index c0898715487..596afa50808 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 @@ -112,6 +112,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ConceptCardFragment]. */ @RunWith(AndroidJUnit4::class) @@ -434,7 +435,7 @@ class ConceptCardFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/info/TopicInfoFragmentTest.kt index 4c8c0eef8b6..db25af43322 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 @@ -110,6 +110,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TOPIC_NAME = "Fractions" @@ -490,7 +491,7 @@ class TopicInfoFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentTest.kt index d8036b9b35a..5f9e8966d19 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 @@ -122,6 +122,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicLessonsFragment]. */ @RunWith(AndroidJUnit4::class) @@ -1012,7 +1013,7 @@ class TopicLessonsFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt index 5a15bdcbd35..e3a379f65cb 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 @@ -104,6 +104,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicPracticeFragment]. */ @RunWith(AndroidJUnit4::class) @@ -435,7 +436,7 @@ class TopicPracticeFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt index 18bedd79642..2fa3ca70518 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 @@ -146,6 +146,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private val SKILL_ID_LIST = listOf(FRACTIONS_SKILL_ID_0) @@ -724,7 +725,7 @@ class QuestionPlayerActivityTest { 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/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt index 726c9e6eb80..b710be74a1e 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 @@ -106,6 +106,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicRevisionFragment]. */ @RunWith(AndroidJUnit4::class) @@ -331,7 +332,7 @@ class TopicRevisionFragmentTest { 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/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt index c83c20ddef9..7cabc6784e7 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 @@ -100,6 +100,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RevisionCardActivity]. */ @RunWith(AndroidJUnit4::class) @@ -285,7 +286,7 @@ class RevisionCardActivityTest { 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/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt index 445a72e33f0..18702bd7398 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 @@ -124,6 +124,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RevisionCardActivity]. */ @RunWith(AndroidJUnit4::class) @@ -597,7 +598,7 @@ class RevisionCardFragmentTest { 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/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt b/app/src/sharedTest/java/org/oppia/android/app/utility/RatioExtensionsTest.kt index c9dd3e508cc..9290680d2ba 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 @@ -77,6 +77,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RatioExtensions]. */ @RunWith(AndroidJUnit4::class) @@ -158,7 +159,7 @@ class RatioExtensionsTest { 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/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughActivityTest.kt index 4eca4362df3..3ca03317a13 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 @@ -91,6 +91,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughActivity]. */ @RunWith(AndroidJUnit4::class) @@ -236,7 +237,7 @@ class WalkthroughActivityTest { 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/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughFinalFragmentTest.kt index 5180aa3350b..cdd761a3285 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 @@ -95,6 +95,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughFinalFragment]. */ @RunWith(AndroidJUnit4::class) @@ -293,7 +294,7 @@ class WalkthroughFinalFragmentTest { 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/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughTopicListFragmentTest.kt index 7e51303ff99..f746e7fe91c 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 @@ -101,6 +101,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughTopicListFragment]. */ @RunWith(AndroidJUnit4::class) @@ -319,7 +320,7 @@ class WalkthroughTopicListFragmentTest { 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/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/walkthrough/WalkthroughWelcomeFragmentTest.kt index d124e67a7fb..687e2a9b808 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 @@ -92,6 +92,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughWelcomeFragment]. */ @RunWith(AndroidJUnit4::class) @@ -216,7 +217,7 @@ class WalkthroughWelcomeFragmentTest { 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/activity/ActivityIntentFactoriesTest.kt b/app/src/test/java/org/oppia/android/app/activity/ActivityIntentFactoriesTest.kt index 92711443d86..ce0ab90d61a 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 @@ -79,6 +79,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ActivityIntentFactories]. */ // FunctionName: test names are conventionally named with underscores. @@ -181,7 +182,7 @@ class ActivityIntentFactoriesTest { 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/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/home/HomeActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/home/HomeActivityLocalTest.kt index d62ea81ea88..c94069cd2ea 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 @@ -82,6 +82,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -153,7 +154,7 @@ class HomeActivityLocalTest { 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/parser/FractionParsingUiErrorTest.kt b/app/src/test/java/org/oppia/android/app/parser/FractionParsingUiErrorTest.kt index 1c456d24630..ef305322673 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 @@ -77,6 +77,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FractionParsingUiError]. */ @RunWith(AndroidJUnit4::class) @@ -263,7 +264,7 @@ class FractionParsingUiErrorTest { 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/parser/StringToRatioParserTest.kt b/app/src/test/java/org/oppia/android/app/parser/StringToRatioParserTest.kt index 27063d77e1f..4d1237b3156 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 @@ -77,6 +77,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StringToRatioParser]. */ @RunWith(AndroidJUnit4::class) @@ -267,7 +268,7 @@ class StringToRatioParserTest { 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/player/exploration/ExplorationActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/exploration/ExplorationActivityLocalTest.kt index 360220da811..dd9375289e9 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 @@ -88,6 +88,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -214,7 +215,7 @@ class ExplorationActivityLocalTest { 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/player/state/StateFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt index 7e5c339ef1b..9323f13f924 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 @@ -164,6 +164,7 @@ import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [StateFragment] that can only be run locally, e.g. using Robolectric, and not on an @@ -2295,7 +2296,7 @@ class StateFragmentLocalTest { 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/profile/ProfileChooserFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/profile/ProfileChooserFragmentLocalTest.kt index 9831c6fe66f..afcf3d5f45c 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -142,7 +143,7 @@ class ProfileChooserFragmentLocalTest { 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/story/StoryActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/story/StoryActivityLocalTest.kt index ebca8ce96a5..3713b17e437 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 @@ -81,6 +81,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TEST_STORY_ID = "GJ2rLXRKD5hw" @@ -167,7 +168,7 @@ class StoryActivityLocalTest { 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/testing/CompletedStoryListSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/CompletedStoryListSpanTest.kt index 724a72ab179..5fdac372604 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 @@ -81,6 +81,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -176,7 +177,7 @@ class CompletedStoryListSpanTest { 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/testing/HomeSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/HomeSpanTest.kt index db17bd4e90e..4f3f94f0225 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 @@ -82,6 +82,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -190,7 +191,7 @@ class HomeSpanTest { 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/testing/OngoingTopicListSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/OngoingTopicListSpanTest.kt index e1940a4ff1a..fb973fed27c 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 @@ -82,6 +82,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -187,7 +188,7 @@ class OngoingTopicListSpanTest { 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/testing/PlatformParameterIntegrationTest.kt b/app/src/test/java/org/oppia/android/app/testing/PlatformParameterIntegrationTest.kt index 6a6e051cc77..259a6c084ee 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 @@ -111,6 +111,7 @@ import retrofit2.mock.MockRetrofit import java.util.UUID import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests to verify the working of Platform Parameter Architecture. */ @RunWith(AndroidJUnit4::class) @@ -360,7 +361,7 @@ class PlatformParameterIntegrationTest { 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/testing/ProfileChooserSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/ProfileChooserSpanTest.kt index 9b0988a43ed..d63a062966b 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 @@ -82,6 +82,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TAG_PROFILE_CHOOSER_FRAGMENT_RECYCLER_VIEW = "profile_recycler_view" @@ -389,7 +390,7 @@ class ProfileChooserSpanTest { 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/testing/ProfileProgressSpanCountTest.kt b/app/src/test/java/org/oppia/android/app/testing/ProfileProgressSpanCountTest.kt index ddb8bf06ac8..91d91b2c921 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 @@ -81,6 +81,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -173,7 +174,7 @@ class ProfileProgressSpanCountTest { 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/testing/RecentlyPlayedSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt index 493ac5ce52b..2631ad3d92c 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 @@ -87,6 +87,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -308,7 +309,7 @@ class RecentlyPlayedSpanTest { 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/testing/TopicRevisionSpanTest.kt b/app/src/test/java/org/oppia/android/app/testing/TopicRevisionSpanTest.kt index edf710c64b4..3a07760f882 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 @@ -80,6 +80,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -173,7 +174,7 @@ class TopicRevisionSpanTest { 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/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 1866c48e456..fbbc47ae0b3 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 @@ -78,6 +78,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TestActivity]. */ // FunctionName: test names are conventionally named with underscores. @@ -198,7 +199,7 @@ class TestActivityTest { 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/testing/administratorcontrols/AdministratorControlsFragmentTest.kt b/app/src/test/java/org/oppia/android/app/testing/administratorcontrols/AdministratorControlsFragmentTest.kt index 14ce543faa6..9e8edbcf366 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 @@ -88,6 +88,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @Config( @@ -197,7 +198,7 @@ class AdministratorControlsFragmentTest { 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/testing/options/OptionsFragmentTest.kt b/app/src/test/java/org/oppia/android/app/testing/options/OptionsFragmentTest.kt index 0c9b6968ccd..d340159390a 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 @@ -91,6 +91,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @Config(application = OptionsFragmentTest.TestApplication::class, qualifiers = "sw600dp") @@ -251,7 +252,7 @@ class OptionsFragmentTest { 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/testing/player/split/PlayerSplitScreenTest.kt b/app/src/test/java/org/oppia/android/app/testing/player/split/PlayerSplitScreenTest.kt index 5b20e41d31c..be0c9e0cf74 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 @@ -77,6 +77,7 @@ import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Devices reference: https://material.io/resources/devices/ @RunWith(AndroidJUnit4::class) @@ -206,7 +207,7 @@ class PlayerSplitScreenTest { 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/testing/player/state/StateFragmentAccessibilityTest.kt b/app/src/test/java/org/oppia/android/app/testing/player/state/StateFragmentAccessibilityTest.kt index 8c22af7c2d9..f2b8b8d6c46 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 @@ -91,6 +91,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -213,7 +214,7 @@ class StateFragmentAccessibilityTest { 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/topic/info/TopicInfoFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/info/TopicInfoFragmentLocalTest.kt index fd517ae0c36..133452921c2 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 @@ -79,6 +79,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" @@ -155,7 +156,7 @@ class TopicInfoFragmentLocalTest { 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/topic/lessons/TopicLessonsFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentLocalTest.kt index 919664f7e58..9dfdc6a5eff 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 @@ -78,6 +78,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TEST_STORY_ID = "GJ2rLXRKD5hw" @@ -158,7 +159,7 @@ class TopicLessonsFragmentLocalTest { 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/topic/questionplayer/QuestionPlayerActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt index 35862612657..023c6e20f91 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 @@ -110,6 +110,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [QuestionPlayerActivity] that can only be run locally, e.g. using Robolectric, and not on an @@ -506,7 +507,7 @@ class QuestionPlayerActivityLocalTest { 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/topic/revisioncard/RevisionCardActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityLocalTest.kt index fda93fb355e..92d9c9d5e89 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 @@ -80,6 +80,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -147,7 +148,7 @@ class RevisionCardActivityLocalTest { 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/translation/AppLanguageResourceHandlerTest.kt b/app/src/test/java/org/oppia/android/app/translation/AppLanguageResourceHandlerTest.kt index e23173d0f1b..8edcb7e687d 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 @@ -88,6 +88,7 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [AppLanguageResourceHandler]. @@ -556,7 +557,7 @@ class AppLanguageResourceHandlerTest { 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/translation/AppLanguageWatcherMixinTest.kt b/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt index daa9f02e4a6..d96d5c74f22 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 @@ -92,6 +92,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppLanguageWatcherMixin]. */ // FunctionName: test names are conventionally named with underscores. @@ -271,7 +272,7 @@ class AppLanguageWatcherMixinTest { 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/translation/BUILD.bazel b/app/src/test/java/org/oppia/android/app/translation/BUILD.bazel index 5777d66c61e..201b506237e 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..c5063cfe9a5 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 @@ -81,6 +81,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Tue Apr 23 2019 23:22:00 private const val EVENING_TIMESTAMP = 1556061720000 @@ -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 badd403918f..6eec1545d2d 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 @@ -110,6 +110,7 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [MathExpressionAccessibilityUtil]. @@ -1330,7 +1331,7 @@ class MathExpressionAccessibilityUtilTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::class, TestingBuildFlavorModule::class ] ) interface TestApplicationComponent : ApplicationComponent { diff --git a/domain/BUILD.bazel b/domain/BUILD.bazel index 396d1f7e97b..58172f57081 100755 --- a/domain/BUILD.bazel +++ b/domain/BUILD.bazel @@ -169,6 +169,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/onboarding/AppStartupStateController.kt b/domain/src/main/java/org/oppia/android/domain/onboarding/AppStartupStateController.kt index 264e799327e..7e6b3172bba 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 @@ -143,7 +143,7 @@ class AppStartupStateController @Inject constructor( private fun computeBuildNoticeMode(onboardingState: OnboardingState): BuildFlavorNoticeMode { return when (currentBuildFlavor) { - BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED -> + 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 @@ -161,8 +161,8 @@ class AppStartupStateController @Inject constructor( } 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.DEVELOPER, - BuildFlavor.GENERAL_AVAILABILITY -> BuildFlavorNoticeMode.NO_NOTICE + BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED, BuildFlavor.TESTING, + BuildFlavor.DEVELOPER, BuildFlavor.GENERAL_AVAILABILITY -> BuildFlavorNoticeMode.NO_NOTICE } } } 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..498f59c2c2b 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 @@ -48,6 +48,7 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppStartupStateController]. */ // FunctionName: test names are conventionally named with underscores. @@ -373,7 +374,7 @@ class AppStartupStateControllerTest { ExpirationMetaDataRetrieverModule::class, // Use real implementation to test closer to prod. LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, PlatformParameterModule::class, - PlatformParameterSingletonModule::class + PlatformParameterSingletonModule::class, TestingBuildFlavorModule::class ] ) interface TestApplicationComponent : DataProvidersInjector { 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 22bedb239cb..225559f51ec 100644 --- a/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt +++ b/instrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt @@ -56,6 +56,7 @@ import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.system.OppiaClockModule import org.oppia.android.util.threading.DispatcherModule import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Root Dagger component for the test application. All application-scoped modules should be included @@ -90,7 +91,8 @@ import javax.inject.Singleton NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class, NetworkConnectionDebugUtilModule::class + SyncStatusModule::class, NetworkConnectionDebugUtilModule::class, + TestingBuildFlavorModule::class ] ) interface TestApplicationComponent : ApplicationComponent { diff --git a/model/src/main/proto/version.proto b/model/src/main/proto/version.proto index cdac45eca15..a0878579e57 100644 --- a/model/src/main/proto/version.proto +++ b/model/src/main/proto/version.proto @@ -11,15 +11,22 @@ enum BuildFlavor { // 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 = 1; + DEVELOPER = 2; // Corresponds to an alpha (closed testing track) pre-release build of the app. - ALPHA = 2; + ALPHA = 3; // Corresponds to a beta (open testing track) pre-release/early access build of the app. - BETA = 3; + BETA = 4; // Corresponds to a generally available production build of the app. - GENERAL_AVAILABILITY = 4; + GENERAL_AVAILABILITY = 5; } 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 ff396b8c9a1..71765ee3fe2 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 @@ -77,6 +77,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [InitializeDefaultLocaleRule]. @@ -264,7 +265,7 @@ class InitializeDefaultLocaleRuleCustomContextTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::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 455863ee05c..c8e74d12d08 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 @@ -75,6 +75,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [InitializeDefaultLocaleRule]. @@ -140,7 +141,7 @@ class InitializeDefaultLocaleRuleOmissionTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::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 79b77aefb03..fc383c1a14c 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 @@ -78,6 +78,7 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [InitializeDefaultLocaleRule]. */ // FunctionName: test names are conventionally named with underscores. @@ -144,7 +145,7 @@ class InitializeDefaultLocaleRuleTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class + SyncStatusModule::class, TestingBuildFlavorModule::class ] ) interface TestApplicationComponent : ApplicationComponent { From 67257eccbd79a99e426d3213ac1c46079c953794 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 22 Jul 2022 17:19:26 -0700 Subject: [PATCH 06/19] Lint fixes. --- .../AdministratorControlsActivityTest.kt | 2 +- .../AdministratorControlsFragmentTest.kt | 2 +- .../app/administratorcontrols/AppVersionActivityTest.kt | 2 +- .../learneranalytics/ProfileAndDeviceIdActivityTest.kt | 2 +- .../learneranalytics/ProfileAndDeviceIdFragmentTest.kt | 2 +- .../app/completedstorylist/CompletedStoryListActivityTest.kt | 2 +- .../android/app/customview/LessonThumbnailImageViewTest.kt | 2 +- .../interaction/MathExpressionInteractionsViewTest.kt | 2 +- .../android/app/databinding/DrawableBindingAdaptersTest.kt | 2 +- .../android/app/databinding/ImageViewBindingAdaptersTest.kt | 2 +- .../android/app/databinding/MarginBindingAdaptersTest.kt | 2 +- .../databinding/StateAssemblerMarginBindingAdaptersTest.kt | 2 +- .../databinding/StateAssemblerPaddingBindingAdaptersTest.kt | 2 +- .../android/app/databinding/TextViewBindingAdaptersTest.kt | 2 +- .../oppia/android/app/databinding/ViewBindingAdaptersTest.kt | 2 +- .../android/app/devoptions/DeveloperOptionsActivityTest.kt | 2 +- .../android/app/devoptions/DeveloperOptionsFragmentTest.kt | 2 +- .../app/devoptions/MarkChaptersCompletedActivityTest.kt | 2 +- .../app/devoptions/MarkChaptersCompletedFragmentTest.kt | 2 +- .../app/devoptions/MarkStoriesCompletedActivityTest.kt | 2 +- .../app/devoptions/MarkStoriesCompletedFragmentTest.kt | 2 +- .../app/devoptions/MarkTopicsCompletedActivityTest.kt | 2 +- .../app/devoptions/MarkTopicsCompletedFragmentTest.kt | 2 +- .../android/app/devoptions/ViewEventLogsActivityTest.kt | 2 +- .../android/app/devoptions/ViewEventLogsFragmentTest.kt | 2 +- .../forcenetworktype/ForceNetworkTypeActivityTest.kt | 2 +- .../forcenetworktype/ForceNetworkTypeFragmentTest.kt | 2 +- .../mathexpressionparser/MathExpressionParserActivityTest.kt | 2 +- .../mathexpressionparser/MathExpressionParserFragmentTest.kt | 2 +- .../java/org/oppia/android/app/faq/FAQListFragmentTest.kt | 2 +- .../java/org/oppia/android/app/faq/FAQSingleActivityTest.kt | 2 +- .../java/org/oppia/android/app/faq/FaqListActivityTest.kt | 2 +- .../java/org/oppia/android/app/help/HelpActivityTest.kt | 2 +- .../java/org/oppia/android/app/help/HelpFragmentTest.kt | 2 +- .../java/org/oppia/android/app/home/HomeActivityTest.kt | 2 +- .../org/oppia/android/app/home/RecentlyPlayedFragmentTest.kt | 2 +- .../org/oppia/android/app/home/TopicSummaryViewModelTest.kt | 2 +- .../java/org/oppia/android/app/home/WelcomeViewModelTest.kt | 2 +- .../app/home/promotedlist/PromotedStoryListViewModelTest.kt | 2 +- .../app/home/promotedlist/PromotedStoryViewModelTest.kt | 2 +- .../oppia/android/app/mydownloads/MyDownloadsActivityTest.kt | 2 +- .../oppia/android/app/mydownloads/MyDownloadsFragmentTest.kt | 2 +- .../oppia/android/app/onboarding/OnboardingActivityTest.kt | 2 +- .../oppia/android/app/onboarding/OnboardingFragmentTest.kt | 2 +- .../app/ongoingtopiclist/OngoingTopicListActivityTest.kt | 2 +- .../org/oppia/android/app/options/AppLanguageActivityTest.kt | 2 +- .../org/oppia/android/app/options/AppLanguageFragmentTest.kt | 2 +- .../oppia/android/app/options/AudioLanguageActivityTest.kt | 2 +- .../oppia/android/app/options/AudioLanguageFragmentTest.kt | 2 +- .../org/oppia/android/app/options/OptionsActivityTest.kt | 2 +- .../org/oppia/android/app/options/OptionsFragmentTest.kt | 2 +- .../oppia/android/app/options/ReadingTextSizeActivityTest.kt | 2 +- .../oppia/android/app/options/ReadingTextSizeFragmentTest.kt | 2 +- .../org/oppia/android/app/parser/CustomBulletSpanTest.kt | 5 +++-- .../java/org/oppia/android/app/parser/HtmlParserTest.kt | 2 +- .../org/oppia/android/app/player/audio/AudioFragmentTest.kt | 2 +- .../app/player/exploration/ExplorationActivityTest.kt | 5 +++-- .../org/oppia/android/app/player/state/StateFragmentTest.kt | 2 +- .../org/oppia/android/app/profile/AddProfileActivityTest.kt | 2 +- .../org/oppia/android/app/profile/AdminAuthActivityTest.kt | 2 +- .../org/oppia/android/app/profile/AdminPinActivityTest.kt | 2 +- .../org/oppia/android/app/profile/PinPasswordActivityTest.kt | 2 +- .../oppia/android/app/profile/ProfileChooserFragmentTest.kt | 2 +- .../app/profileprogress/ProfilePictureActivityTest.kt | 2 +- .../app/profileprogress/ProfileProgressActivityTest.kt | 2 +- .../app/profileprogress/ProfileProgressFragmentTest.kt | 2 +- .../oppia/android/app/recyclerview/BindableAdapterTest.kt | 2 +- .../android/app/resumelesson/ResumeLessonActivityTest.kt | 2 +- .../android/app/resumelesson/ResumeLessonFragmentTest.kt | 2 +- .../android/app/settings/profile/ProfileEditActivityTest.kt | 2 +- .../android/app/settings/profile/ProfileEditFragmentTest.kt | 2 +- .../android/app/settings/profile/ProfileListActivityTest.kt | 2 +- .../android/app/settings/profile/ProfileListFragmentTest.kt | 2 +- .../app/settings/profile/ProfileRenameActivityTest.kt | 2 +- .../app/settings/profile/ProfileRenameFragmentTest.kt | 2 +- .../app/settings/profile/ProfileResetPinActivityTest.kt | 2 +- .../app/settings/profile/ProfileResetPinFragmentTest.kt | 2 +- .../java/org/oppia/android/app/splash/SplashActivityTest.kt | 2 +- .../java/org/oppia/android/app/story/StoryActivityTest.kt | 2 +- .../java/org/oppia/android/app/story/StoryFragmentTest.kt | 2 +- .../oppia/android/app/testing/DragDropTestActivityTest.kt | 2 +- .../app/testing/ImageRegionSelectionInteractionViewTest.kt | 2 +- .../app/testing/InputInteractionViewTestActivityTest.kt | 2 +- .../android/app/testing/NavigationDrawerActivityDebugTest.kt | 2 +- .../android/app/testing/NavigationDrawerActivityProdTest.kt | 2 +- .../testing/TestFontScaleConfigurationUtilActivityTest.kt | 2 +- .../android/app/testing/TopicTestActivityForStoryTest.kt | 2 +- .../oppia/android/app/thirdparty/LicenseListActivityTest.kt | 5 +++-- .../oppia/android/app/thirdparty/LicenseListFragmentTest.kt | 5 +++-- .../android/app/thirdparty/LicenseTextViewerActivityTest.kt | 5 +++-- .../android/app/thirdparty/LicenseTextViewerFragmentTest.kt | 5 +++-- .../app/thirdparty/ThirdPartyDependencyListActivityTest.kt | 5 +++-- .../app/thirdparty/ThirdPartyDependencyListFragmentTest.kt | 5 +++-- .../java/org/oppia/android/app/topic/TopicActivityTest.kt | 2 +- .../java/org/oppia/android/app/topic/TopicFragmentTest.kt | 2 +- .../android/app/topic/conceptcard/ConceptCardFragmentTest.kt | 2 +- .../oppia/android/app/topic/info/TopicInfoFragmentTest.kt | 2 +- .../android/app/topic/lessons/TopicLessonsFragmentTest.kt | 2 +- .../android/app/topic/practice/TopicPracticeFragmentTest.kt | 2 +- .../app/topic/questionplayer/QuestionPlayerActivityTest.kt | 2 +- .../android/app/topic/revision/TopicRevisionFragmentTest.kt | 2 +- .../app/topic/revisioncard/RevisionCardActivityTest.kt | 2 +- .../app/topic/revisioncard/RevisionCardFragmentTest.kt | 2 +- .../org/oppia/android/app/utility/RatioExtensionsTest.kt | 2 +- .../oppia/android/app/walkthrough/WalkthroughActivityTest.kt | 2 +- .../android/app/walkthrough/WalkthroughFinalFragmentTest.kt | 2 +- .../app/walkthrough/WalkthroughTopicListFragmentTest.kt | 2 +- .../app/walkthrough/WalkthroughWelcomeFragmentTest.kt | 2 +- .../android/app/activity/ActivityIntentFactoriesTest.kt | 2 +- .../java/org/oppia/android/app/home/HomeActivityLocalTest.kt | 2 +- .../oppia/android/app/parser/FractionParsingUiErrorTest.kt | 2 +- .../org/oppia/android/app/parser/StringToRatioParserTest.kt | 2 +- .../app/player/exploration/ExplorationActivityLocalTest.kt | 2 +- .../oppia/android/app/player/state/StateFragmentLocalTest.kt | 2 +- .../android/app/profile/ProfileChooserFragmentLocalTest.kt | 2 +- .../org/oppia/android/app/story/StoryActivityLocalTest.kt | 2 +- .../oppia/android/app/testing/CompletedStoryListSpanTest.kt | 2 +- .../test/java/org/oppia/android/app/testing/HomeSpanTest.kt | 2 +- .../oppia/android/app/testing/OngoingTopicListSpanTest.kt | 2 +- .../android/app/testing/PlatformParameterIntegrationTest.kt | 2 +- .../org/oppia/android/app/testing/ProfileChooserSpanTest.kt | 2 +- .../android/app/testing/ProfileProgressSpanCountTest.kt | 2 +- .../org/oppia/android/app/testing/RecentlyPlayedSpanTest.kt | 2 +- .../org/oppia/android/app/testing/TopicRevisionSpanTest.kt | 2 +- .../oppia/android/app/testing/activity/TestActivityTest.kt | 2 +- .../AdministratorControlsFragmentTest.kt | 2 +- .../oppia/android/app/testing/options/OptionsFragmentTest.kt | 2 +- .../app/testing/player/split/PlayerSplitScreenTest.kt | 2 +- .../testing/player/state/StateFragmentAccessibilityTest.kt | 2 +- .../android/app/topic/info/TopicInfoFragmentLocalTest.kt | 2 +- .../app/topic/lessons/TopicLessonsFragmentLocalTest.kt | 2 +- .../topic/questionplayer/QuestionPlayerActivityLocalTest.kt | 2 +- .../app/topic/revisioncard/RevisionCardActivityLocalTest.kt | 2 +- .../app/translation/AppLanguageResourceHandlerTest.kt | 2 +- .../android/app/translation/AppLanguageWatcherMixinTest.kt | 2 +- .../oppia/android/app/utility/datetime/DateTimeUtilTest.kt | 2 +- .../app/utility/math/MathExpressionAccessibilityUtilTest.kt | 2 +- .../domain/onboarding/AppStartupStateControllerTest.kt | 2 +- .../instrumentation/application/TestApplicationComponent.kt | 2 +- .../junit/InitializeDefaultLocaleRuleCustomContextTest.kt | 2 +- .../testing/junit/InitializeDefaultLocaleRuleOmissionTest.kt | 2 +- .../android/testing/junit/InitializeDefaultLocaleRuleTest.kt | 2 +- 142 files changed, 158 insertions(+), 150 deletions(-) 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 11bfa7500aa..2afcc23a243 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 @@ -120,7 +121,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AdministratorControlsActivity]. */ @RunWith(AndroidJUnit4::class) 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 cb663d6ca78..618b8722473 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 @@ -111,7 +112,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 1904dc722e2..bb3c86d7a14 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 @@ -99,7 +100,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppVersionActivity]. */ @RunWith(AndroidJUnit4::class) 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 01f9741c9c6..bddba36f20c 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 @@ -93,7 +94,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileAndDeviceIdActivity]. */ // Function name: test names are conventionally named with underscores. 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 d19bfbba1d7..22b37de511a 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 @@ -113,7 +114,6 @@ import org.robolectric.annotation.LooperMode import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileAndDeviceIdFragment]. */ // Same parameter value: helpers reduce test context, even if they are used by 1 test. 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 e526dcace98..2dd6cfada14 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 @@ -104,7 +105,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [CompletedStoryListActivity]. */ @RunWith(AndroidJUnit4::class) 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 3a92a42d93a..56c8ddb1d91 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 @@ -81,7 +82,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LessonThumbnailImageView]. */ @RunWith(AndroidJUnit4::class) 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 1a6c0b6d189..e918a9cfe40 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 @@ -104,7 +105,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [MathExpressionInteractionsView] & its view model. 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 f77b30b26a1..0890f49e256 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 @@ -86,7 +87,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DrawableBindingAdapters]. */ @RunWith(AndroidJUnit4::class) 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 d68f92f78ce..98fcbd616fb 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 @@ -91,7 +92,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ImageViewBindingAdapters]. */ @RunWith(AndroidJUnit4::class) 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 db48bb81df9..ef7b67bf280 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 @@ -93,7 +94,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f 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 f7360ae58e2..5211a0a5889 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 @@ -95,7 +96,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f 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 3db1763442f..dfe9d37cab0 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 @@ -93,7 +94,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TOLERANCE = 1e-5f 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 90bf776c495..fec3026c668 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 @@ -86,7 +87,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TextViewBindingAdapters]. */ @RunWith(AndroidJUnit4::class) 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 c7a00e4d917..ca5e6646f8d 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 @@ -90,7 +91,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Default value for float comparison. */ private const val TOLERANCE = 1e-5f 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 4f667f67296..4a0b830f240 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 @@ -109,7 +110,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DeveloperOptionsActivity]. */ @RunWith(AndroidJUnit4::class) 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 1493bc1b88f..3da143ea058 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 @@ -106,7 +107,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [DeveloperOptionsFragment]. */ @RunWith(AndroidJUnit4::class) 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 60258af2454..2aa1b858f52 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 @@ -85,7 +86,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkChaptersCompletedActivity]. */ @RunWith(AndroidJUnit4::class) 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 04b6d65195e..182cd4dcecf 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 @@ -97,7 +98,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkChaptersCompletedFragment]. */ @RunWith(AndroidJUnit4::class) 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 c87dc185a9c..7f5ae56757e 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 @@ -85,7 +86,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkStoriesCompletedActivity]. */ @RunWith(AndroidJUnit4::class) 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 f32970b7bf6..ef72f280bd6 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 @@ -97,7 +98,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkStoriesCompletedFragment]. */ @RunWith(AndroidJUnit4::class) 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 27ec49cf93f..839a40e8d03 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 @@ -85,7 +86,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkTopicsCompletedActivity]. */ @RunWith(AndroidJUnit4::class) 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 d107d3fe78d..9d75475c714 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 @@ -97,7 +98,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MarkTopicsCompletedFragment]. */ @RunWith(AndroidJUnit4::class) 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 0bcba4d30c4..a6a28eb215e 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 @@ -86,7 +87,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ViewEventLogsActivity]. */ @RunWith(AndroidJUnit4::class) 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 3486139f713..fbae437c181 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 @@ -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.vieweventlogs.testing.ViewEventLogsTestActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView @@ -94,7 +95,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TIMESTAMP = 1624902815000 private const val TEST_TOPIC_ID = "test_topicId" 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 b5287175caf..497820d89f6 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 @@ -86,7 +87,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ForceNetworkTypeActivity]. */ @RunWith(AndroidJUnit4::class) 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 00ea3b60861..e9b66ef1e82 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 @@ -92,7 +93,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ForceNetworkTypeFragment]. */ @RunWith(AndroidJUnit4::class) 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 42c4c17b1ff..619f3b1b5fb 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 @@ -83,7 +84,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MathExpressionParserActivity] and its presenter. */ // FunctionName: test names are conventionally named with underscores. 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 d44adebd89c..f7bd1c2ac7d 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 @@ -95,7 +96,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MathExpressionParserFragment] and its presenter and view model. */ // FunctionName: test names are conventionally named with underscores. 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 35cff72b8ed..20b6a2b847d 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 @@ -96,7 +97,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQListFragment]. */ @RunWith(AndroidJUnit4::class) 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 6644bf537d4..39c3df0dd44 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 @@ -93,7 +94,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQSingleActivity]. */ @RunWith(AndroidJUnit4::class) 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 4db6c70c826..635a47c37bf 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 @@ -80,7 +81,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FAQListActivity]. */ @RunWith(AndroidJUnit4::class) 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 3343754e7d2..57c15236622 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [HelpActivity]. */ @RunWith(AndroidJUnit4::class) 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 e371f7c2a0c..3e97aa7770d 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 @@ -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.help.faq.FAQListActivity @@ -107,7 +108,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 abd13ff7856..664b46f6bf3 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 @@ -138,7 +139,6 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Tue Apr 23 2019 23:22:00 private const val EVENING_TIMESTAMP = 1556061720000 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 a0f919e98ac..5856f643d45 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 @@ -123,7 +124,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_FRAGMENT_TAG = "recently_played_test_fragment" private const val TOLERANCE = 1e-5f 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 fbb03811eb1..903aad2dfb6 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 @@ -81,7 +82,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_FRAGMENT_TAG = "topic_summary_view_model_test_fragment" 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 edbaac93836..7301d761300 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 @@ -82,7 +83,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Wed Apr 24 2019 08:22:00 private const val MORNING_TIMESTAMP = 1556094120000 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 957fbb3165f..b085497787f 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 @@ -82,7 +83,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [PromotedStoryListViewModel] data. */ @RunWith(AndroidJUnit4::class) 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 661b1448693..8c1256ba6ce 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 @@ -80,7 +81,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [PromotedStoryViewModel] data. */ @RunWith(AndroidJUnit4::class) 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 7b878ea25bb..3391dfbe26c 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 @@ -78,7 +79,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 87c930de876..f2d27310017 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 @@ -88,7 +89,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [MyDownloadsFragment]. */ @RunWith(AndroidJUnit4::class) 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 ab253c35f0b..ea7bb7f5f22 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OnboardingActivity]. */ @RunWith(AndroidJUnit4::class) 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 c4fb0fc8c6f..9eb8f4dd33b 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 @@ -107,7 +108,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OnboardingFragment]. */ @RunWith(AndroidJUnit4::class) 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 959a50f2610..14d2f386051 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 @@ -102,7 +103,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OngoingTopicListActivity]. */ @RunWith(AndroidJUnit4::class) 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 c746159890d..c6f33f52180 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppLanguageActivity]. */ @RunWith(AndroidJUnit4::class) 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 889c18210cf..ab5f7f63003 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 @@ -91,7 +92,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val ENGLISH = 0 private const val FRENCH = 1 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 3c5c15a3b8a..81db9342f36 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AudioLanguageActivity]. */ @RunWith(AndroidJUnit4::class) 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 ac1346ca1d7..27bcf5523b3 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 @@ -90,7 +91,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val ENGLISH = 1 private const val FRENCH = 2 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 43963c8108a..62cee270e08 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OptionsActivity]. */ @RunWith(AndroidJUnit4::class) 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 465e297c24e..c7c21d0d7fe 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 @@ -101,7 +102,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [OptionsFragment]. */ @RunWith(AndroidJUnit4::class) 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 965b3785223..b9221ed5277 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ReadingTextSizeActivity]. */ @RunWith(AndroidJUnit4::class) 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 a957ab9a96d..e0c75209285 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 @@ -97,7 +98,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // TODO(#1815): Remove these duplicate values once Screenshot tests are implemented. private const val SMALL_TEXT_SIZE_SCALE = 0.8f diff --git a/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt b/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt index ae9ac70dbab..6f1c52b95c3 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.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 @@ -81,7 +82,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -256,7 +256,8 @@ class CustomBulletSpanTest { NetworkConfigProdModule::class, PlatformParameterSingletonModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, + TestingBuildFlavorModule::class ] ) 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 c37716e37f5..86b1dabd293 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 @@ -48,6 +48,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 @@ -112,7 +113,6 @@ import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton import kotlin.reflect.KClass -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // TODO(#277): Add tests for UrlImageParser. /** Tests for [HtmlParser]. */ 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 8b89ef4ad00..64f0f5bf617 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 @@ -110,7 +111,6 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * TODO(#59): Make this test work with Espresso. 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 fcd96eee7ef..ca946cfd7ad 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 @@ -160,7 +161,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ExplorationActivity]. */ @RunWith(AndroidJUnit4::class) @@ -1957,7 +1957,8 @@ class ExplorationActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, + TestingBuildFlavorModule::class ] ) 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 2278389ebb8..ee32ee40f1e 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 @@ -183,7 +184,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) 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 3c7d6c86fa1..8b780ed4ee5 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 @@ -118,7 +119,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 1a935372aec..95b9d386d6c 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 @@ -102,7 +103,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 f773533cbdc..1c484e27df5 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 @@ -113,7 +114,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 3d69545a7ec..ff0917aab2e 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 @@ -108,7 +109,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 e6cf2a9dd6c..fb9f1774f5f 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 @@ -104,7 +105,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileChooserFragment]. */ @RunWith(AndroidJUnit4::class) 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 6fd6514989a..a7d3dbf39b0 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 @@ -88,7 +89,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfilePictureActivity]. */ @RunWith(AndroidJUnit4::class) 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 b48fe5ae0ea..bdd552d0d8b 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileProgressActivity]. */ @RunWith(AndroidJUnit4::class) 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 135ba2bf311..f8f5d799301 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 @@ -128,7 +129,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileProgressFragment]. */ @RunWith(AndroidJUnit4::class) 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 407fdcf0e70..7007c10ab54 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 @@ -120,7 +121,6 @@ import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Provider import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [BindableAdapter]. */ @RunWith(AndroidJUnit4::class) 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 a25bd467199..bcb6b24dd5c 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 @@ -98,7 +99,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ResumeLessonActivity]. */ @RunWith(AndroidJUnit4::class) 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 ce0f35d03b7..9ced623275c 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 @@ -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.model.ExplorationCheckpoint @@ -96,7 +97,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ResumeLessonFragment]. */ @RunWith(AndroidJUnit4::class) 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 8fe593278a7..222b55733ea 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 @@ -98,7 +99,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileEditActivity]. */ @RunWith(AndroidJUnit4::class) 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 8406d9287fc..8651d2a9670 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 @@ -99,7 +100,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 1b996fce065..a0f604599e9 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileListActivity]. */ @RunWith(AndroidJUnit4::class) 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 53d47ac6685..93a238326a2 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 @@ -96,7 +97,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 bd47ed010b4..d03f43fd5a1 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 @@ -83,7 +84,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Test for [ProfileRenameActivity]. */ @RunWith(AndroidJUnit4::class) 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 f45b2ca2c46..315aae3605f 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 @@ -101,7 +102,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ProfileRenameFragment]. */ @RunWith(AndroidJUnit4::class) 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 fb6aa153349..e4248edc94f 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 @@ -84,7 +85,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 b7fd278d594..8c0ad1cabb9 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 @@ -102,7 +103,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 e9c16017336..fddb0cc566d 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 @@ -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.OppiaLanguage.ARABIC @@ -111,7 +112,6 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [SplashActivity]. For context on the activity test rule setup see: 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 c87330ff0a7..b54757051e9 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 @@ -98,7 +99,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StoryActivity]. */ @RunWith(AndroidJUnit4::class) 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 94fc3dcf4e1..d57ed6dd9b6 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 @@ -141,7 +142,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StoryFragment]. */ @RunWith(AndroidJUnit4::class) 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 c24368297b2..33d0f220b8e 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 @@ -89,7 +90,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 24d4da0e884..4f29dd97146 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 @@ -104,7 +105,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) 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 4d03556eb52..def28ec4442 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 @@ -99,7 +100,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [InputInteractionViewTestActivity]. */ @RunWith(AndroidJUnit4::class) 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 e5a025073d4..6e8d8929804 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 @@ -120,7 +121,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [NavigationDrawerTestActivity]. */ @RunWith(AndroidJUnit4::class) 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 d0d6d3396a6..0af6df1dd82 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 @@ -130,7 +131,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [NavigationDrawerTestActivity]. */ @RunWith(AndroidJUnit4::class) 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 bff8656f688..4220e114984 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 @@ -84,7 +85,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TestFontScaleConfigurationUtilActivity]. */ @RunWith(AndroidJUnit4::class) 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 63823251b7f..5820f68de3c 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 @@ -92,7 +93,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicTestActivityForStory]. */ @RunWith(AndroidJUnit4::class) 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 47897791ef3..557b3862878 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 @@ -80,7 +81,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -162,7 +162,8 @@ class LicenseListActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 d40e0777169..4ee84516b4d 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 @@ -97,7 +98,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -372,7 +372,8 @@ class LicenseListFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 7b53b8de850..33b5faa70dd 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 @@ -81,7 +82,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -171,7 +171,8 @@ class LicenseTextViewerActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 0db699701c0..8c4c2376f09 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 @@ -87,7 +88,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [LicenseTextViewerFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -350,7 +350,8 @@ class LicenseTextViewerFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 efd31b8e545..ee90530a9e1 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 @@ -80,7 +81,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ThirdPartyDependencyListActivity]. */ @RunWith(AndroidJUnit4::class) @@ -159,7 +159,8 @@ class ThirdPartyDependencyListActivityTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 b4ca98196f1..002e4fcd0a0 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 @@ -96,7 +97,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ThirdPartyDependencyListFragmentTest]. */ @RunWith(AndroidJUnit4::class) @@ -482,7 +482,8 @@ class ThirdPartyDependencyListFragmentTest { AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class + LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::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 e56de25d78c..6821a881303 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 @@ -96,7 +97,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicActivity]. */ @RunWith(AndroidJUnit4::class) 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 402587cc8e1..b4b5a59266c 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 @@ -106,7 +107,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val INFO_TAB_POSITION = 0 private const val LESSON_TAB_POSITION = 1 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 596afa50808..4e65406d78c 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 @@ -112,7 +113,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ConceptCardFragment]. */ @RunWith(AndroidJUnit4::class) 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 db25af43322..8a690ae2bc7 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 @@ -110,7 +111,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TOPIC_NAME = "Fractions" 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 5f9e8966d19..eb18efcf85a 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 @@ -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.model.ProfileId @@ -122,7 +123,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicLessonsFragment]. */ @RunWith(AndroidJUnit4::class) 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 e3a379f65cb..7c65d3c5e32 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 @@ -104,7 +105,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicPracticeFragment]. */ @RunWith(AndroidJUnit4::class) 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 2fa3ca70518..3c08f18ab8f 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 @@ -146,7 +147,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private val SKILL_ID_LIST = listOf(FRACTIONS_SKILL_ID_0) 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 b710be74a1e..3f4b3bef4f4 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 @@ -106,7 +107,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TopicRevisionFragment]. */ @RunWith(AndroidJUnit4::class) 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 7cabc6784e7..23c538ddfda 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 @@ -100,7 +101,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RevisionCardActivity]. */ @RunWith(AndroidJUnit4::class) 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 18702bd7398..f519ee7382b 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 @@ -124,7 +125,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RevisionCardActivity]. */ @RunWith(AndroidJUnit4::class) 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 9290680d2ba..e1aab1f7bde 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 @@ -77,7 +78,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [RatioExtensions]. */ @RunWith(AndroidJUnit4::class) 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 3ca03317a13..ab70e8e0168 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 @@ -91,7 +92,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughActivity]. */ @RunWith(AndroidJUnit4::class) 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 cdd761a3285..44ec86f1180 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 @@ -95,7 +96,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughFinalFragment]. */ @RunWith(AndroidJUnit4::class) 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 f746e7fe91c..d5697b7bf3c 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 @@ -101,7 +102,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughTopicListFragment]. */ @RunWith(AndroidJUnit4::class) 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 687e2a9b808..b5e50f7c453 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 @@ -92,7 +93,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [WalkthroughWelcomeFragment]. */ @RunWith(AndroidJUnit4::class) 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 ce0ab90d61a..586c8c769a4 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 @@ -79,7 +80,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [ActivityIntentFactories]. */ // FunctionName: test names are conventionally named with underscores. 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 c94069cd2ea..94219314503 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 @@ -82,7 +83,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 ef305322673..578e57cd107 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 @@ -77,7 +78,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [FractionParsingUiError]. */ @RunWith(AndroidJUnit4::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 4d1237b3156..030acefe627 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 @@ -77,7 +78,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StringToRatioParser]. */ @RunWith(AndroidJUnit4::class) 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 dd9375289e9..92d138c7f70 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 @@ -88,7 +89,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 9323f13f924..4a1ed4a2eab 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 @@ -164,7 +165,6 @@ import java.util.Locale import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [StateFragment] that can only be run locally, e.g. using Robolectric, and not on an 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 afcf3d5f45c..a00e661afb6 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 3713b17e437..de12b687e19 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 @@ -81,7 +82,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TEST_STORY_ID = "GJ2rLXRKD5hw" 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 5fdac372604..71113ca10c9 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 @@ -81,7 +82,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 4f3f94f0225..14246ce14d8 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 @@ -82,7 +83,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 fb973fed27c..fef0fc5281f 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 @@ -82,7 +83,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 259a6c084ee..105f0e1b395 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 @@ -111,7 +112,6 @@ import retrofit2.mock.MockRetrofit import java.util.UUID import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests to verify the working of Platform Parameter Architecture. */ @RunWith(AndroidJUnit4::class) 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 d63a062966b..bf540909e4c 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 @@ -82,7 +83,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TAG_PROFILE_CHOOSER_FRAGMENT_RECYCLER_VIEW = "profile_recycler_view" 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 91d91b2c921..6e558a28089 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 @@ -81,7 +82,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 2631ad3d92c..6830c8127fa 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 @@ -87,7 +88,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 3a07760f882..a3ed00f6ddc 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 @@ -80,7 +81,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 fbbc47ae0b3..8587a599235 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 @@ -78,7 +79,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [TestActivity]. */ // FunctionName: test names are conventionally named with underscores. 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 9e8edbcf366..5307cad0a30 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 @@ -88,7 +89,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @Config( 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 d340159390a..d7894de511c 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 @@ -91,7 +92,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @Config(application = OptionsFragmentTest.TestApplication::class, qualifiers = "sw600dp") 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 be0c9e0cf74..6156f22af99 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 @@ -77,7 +78,6 @@ import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Devices reference: https://material.io/resources/devices/ @RunWith(AndroidJUnit4::class) 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 f2b8b8d6c46..6ee63fd513c 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 @@ -91,7 +92,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) 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 133452921c2..a54f25b733d 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 @@ -79,7 +80,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" 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 9dfdc6a5eff..4be230db968 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 @@ -78,7 +79,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule private const val TEST_TOPIC_ID = "GJ2rLXRKD5hw" private const val TEST_STORY_ID = "GJ2rLXRKD5hw" 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 023c6e20f91..fe3ae074d74 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 @@ -110,7 +111,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [QuestionPlayerActivity] that can only be run locally, e.g. using Robolectric, and not on an 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 92d9c9d5e89..a264b459487 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 @@ -80,7 +81,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) 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 8edcb7e687d..223bd899953 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 @@ -88,7 +89,6 @@ import org.robolectric.annotation.LooperMode import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [AppLanguageResourceHandler]. 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 d96d5c74f22..72848d1dd23 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 @@ -92,7 +93,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppLanguageWatcherMixin]. */ // FunctionName: test names are conventionally named with underscores. 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 c5063cfe9a5..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 @@ -81,7 +82,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule // Time: Tue Apr 23 2019 23:22:00 private const val EVENING_TIMESTAMP = 1556061720000 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 6eec1545d2d..712e6f4328a 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 @@ -110,7 +111,6 @@ import org.oppia.android.util.parser.image.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [MathExpressionAccessibilityUtil]. 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 498f59c2c2b..a81630b49aa 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 @@ -14,6 +14,7 @@ import dagger.Module import dagger.Provides import org.junit.Test import org.junit.runner.RunWith +import org.oppia.android.app.application.testing.TestingBuildFlavorModule 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 @@ -48,7 +49,6 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [AppStartupStateController]. */ // FunctionName: test names are conventionally named with underscores. 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 225559f51ec..bfaaf45cfac 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 @@ -56,7 +57,6 @@ import org.oppia.android.util.parser.image.GlideImageLoaderModule import org.oppia.android.util.system.OppiaClockModule import org.oppia.android.util.threading.DispatcherModule import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Root Dagger component for the test application. All application-scoped modules should be included 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 71765ee3fe2..4b9a6d20808 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 @@ -77,7 +78,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [InitializeDefaultLocaleRule]. 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 c8e74d12d08..ed28aaf47b1 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 @@ -75,7 +76,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** * Tests for [InitializeDefaultLocaleRule]. 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 fc383c1a14c..a5bcb7c8e3d 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 @@ -78,7 +79,6 @@ import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.app.application.testing.TestingBuildFlavorModule /** Tests for [InitializeDefaultLocaleRule]. */ // FunctionName: test names are conventionally named with underscores. From c9205017b1ed2a469e54a2cc377f3ba5424bcdc0 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 2 Aug 2022 02:46:57 -0700 Subject: [PATCH 07/19] First part of adding tests for GA notices. There's a bunch left to do here, this is mainly needed so that I can transfer changes to a different machine. --- app/src/main/AndroidManifest.xml | 6 + .../android/app/application/alpha/BUILD.bazel | 5 +- .../android/app/application/beta/BUILD.bazel | 5 +- .../android/app/application/dev/BUILD.bazel | 5 +- .../android/app/application/ga/BUILD.bazel | 5 +- .../BetaNoticeDialogFragmentPresenter.kt | 2 +- ...ityUpgradeNoticeDialogFragmentPresenter.kt | 2 +- .../android/app/notice/testing/BUILD.bazel | 34 + .../BetaNoticeDialogFragmentTestActivity.kt | 20 + ...UpgradeNoticeDialogFragmentTestActivity.kt | 22 + .../app/splash/SplashActivityPresenter.kt | 22 +- .../org/oppia/android/app/notice/BUILD.bazel | 81 ++ .../notice/BetaNoticeDialogFragmentTest.kt | 277 ++++ ...labilityUpgradeNoticeDialogFragmentTest.kt | 284 ++++ .../org/oppia/android/app/splash/BUILD.bazel | 54 + .../android/app/splash/SplashActivityTest.kt | 464 +++--- .../alpha/AlphaBuildFlavorModuleTest.kt | 81 ++ .../android/app/application/alpha/BUILD.bazel | 26 + .../android/app/application/beta/BUILD.bazel | 26 + .../beta/BetaBuildFlavorModuleTest.kt | 81 ++ .../android/app/application/dev/BUILD.bazel | 26 + .../dev/DeveloperBuildFlavorModuleTest.kt | 81 ++ .../android/app/application/ga/BUILD.bazel | 26 + .../application/ga/GaBuildFlavorModuleTest.kt | 81 ++ .../app/application/testing/BUILD.bazel | 26 + .../testing/TestingBuildFlavorModuleTest.kt | 81 ++ data/BUILD.bazel | 6 + .../data/persistence/PersistentCacheStore.kt | 186 ++- .../android/data/persistence/BUILD.bazel | 32 + .../persistence/PersistentCacheStoreTest.kt | 1266 +++++++++++++++-- .../ExplorationCheckpointController.kt | 7 +- .../onboarding/AppStartupStateController.kt | 113 +- .../LoggingIdentifierController.kt | 11 +- .../profile/ProfileManagementController.kt | 7 +- .../topic/PrimeTopicAssetsControllerImpl.kt | 3 +- .../domain/topic/StoryProgressController.kt | 9 +- .../AppStartupStateControllerTest.kt | 539 ++++++- .../android/domain/onboarding/BUILD.bazel | 39 + .../oppia/android/testing/OppiaTestRunner.kt | 2 +- .../junit/ParameterizedRunnerDelegate.kt | 1 + .../util/data/AsyncDataSubscriptionManager.kt | 14 +- .../oppia/android/util/data/DataProvider.kt | 5 +- .../oppia/android/util/data/DataProviders.kt | 33 +- .../util/data/InMemoryBlockingCache.kt | 10 +- .../logging/firebase/FirebaseEventLogger.kt | 4 +- .../parser/image/RepositoryGlideModule.kt | 5 +- .../util/parser/math/MathBitmapModelLoader.kt | 20 +- 47 files changed, 3611 insertions(+), 524 deletions(-) create mode 100644 app/src/main/java/org/oppia/android/app/notice/testing/BUILD.bazel create mode 100644 app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt create mode 100644 app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt create mode 100644 app/src/sharedTest/java/org/oppia/android/app/notice/BUILD.bazel create mode 100644 app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt create mode 100644 app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt create mode 100644 app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/alpha/AlphaBuildFlavorModuleTest.kt create mode 100644 app/src/test/java/org/oppia/android/app/application/alpha/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/beta/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/beta/BetaBuildFlavorModuleTest.kt create mode 100644 app/src/test/java/org/oppia/android/app/application/dev/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/dev/DeveloperBuildFlavorModuleTest.kt create mode 100644 app/src/test/java/org/oppia/android/app/application/ga/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/ga/GaBuildFlavorModuleTest.kt create mode 100644 app/src/test/java/org/oppia/android/app/application/testing/BUILD.bazel create mode 100644 app/src/test/java/org/oppia/android/app/application/testing/TestingBuildFlavorModuleTest.kt create mode 100644 data/src/test/java/org/oppia/android/data/persistence/BUILD.bazel create mode 100644 domain/src/test/java/org/oppia/android/domain/onboarding/BUILD.bazel diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 89dc7903f41..4370def3867 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -85,6 +85,12 @@ android:label="@string/my_downloads_activity_title" android:screenOrientation="portrait" android:theme="@style/OppiaThemeWithoutActionBar" /> + + + .setPositiveButton(R.string.beta_notice_dialog_close_button_text) { _, _ -> betaNoticeClosedListener.onBetaNoticeOkayButtonClicked(preferenceCheckbox.isChecked) } .setCancelable(false) 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 index 986bae14316..74cdca94c9d 100644 --- a/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter.kt @@ -30,7 +30,7 @@ class GeneralAvailabilityUpgradeNoticeDialogFragmentPresenter @Inject constructo return AlertDialog.Builder(activity) .setTitle(R.string.general_availability_notice_dialog_title) .setView(contentView) - .setNegativeButton(R.string.general_availability_notice_dialog_close_button_text) { _, _ -> + .setPositiveButton(R.string.general_availability_notice_dialog_close_button_text) { _, _ -> gaUpgradeNoticeClosedListener.onGaUpgradeNoticeOkayButtonClicked( preferenceCheckbox.isChecked ) 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..cd20d32c6f7 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/notice/testing/BetaNoticeDialogFragmentTestActivity.kt @@ -0,0 +1,20 @@ +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 + +class BetaNoticeDialogFragmentTestActivity: TestActivity(), BetaNoticeClosedListener { + // Must be initialized by the test. + 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..cc64b68bf27 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/notice/testing/GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity.kt @@ -0,0 +1,22 @@ +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 + +class GeneralAvailabilityUpgradeNoticeDialogFragmentTestActivity + : TestActivity(), GeneralAvailabilityUpgradeNoticeClosedListener { + // Must be initialized by the test. + 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/SplashActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/splash/SplashActivityPresenter.kt index f8b7acf3ec6..616767ccf7f 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 @@ -97,12 +97,9 @@ class SplashActivityPresenter @Inject constructor( // 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)) - } - ) + lifecycleSafeTimerFactory.createTimer(timeoutMillis = 5000).observe(activity) { + processInitState(SplashInitState.computeDefault(localeController)) + } } is AsyncResult.Failure -> { oppiaLogger.e( @@ -140,14 +137,12 @@ class SplashActivityPresenter @Inject constructor( // (for development purposes). when (currentBuildFlavor) { BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED, - BuildFlavor.DEVELOPER, BuildFlavor.GENERAL_AVAILABILITY -> processStartupMode() + BuildFlavor.TESTING, BuildFlavor.DEVELOPER, BuildFlavor.GENERAL_AVAILABILITY -> + processStartupMode() BuildFlavor.ALPHA, BuildFlavor.BETA -> { - lifecycleSafeTimerFactory.createTimer(timeoutMillis = 2000).observe( - activity, - { - processStartupMode() - } - ) + lifecycleSafeTimerFactory.createTimer(timeoutMillis = 2000).observe(activity) { + processStartupMode() + } } } } @@ -163,6 +158,7 @@ class SplashActivityPresenter @Inject constructor( } private fun processStartupMode() { + println("@@@@@ process startup: $startupMode") when (startupMode) { StartupMode.USER_IS_ONBOARDED -> { activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity)) 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..5e3f3397279 --- /dev/null +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt @@ -0,0 +1,277 @@ +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 javax.inject.Inject +import javax.inject.Singleton +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.testing.OppiaTestRule +import org.oppia.android.testing.junit.InitializeDefaultLocaleRule +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +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.loguploader.LogUploadWorkerModule +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.TestImageLoaderModule +import org.oppia.android.testing.TestLogReportingModule +import org.oppia.android.testing.robolectric.RobolectricModule +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.oppia.android.testing.threading.TestCoroutineDispatchers + +/** 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, LogUploadWorkerModule::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 + ] + ) + 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..ba45d9fccd7 --- /dev/null +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt @@ -0,0 +1,284 @@ +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 javax.inject.Inject +import javax.inject.Singleton +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.testing.OppiaTestRule +import org.oppia.android.testing.junit.InitializeDefaultLocaleRule +import org.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +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.loguploader.LogUploadWorkerModule +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.TestImageLoaderModule +import org.oppia.android.testing.TestLogReportingModule +import org.oppia.android.testing.robolectric.RobolectricModule +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.oppia.android.testing.threading.TestCoroutineDispatchers + +/** 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, LogUploadWorkerModule::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 + ] + ) + 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/splash/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel new file mode 100644 index 00000000000..5e4b171a504 --- /dev/null +++ b/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel @@ -0,0 +1,54 @@ +""" +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") + +# TODO: Revert src & R import. +app_test( + name = "SplashActivityTest", + processed_src = "SplashActivityTest.kt", #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 fddb0cc566d..e881e9cf651 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,7 +3,10 @@ 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 @@ -13,19 +16,20 @@ 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.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.oppia.android.R +import org.oppia.android.app.test.R import org.oppia.android.app.activity.ActivityComponent import org.oppia.android.app.activity.ActivityComponentFactory import org.oppia.android.app.application.ApplicationComponent @@ -33,7 +37,6 @@ 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.ARABIC @@ -112,34 +115,48 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.hamcrest.Matcher +import org.oppia.android.app.model.BuildFlavor +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.util.data.AsyncDataSubscriptionManager /** * 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 + // TODO: Still needed? + @Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory - @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 +166,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)) + launchSplashActivity { + intended(hasComponent(OnboardingActivity::class.java.name)) + } } @Test @@ -173,10 +180,9 @@ class SplashActivityTest { simulateAppAlreadyOnboarded() initializeTestApplication() - activityTestRule.launchActivity(null) - testCoroutineDispatchers.advanceUntilIdle() - - intended(hasComponent(ProfileChooserActivity::class.java.name)) + launchSplashActivity { + intended(hasComponent(ProfileChooserActivity::class.java.name)) + } } @Test @@ -185,26 +191,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)) + launchSplashActivity { + // 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())) + launchSplashActivity { + // The current app is expired. + onView(withText(R.string.unsupported_app_version_dialog_title)) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + } } @Test @@ -212,61 +216,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() + launchSplashActivity { 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)) + launchSplashActivity { + // 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)) + launchSplashActivity { + // 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())) + launchSplashActivity { + // 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 +278,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) + launchSplashActivity { + // 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 +299,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) + launchSplashActivity { + // 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 +314,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) + launchSplashActivity { + // 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,15 +328,14 @@ 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") + launchSplashActivity { + // 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 @@ -345,56 +344,146 @@ class SplashActivityTest { 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) + launchSplashActivity { + // 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() + launchSplashActivity { + // 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() + launchSplashActivity { 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() + @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) - activityTestRule.launchActivity(null) - testCoroutineDispatchers.advanceUntilIdle() + initializeTestApplicationWithFlavor(secondOpenFlavor) - val title = activityTestRule.activity.title - assertThat(title).isEqualTo(context.getString(R.string.app_name)) + launchSplashActivity { + 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) + + launchSplashActivity { + // 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) + + launchSplashActivity { + // 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)) + } + } + + // TODO: Add new tests. + // + // + // + // testSplashActivity_newUser_dismissBetaNotice_reopenApp_doesNotShowNotice + // testSplashActivity_newUser_dismissBetaNotice_retriggerNotice_showsBetaNotice + // testSplashActivity_newUser_dismissBetaNoticeForever_retriggerNotice_doesNotShowNotice + + // testSplashActivity_onboarded_gaFlavorTransitions_showsGaUpgradeNotice + // testSplashActivity_onboarded_gaFlavorTransitions_closeNotice_routesToProfileChooser + // testSplashActivity_onboarded_gaFlavorTransitions_doNotShowAgain_routesToProfileChooser + // testSplashActivity_onboarded_dismissGaNotice_reopenApp_doesNotShowNotice + // testSplashActivity_onboarded_dismissGaNotice_retriggerNotice_showsGaNotice + // testSplashActivity_onboarded_dismissGaNoticeForever_retriggerNotice_doesNotShowNotice + + // testSplashActivity_newUser_ignoredFlavorTransitions_routesToOnboardingFlow + // testSplashActivity_onboarded_ignoredFlavorTransitions_routesToProfileChooser + // testSplashActivity_appDeprecated_allFlavorTransitions_showsDeprecationNotice + + // (Wait ones are Robo-only) + // testSplashActivity_onboarded_devFlavor_showDevText + // testSplashActivity_onboarded_alphaFlavor_showAlphaText + // testSplashActivity_onboarded_betaFlavor_showBetaText + // testSplashActivity_onboarded_testingFlavor_doesNotWaitToStart + // testSplashActivity_onboarded_devFlavor_doesNotWaitToStart + // testSplashActivity_onboarded_alphaFlavor_waitsTwoSecondsToStart + // testSplashActivity_onboarded_betaFlavor_waitsTwoSecondsToStart + // testSplashActivity_onboarded_gaFlavor_doesNotWaitToStart + + private fun simulateAppAlreadyOpened() { + println("@@@@@ root application: ${ApplicationProvider.getApplicationContext()}") + runInNewTestApplication { + println("@@@@@ create monitor; current context: $this") + println("@@@@@ expected test context: $asdfContext, app context: ${asdfContext.applicationContext}") + println("@@@@@ separate mgr: $asdf") + println("@@@@@ app controller: $appStartupStateController") + val monitor = monitorFactory.createMonitor(appStartupStateController.getAppStartupState()) + println("@@@@@ wait for execution") + testCoroutineDispatchers.advanceUntilIdle() + println("@@@@@ finished wait") + monitor.ensureNextResultIsSuccess() + } } private fun simulateAppAlreadyOnboarded() { @@ -403,12 +492,30 @@ 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() + } + + private fun simulateAppAlreadyOpenedWithFlavor(buildFlavor: BuildFlavor) { + TestModule.buildFlavor = buildFlavor + simulateAppAlreadyOpened() + } + + private fun simulateAppAlreadyOnboardedWithFlavor(buildFlavor: BuildFlavor) { + TestModule.buildFlavor = buildFlavor + simulateAppAlreadyOnboarded() } private fun initializeTestApplication() { @@ -417,6 +524,21 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = false) // Default to disabled. } + private fun initializeTestApplicationWithFlavor(buildFlavor: BuildFlavor) { + TestModule.buildFlavor = buildFlavor + initializeTestApplication() + } + + 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).also { + testCoroutineDispatchers.advanceUntilIdle() + }.use(testBlock) + } + private fun setAutoAppExpirationEnabled(enabled: Boolean) { fakeMetaDataRetriever.putMetaDataBoolean("automatic_app_expiration_enabled", enabled) } @@ -448,26 +570,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, @@ -489,7 +611,7 @@ class SplashActivityTest { NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, - SyncStatusModule::class, TestingBuildFlavorModule::class + SyncStatusModule::class ] ) interface TestApplicationComponent : ApplicationComponent { @@ -505,6 +627,12 @@ class SplashActivityTest { fun getTestCoroutineDispatchers(): TestCoroutineDispatchers + fun getMonitorFactory(): DataProviderTestMonitor.Factory + + // TODO: Remove. + fun getAsdf(): AsyncDataSubscriptionManager + fun getContext(): Context + fun inject(splashActivityTest: SplashActivityTest) } @@ -515,14 +643,16 @@ class SplashActivityTest { .build() } + val appStartupStateController by lazy { component.getAppStartupStateController() } + val testCoroutineDispatchers by lazy { component.getTestCoroutineDispatchers() } + val monitorFactory by lazy { component.getMonitorFactory() } + val asdf by lazy { component.getAsdf() } + val asdfContext by lazy { component.getContext() } + 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() } @@ -534,5 +664,7 @@ class SplashActivityTest { 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/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..7ebe4b4faa8 --- /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.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import javax.inject.Inject +import javax.inject.Singleton +import org.oppia.android.app.model.BuildFlavor + +/** 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..7fe4f3cb7f8 --- /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_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/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..b2c32b7c848 --- /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.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import javax.inject.Inject +import javax.inject.Singleton +import org.oppia.android.app.model.BuildFlavor + +/** 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..1abb9629162 --- /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.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import javax.inject.Inject +import javax.inject.Singleton +import org.oppia.android.app.model.BuildFlavor + +/** 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..7eadeb138f2 --- /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.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import javax.inject.Inject +import javax.inject.Singleton +import org.oppia.android.app.model.BuildFlavor + +/** 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..feb35c4b596 --- /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.robolectric.annotation.Config +import org.robolectric.annotation.LooperMode +import javax.inject.Inject +import javax.inject.Singleton +import org.oppia.android.app.model.BuildFlavor + +/** 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/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..010d2fae47e 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,5 +1,6 @@ package org.oppia.android.data.persistence +import android.app.Application import android.content.Context import androidx.annotation.GuardedBy import com.google.protobuf.MessageLite @@ -21,24 +22,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,17 +54,27 @@ class PersistentCacheStore private constructor( cacheFactory.create(CachePayload(state = CacheState.UNLOADED, value = initialValue)) init { - cache.observeChanges { - asyncDataSubscriptionManager.notifyChange(providerId) + cache.observeChanges { oldValue, newValue -> + println("@@@@@ cache store changes: $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 { + println("@@@@@ retrieveData()") cache.readIfPresentAsync().await().let { cachePayload -> + println("@@@@ retrieving payload: $cachePayload") // First, determine whether the current cache has been attempted to be retrieved from disk. if (cachePayload.state == CacheState.UNLOADED) { deferLoadFile() @@ -85,69 +99,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]. + * + * 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 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. + * Each of the above states are possible using different combinations of the provided [UpdateMode] + * and [PublishMode] parameters. * - * 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. + * 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). * - * 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. + * @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 { + println("@@@@@ primeInMemoryAndDiskCacheAsync") return cache.updateIfPresentAsync { cachePayload -> - when (cachePayload.state) { + println("@@@@@ updateIfPresentAsync for prime: $cachePayload") + // 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 +316,15 @@ 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 +341,23 @@ class PersistentCacheStore private constructor( private data class CachePayload(val state: CacheState, val value: T) + enum class UpdateMode { + UPDATE_IF_NEW_CACHE, + UPDATE_ALWAYS + } + + enum class PublishMode { + PUBLISH_TO_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 @@ -324,13 +369,16 @@ class PersistentCacheStore private constructor( * Use this method when data is shared by all profiles. */ fun create(cacheName: String, initialValue: T): PersistentCacheStore { + println("@@@@@ sanity check: create cache $cacheName") return PersistentCacheStore( - context, + application, cacheFactory, asyncDataSubscriptionManager, cacheName, initialValue - ) + ).also { + Exception("@@@@@ create store with context: $application (store: $it)").printStackTrace(System.out) + } } /** @@ -344,7 +392,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..e5699623497 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 @@ -42,6 +42,20 @@ import java.io.IOException import java.lang.IllegalStateException import javax.inject.Inject import javax.inject.Singleton +import kotlin.reflect.full.staticFunctions +import org.junit.Rule +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.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.util.data.AsyncDataSubscriptionManager +import org.oppia.android.util.data.ObserveAsyncChange 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,27 @@ 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 +185,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 +198,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 +206,13 @@ 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 +223,13 @@ 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 +241,26 @@ 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 +279,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 +294,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 +311,34 @@ 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 +348,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 +362,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 +372,14 @@ 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 +424,998 @@ 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 +1426,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 +1447,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 +1470,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 +1494,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 +1517,60 @@ 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 +1588,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 +1670,12 @@ class PersistentCacheStoreTest { override fun getDataProvidersInjector(): DataProvidersInjector = component } + + interface SubscriptionCallback { + fun onDataProviderChanged() + + companion object { + fun SubscriptionCallback.toAsyncChange(): ObserveAsyncChange = { onDataProviderChanged() } + } + } } 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..24e2308e59b 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 @@ -16,6 +16,8 @@ import org.oppia.android.util.data.DataProviders import org.oppia.android.util.data.DataProviders.Companion.transformAsync import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode +import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val CACHE_NAME = "exploration_checkpoint_database" private const val RETRIEVE_EXPLORATION_CHECKPOINT_DATA_PROVIDER_ID = @@ -282,7 +284,10 @@ class ExplorationCheckpointController @Inject constructor( cacheStore } - cacheStore.primeInMemoryCacheAsync().invokeOnCompletion { throwable -> + cacheStore.primeInMemoryAndDiskCacheAsync( + updateMode = UpdateMode.UPDATE_IF_NEW_CACHE, + publishMode = PublishMode.DO_NOT_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 7e6b3172bba..fe01411e978 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,11 +1,7 @@ package org.oppia.android.domain.onboarding -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.async -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject +import javax.inject.Singleton import org.oppia.android.app.model.AppStartupState import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode import org.oppia.android.app.model.AppStartupState.StartupMode @@ -13,17 +9,11 @@ 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 -import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProvider -import org.oppia.android.util.data.DataProviders -import org.oppia.android.util.data.DataProviders.Companion.combineWith +import org.oppia.android.util.data.DataProviders.Companion.transform import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.locale.OppiaLocale -import org.oppia.android.util.threading.BackgroundDispatcher -import javax.inject.Inject -import javax.inject.Singleton -private const val UPDATE_ONBOARDING_STATE_PROVIDER_ID = "update_onboarding_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. */ @@ -33,24 +23,31 @@ class AppStartupStateController @Inject constructor( private val oppiaLogger: OppiaLogger, private val expirationMetaDataRetriever: ExpirationMetaDataRetriever, private val machineLocale: OppiaLocale.MachineLocale, - private val dataProviders: DataProviders, - @BackgroundDispatcher private val backgroundDispatcher: CoroutineDispatcher, private val currentBuildFlavor: BuildFlavor ) { - private val onboardingFlowStore = + private val onboardingFlowStore by lazy { cacheStoreFactory.create("on_boarding_flow", OnboardingState.getDefaultInstance()) + } 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 { failure -> - if (failure != null) { + // 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). + println("@@@@@ initing controller: $this") + 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( "StartupController", "Failed to prime cache ahead of data retrieval for user onboarding data.", - failure + primeFailure ) } } @@ -63,11 +60,7 @@ class AppStartupStateController @Inject constructor( * subscribers observe this state until the app restarts. */ fun markOnboardingFlowCompleted() { - updateOnboardingState { onboardingState -> - onboardingState.toBuilder().apply { - alreadyOnboardedApp = true - }.build() - } + updateOnboardingState { alreadyOnboardedApp = true } } /** @@ -77,11 +70,7 @@ class AppStartupStateController @Inject constructor( * subscribers observe this state until the app restarts. */ fun dismissBetaNoticesPermanently() { - updateOnboardingState { onboardingState -> - onboardingState.toBuilder().apply { - permanentlyDismissedBetaNotice = true - }.build() - } + updateOnboardingState { permanentlyDismissedBetaNotice = true } } /** @@ -92,11 +81,7 @@ class AppStartupStateController @Inject constructor( * subscribers observe this state until the app restarts. */ fun dismissGaUpgradeNoticesPermanently() { - updateOnboardingState { onboardingState -> - onboardingState.toBuilder().apply { - permanentlyDismissedGaUpgradeNotice = true - }.build() - } + updateOnboardingState { permanentlyDismissedGaUpgradeNotice = true } } /** @@ -106,26 +91,26 @@ class AppStartupStateController @Inject constructor( fun getAppStartupState(): DataProvider = appStartupStateDataProvider private fun computeAppStartupStateProvider(): DataProvider { - val updateProvider = dataProviders.run { - onboardingFlowStore.storeDataAsync(updateInMemoryCache = false) { state -> - state.toBuilder().apply { lastUsedBuildFlavor = currentBuildFlavor }.build() - }.toStateFlow().convertAsyncToSimpleDataProvider(UPDATE_ONBOARDING_STATE_PROVIDER_ID) - } - // Combining a cache read and update like this may seem like it could introduce a data race, - // however it won't because: (1) the update doesn't alter the in-memory cache, and (2) the - // in-memory cache should already be primed before this point. - return updateProvider.combineWith( - onboardingFlowStore, APP_STARTUP_STATE_PROVIDER_ID - ) { _, onboardingState -> + println("@@@@@ computeAppStartupStateProvider") + return onboardingFlowStore.transform(APP_STARTUP_STATE_PROVIDER_ID) { onboardingState -> + println("@@@@@ transform: $onboardingState") AppStartupState.newBuilder().apply { startupMode = computeAppStartupMode(onboardingState) - buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState) + buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState, startupMode) }.build() } } - private fun updateOnboardingState(updateState: (OnboardingState) -> OnboardingState) { - val deferred = onboardingFlowStore.storeDataAsync(updateInMemoryCache = false, updateState) + 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) @@ -141,18 +126,24 @@ class AppStartupStateController @Inject constructor( } } - private fun computeBuildNoticeMode(onboardingState: OnboardingState): BuildFlavorNoticeMode { + 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 + onboardingState.lastUsedBuildFlavor != BuildFlavor.BETA && + startupMode != StartupMode.APP_IS_DEPRECATED ) { BuildFlavorNoticeMode.SHOW_BETA_NOTICE - } else BuildFlavorNoticeMode.NO_NOTICE // The user doesn't want to see the notice again. + } else BuildFlavorNoticeMode.NO_NOTICE } BuildFlavor.GENERAL_AVAILABILITY -> when (onboardingState.lastUsedBuildFlavor) { BuildFlavor.ALPHA, BuildFlavor.BETA, null -> { @@ -180,22 +171,4 @@ class AppStartupStateController @Inject constructor( expirationDate?.isBeforeToday() ?: true } else false } - - /** - * Converts a [Deferred] to a [StateFlow] in a way that avoids potentially deadlocking when - * asynchronously blocking on the [Deferred] since it leverages a background coroutine. - * - * The returned [StateFlow] will never be updated more than once, and will always start in a - * pending state. - */ - private fun Deferred.toStateFlow(): StateFlow> { - val deferred = this - return MutableStateFlow>(value = AsyncResult.Pending()).also { flow -> - CoroutineScope(backgroundDispatcher).async { - flow.emit(AsyncResult.Success(deferred.await())) - }.invokeOnCompletion { - it?.let { flow.tryEmit(AsyncResult.Failure(it)) } - } - } - } } 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..2b8ada82a98 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 @@ -13,6 +13,8 @@ import java.util.Random import java.util.UUID import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode +import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val SESSION_ID_DATA_PROVIDER_ID = "LoggingIdentifierController.session_id" private const val INSTALLATION_ID_DATA_PROVIDER_ID = "LoggingIdentifierController.installation_id" @@ -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 d7ec152f38e..a2f5b42f12d 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 @@ -35,6 +35,8 @@ import java.io.File import java.io.FileOutputStream import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode +import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val GET_PROFILES_PROVIDER_ID = "get_profiles_provider_id" private const val GET_PROFILE_PROVIDER_ID = "get_profile_provider_id" @@ -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.DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE + ).invokeOnCompletion { it?.let { oppiaLogger.e( "DOMAIN", 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..8005825c4a4 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 @@ -73,7 +73,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 +197,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..4293c6a47f3 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 @@ -15,6 +15,8 @@ import org.oppia.android.util.data.DataProviders import org.oppia.android.util.data.DataProviders.Companion.transformAsync import javax.inject.Inject import javax.inject.Singleton +import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode +import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode const val TEST_STORY_ID_0 = "test_story_id_0" const val TEST_STORY_ID_2 = "test_story_id_2" @@ -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.DO_NOT_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 a81630b49aa..b6420edf578 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,7 +6,6 @@ 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 @@ -14,7 +13,6 @@ import dagger.Module import dagger.Provides import org.junit.Test import org.junit.runner.RunWith -import org.oppia.android.app.application.testing.TestingBuildFlavorModule 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 @@ -49,22 +47,41 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton +import org.junit.Before +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.BuildFlavor +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 /** 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() @@ -92,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() } @@ -101,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) } @@ -111,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() @@ -181,7 +198,7 @@ class AppStartupStateControllerTest { @Test fun testSecondAppOpen_onboardingFlowNotDone_deprecationEnabled_beforeDepDate_appNotDeprecated() { - executeInPreviousApp { testComponent -> + executeInPreviousAppInstance { testComponent -> setUpOppiaApplicationForContext( context = testComponent.getContext(), expirationEnabled = true, @@ -199,7 +216,7 @@ class AppStartupStateControllerTest { @Test fun testSecondAppOpen_onboardingFlowNotDone_deprecationEnabled_afterDepDate_appIsDeprecated() { - executeInPreviousApp { testComponent -> + executeInPreviousAppInstance { testComponent -> setUpOppiaApplicationForContext( context = testComponent.getContext(), expirationEnabled = true, @@ -217,7 +234,7 @@ class AppStartupStateControllerTest { @Test fun testSecondAppOpen_onboardingFlowCompleted_depEnabled_beforeDepDate_appNotDeprecated() { - executeInPreviousApp { testComponent -> + executeInPreviousAppInstance { testComponent -> setUpOppiaApplicationForContext( context = testComponent.getContext(), expirationEnabled = true, @@ -239,7 +256,7 @@ class AppStartupStateControllerTest { @Test fun testSecondAppOpen_onboardingFlowCompleted_deprecationEnabled_afterDepDate_appIsDeprecated() { - executeInPreviousApp { testComponent -> + executeInPreviousAppInstance { testComponent -> setUpOppiaApplicationForContext( context = testComponent.getContext(), expirationEnabled = true, @@ -259,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) } @@ -278,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 @@ -342,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 { @@ -361,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. @@ -374,7 +887,7 @@ class AppStartupStateControllerTest { ExpirationMetaDataRetrieverModule::class, // Use real implementation to test closer to prod. LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, PlatformParameterModule::class, - PlatformParameterSingletonModule::class, TestingBuildFlavorModule::class + PlatformParameterSingletonModule::class ] ) interface TestApplicationComponent : DataProvidersInjector { 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/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/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt b/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt index efaa713fe7b..17708a7034b 100644 --- a/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt +++ b/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt @@ -66,6 +66,7 @@ class ParameterizedRunnerDelegate( override fun evaluate() { // Initialize the test prior to execution. parameterizedMethod.initializeForTest(test, iterationName) + println("@@@@@ INVOKE: $methodName in $iterationName") invoker.evaluate() } } diff --git a/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt b/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt index a109845301b..2644a1b205e 100644 --- a/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt +++ b/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt @@ -44,6 +44,7 @@ class AsyncDataSubscriptionManager @Inject constructor( * Duplicate subscriptions are ignored. */ fun associateIds(childId: Any, parentId: Any) { + println("@@@@@ associate $childId as dependent on $parentId") // TODO(#3625): Find a way to determine parent-child ID associations during subscription time to // avoid needing to store long-lived references to IDs prior to subscriptions. subscriptionLock.withLock { @@ -66,6 +67,9 @@ class AsyncDataSubscriptionManager @Inject constructor( associatedIds.getOrPut(parentId) { mutableSetOf() } += childId } } + println("@@@@@ IDs (associate): $associatedIds") + println("@@@@@ all subs (associate): $subscriptionMap") + println("@@@@@ current mgr (associate): $this") } /** @@ -73,6 +77,7 @@ class AsyncDataSubscriptionManager @Inject constructor( * parent ID from also notifying observers of the child ID. */ fun dissociateIds(childId: Any, parentId: Any) { + Exception("@@@@@ disassociate $childId from $parentId").printStackTrace(System.out) subscriptionLock.withLock { associatedIds[parentId]?.remove(childId) } } @@ -82,7 +87,14 @@ class AsyncDataSubscriptionManager @Inject constructor( */ suspend fun notifyChange(id: Any) { // First, retrieve subscribers & the closure of child IDs to notify. - val subscriptions = subscriptionLock.withLock { computeSubscriptionClosure(id) } + val subscriptions = subscriptionLock.withLock { + println("@@@@@ IDs (notify): $associatedIds") + println("@@@@@ all subs (notify): $subscriptionMap") + println("@@@@@ current mgr (notify): $this") + computeSubscriptionClosure(id) + } + + println("@@@@@ notify change for provider: $id (${subscriptions.size} subs)") // Notify all subscribers (both directly for this parent & all child IDs). subscriptions.forEach { observeChange -> observeChange() } 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..90673a6f670 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,5 +1,6 @@ package org.oppia.android.util.data +import android.app.Application import android.content.Context import androidx.lifecycle.LiveData import dagger.Reusable @@ -26,7 +27,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 @@ -45,8 +46,10 @@ class DataProviders @Inject constructor( */ fun DataProvider.transform(newId: Any, function: (I) -> O): DataProvider { val dataProviders = getDataProviders() + println("@@@@@ data providers inst (transform): $dataProviders") + println("@@@@@ context inst (transform): $application from $this") 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 +73,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 +93,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 +116,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 +144,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 } @@ -161,13 +164,14 @@ class DataProviders @Inject constructor( */ fun DataProvider.toLiveData(): LiveData> { val dataProviders = getDataProviders() + println("@@@@@ create livedata for ${getId()}") return NotifiableAsyncLiveData( dataProviders.backgroundDispatcher, dataProviders.asyncDataSubscriptionManager, this ) } private fun DataProvider.getDataProviders(): DataProviders { - val injectorProvider = context.applicationContext as DataProvidersInjectorProvider + val injectorProvider = application as DataProvidersInjectorProvider return injectorProvider.getDataProvidersInjector().getDataProviders() } } @@ -184,7 +188,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 +212,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 +281,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 +324,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) } @@ -358,6 +362,7 @@ class DataProviders @Inject constructor( override fun onActive() { super.onActive() // Subscribe to the ID immediately in case there's a value in the data provider already ready. + println("@@@@ registering livedata to ${dataProvider.getId()}") asyncDataSubscriptionManager.subscribe(dataProvider.getId(), asyncSubscriber) isActive.set(true) @@ -375,6 +380,7 @@ class DataProviders @Inject constructor( override fun onInactive() { super.onInactive() + println("@@@@ unregistering livedata from ${dataProvider.getId()}") // Stop watching for updates immediately, then cancel any existing operations. asyncDataSubscriptionManager.unsubscribe(dataProvider.getId(), asyncSubscriber) isActive.set(false) @@ -408,6 +414,7 @@ class DataProviders @Inject constructor( // mechanism which in turn always calls setValue(), even if there are no active observers. See // the override of setValue() above for the adjusted semantics this class requires to ensure // its own cache remains up-to-date. + println("@@@@@ receive update from ${dataProvider.getId()}") retrieveFromDataProvider()?.let { super.postValue(it) runningJob.set(null) 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 a4f61516e54..c42da33626f 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 @@ -49,9 +49,7 @@ class FirebaseEventLogger private constructor( private val networkConnectionUtil: NetworkConnectionUtil, private val eventBundleCreator: EventBundleCreator ) { - private val firebaseAnalytics by lazy { - FirebaseAnalytics.getInstance(context.applicationContext) - } + private val firebaseAnalytics by lazy { FirebaseAnalytics.getInstance(context) } /** * 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() {} From e3fb62c63b6419bf216242f61d09a10f96cb92a5 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Thu, 18 Aug 2022 11:59:39 -0500 Subject: [PATCH 08/19] Update TransformAndroidManifestTest.kt Correct typos. --- .../scripts/build/TransformAndroidManifestTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt index 4fa19a2faa2..fadf3154983 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt +++ b/scripts/src/javatests/org/oppia/android/scripts/build/TransformAndroidManifestTest.kt @@ -98,7 +98,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_threeAgs_failsWithUsageString() { + fun testUtility_threeArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) @@ -114,7 +114,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_fourAgs_failsWithUsageString() { + fun testUtility_fourArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) @@ -131,7 +131,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_fiveAgs_failsWithUsageString() { + fun testUtility_fiveArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) @@ -149,7 +149,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_sixAgs_failsWithUsageString() { + fun testUtility_sixArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) @@ -168,7 +168,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_sevenAgs_failsWithUsageString() { + fun testUtility_sevenArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) @@ -188,7 +188,7 @@ class TransformAndroidManifestTest { } @Test - fun testUtility_eightAgs_failsWithUsageString() { + fun testUtility_eightArgs_failsWithUsageString() { initializeEmptyGitRepository() val manifestFile = tempFolder.newFile(TEST_MANIFEST_FILE_NAME) From 3b9b6afb1a0c290b49a2eee7bf80f2472d3467b9 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 26 Aug 2022 21:14:56 -0500 Subject: [PATCH 09/19] Fix tests & static checks. This also removes temporary debug code and TODOs, and finishes the tests for SplashActivity. --- .github/CODEOWNERS | 4 +- app/src/main/AndroidManifest.xml | 2 + .../BetaNoticeDialogFragmentTestActivity.kt | 10 +- ...UpgradeNoticeDialogFragmentTestActivity.kt | 12 +- .../app/splash/SplashActivityPresenter.kt | 49 +- app/src/main/res/layout/splash_activity.xml | 1 + .../main/res/values/untranslated_strings.xml | 2 + .../notice/BetaNoticeDialogFragmentTest.kt | 14 +- ...labilityUpgradeNoticeDialogFragmentTest.kt | 14 +- .../org/oppia/android/app/splash/BUILD.bazel | 3 +- .../android/app/splash/SplashActivityTest.kt | 760 ++++++++++++++++-- .../alpha/AlphaBuildFlavorModuleTest.kt | 2 +- .../beta/BetaBuildFlavorModuleTest.kt | 2 +- .../dev/DeveloperBuildFlavorModuleTest.kt | 2 +- .../application/ga/GaBuildFlavorModuleTest.kt | 2 +- .../testing/TestingBuildFlavorModuleTest.kt | 2 +- .../data/persistence/PersistentCacheStore.kt | 41 +- .../persistence/PersistentCacheStoreTest.kt | 72 +- .../ExplorationCheckpointController.kt | 4 +- .../onboarding/AppStartupStateController.kt | 10 +- .../LoggingIdentifierController.kt | 4 +- .../profile/ProfileManagementController.kt | 4 +- .../topic/PrimeTopicAssetsControllerImpl.kt | 1 - .../domain/topic/StoryProgressController.kt | 4 +- .../AppStartupStateControllerTest.kt | 24 +- .../file_content_validation_checks.textproto | 2 + scripts/assets/test_file_exemptions.textproto | 2 + .../junit/ParameterizedRunnerDelegate.kt | 1 - .../util/data/AsyncDataSubscriptionManager.kt | 10 - .../oppia/android/util/data/DataProviders.kt | 7 - .../android/util/data/DataProvidersTest.kt | 18 +- 31 files changed, 854 insertions(+), 231 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a9aaf5ce694..6329d2c44cc 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/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4370def3867..84e00926696 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -87,9 +87,11 @@ android:theme="@style/OppiaThemeWithoutActionBar" /> { - // 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) { + // 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 + ) + } } - 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() { - println("@@@@@ process startup: $startupMode") when (startupMode) { StartupMode.USER_IS_ONBOARDED -> { activity.startActivity(ProfileChooserActivity.createProfileChooserActivity(activity)) diff --git a/app/src/main/res/layout/splash_activity.xml b/app/src/main/res/layout/splash_activity.xml index 3757a5c97d7..f8d6bb317d5 100644 --- a/app/src/main/res/layout/splash_activity.xml +++ b/app/src/main/res/layout/splash_activity.xml @@ -40,6 +40,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/logo_top_margin" /> 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/notice/BetaNoticeDialogFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt index 5e3f3397279..07edd247d09 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt @@ -16,8 +16,6 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.BindsInstance import dagger.Component -import javax.inject.Inject -import javax.inject.Singleton import org.hamcrest.Matcher import org.junit.Before import org.junit.Rule @@ -38,10 +36,6 @@ 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.testing.OppiaTestRule -import org.oppia.android.testing.junit.InitializeDefaultLocaleRule -import org.robolectric.annotation.Config -import org.robolectric.annotation.LooperMode import org.oppia.android.app.notice.testing.BetaNoticeDialogFragmentTestActivity import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule @@ -76,9 +70,12 @@ import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModu 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 @@ -93,7 +90,10 @@ 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.oppia.android.testing.threading.TestCoroutineDispatchers +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. 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 index ba45d9fccd7..263def93b18 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt @@ -16,8 +16,6 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.BindsInstance import dagger.Component -import javax.inject.Inject -import javax.inject.Singleton import org.hamcrest.Matcher import org.junit.Before import org.junit.Rule @@ -39,10 +37,6 @@ 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.testing.OppiaTestRule -import org.oppia.android.testing.junit.InitializeDefaultLocaleRule -import org.robolectric.annotation.Config -import org.robolectric.annotation.LooperMode import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.topic.PracticeTabModule @@ -76,9 +70,12 @@ import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModu 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 @@ -93,7 +90,10 @@ 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.oppia.android.testing.threading.TestCoroutineDispatchers +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. 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 index 5e4b171a504..8a15fa58892 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel +++ b/app/src/sharedTest/java/org/oppia/android/app/splash/BUILD.bazel @@ -6,10 +6,9 @@ load("@dagger//:workspace_defs.bzl", "dagger_rules") load("//app:app_test.bzl", "app_test") load("//app:test_with_resources.bzl", "test_with_resources") -# TODO: Revert src & R import. app_test( name = "SplashActivityTest", - processed_src = "SplashActivityTest.kt", #test_with_resources("SplashActivityTest.kt"), + processed_src = test_with_resources("SplashActivityTest.kt"), test_class = "org.oppia.android.app.splash.SplashActivityTest", deps = [ ":dagger", 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 e881e9cf651..aba3d274b88 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 @@ -10,6 +10,7 @@ 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 @@ -24,12 +25,14 @@ 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 import org.junit.Test import org.junit.runner.RunWith -import org.oppia.android.app.test.R +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 @@ -39,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 @@ -88,6 +92,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 @@ -113,18 +124,9 @@ 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 -import org.hamcrest.Matcher -import org.oppia.android.app.model.BuildFlavor -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.util.data.AsyncDataSubscriptionManager /** * Tests for [SplashActivity]. For context on the activity test rule setup see: @@ -144,8 +146,8 @@ class SplashActivityTest { @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers @Inject lateinit var fakeMetaDataRetriever: FakeExpirationMetaDataRetriever @Inject lateinit var appLanguageLocaleHandler: AppLanguageLocaleHandler - // TODO: Still needed? @Inject lateinit var monitorFactory: DataProviderTestMonitor.Factory + @Inject lateinit var appStartupStateController: AppStartupStateController @Parameter lateinit var firstOpen: String @Parameter lateinit var secondOpen: String @@ -170,7 +172,7 @@ class SplashActivityTest { fun testSplashActivity_initialOpen_routesToOnboardingActivity() { initializeTestApplication() - launchSplashActivity { + launchSplashActivityFully { intended(hasComponent(OnboardingActivity::class.java.name)) } } @@ -180,7 +182,7 @@ class SplashActivityTest { simulateAppAlreadyOnboarded() initializeTestApplication() - launchSplashActivity { + launchSplashActivityFully { intended(hasComponent(ProfileChooserActivity::class.java.name)) } } @@ -191,7 +193,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = true) setAutoAppExpirationDate(dateStringAfterToday()) - launchSplashActivity { + launchSplashActivityFully { // App deprecation is enabled, but this app hasn't yet expired. intended(hasComponent(OnboardingActivity::class.java.name)) } @@ -203,7 +205,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = true) setAutoAppExpirationDate(dateStringBeforeToday()) - launchSplashActivity { + launchSplashActivityFully { // The current app is expired. onView(withText(R.string.unsupported_app_version_dialog_title)) .inRoot(isDialog()) @@ -217,7 +219,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = true) setAutoAppExpirationDate(dateStringBeforeToday()) - launchSplashActivity { scenario -> + launchSplashActivityFully { scenario -> onView(withText(R.string.unsupported_app_version_dialog_close_button_text)) .inRoot(isDialog()) .perform(click()) @@ -236,7 +238,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = false) setAutoAppExpirationDate(dateStringBeforeToday()) - launchSplashActivity { + 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)) @@ -250,7 +252,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = true) setAutoAppExpirationDate(dateStringAfterToday()) - launchSplashActivity { + 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)) @@ -264,7 +266,7 @@ class SplashActivityTest { setAutoAppExpirationEnabled(enabled = true) setAutoAppExpirationDate(dateStringBeforeToday()) - launchSplashActivity { + launchSplashActivityFully { // Reopening the app after it expires should prevent further access. onView(withText(R.string.unsupported_app_version_dialog_title)) .inRoot(isDialog()) @@ -278,7 +280,7 @@ class SplashActivityTest { initializeTestApplication() forceDefaultLocale(Locale.ENGLISH) - launchSplashActivity { + 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() @@ -299,7 +301,7 @@ class SplashActivityTest { initializeTestApplication() forceDefaultLocale(EGYPT_ARABIC_LOCALE) - launchSplashActivity { + 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() @@ -314,7 +316,7 @@ class SplashActivityTest { initializeTestApplication() forceDefaultLocale(BRAZIL_PORTUGUESE_LOCALE) - launchSplashActivity { + 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() @@ -328,7 +330,7 @@ class SplashActivityTest { initializeTestApplication() forceDefaultLocale(TURKEY_TURKISH_LOCALE) - launchSplashActivity { + launchSplashActivityFully { // Verify that the context is the default state (due to the unsupported locale). val displayLocale = appLanguageLocaleHandler.getDisplayLocale() val languageDefinition = displayLocale.localeContext.languageDefinition @@ -344,7 +346,7 @@ class SplashActivityTest { corruptCacheFile() initializeTestApplication() - launchSplashActivity { + launchSplashActivityFully { // Verify that the context is the default state (due to the unsupported locale). val displayLocale = appLanguageLocaleHandler.getDisplayLocale() val context = displayLocale.localeContext @@ -363,7 +365,7 @@ class SplashActivityTest { corruptCacheFile() initializeTestApplication() - launchSplashActivity { + launchSplashActivityFully { // Verify that an initialization failure leads to the onboarding activity by default. intended(hasComponent(OnboardingActivity::class.java.name)) } @@ -373,7 +375,7 @@ class SplashActivityTest { fun testSplashActivity_hasCorrectActivityLabel() { initializeTestApplication() - launchSplashActivity { scenario -> + launchSplashActivityFully { scenario -> scenario.onActivity { activity -> val title = activity.title @@ -382,6 +384,16 @@ class SplashActivityTest { } } + @Test + fun testSplashActivity_newUser_firstTimeOpeningBetaFlavor_doesNotShowBetaNotice() { + initializeTestApplicationWithFlavor(BuildFlavor.BETA) + + 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"), @@ -394,7 +406,7 @@ class SplashActivityTest { initializeTestApplicationWithFlavor(secondOpenFlavor) - launchSplashActivity { + launchSplashActivityFully { onDialogView(withText(R.string.beta_notice_dialog_title)).check(matches(isDisplayed())) onDialogView(withId(R.id.beta_notice_dialog_message)).check(matches(isDisplayed())) } @@ -411,7 +423,7 @@ class SplashActivityTest { simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor) initializeTestApplicationWithFlavor(secondOpenFlavor) - launchSplashActivity { + launchSplashActivityFully { // Close the notice. onDialogView(withText(R.string.beta_notice_dialog_close_button_text)).perform(click()) testCoroutineDispatchers.runCurrent() @@ -432,56 +444,585 @@ class SplashActivityTest { simulateAppAlreadyOpenedWithFlavor(firstOpenFlavor) initializeTestApplicationWithFlavor(secondOpenFlavor) - launchSplashActivity { - // Close the notice. + 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)) } } - // TODO: Add new tests. - // - // - // - // testSplashActivity_newUser_dismissBetaNotice_reopenApp_doesNotShowNotice - // testSplashActivity_newUser_dismissBetaNotice_retriggerNotice_showsBetaNotice - // testSplashActivity_newUser_dismissBetaNoticeForever_retriggerNotice_doesNotShowNotice - - // testSplashActivity_onboarded_gaFlavorTransitions_showsGaUpgradeNotice - // testSplashActivity_onboarded_gaFlavorTransitions_closeNotice_routesToProfileChooser - // testSplashActivity_onboarded_gaFlavorTransitions_doNotShowAgain_routesToProfileChooser - // testSplashActivity_onboarded_dismissGaNotice_reopenApp_doesNotShowNotice - // testSplashActivity_onboarded_dismissGaNotice_retriggerNotice_showsGaNotice - // testSplashActivity_onboarded_dismissGaNoticeForever_retriggerNotice_doesNotShowNotice - - // testSplashActivity_newUser_ignoredFlavorTransitions_routesToOnboardingFlow - // testSplashActivity_onboarded_ignoredFlavorTransitions_routesToProfileChooser - // testSplashActivity_appDeprecated_allFlavorTransitions_showsDeprecationNotice - - // (Wait ones are Robo-only) - // testSplashActivity_onboarded_devFlavor_showDevText - // testSplashActivity_onboarded_alphaFlavor_showAlphaText - // testSplashActivity_onboarded_betaFlavor_showBetaText - // testSplashActivity_onboarded_testingFlavor_doesNotWaitToStart - // testSplashActivity_onboarded_devFlavor_doesNotWaitToStart - // testSplashActivity_onboarded_alphaFlavor_waitsTwoSecondsToStart - // testSplashActivity_onboarded_betaFlavor_waitsTwoSecondsToStart - // testSplashActivity_onboarded_gaFlavor_doesNotWaitToStart + @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) + + // 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() { - println("@@@@@ root application: ${ApplicationProvider.getApplicationContext()}") runInNewTestApplication { - println("@@@@@ create monitor; current context: $this") - println("@@@@@ expected test context: $asdfContext, app context: ${asdfContext.applicationContext}") - println("@@@@@ separate mgr: $asdf") - println("@@@@@ app controller: $appStartupStateController") val monitor = monitorFactory.createMonitor(appStartupStateController.getAppStartupState()) - println("@@@@@ wait for execution") testCoroutineDispatchers.advanceUntilIdle() - println("@@@@@ finished wait") monitor.ensureNextResultIsSuccess() } } @@ -508,6 +1049,39 @@ class SplashActivityTest { 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() @@ -529,14 +1103,31 @@ class SplashActivityTest { 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).also { - testCoroutineDispatchers.advanceUntilIdle() - }.use(testBlock) + ActivityScenario.launch(openFromLauncher).use(testBlock) } private fun setAutoAppExpirationEnabled(enabled: Boolean) { @@ -629,25 +1220,18 @@ class SplashActivityTest { fun getMonitorFactory(): DataProviderTestMonitor.Factory - // TODO: Remove. - fun getAsdf(): AsyncDataSubscriptionManager - fun getContext(): Context - 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 by lazy { component.getAppStartupStateController() } - val testCoroutineDispatchers by lazy { component.getTestCoroutineDispatchers() } - val monitorFactory by lazy { component.getMonitorFactory() } - val asdf by lazy { component.getAsdf() } - val asdfContext by lazy { component.getContext() } + 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) @@ -658,6 +1242,16 @@ class SplashActivityTest { } override fun getApplicationInjector(): ApplicationInjector = component + + fun recreateDaggerGraph() { + component = createTestApplicationComponent() + } + + private fun createTestApplicationComponent(): TestApplicationComponent { + return DaggerSplashActivityTest_TestApplicationComponent.builder() + .setApplication(this) + .build() + } } private companion object { 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 index 7ebe4b4faa8..f40c984f7b4 100644 --- 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 @@ -12,11 +12,11 @@ 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 -import org.oppia.android.app.model.BuildFlavor /** Tests for [AlphaBuildFlavorModule]. */ // FunctionName: test names are conventionally named with underscores. 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 index b2c32b7c848..efbe6eab3e7 100644 --- 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 @@ -12,11 +12,11 @@ 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 -import org.oppia.android.app.model.BuildFlavor /** Tests for [BetaBuildFlavorModule]. */ // FunctionName: test names are conventionally named with underscores. 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 index 1abb9629162..9f5a1c6e7ee 100644 --- 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 @@ -12,11 +12,11 @@ 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 -import org.oppia.android.app.model.BuildFlavor /** Tests for [DeveloperBuildFlavorModule]. */ // FunctionName: test names are conventionally named with underscores. 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 index 7eadeb138f2..b8a848a432e 100644 --- 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 @@ -12,11 +12,11 @@ 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 -import org.oppia.android.app.model.BuildFlavor /** Tests for [GaBuildFlavorModule]. */ // FunctionName: test names are conventionally named with underscores. 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 index feb35c4b596..f1b3136ccb0 100644 --- 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 @@ -12,11 +12,11 @@ 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 -import org.oppia.android.app.model.BuildFlavor /** Tests for [TestingBuildFlavorModule]. */ // FunctionName: test names are conventionally named with underscores. 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 010d2fae47e..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,7 +1,6 @@ package org.oppia.android.data.persistence import android.app.Application -import android.content.Context import androidx.annotation.GuardedBy import com.google.protobuf.MessageLite import kotlinx.coroutines.Deferred @@ -55,7 +54,6 @@ class PersistentCacheStore private constructor( init { cache.observeChanges { oldValue, newValue -> - println("@@@@@ cache store changes: $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 @@ -72,9 +70,7 @@ class PersistentCacheStore private constructor( override fun getId(): Any = providerId override suspend fun retrieveData(): AsyncResult { - println("@@@@@ retrieveData()") cache.readIfPresentAsync().await().let { cachePayload -> - println("@@@@ retrieving payload: $cachePayload") // First, determine whether the current cache has been attempted to be retrieved from disk. if (cachePayload.state == CacheState.UNLOADED) { deferLoadFile() @@ -135,11 +131,11 @@ class PersistentCacheStore private constructor( * @return a [Deferred] tracking the success/failure of priming this cache store */ fun primeInMemoryAndDiskCacheAsync( - updateMode: UpdateMode, publishMode: PublishMode, update: (T) -> T = { it } + updateMode: UpdateMode, + publishMode: PublishMode, + update: (T) -> T = { it } ): Deferred { - println("@@@@@ primeInMemoryAndDiskCacheAsync") return cache.updateIfPresentAsync { cachePayload -> - println("@@@@@ updateIfPresentAsync for prime: $cachePayload") // 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) { @@ -317,7 +313,8 @@ class PersistentCacheStore private constructor( } private fun CachePayload.maybeReprimePayload( - updateMode: UpdateMode, initialize: (T) -> T + updateMode: UpdateMode, + initialize: (T) -> T ): CachePayload { return when (updateMode) { UpdateMode.UPDATE_IF_NEW_CACHE -> this // Nothing extra to do. @@ -341,13 +338,36 @@ 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 } @@ -369,16 +389,13 @@ class PersistentCacheStore private constructor( * Use this method when data is shared by all profiles. */ fun create(cacheName: String, initialValue: T): PersistentCacheStore { - println("@@@@@ sanity check: create cache $cacheName") return PersistentCacheStore( application, cacheFactory, asyncDataSubscriptionManager, cacheName, initialValue - ).also { - Exception("@@@@@ create store with context: $application (store: $it)").printStackTrace(System.out) - } + ) } /** 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 e5699623497..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 @@ -43,19 +56,6 @@ import java.lang.IllegalStateException import javax.inject.Inject import javax.inject.Singleton import kotlin.reflect.full.staticFunctions -import org.junit.Rule -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.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.util.data.AsyncDataSubscriptionManager -import org.oppia.android.util.data.ObserveAsyncChange private const val CACHE_NAME_1 = "test_cache_1" private const val CACHE_NAME_2 = "test_cache_2" @@ -162,7 +162,8 @@ class PersistentCacheStoreTest { // 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -206,7 +207,8 @@ 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -223,7 +225,8 @@ 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -241,7 +244,8 @@ 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -260,7 +264,8 @@ class PersistentCacheStoreTest { // update was posted before the read occurred. assertThat(storeOp1.isCompleted).isTrue() assertThat(storeOp2.isCompleted).isTrue() - assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_INT_MESSAGE_V2) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V2) } @Test @@ -322,7 +327,8 @@ class PersistentCacheStoreTest { // 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_INT_MESSAGE_V2) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V2) } @Test @@ -338,7 +344,8 @@ class PersistentCacheStoreTest { // 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -372,7 +379,8 @@ 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_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore1b)) + .isEqualTo(TEST_INT_MESSAGE_V1) assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2b)).isEqualToDefaultInstance() } @@ -1224,7 +1232,7 @@ class PersistentCacheStoreTest { val primeDeferred = cacheStore.primeInMemoryAndDiskCacheAsync( UPDATE_IF_NEW_CACHE, DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE - ) { it.addString(TEST_STR_V1) } + ) { 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 @@ -1352,7 +1360,8 @@ class PersistentCacheStoreTest { assertThat(storeOp1.isCompleted).isTrue() assertThat(storeOp2.isCompleted).isTrue() assertThat(primeOp.isCompleted).isTrue() - assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_INT_MESSAGE_V1) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V1) } @Test @@ -1378,7 +1387,8 @@ class PersistentCacheStoreTest { assertThat(storeOp1.isCompleted).isTrue() assertThat(storeOp2.isCompleted).isTrue() assertThat(primeOp.isCompleted).isTrue() - assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)).isEqualTo(TEST_INT_MESSAGE_V2) + assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore2)) + .isEqualTo(TEST_INT_MESSAGE_V2) } @Test @@ -1534,20 +1544,22 @@ class PersistentCacheStoreTest { assertThat(onDiskValue.strValue).isEqualTo("different initial first transform") } - private fun subscribeToCacheStoreChanges(cacheStore: PersistentCacheStore) { + private fun subscribeToCacheStoreChanges(cacheStore: PersistentCacheStore) { asyncDataSubscriptionManager.subscribe( cacheStore.getId(), mockSubscriptionCallback.toAsyncChange() ) } - private fun loadCacheIntoMemory(cacheStore: PersistentCacheStore) { + 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 + 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. @@ -1555,7 +1567,7 @@ class PersistentCacheStoreTest { assertThat(monitorFactory.waitForNextSuccessfulResult(cacheStore)).isEqualTo(value) } - private fun verifyDiskCacheHasValue(cacheName: String, value: T) { + private fun verifyDiskCacheHasValue(cacheName: String, value: T) { assertThat(readFileCache(cacheName)).isEqualTo(value) } @@ -1566,7 +1578,7 @@ class PersistentCacheStoreTest { 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() 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 24e2308e59b..97b091f9ba0 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 @@ -16,8 +18,6 @@ import org.oppia.android.util.data.DataProviders import org.oppia.android.util.data.DataProviders.Companion.transformAsync import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode -import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val CACHE_NAME = "exploration_checkpoint_database" private const val RETRIEVE_EXPLORATION_CHECKPOINT_DATA_PROVIDER_ID = 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 fe01411e978..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,5 @@ package org.oppia.android.domain.onboarding -import javax.inject.Inject -import javax.inject.Singleton import org.oppia.android.app.model.AppStartupState import org.oppia.android.app.model.AppStartupState.BuildFlavorNoticeMode import org.oppia.android.app.model.AppStartupState.StartupMode @@ -13,6 +11,8 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.transform import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.locale.OppiaLocale +import javax.inject.Inject +import javax.inject.Singleton private const val APP_STARTUP_STATE_PROVIDER_ID = "app_startup_state_data_provider_id" @@ -36,7 +36,6 @@ class AppStartupStateController @Inject constructor( // 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). - println("@@@@@ initing controller: $this") onboardingFlowStore.primeInMemoryAndDiskCacheAsync( updateMode = PersistentCacheStore.UpdateMode.UPDATE_ALWAYS, publishMode = PersistentCacheStore.PublishMode.DO_NOT_PUBLISH_TO_IN_MEMORY_CACHE @@ -91,9 +90,7 @@ class AppStartupStateController @Inject constructor( fun getAppStartupState(): DataProvider = appStartupStateDataProvider private fun computeAppStartupStateProvider(): DataProvider { - println("@@@@@ computeAppStartupStateProvider") return onboardingFlowStore.transform(APP_STARTUP_STATE_PROVIDER_ID) { onboardingState -> - println("@@@@@ transform: $onboardingState") AppStartupState.newBuilder().apply { startupMode = computeAppStartupMode(onboardingState) buildFlavorNoticeMode = computeBuildNoticeMode(onboardingState, startupMode) @@ -127,7 +124,8 @@ class AppStartupStateController @Inject constructor( } private fun computeBuildNoticeMode( - onboardingState: OnboardingState, startupMode: StartupMode + onboardingState: OnboardingState, + startupMode: StartupMode ): BuildFlavorNoticeMode { return when (currentBuildFlavor) { BuildFlavor.TESTING, BuildFlavor.BUILD_FLAVOR_UNSPECIFIED, BuildFlavor.UNRECOGNIZED -> 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 2b8ada82a98..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 @@ -13,8 +15,6 @@ import java.util.Random import java.util.UUID import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode -import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val SESSION_ID_DATA_PROVIDER_ID = "LoggingIdentifierController.session_id" private const val INSTALLATION_ID_DATA_PROVIDER_ID = "LoggingIdentifierController.installation_id" 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 2af8408320e..aa8335a61ae 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 @@ -35,8 +37,6 @@ import java.io.File import java.io.FileOutputStream import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode -import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode private const val GET_PROFILES_PROVIDER_ID = "get_profiles_provider_id" private const val GET_PROFILE_PROVIDER_ID = "get_profile_provider_id" 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 8005825c4a4..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 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 4293c6a47f3..3118d52def2 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 @@ -15,8 +17,6 @@ import org.oppia.android.util.data.DataProviders import org.oppia.android.util.data.DataProviders.Companion.transformAsync import javax.inject.Inject import javax.inject.Singleton -import org.oppia.android.data.persistence.PersistentCacheStore.PublishMode -import org.oppia.android.data.persistence.PersistentCacheStore.UpdateMode const val TEST_STORY_ID_0 = "test_story_id_0" const val TEST_STORY_ID_2 = "test_story_id_2" 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 b6420edf578..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 @@ -11,11 +11,17 @@ 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 @@ -25,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 @@ -47,18 +59,6 @@ import java.util.Date import java.util.Locale import javax.inject.Inject import javax.inject.Singleton -import org.junit.Before -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.BuildFlavor -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 /** Tests for [AppStartupStateController]. */ // FunctionName: test names are conventionally named with underscores. diff --git a/scripts/assets/file_content_validation_checks.textproto b/scripts/assets/file_content_validation_checks.textproto index 175be60d218..0d1317e8ad2 100644 --- a/scripts/assets/file_content_validation_checks.textproto +++ b/scripts/assets/file_content_validation_checks.textproto @@ -288,6 +288,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" @@ -298,6 +299,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/test_file_exemptions.textproto b/scripts/assets/test_file_exemptions.textproto index 362d28d3e85..aa2c8991072 100644 --- a/scripts/assets/test_file_exemptions.textproto +++ b/scripts/assets/test_file_exemptions.textproto @@ -222,6 +222,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/junit/ParameterizedRunnerDelegate.kt b/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt index 17708a7034b..efaa713fe7b 100644 --- a/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt +++ b/testing/src/main/java/org/oppia/android/testing/junit/ParameterizedRunnerDelegate.kt @@ -66,7 +66,6 @@ class ParameterizedRunnerDelegate( override fun evaluate() { // Initialize the test prior to execution. parameterizedMethod.initializeForTest(test, iterationName) - println("@@@@@ INVOKE: $methodName in $iterationName") invoker.evaluate() } } diff --git a/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt b/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt index 2644a1b205e..1eff12ca92e 100644 --- a/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt +++ b/utility/src/main/java/org/oppia/android/util/data/AsyncDataSubscriptionManager.kt @@ -44,7 +44,6 @@ class AsyncDataSubscriptionManager @Inject constructor( * Duplicate subscriptions are ignored. */ fun associateIds(childId: Any, parentId: Any) { - println("@@@@@ associate $childId as dependent on $parentId") // TODO(#3625): Find a way to determine parent-child ID associations during subscription time to // avoid needing to store long-lived references to IDs prior to subscriptions. subscriptionLock.withLock { @@ -67,9 +66,6 @@ class AsyncDataSubscriptionManager @Inject constructor( associatedIds.getOrPut(parentId) { mutableSetOf() } += childId } } - println("@@@@@ IDs (associate): $associatedIds") - println("@@@@@ all subs (associate): $subscriptionMap") - println("@@@@@ current mgr (associate): $this") } /** @@ -77,7 +73,6 @@ class AsyncDataSubscriptionManager @Inject constructor( * parent ID from also notifying observers of the child ID. */ fun dissociateIds(childId: Any, parentId: Any) { - Exception("@@@@@ disassociate $childId from $parentId").printStackTrace(System.out) subscriptionLock.withLock { associatedIds[parentId]?.remove(childId) } } @@ -88,14 +83,9 @@ class AsyncDataSubscriptionManager @Inject constructor( suspend fun notifyChange(id: Any) { // First, retrieve subscribers & the closure of child IDs to notify. val subscriptions = subscriptionLock.withLock { - println("@@@@@ IDs (notify): $associatedIds") - println("@@@@@ all subs (notify): $subscriptionMap") - println("@@@@@ current mgr (notify): $this") computeSubscriptionClosure(id) } - println("@@@@@ notify change for provider: $id (${subscriptions.size} subs)") - // Notify all subscribers (both directly for this parent & all child IDs). subscriptions.forEach { observeChange -> observeChange() } } 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 90673a6f670..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,7 +1,6 @@ package org.oppia.android.util.data import android.app.Application -import android.content.Context import androidx.lifecycle.LiveData import dagger.Reusable import kotlinx.coroutines.CoroutineDispatcher @@ -46,8 +45,6 @@ class DataProviders @Inject constructor( */ fun DataProvider.transform(newId: Any, function: (I) -> O): DataProvider { val dataProviders = getDataProviders() - println("@@@@@ data providers inst (transform): $dataProviders") - println("@@@@@ context inst (transform): $application from $this") dataProviders.asyncDataSubscriptionManager.associateIds(newId, getId()) return object : DataProvider(application) { override fun getId(): Any = newId @@ -164,7 +161,6 @@ class DataProviders @Inject constructor( */ fun DataProvider.toLiveData(): LiveData> { val dataProviders = getDataProviders() - println("@@@@@ create livedata for ${getId()}") return NotifiableAsyncLiveData( dataProviders.backgroundDispatcher, dataProviders.asyncDataSubscriptionManager, this ) @@ -362,7 +358,6 @@ class DataProviders @Inject constructor( override fun onActive() { super.onActive() // Subscribe to the ID immediately in case there's a value in the data provider already ready. - println("@@@@ registering livedata to ${dataProvider.getId()}") asyncDataSubscriptionManager.subscribe(dataProvider.getId(), asyncSubscriber) isActive.set(true) @@ -380,7 +375,6 @@ class DataProviders @Inject constructor( override fun onInactive() { super.onInactive() - println("@@@@ unregistering livedata from ${dataProvider.getId()}") // Stop watching for updates immediately, then cancel any existing operations. asyncDataSubscriptionManager.unsubscribe(dataProvider.getId(), asyncSubscriber) isActive.set(false) @@ -414,7 +408,6 @@ class DataProviders @Inject constructor( // mechanism which in turn always calls setValue(), even if there are no active observers. See // the override of setValue() above for the adjusted semantics this class requires to ensure // its own cache remains up-to-date. - println("@@@@@ receive update from ${dataProvider.getId()}") retrieveFromDataProvider()?.let { super.postValue(it) runningJob.set(null) 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. From 9b30a686fc80831dcad83bee95d78a7bc807114a Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Sun, 4 Sep 2022 20:03:01 -0500 Subject: [PATCH 10/19] Post-merge fixes. --- .../android/app/application/alpha/BUILD.bazel | 16 +- .../AlphaKenyaApplicationComponent.kt | 3 +- .../app/application/alphakenya/BUILD.bazel | 1 + .../beta/BetaApplicationComponent.kt | 12 +- .../application/ga/GaApplicationComponent.kt | 12 +- .../notice/BetaNoticeDialogFragmentTest.kt | 7 +- ...labilityUpgradeNoticeDialogFragmentTest.kt | 7 +- .../app/parser/CustomBulletSpanTest.kt | 288 ------------------ 8 files changed, 44 insertions(+), 302 deletions(-) delete mode 100644 app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt diff --git a/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel index 7c374b71cb8..85e2d2f0e26 100644 --- a/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel +++ b/app/src/main/java/org/oppia/android/app/application/alpha/BUILD.bazel @@ -9,7 +9,6 @@ kt_android_library( name = "alpha_application", srcs = [ "AlphaApplicationComponent.kt", - "AlphaBuildFlavorModule.kt", "AlphaOppiaApplication.kt", ], visibility = [ @@ -17,6 +16,7 @@ kt_android_library( "//app/src/test/java/org/oppia/android/app/application/alpha:__pkg__", ], deps = [ + ":alpha_build_flavor_module", ":dagger", "//app", "//app/src/main/java/org/oppia/android/app/application:abstract_application", @@ -27,4 +27,18 @@ kt_android_library( ], ) +kt_android_library( + name = "alpha_build_flavor_module", + srcs = [ + "AlphaBuildFlavorModule.kt", + ], + visibility = [ + "//app/src/main/java/org/oppia/android/app/application/alphakenya:__pkg__", + ], + deps = [ + ":dagger", + "//model/src/main/proto:version_java_proto_lite", + ], +) + dagger_rules() diff --git a/app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaApplicationComponent.kt index 2d20f037a76..d66d01a719f 100644 --- a/app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/alphakenya/AlphaKenyaApplicationComponent.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.alpha.AlphaBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.IntentFactoryShimModule @@ -95,7 +96,7 @@ import javax.inject.Singleton SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, HintsAndSolutionProdModule::class, MetricLogSchedulerModule::class, PerformanceMetricsLoggerModule::class, PerformanceMetricsAssessorModule::class, - PerformanceMetricsConfigurationsModule::class + PerformanceMetricsConfigurationsModule::class, AlphaBuildFlavorModule::class ] ) interface AlphaKenyaApplicationComponent : ApplicationComponent { diff --git a/app/src/main/java/org/oppia/android/app/application/alphakenya/BUILD.bazel b/app/src/main/java/org/oppia/android/app/application/alphakenya/BUILD.bazel index 732bb23a39a..f27362050ba 100644 --- a/app/src/main/java/org/oppia/android/app/application/alphakenya/BUILD.bazel +++ b/app/src/main/java/org/oppia/android/app/application/alphakenya/BUILD.bazel @@ -19,6 +19,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/alpha:alpha_build_flavor_module", "//utility/src/main/java/org/oppia/android/util/logging/firebase:prod_module", "//utility/src/main/java/org/oppia/android/util/networking:prod_module", ], diff --git a/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt index 4002d1a99de..b772edd8689 100644 --- a/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt @@ -33,8 +33,10 @@ 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.analytics.PerformanceMetricsLoggerModule import org.oppia.android.domain.oppialogger.exceptions.UncaughtExceptionLoggerModule -import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +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.platformparameter.syncup.PlatformParameterSyncUpWorkerModule @@ -50,6 +52,8 @@ 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.logging.firebase.LogReportingModule +import org.oppia.android.util.logging.performancemetrics.PerformanceMetricsAssessorModule +import org.oppia.android.util.logging.performancemetrics.PerformanceMetricsConfigurationsModule import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule import org.oppia.android.util.networking.NetworkConnectionUtilProdModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule @@ -77,7 +81,7 @@ import javax.inject.Singleton LogStorageModule::class, IntentFactoryShimModule::class, ViewBindingShimModule::class, PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, RatioInputModule::class, UncaughtExceptionLoggerModule::class, - ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, + ApplicationStartupListenerModule::class, LogReportWorkerModule::class, WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, PlatformParameterModule::class, PlatformParameterSingletonModule::class, @@ -89,7 +93,9 @@ import javax.inject.Singleton LoggingIdentifierModule::class, ApplicationLifecycleModule::class, NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, - HintsAndSolutionProdModule::class, BetaBuildFlavorModule::class + HintsAndSolutionProdModule::class, MetricLogSchedulerModule::class, + PerformanceMetricsLoggerModule::class, PerformanceMetricsAssessorModule::class, + PerformanceMetricsConfigurationsModule::class, BetaBuildFlavorModule::class ] ) interface BetaApplicationComponent : ApplicationComponent { diff --git a/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt index 8c6e98fb28c..d9eb173fb4f 100644 --- a/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt +++ b/app/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt @@ -33,8 +33,10 @@ 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.analytics.PerformanceMetricsLoggerModule import org.oppia.android.domain.oppialogger.exceptions.UncaughtExceptionLoggerModule -import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule +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.platformparameter.syncup.PlatformParameterSyncUpWorkerModule @@ -50,6 +52,8 @@ 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.logging.firebase.LogReportingModule +import org.oppia.android.util.logging.performancemetrics.PerformanceMetricsAssessorModule +import org.oppia.android.util.logging.performancemetrics.PerformanceMetricsConfigurationsModule import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule import org.oppia.android.util.networking.NetworkConnectionUtilProdModule import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule @@ -77,7 +81,7 @@ import javax.inject.Singleton LogStorageModule::class, IntentFactoryShimModule::class, ViewBindingShimModule::class, PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, RatioInputModule::class, UncaughtExceptionLoggerModule::class, - ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, + ApplicationStartupListenerModule::class, LogReportWorkerModule::class, WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, FirebaseLogUploaderModule::class, NetworkModule::class, PracticeTabModule::class, PlatformParameterModule::class, PlatformParameterSingletonModule::class, @@ -89,7 +93,9 @@ import javax.inject.Singleton LoggingIdentifierModule::class, ApplicationLifecycleModule::class, NetworkConnectionDebugUtilModule::class, LoggingIdentifierModule::class, SyncStatusModule::class, LogReportingModule::class, NetworkConnectionUtilProdModule::class, - HintsAndSolutionProdModule::class, GaBuildFlavorModule::class + HintsAndSolutionProdModule::class, MetricLogSchedulerModule::class, + PerformanceMetricsLoggerModule::class, PerformanceMetricsAssessorModule::class, + PerformanceMetricsConfigurationsModule::class, GaBuildFlavorModule::class ] ) interface GaApplicationComponent : ApplicationComponent { 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 index 07edd247d09..0a694c49b2b 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/BetaNoticeDialogFragmentTest.kt @@ -64,7 +64,8 @@ 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.loguploader.LogUploadWorkerModule +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 @@ -234,7 +235,7 @@ class BetaNoticeDialogFragmentTest { ExpirationMetaDataRetrieverModule::class, ViewBindingShimModule::class, RatioInputModule::class, ApplicationStartupListenerModule::class, HintsAndSolutionConfigFastShowTestModule::class, HintsAndSolutionProdModule::class, - WorkManagerConfigurationModule::class, LogUploadWorkerModule::class, + WorkManagerConfigurationModule::class, LogReportWorkerModule::class, FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class, DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class, ExplorationStorageModule::class, NetworkConnectionUtilDebugModule::class, @@ -244,7 +245,7 @@ class BetaNoticeDialogFragmentTest { AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class, - CachingTestModule::class + CachingTestModule::class, MetricLogSchedulerModule::class ] ) interface TestApplicationComponent : ApplicationComponent { 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 index 263def93b18..4207a1d5e78 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragmentTest.kt @@ -64,7 +64,8 @@ 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.loguploader.LogUploadWorkerModule +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 @@ -241,7 +242,7 @@ class GeneralAvailabilityUpgradeNoticeDialogFragmentTest { ExpirationMetaDataRetrieverModule::class, ViewBindingShimModule::class, RatioInputModule::class, ApplicationStartupListenerModule::class, HintsAndSolutionConfigFastShowTestModule::class, HintsAndSolutionProdModule::class, - WorkManagerConfigurationModule::class, LogUploadWorkerModule::class, + WorkManagerConfigurationModule::class, LogReportWorkerModule::class, FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class, DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class, ExplorationStorageModule::class, NetworkConnectionUtilDebugModule::class, @@ -251,7 +252,7 @@ class GeneralAvailabilityUpgradeNoticeDialogFragmentTest { AlgebraicExpressionInputModule::class, MathEquationInputModule::class, SplitScreenInteractionModule::class, LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, TestingBuildFlavorModule::class, - CachingTestModule::class + CachingTestModule::class, MetricLogSchedulerModule::class ] ) interface TestApplicationComponent : ApplicationComponent { diff --git a/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt b/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt deleted file mode 100644 index 6f1c52b95c3..00000000000 --- a/app/src/sharedTest/java/org/oppia/android/app/parser/CustomBulletSpanTest.kt +++ /dev/null @@ -1,288 +0,0 @@ -package org.oppia.android.app.parser - -import android.app.Application -import android.content.Context -import android.text.Spannable -import android.text.SpannableString -import android.text.SpannableStringBuilder -import android.text.style.BulletSpan -import android.text.style.UnderlineSpan -import androidx.appcompat.app.AppCompatActivity -import androidx.core.text.getSpans -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.common.truth.Truth.assertThat -import dagger.Component -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -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.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.HintsAndSolutionConfigModule -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.loguploader.LogUploadWorkerModule -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.TestLogReportingModule -import org.oppia.android.testing.junit.InitializeDefaultLocaleRule -import org.oppia.android.testing.robolectric.RobolectricModule -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.CustomBulletSpan -import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule -import org.oppia.android.util.parser.image.GlideImageLoaderModule -import org.oppia.android.util.parser.image.ImageParsingModule -import org.robolectric.annotation.Config -import org.robolectric.annotation.LooperMode -import javax.inject.Singleton - -@RunWith(AndroidJUnit4::class) -@LooperMode(LooperMode.Mode.PAUSED) -@Config(application = CustomBulletSpanTest.TestApplication::class, qualifiers = "port-xxhdpi") -class CustomBulletSpanTest { - @get:Rule - val initializeDefaultLocaleRule = InitializeDefaultLocaleRule() - - @get:Rule - val oppiaTestRule = OppiaTestRule() - - private var context: Context = ApplicationProvider.getApplicationContext() - - private val testStringWithoutBulletSpan = SpannableString("Text Without BulletSpan") - private val testStringWithBulletSpan = SpannableString("Text With \nBullet Point").apply { - setSpan(BulletSpan(), 10, 22, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - } - private val testStringWithMultipleBulletSpan = SpannableString( - "Text With \nfirst \nsecond \nthird \nfour \nfive" - ).apply { - setSpan(BulletSpan(), 10, 18, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(BulletSpan(), 18, 27, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(BulletSpan(), 27, 35, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(BulletSpan(), 35, 42, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(UnderlineSpan(), 42, 43, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - } - private val testStringWithCustomBulletSpan = SpannableString("Text With \nBullet Point").apply { - this.setSpan( - CustomBulletSpan(context), - 10, - 22, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - - @Test - fun customBulletSpan_testReplaceBulletSpan_spannableStringWithoutBulletSpanRemainSame() { - val spannableString1 = testStringWithoutBulletSpan - val convertedSpannableStringBuilder = CustomBulletSpan.replaceBulletSpan( - SpannableStringBuilder(spannableString1), - context - ) - val spannableString2 = SpannableString.valueOf(convertedSpannableStringBuilder) - assertThat(getBulletSpanCount(spannableString1)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString1)).isEqualTo(0) - assertThat(getBulletSpanCount(spannableString2)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString2)).isEqualTo(0) - } - - @Test - fun customBulletSpan_testReplaceBulletSpan_spannableStringWithBulletSpan_isNotSame() { - val spannableString1 = testStringWithBulletSpan - val convertedSpannableStringBuilder = CustomBulletSpan.replaceBulletSpan( - SpannableStringBuilder(spannableString1), - context - ) - val spannableString2 = SpannableString.valueOf(convertedSpannableStringBuilder) - - assertThat(getBulletSpanCount(spannableString1)).isEqualTo(1) - assertThat(getCustomBulletSpanCount(spannableString1)).isEqualTo(0) - assertThat(getBulletSpanCount(spannableString2)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString2)).isEqualTo(1) - } - - @Test - fun customBulletSpan_testReplaceBulletSpan_includingUnderlineSpan_underlineSpanRemainsSame() { - val spannableString1 = testStringWithMultipleBulletSpan - val convertedSpannableStringBuilder = CustomBulletSpan.replaceBulletSpan( - SpannableStringBuilder(spannableString1), - context - ) - val spannableString2 = SpannableString.valueOf(convertedSpannableStringBuilder) - assertThat(getBulletSpanCount(spannableString1)).isEqualTo(4) - assertThat(getCustomBulletSpanCount(spannableString1)).isEqualTo(0) - assertThat(getUnderlineSpanCount(spannableString1)).isEqualTo(1) - assertThat(getBulletSpanCount(spannableString2)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString2)).isEqualTo(4) - assertThat(getUnderlineSpanCount(spannableString2)).isEqualTo(1) - } - - @Test - fun customBulletSpan_testReplaceBulletSpan_customBulletSpans_remainsSame() { - val spannableString1 = testStringWithCustomBulletSpan - val convertedSpannableStringBuilder = CustomBulletSpan.replaceBulletSpan( - SpannableStringBuilder(spannableString1), - context - ) - val spannableString2 = SpannableString.valueOf(convertedSpannableStringBuilder) - assertThat(getBulletSpanCount(spannableString1)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString1)).isEqualTo(1) - assertThat(getBulletSpanCount(spannableString2)).isEqualTo(0) - assertThat(getCustomBulletSpanCount(spannableString2)).isEqualTo(1) - } - - @Test - fun customBulletSpan_testLeadMargin_isComputedToProperlyIndentText() { - val bulletRadius = context.resources.getDimensionPixelSize( - org.oppia.android.util.R.dimen.bullet_radius - ) - val spacingBeforeBullet = context.resources.getDimensionPixelSize( - org.oppia.android.util.R.dimen.spacing_before_bullet - ) - val spacingBeforeText = context.resources.getDimensionPixelSize( - org.oppia.android.util.R.dimen.spacing_before_text - ) - val expectedMargin = spacingBeforeBullet + spacingBeforeText + 2 * bulletRadius - val spannableString = SpannableStringBuilder(testStringWithBulletSpan) - val customBulletSpannable = CustomBulletSpan.replaceBulletSpan(spannableString, context) - val leadingMargin = customBulletSpannable.getSpans( - 0, - spannableString.length, - CustomBulletSpan::class.java - )[0].getLeadingMargin(true) - assertThat(leadingMargin).isEqualTo(expectedMargin) - } - - private fun getBulletSpans(spannableString: SpannableString): Array { - return spannableString.getSpans( - 0, - spannableString.length - ) - } - - private fun getCustomBulletSpans( - spannableString: SpannableString - ): Array { - return spannableString.getSpans( - 0, - spannableString.length - ) - } - - private fun getUnderlineSpans(spannableString: SpannableString): Array { - return spannableString.getSpans( - 0, - spannableString.length - ) - } - - private fun getBulletSpanCount(spannableString: SpannableString): Int { - return getBulletSpans(spannableString).size - } - - private fun getCustomBulletSpanCount(spannableString: SpannableString): Int { - return getCustomBulletSpans(spannableString).size - } - - private fun getUnderlineSpanCount(spannableString: SpannableString): Int { - return getUnderlineSpans(spannableString).size - } - - @Singleton - @Component( - modules = [ - RobolectricModule::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, GlideImageLoaderModule::class, ImageParsingModule::class, - HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class, - AccessibilityTestModule::class, LogStorageModule::class, CachingTestModule::class, - PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class, - ViewBindingShimModule::class, RatioInputModule::class, PlatformParameterModule::class, - ApplicationStartupListenerModule::class, LogUploadWorkerModule::class, - WorkManagerConfigurationModule::class, HintsAndSolutionConfigModule::class, - FirebaseLogUploaderModule::class, FakeOppiaClockModule::class, PracticeTabModule::class, - DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class, - ExplorationStorageModule::class, NetworkModule::class, HintsAndSolutionProdModule::class, - NetworkConnectionUtilDebugModule::class, NetworkConnectionDebugUtilModule::class, - AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class, - NetworkConfigProdModule::class, PlatformParameterSingletonModule::class, - NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class, - MathEquationInputModule::class, SplitScreenInteractionModule::class, - LoggingIdentifierModule::class, ApplicationLifecycleModule::class, SyncStatusModule::class, - TestingBuildFlavorModule::class - ] - ) - - interface TestApplicationComponent : ApplicationComponent { - @Component.Builder - interface Builder : ApplicationComponent.Builder - - fun inject(customBulletSpanTest: CustomBulletSpanTest) - } - - class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider { - private val component: TestApplicationComponent by lazy { - DaggerCustomBulletSpanTest_TestApplicationComponent.builder() - .setApplication(this) - .build() as TestApplicationComponent - } - - fun inject(customBulletSpanTest: CustomBulletSpanTest) { - component.inject(customBulletSpanTest) - } - - override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent { - return component.getActivityComponentBuilderProvider().get().setActivity(activity).build() - } - - override fun getApplicationInjector(): ApplicationInjector = component - } -} From f719dd05a05b0fc9610496be3c091a0221b083a8 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Sun, 4 Sep 2022 22:25:09 -0500 Subject: [PATCH 11/19] Test fixes. --- .../oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt | 3 ++- .../org/oppia/android/app/policies/PoliciesActivityTest.kt | 3 ++- .../org/oppia/android/app/policies/PoliciesFragmentTest.kt | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt b/app/src/sharedTest/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt index 1ccba698308..8c3fc424a40 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/parser/ListItemLeadingMarginSpanTest.kt +++ b/app/src/sharedTest/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/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 { From 4e25e16f20aa908f66a99d7d8bffc95182480ded Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Mon, 5 Sep 2022 00:06:06 -0500 Subject: [PATCH 12/19] Fix Gradle test. --- .../java/org/oppia/android/app/splash/SplashActivityTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1f87c43f7d0..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 @@ -342,7 +342,7 @@ class SplashActivityTest { } @Test - @RunOn(TestPlatform.ROBOLECTRIC) + @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL]) fun testSplashActivity_initializationFailure_initializesLocaleHandlerWithDefaultContext() { corruptCacheFile() initializeTestApplication() From 61ee2139cbbf21106475a92b402a8214da23dfa1 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 6 Sep 2022 22:13:22 -0500 Subject: [PATCH 13/19] Add some string resource checks/tools. Also, fixes major performance issue with all file-based CI checks. --- .github/workflows/static_checks.yml | 5 ++ scripts/BUILD.bazel | 14 ++++ .../oppia/android/scripts/common/BUILD.bazel | 3 + .../android/scripts/common/RepositoryFile.kt | 9 ++- .../org/oppia/android/scripts/xml/BUILD.bazel | 29 +++++++ .../xml/StringLanguageTranslationCheck.kt | 43 ++++++++++ .../scripts/xml/StringResourceParser.kt | 76 ++++++++++++++++++ .../xml/StringResourceValidationCheck.kt | 80 +++++++++++++++++++ third_party/maven_install.json | 12 ++- third_party/versions.bzl | 2 +- 10 files changed, 267 insertions(+), 6 deletions(-) create mode 100644 scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt create mode 100644 scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml index ff531ffbebb..f9dfbaa86fb 100644 --- a/.github/workflows/static_checks.yml +++ b/.github/workflows/static_checks.yml @@ -180,6 +180,11 @@ jobs: gh issue list --limit 2000 --repo oppia/oppia-android --json number > $(pwd)/open_issues.json bazel run //scripts:todo_open_check -- $(pwd) scripts/assets/todo_open_exemptions.pb open_issues.json + - name: String Resource Validation Check + if: always() + run: | + bazel run //scripts:string_resource_validation_check -- $(pwd) + # Note that caching is intentionally not enabled for this check since licenses should always be # verified without any potential influence from earlier builds (i.e. always from a clean build to # ensure the results exactly match the current state of the repository). diff --git a/scripts/BUILD.bazel b/scripts/BUILD.bazel index 2fcfb81eba5..4e8fca5d41e 100644 --- a/scripts/BUILD.bazel +++ b/scripts/BUILD.bazel @@ -92,6 +92,20 @@ kt_jvm_binary( runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/xml:xml_syntax_check_lib"], ) +kt_jvm_binary( + name = "string_language_translation_check", + testonly = True, + main_class = "org.oppia.android.scripts.xml.StringLanguageTranslationCheckKt", + runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/xml:string_language_translation_check_lib"], +) + +kt_jvm_binary( + name = "string_resource_validation_check", + testonly = True, + main_class = "org.oppia.android.scripts.xml.StringResourceValidationCheckKt", + runtime_deps = ["//scripts/src/java/org/oppia/android/scripts/xml:string_resource_validation_check_lib"], +) + TEST_FILE_EXEMPTION_ASSETS = generate_test_file_assets_list_from_text_protos( name = "test_file_exemption_assets", test_file_exemptions_name = ["test_file_exemptions"], diff --git a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel index 1b3b76ec17b..3a94254e9fc 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/common/BUILD.bazel @@ -53,4 +53,7 @@ kt_jvm_library( testonly = True, srcs = ["RepositoryFile.kt"], visibility = ["//scripts:oppia_script_library_visibility"], + deps = [ + "//third_party:org_jetbrains_kotlin_kotlin-stdlib-jdk8_jar", + ], ) diff --git a/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt b/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt index 1e0565e1dbd..5587dcc50a7 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt @@ -1,6 +1,10 @@ package org.oppia.android.scripts.common import java.io.File +import java.nio.file.FileVisitOption +import java.nio.file.Files +import java.nio.file.Path +import kotlin.streams.asSequence /** Helper class for managing & accessing files within the project repository. */ class RepositoryFile() { @@ -41,7 +45,10 @@ class RepositoryFile() { expectedExtension: String = "", exemptionsList: List = listOf() ): List { - return File(repoPath).walk().filter { file -> + // Note that Files.walk() is used instead of Kotlin's walk() function since the latter follows + // symbolic links which is almost 10x slower than not following them (due to very deep Bazel + // build directories), and it's not necessary to follow the symlinks. + return Files.walk(File(repoPath).toPath()).asSequence().map(Path::toFile).filter { file -> val isProhibited = checkIfProhibitedFile(retrieveRelativeFilePath(file, repoPath)) !isProhibited && file.isFile && diff --git a/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel index 56dc1789827..76e457f3774 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel @@ -5,6 +5,35 @@ are syntactically correct. load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") +kt_jvm_library( + name = "string_language_translation_check_lib", + testonly = True, + srcs = ["StringLanguageTranslationCheck.kt"], + visibility = ["//scripts:oppia_script_binary_visibility"], + deps = [ + ":string_resource_parser", + ], +) + +kt_jvm_library( + name = "string_resource_parser", + testonly = True, + srcs = ["StringResourceParser.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/common:repository_file", + ], +) + +kt_jvm_library( + name = "string_resource_validation_check_lib", + testonly = True, + srcs = ["StringResourceValidationCheck.kt"], + visibility = ["//scripts:oppia_script_binary_visibility"], + deps = [ + ":string_resource_parser", + ], +) + kt_jvm_library( name = "xml_syntax_error_handler", testonly = True, diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt new file mode 100644 index 00000000000..550ed9614ed --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt @@ -0,0 +1,43 @@ +package org.oppia.android.scripts.xml + +import java.io.File + +/** + * Script for checking if all strings have been translated across all supported languages. + * + * Usage: + * bazel run //scripts:string_language_translation_check -- + * + * Arguments: + * - path_to_directory_root: directory path to the root of the Oppia Android repository. + * + * Example: + * bazel run //scripts:string_language_translation_check -- $(pwd) + */ +fun main(vararg args: String) { + require(args.isNotEmpty()) { + "Expected: bazel run //scripts:string_language_translation_check -- " + } + + // Path of the repo to be analyzed. + val repoPath = "${args[0]}/" + + val parser = StringResourceParser(File(repoPath)) + val baseTranslations = parser.retrieveBaseStringNames() + val missingTranslations = parser.retrieveAllNonEnglishTranslations().mapValues { (_, xlations) -> + baseTranslations - xlations.strings.keys + } + val missingTranslationCount = missingTranslations.values.sumOf { it.size } + println("$missingTranslationCount translation(s) were found missing.") + if (missingTranslationCount > 0) { + println() + println("Missing translations:") + missingTranslations.forEach { (language, translations) -> + println("${language.name} (${translations.size}/$missingTranslationCount):") + translations.forEach { translation -> + println("- $translation") + } + println() + } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt new file mode 100644 index 00000000000..63b731d073d --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt @@ -0,0 +1,76 @@ +package org.oppia.android.scripts.xml + +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +import org.oppia.android.scripts.common.RepositoryFile +import org.w3c.dom.Node +import org.w3c.dom.NodeList + +class StringResourceParser(private val repoRoot: File) { + private val translations by lazy { parseTranslations() } + private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } + + enum class TranslationLanguage(val valuesDirectoryName: String) { + ARABIC(valuesDirectoryName = "values-ar"), + BRAZILIAN_PORTUGUESE(valuesDirectoryName = "values-pt-rBR"), + ENGLISH(valuesDirectoryName = "values"), + SWAHILI(valuesDirectoryName = "values-sw"); + } + + data class StringFile( + val language: TranslationLanguage, val file: File, val strings: Map + ) + + fun retrieveBaseStringFile(): StringFile = retrieveTranslations(TranslationLanguage.ENGLISH) + + fun retrieveBaseStringNames(): Set = retrieveBaseStringFile().strings.keys + + fun retrieveTranslations(language: TranslationLanguage): StringFile = + translations.getValue(language) + + fun retrieveAllNonEnglishTranslations(): Map = + translations.filter { (language, _) -> language != TranslationLanguage.ENGLISH } + + private fun parseTranslations(): Map { + // A list of all XML files in the repo to be analyzed. + val stringFiles = RepositoryFile.collectSearchFiles( + repoPath = repoRoot.absolutePath, + expectedExtension = ".xml" + ).filter { + it.relativeTo(repoRoot).startsWith("app/") && it.nameWithoutExtension == "strings" + }.associateBy { + checkNotNull(it.parentFile?.name?.let(::findTranslationLanguage)) { + "Parent directory of ${it.relativeTo(repoRoot)} does not correspond to a known" + + " translation: ${it.parentFile?.name}" + } + } + val expectedLanguages = TranslationLanguage.values().toSet() + check(expectedLanguages == stringFiles.keys) { + "Missing translation strings for language(s): ${expectedLanguages - stringFiles.keys}" + } + return stringFiles.map { (language, file) -> + language to StringFile(language, file, file.parseStrings()) + }.toMap() + } + + private fun File.parseStrings(): Map { + val manifestDocument = documentBuilderFactory.parseXmlFile(this) + val stringsElem = manifestDocument.getChildSequence().single { it.nodeName == "resources" } + val stringElems = stringsElem.getChildSequence().filter { it.nodeName == "string" } + return stringElems.associate { + checkNotNull(it.attributes.getNamedItem("name")?.nodeValue) to checkNotNull(it.textContent) + } + } + + private companion object { + + private fun DocumentBuilderFactory.parseXmlFile(file: File) = newDocumentBuilder().parse(file) + + private fun Node.getChildSequence() = childNodes.asSequence() + + private fun NodeList.asSequence() = (0 until length).asSequence().map(this::item) + + private fun findTranslationLanguage(valuesDirectoryName: String) = + TranslationLanguage.values().find { it.valuesDirectoryName == valuesDirectoryName } + } +} diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt new file mode 100644 index 00000000000..274df0fe198 --- /dev/null +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt @@ -0,0 +1,80 @@ +package org.oppia.android.scripts.xml + +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +import org.oppia.android.scripts.common.RepositoryFile +import org.oppia.android.scripts.xml.StringResourceParser.StringFile +import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage +import org.w3c.dom.Document +import org.w3c.dom.Node +import org.w3c.dom.NodeList + +/** + * Script for validating consistency between translated and base string resources. + * + * Usage: + * bazel run //scripts:string_resource_validation_check -- + * + * Arguments: + * - path_to_directory_root: directory path to the root of the Oppia Android repository. + * + * Example: + * bazel run //scripts:string_resource_validation_check -- $(pwd) + */ +fun main(vararg args: String) { + require(args.isNotEmpty()) { + "Expected: bazel run //scripts:string_resource_validation_check -- " + } + + // Path of the repo to be analyzed. + val repoPath = "${args[0]}/" + val repoRoot = File(repoPath) + + data class Finding(val language: TranslationLanguage, val file: File, val errorLine: String) + val parser = StringResourceParser(repoRoot) + val baseFile = parser.retrieveBaseStringFile() + val otherTranslations = parser.retrieveAllNonEnglishTranslations() + val inconsistencies = otherTranslations.entries.fold(listOf()) { errors, entry -> + val (_, translatedFile) = entry + errors + computeInconsistenciesBetween(baseFile, translatedFile).map { line -> + Finding(translatedFile.language, translatedFile.file, line) + } + }.groupBy(keySelector = { it.language to it.file }, valueTransform = { it.errorLine }) + + if (inconsistencies.isNotEmpty()) { + println("${inconsistencies.size} language(s) were found with string consistency errors.") + println() + + inconsistencies.forEach { (context, errorLines) -> + val (language, file) = context + println( + "${errorLines.size} consistency error(s) were found for ${language.name} strings (file:" + + " ${file.relativeTo(repoRoot)}):" + ) + errorLines.forEach { println("- $it") } + println() + } + throw Exception("STRING RESOURCE VALIDATION CHECKS FAILED") + } else println("STRING RESOURCE VALIDATION CHECKS PASSED") +} + +private fun computeInconsistenciesBetween( + baseFile: StringFile, translatedFile: StringFile +): List { + val commonTranslations = baseFile.strings.intersectWith(translatedFile.strings) + + // Check for inconsistent newlines post-translation. + return commonTranslations.mapNotNull { (stringName, stringPair) -> + val (baseString, translatedString) = stringPair + val baseLines = baseString.split("\\n") + val translatedLines = translatedString.split("\\n") + return@mapNotNull if (baseLines.size != translatedLines.size) { + "string $stringName: original translation uses ${baseLines.size + 1} line(s) but" + + " translation uses ${translatedLines.size + 1} line(s). Please remove any extra lines or" + + " add any that are missing." + } else null // The number of lines match. + } +} + +private fun Map.intersectWith(other: Map) = + keys.intersect(other.keys).associateWith { getValue(it) to other.getValue(it) } diff --git a/third_party/maven_install.json b/third_party/maven_install.json index a25cf4753fa..d743a3bb8ee 100644 --- a/third_party/maven_install.json +++ b/third_party/maven_install.json @@ -1,8 +1,8 @@ { "dependency_tree": { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 134596076, - "__RESOLVED_ARTIFACTS_HASH": -1383535075, + "__INPUT_ARTIFACTS_HASH": -2010577555, + "__RESOLVED_ARTIFACTS_HASH": 6219109, "conflict_resolution": { "androidx.constraintlayout:constraintlayout:1.1.3": "androidx.constraintlayout:constraintlayout:2.0.1", "androidx.core:core:1.0.1": "androidx.core:core:1.3.1", @@ -12,7 +12,7 @@ "com.google.truth:truth:0.43": "com.google.truth:truth:1.1.3", "junit:junit:4.12": "junit:junit:4.13.2", "org.jetbrains.kotlin:kotlin-reflect:1.3.41": "org.jetbrains.kotlin:kotlin-reflect:1.5.0", - "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10", "org.mockito:mockito-core:2.19.0": "org.mockito:mockito-core:3.9.0" }, "dependencies": [ @@ -7157,8 +7157,10 @@ { "coord": "com.squareup:kotlinpoet:1.6.0", "dependencies": [ + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10", - "org.jetbrains.kotlin:kotlin-reflect:1.5.0" + "org.jetbrains.kotlin:kotlin-reflect:1.5.0", + "org.jetbrains.kotlin:kotlin-stdlib:1.5.0" ], "directDependencies": [ "org.jetbrains.kotlin:kotlin-reflect:1.5.0", @@ -7179,6 +7181,8 @@ "coord": "com.squareup:kotlinpoet:jar:sources:1.6.0", "dependencies": [ "org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:sources:1.4.10", + "org.jetbrains.kotlin:kotlin-stdlib:jar:sources:1.5.0", + "org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:sources:1.4.10", "org.jetbrains.kotlin:kotlin-reflect:jar:sources:1.5.0" ], "directDependencies": [ diff --git a/third_party/versions.bzl b/third_party/versions.bzl index 289c58ad5a8..245d14f20b9 100644 --- a/third_party/versions.bzl +++ b/third_party/versions.bzl @@ -69,7 +69,7 @@ MAVEN_PRODUCTION_DEPENDENCY_VERSIONS = { "javax.annotation:javax.annotation-api:jar": "1.3.2", "javax.inject:javax.inject": "1", "nl.dionsegijn:konfetti": "1.2.5", - "org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar": "1.3.72", + "org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar": "1.3.72", "org.jetbrains.kotlinx:kotlinx-coroutines-android": "1.4.1", "org.jetbrains.kotlinx:kotlinx-coroutines-core": "1.4.1", "org.jetbrains:annotations:jar": "13.0", From 1b82bcf503e60269e92ec27025c1e8436a44a48a Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Tue, 6 Sep 2022 22:23:58 -0500 Subject: [PATCH 14/19] Ensure newline consistency in translated strings. Also, fix reporting in new validation check script. --- app/src/main/res/values-ar/strings.xml | 10 +- app/src/main/res/values-sw/strings.xml | 238 +++++++++--------- app/src/main/res/values/strings.xml | 2 +- .../xml/StringResourceValidationCheck.kt | 6 +- 4 files changed, 128 insertions(+), 128 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 087958c60f5..fc9d006d5ea 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -443,13 +443,13 @@ لماذا لا يتم تشغيل الصوت الخاص بي؟ كيف يمكنني تنزيل موضوع؟ لا أجد سؤالي هنا. ماذا الان؟ - <p>إذا كانت هذه هي المرة الأولى التي تنشئ فيها ملفًا شخصيًا وليس لديك رقم تعريف شخصي: </p> \n<p> 1. من منتقي الملف الشخصي ، اضغط على\n<strong> قم بإعداد ملفات تعريف متعددة</strong>\n</p>\n<p> 2. قم بإنشاء رقم تعريف شخصي و\n<strong>احفظ</strong>\n</p> \n<p> 3. املأ جميع البيانات للملف الشخصي.</p> \n<ol> \n<li>(اختياري) قم بتحميل صورة.</li> \n<li>إدخال اسم.</li> \n<li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> \n</ol> \n<p> 4. اضغط\n<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!\n<br/> \n<br/> إذا قمت بإنشاء ملف تعريف من قبل ولديك رقم تعريف شخصي:\n</p>\n<p> 1. من منتقي الملف الشخصي ، اضغط على\n<strong>إضافة الملف الشخصي</strong>\n</p> \n<p> 2. أدخل رقم التعريف الشخصي الخاص بك وانقر فوق\n<strong>إرسال</strong>\n</p> \n<p>3. املأ جميع الحقول للملف الشخصي.</p> \n<ol> \n<li>(اختياري) قم بتحميل صورة.</li> \n<li>إدخال اسم.</li> \n<li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> \n</ol> \n<p> 4. اضغط\n<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!\n<br/><br/> ملاحظة: فقط ال\n<u>مدير</u> قادر على إدارة الملفات الشخصية.\n</p> - <p>بمجرد حذف ملف التعريف:</p>\n<p><br></p> \n<p>\n<li>لا يمكن استعادة ملف التعريف.</li>\n</p>\n<p><li>سيتم حذف معلومات الملف الشخصي مثل الاسم والصور والتقدم بشكل دائم.</li></p>\n<p><br></p>\n<p>لحذف ملف تعريف (باستثناء<u>المسؤول</u></p>\n<p>1. من الصفحة الرئيسية للمسؤول ، اضغط على زر القائمة أعلى اليسار.</p>\n<p>2. اضغط على<strong>ضوابط المسؤول</strong></p>\n<p>3. اضغط على<strong>تحرير ملفات التعريف</strong></p>\n<p>4. اضغط على الملف الشخصي الذي ترغب في حذفه.</p>\n<p>5. في الجزء السفلي من الشاشة ، انقر فوق<strong>حذف الملف الشخصي</strong></p>\n<p>6. اضغط<strong>حذف</strong>لتأكيد الحذف.</p>\n<p><br></p>\n<p>ملاحظة:<u>المسؤول</u>فقط هو القادر على إدارة الملفات الشخصية.</p> + <p>إذا كانت هذه هي المرة الأولى التي تنشئ فيها ملفًا شخصيًا وليس لديك رقم تعريف شخصي: </p> <p> 1. من منتقي الملف الشخصي ، اضغط على<strong> قم بإعداد ملفات تعريف متعددة</strong></p><p> 2. قم بإنشاء رقم تعريف شخصي و<strong>احفظ</strong></p> <p> 3. املأ جميع البيانات للملف الشخصي.</p> <ol> <li>(اختياري) قم بتحميل صورة.</li> <li>إدخال اسم.</li> <li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> </ol> <p> 4. اضغط<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!<br/> <br/> إذا قمت بإنشاء ملف تعريف من قبل ولديك رقم تعريف شخصي:</p><p> 1. من منتقي الملف الشخصي ، اضغط على<strong>إضافة الملف الشخصي</strong></p> <p> 2. أدخل رقم التعريف الشخصي الخاص بك وانقر فوق<strong>إرسال</strong></p> <p>3. املأ جميع الحقول للملف الشخصي.</p> <ol> <li>(اختياري) قم بتحميل صورة.</li> <li>إدخال اسم.</li> <li>(اختياري) قم بتعيين رقم تعريف شخصي مكون من 3 أرقام.</li> </ol> <p> 4. اضغط<strong>إنشاء</strong> . تمت إضافة هذا الملف الشخصي إلى منتقي ملف التعريف الخاص بك!<br/><br/> ملاحظة: فقط ال<u>مدير</u> قادر على إدارة الملفات الشخصية.</p> + <p>بمجرد حذف ملف التعريف:</p><p><br></p> <p><li>لا يمكن استعادة ملف التعريف.</li></p><p><li>سيتم حذف معلومات الملف الشخصي مثل الاسم والصور والتقدم بشكل دائم.</li></p><p><br></p><p>لحذف ملف تعريف (باستثناء<u>المسؤول</u></p><p>1. من الصفحة الرئيسية للمسؤول ، اضغط على زر القائمة أعلى اليسار.</p><p>2. اضغط على<strong>ضوابط المسؤول</strong></p><p>3. اضغط على<strong>تحرير ملفات التعريف</strong></p><p>4. اضغط على الملف الشخصي الذي ترغب في حذفه.</p><p>5. في الجزء السفلي من الشاشة ، انقر فوق<strong>حذف الملف الشخصي</strong></p><p>6. اضغط<strong>حذف</strong>لتأكيد الحذف.</p><p><br></p><p>ملاحظة:<u>المسؤول</u>فقط هو القادر على إدارة الملفات الشخصية.</p> <p>لتغيير بريدك الإلكتروني / رقم هاتفك:</p> <p>1. من الصفحة الرئيسية للمشرف ، اضغط على زر القائمة أعلى اليسار.</p> <p>2. اضغط على <strong> عناصر تحكم المسؤول </ strong>.</p> <p>3. اضغط على <strong> تعديل الحساب </ strong>.</p> <p><br></p> <p>إذا كنت تريد تغيير بريدك الإلكتروني:</p> <p>4. أدخل بريدك الإلكتروني الجديد وانقر على <strong> حفظ </ strong>.</p> <p>5. يتم إرسال رابط التأكيد لتأكيد بريدك الإلكتروني الجديد. ستنتهي صلاحية الرابط بعد 24 ساعة ويجب النقر عليه لربطه بحسابك.</p> <p><br></p> <p>في حالة تغيير رقم هاتفك: </ p> <p> 4. أدخل رقم هاتفك الجديد وانقر على <strong> تحقق </ strong>.</p> <p>5. يتم إرسال رمز لتأكيد رقمك الجديد. ستنتهي صلاحية الرمز بعد 5 دقائق ويجب إدخاله في الشاشة الجديدة لربطه بحسابك.</p> - <p>%1$s \n<i>\"أو-بي-يا\"</i>(فنلندية) - \"للتعلم\"</p>\n<p><br></p><p>%1$sمهمتنا هي مساعدة أي شخص على تعلم أي شيء يريده بطريقة فعالة وممتعة.</p><p><br></p><p>من خلال إنشاء مجموعة من الدروس المجانية عالية الجودة والفعالة بشكل واضح بمساعدة معلمين من جميع أنحاء العالم ، تهدف %1$s إلى تزويد الطلاب بتعليم جيد - بغض النظر عن مكان وجودهم أو الموارد التقليدية التي يمكنهم الوصول إليها.</p><p><br></p><p>كطالب ، يمكنك أن تبدأ مغامرتك التعليمية من خلال تصفح الموضوعات المدرجة في الصفحة الرئيسية!</p> + <p>%1$s <i>\"أو-بي-يا\"</i>(فنلندية) - \"للتعلم\"</p><p><br></p><p>%1$sمهمتنا هي مساعدة أي شخص على تعلم أي شيء يريده بطريقة فعالة وممتعة.</p><p><br></p><p>من خلال إنشاء مجموعة من الدروس المجانية عالية الجودة والفعالة بشكل واضح بمساعدة معلمين من جميع أنحاء العالم ، تهدف %1$s إلى تزويد الطلاب بتعليم جيد - بغض النظر عن مكان وجودهم أو الموارد التقليدية التي يمكنهم الوصول إليها.</p><p><br></p><p>كطالب ، يمكنك أن تبدأ مغامرتك التعليمية من خلال تصفح الموضوعات المدرجة في الصفحة الرئيسية!</p> <p>المشرف هو المستخدم الرئيسي الذي يدير ملفات التعريف والإعدادات لكل ملف تعريف على حسابه. هم على الأرجح والدك أو معلمك أو وصي عليك الذي أنشأ هذا الملف الشخصي لك.</p><p><br></p><p>يمكن للمسؤولين إدارة الملفات الشخصية وتعيين أرقام التعريف الشخصية وتغيير الإعدادات الأخرى ضمن حساباتهم. بناءً على ملف التعريف الخاص بك ، قد تكون أذونات المسؤول مطلوبة لبعض الميزات مثل تنزيل الموضوعات وتغيير رقم التعريف الشخصي وغير ذلك.</p><p><br></p><p>لمعرفة من هو المسؤول لديك ، انتقل إلى منتقي الملف الشخصي. الملف الشخصي الأول المدرج ولديه \"المسؤول\" مكتوب باسمه هو المسؤول.</p> <p>إذا لم يتم تحميل مشغل الاستكشاف</p><p><br></p><p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p><p> <ol> <li> انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار </li> </ol> </p><p><br></p><p>تحقق من اتصالك بالإنترنت:</p><p> <li> إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. </li> </p><p><br></p><p>اطلب من المشرف التحقق من أجهزتهم واتصال الإنترنت:</p><p> <li> اطلب من المشرف استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه </li> </p><p><br></p><p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><p> <li> أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org. </li> </p> - <p>إذا لم يتم تشغيل الصوت الخاص بك</p><p><br></p>\n<p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p>\n<p> <li>انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li> </p><p><br></p>\n<p>تحقق من اتصالك بالإنترنت:</p><p> <li>إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. قد يتسبب الإنترنت البطيء في تحميل الصوت بشكل غير منتظم ، مما يجعل من الصعب تشغيله.</li> </p><p><br></p>\n<p>اطلب من المسؤول التحقق من أجهزتهم واتصال الإنترنت:</p><p> \n<li>اطلب من المسؤول استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه</li> </p><p><br></p>\n<p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><p> <li>أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></p> - <p>لتنزيل استكشاف:</p>\n<p>1. من الصفحة الرئيسية ، انقر فوق موضوع أو استكشاف.</p>\n<p>2. من صفحة الموضوع هذه ، انقر على علامة التبويب <strong> معلومات </ strong>.</p>\n<p>3. اضغط على <strong> تنزيل الموضوع </ strong>.</p>\n<p>4. اعتمادًا على إعدادات التطبيق ، قد تحتاج إلى موافقة المسؤول أو اتصال Wifi ثابتًا لإكمال التنزيل. إذا لزم الأمر ، بمجرد استيفاء هذه المتطلبات ، يتم تنزيل الموضوع على الجهاز ويمكن استخدامه في وضع عدم الاتصال بواسطة جميع ملفات التعريف. <p> + <p>إذا لم يتم تشغيل الصوت الخاص بك</p><p><br></p><p>تحقق لمعرفة ما إذا كان التطبيق محدثًا أم لا:</p><p> <li>انتقل إلى متجر Play وتأكد من تحديث التطبيق إلى أحدث إصدار</li> </p><p><br></p><p>تحقق من اتصالك بالإنترنت:</p><p> <li>إذا كان اتصالك بالإنترنت بطيئًا ، فحاول إعادة الاتصال بشبكة Wi-Fi أو الاتصال بشبكة أخرى. قد يتسبب الإنترنت البطيء في تحميل الصوت بشكل غير منتظم ، مما يجعل من الصعب تشغيله.</li> </p><p><br></p><p>اطلب من المسؤول التحقق من أجهزتهم واتصال الإنترنت:</p><p> <li>اطلب من المسؤول استكشاف الأخطاء وإصلاحها باستخدام الخطوات المذكورة أعلاه</li> </p><p><br></p><p>أخبرنا إذا كنت لا تزال تواجه مشكلات في التحميل:</p><p> <li>أبلغ عن مشكلة عن طريق الاتصال بنا على admin@oppia.org.</li></p> + <p>لتنزيل استكشاف:</p><p>1. من الصفحة الرئيسية ، انقر فوق موضوع أو استكشاف.</p><p>2. من صفحة الموضوع هذه ، انقر على علامة التبويب <strong> معلومات </ strong>.</p><p>3. اضغط على <strong> تنزيل الموضوع </ strong>.</p><p>4. اعتمادًا على إعدادات التطبيق ، قد تحتاج إلى موافقة المسؤول أو اتصال Wifi ثابتًا لإكمال التنزيل. إذا لزم الأمر ، بمجرد استيفاء هذه المتطلبات ، يتم تنزيل الموضوع على الجهاز ويمكن استخدامه في وضع عدم الاتصال بواسطة جميع ملفات التعريف. <p> <p> إذا لم تتمكن من العثور على سؤالك أو كنت ترغب في الإبلاغ عن خطأ ، فاتصل بنا على admin@oppia.org. </p> diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index e258e430b69..140af141b6b 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -109,15 +109,15 @@ Samahani,vipeo vingi havitumiki na programu.Tafadhali rekebisha jibu lako. Samahani, nguvu za juu zaidi ya 5 haiungwi mkono na programu. Tafadhali rekebisha jibu lako. Samahani, nguvu zinazorudiwa/vielezo hazihimiliwi na programu. Tafadhali punguza jibu lako kwa nguvu moja. - \nPembejeo haipo kwa mzizi wa mraba. - \nKugawanya kwa sufuri si halali. Tafadhali rekebisha jibu lako. + Pembejeo haipo kwa mzizi wa mraba. + Kugawanya kwa sufuri si halali. Tafadhali rekebisha jibu lako. Inaonekana umeingiza baadhi ya vigezo. Tafadhali hakikisha kuwa jibu lako lina nambari pekee na uondoe vigezo vyovyote kutoka kwa jibu lako. - \nTafadhali tumia vigezo vilivyobainishwa katika swali na si %s. + Tafadhali tumia vigezo vilivyobainishwa katika swali na si %s. Mlinganyo yako inakosa ishara \'=\'. - \nMlinganyo wako una ishara nyingi sana \'=\'. Inapaswa kuwa na moja tu. - \nMoja ya pande za \'=\' katika mlinganyo wako ni tupu. - \nChaguo la kukokotoa \'%s\' haitumiki. Tafadhali rekebisha jibu lako. - \nUlimaanisha sqrt? Ikiwa sivyo, tafadhali tenganisha vigeu kwa kutumia alama za kuzidisha. + Mlinganyo wako una ishara nyingi sana \'=\'. Inapaswa kuwa na moja tu. + Moja ya pande za \'=\' katika mlinganyo wako ni tupu. + Chaguo la kukokotoa \'%s\' haitumiki. Tafadhali rekebisha jibu lako. + Ulimaanisha sqrt? Ikiwa sivyo, tafadhali tenganisha vigeu kwa kutumia alama za kuzidisha. Samahani, hatukuweza kuelewa jibu lako. Tafadhali iangalie ili kuhakikisha kuwa hakuna hitilafu zozote. Washa sauti kwa somo hili. Hadithi Zilizochezwa hivi karibuni @@ -157,21 +157,21 @@ Jibu lako lina koloni mbili (:) karibu na kila moja. Idadi ya masharti si sawa na masharti yanayohitajika. Uwiano hauwezi kuwa na 0 kama kipengele. - \nUkubwa usiojulikana - \n Baiti %s + Ukubwa usiojulikana + Baiti %s %s KB %s MB %s GB - \nSahihi! - \nMada: %s - \n%1$s katika %2$s + Sahihi! + Mada: %s + %1$s katika %2$s Sura 1\n - \n\n Sura %s \n + \n Sura %s \n Hadithi 1\n - \n Hadithi %s\n + Hadithi %s\n %s kati ya Sura %s Imekamilika @@ -179,104 +179,104 @@ Somo 1\n - \n Masomo %s \n + Masomo %s \n - \nUkurasa wa kuchagua wasifu - \nMsimamizi - \nChagua wasifu wako - \nOngeza Wasifu + Ukurasa wa kuchagua wasifu + Msimamizi + Chagua wasifu wako + Ongeza Wasifu Weka Wasifu Nyingi - \nOngeza hadi watumiaji 10 kwenye akaunti yako. Nzuri sana kwa familia na madarasa. - \nUdhibiti vya Msimamizi + Ongeza hadi watumiaji 10 kwenye akaunti yako. Nzuri sana kwa familia na madarasa. + Udhibiti vya Msimamizi Lugha Vidhibiti vya Msimamizi - \nIdhinisha kuongeza wasifu + Idhinisha kuongeza wasifu Idhinisha kufikia Vidhibiti vya Msimamizi - \nIdhini ya Msimamizi Inahitajika + Idhini ya Msimamizi Inahitajika Weka Nambari ya Siri ya Msimamizi ili kuunda akaunti mpya. Weka Nambari ya Siri ya Msimamizi ili kufikia Vidhibiti vya Msimamizi. - \nNambari ya Siri ya Msimamizi + Nambari ya Siri ya Msimamizi Nambari ya Siri ya Msimamizi si Sahihi. Tafadhali jaribu tena. - \nTafadhali weka Nambari ya Siri ya Msimamizi. - \nWasilisha + Tafadhali weka Nambari ya Siri ya Msimamizi. + Wasilisha Funga - \nKabla ya kuongeza wasifu, tunahitaji kulinda akaunti yako kwa kuunda Nambari ya Siri. Hii hukupa uwezo wa kuidhinisha upakuaji na kudhibiti wasifu kwenye kifaa. - \nTumia Nambari yako ya siri uliyoweka kwa akaunti za kibinafsi kama vile benki au usalama wa kijamii. - \nNambari mpya ya Siri ya tarakimu 5 + Kabla ya kuongeza wasifu, tunahitaji kulinda akaunti yako kwa kuunda Nambari ya Siri. Hii hukupa uwezo wa kuidhinisha upakuaji na kudhibiti wasifu kwenye kifaa. + Tumia Nambari yako ya siri uliyoweka kwa akaunti za kibinafsi kama vile benki au usalama wa kijamii. + Nambari mpya ya Siri ya tarakimu 5 Thibitisha Nambari ya Siri ya tarakimu 5 - \nNambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 5. - \nTafadhali hakikisha kwamba Nambari za Siri zote mbili zinalingana. + Nambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 5. + Tafadhali hakikisha kwamba Nambari za Siri zote mbili zinalingana. Hifadhi - \nIdhinisha kuongeza wasifu - \nOngeza Wasifu - \nOngeza Wasifu + Idhinisha kuongeza wasifu + Ongeza Wasifu + Ongeza Wasifu Jina* - \nNambari ya Siri ya tarakimu 3* + Nambari ya Siri ya tarakimu 3* Thibitisha Nambari ya Siri ya tarakimu 3* - \nRuhusu Upakuaji wa Ufikiaji - \nMtumiaji anaweza kupakua na kufuta maudhui bila Nambari ya Siri ya Msimamizi. + Ruhusu Upakuaji wa Ufikiaji + Mtumiaji anaweza kupakua na kufuta maudhui bila Nambari ya Siri ya Msimamizi. Anzisha Funga - \nUkiwa na Nambari ya Siri, hakuna mtu mwingine anayeweza kufikia wasifu kando na mtumiaji huyu aliyekabidhiwa. - \nTumeshindwa kuhifadhi picha yako. Tafadhali jaribu tena. + Ukiwa na Nambari ya Siri, hakuna mtu mwingine anayeweza kufikia wasifu kando na mtumiaji huyu aliyekabidhiwa. + Tumeshindwa kuhifadhi picha yako. Tafadhali jaribu tena. Jina hili tayari linatumiwa na wasifu mwingine. Tafadhali weka jina la wasifu huu. - \nMajina yanaweza kuwa na herufi pekee. Jaribu jina lingine? - \nNambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 3. - \nTafadhali hakikisha kwamba Nambari za Siri zote mbili zinalingana. - \nMaelezo zaidi kuhusu Nambari za Siri zenye tarakimu 3. - \nSehemu zilizo na alama ya * zinahitajika. + Majina yanaweza kuwa na herufi pekee. Jaribu jina lingine? + Nambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 3. + Tafadhali hakikisha kwamba Nambari za Siri zote mbili zinalingana. + Maelezo zaidi kuhusu Nambari za Siri zenye tarakimu 3. + Sehemu zilizo na alama ya * zinahitajika. Picha ya sasa ya wasifu - \nHariri picha ya wasifu - \nKaribu kwa %s! + Hariri picha ya wasifu + Karibu kwa %s! Jifunze chochote unachotaka kwa njia bora na ya kufurahisha. Ongeza watumiaji kwenye akaunti yako. Elezea uzoefu na uunde hadi wasifu 10. - \nPakua ya nje ya mtandao. + Pakua ya nje ya mtandao. Endelea kujifunza masomo yako bila muunganisho wa mtandao. - \nFurahia! + Furahia! Furahia matukio yako ya kujifunza kwa masomo yetu ya bure na ya ufanisi. Ruka Inayofuata Anza - \nSlaidi %s ya %s - \nHabari, %s! + Slaidi %s ya %s + Habari, %s! Tafadhali weka Nambari yako ya Siri ya Msimamizi. - \nTafadhali weka Nambari yako ya Siri. - \nNambari ya Siri ya Tarakimu 5 ya Msimamizi. - \nNambari ya Siri ya Tarakimu 3 ya Mtumiaji. + Tafadhali weka Nambari yako ya Siri. + Nambari ya Siri ya Tarakimu 5 ya Msimamizi. + Nambari ya Siri ya Tarakimu 3 ya Mtumiaji. Nilisahau nambari yangu. Nambari ya Siri si sahihi. Onyesha ficha Funga - \nMabadiliko ya Nambari ya Siri yamefaulu + Mabadiliko ya Nambari ya Siri yamefaulu Je, umesahau Nambari ya Siri? Ili kuweka upya Nambari yako ya Siri, tafadhali ondoa %s kisha uisakinishe upya.\n\nKumbuka kwamba ikiwa kifaa hakijakuwa mtandaoni, unaweza kupoteza maendeleo ya mtumiaji kwenye akaunti nyingi. Nenda kwenye hifadhi ya Google play. - \nOnyesha/Ficha ishara ya nenosiri - \nIshara ya nenosiri iliyoonyeshwa - \nIshara ya nenosiri iliyofichwa - \nWeka Nambari yako ya Siri - \nWeka Nambari ya Siri + Onyesha/Ficha ishara ya nenosiri + Ishara ya nenosiri iliyoonyeshwa + Ishara ya nenosiri iliyofichwa + Weka Nambari yako ya Siri + Weka Nambari ya Siri Nambari ya Siri ya Msimamizi Ufikiaji wa mipangilio ya msimamizi - \nNambari ya Siri ya Msimamizi inahitajika ili kubadilisha Nambari ya Siri ya mtumiaji + Nambari ya Siri ya Msimamizi inahitajika ili kubadilisha Nambari ya Siri ya mtumiaji Ghairi Wasilisha Nambari ya Siri ya msimamizi si Sahihi. Tafadhali jaribu tena. - \nNambari ya Siri mpya ya %1$s. - \nWeka Nambari mpya ya Siri - \nVipakuliwa Vyangu - \nVipakuliwa + Nambari ya Siri mpya ya %1$s. + Weka Nambari mpya ya Siri + Vipakuliwa Vyangu + Vipakuliwa Sasisho (2) Je, ungependa kuondoka kwenye wasifu wako? Ghairi Toka Mwanzo - \nWasifu + Wasifu Ilitengenezwa kwa %s - \nMara ya mwisho kutumika + Mara ya mwisho kutumika Badilisha jina Weka upya Nambari ya Siri Ufutaji wa Wasifu @@ -284,8 +284,8 @@ Maendeleo yote yatafutwa na hayawezi kurejeshwa. Futa Ghairi - \nRuhusu Ufikiaji wa Upakuaji - \nMtumiaji anaweza kupakua na kufuta maudhui bila Nambari ya Siri ya msimamizi. + Ruhusu Ufikiaji wa Upakuaji + Mtumiaji anaweza kupakua na kufuta maudhui bila Nambari ya Siri ya msimamizi. Picha ya Wasifu Picha ya Wasifu Ghairi @@ -296,51 +296,51 @@ Hifadhi Weka upya Nambari ya Siri Weka Nambari mpya ya Siri ili mtumiaji aiweke anapofikia wasifu wake. - \nNambari ya Siri ya tarakimu 3* - \nNambari ya Siri ya tarakimu 5* + Nambari ya Siri ya tarakimu 3* + Nambari ya Siri ya tarakimu 5* Thibitisha Nambari ya Siri ya tarakimu 3* Thibitisha Nambari ya Siri ya tarakimu 5 - \nNambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 3. - \nNambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 5. + Nambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 3. + Nambari yako ya Siri inapaswa kuwa na urefu wa tarakimu 5. Unda Nambari ya Siri ya tarakimu 3 - \nInahitajika - \nKitufe cha Nyuma + Inahitajika + Kitufe cha Nyuma Inayofuata Jumla - \nHariri akaunti - \nUsimamizi wa Wasifu + Hariri akaunti + Usimamizi wa Wasifu Hariri wasifu - \nRuhusa ya kupakua + Ruhusa ya kupakua Pakua na usasishe kwenye Wi-fi pekee Mada zitapakuliwa na kusasishwa kwenye Wi-fi pekee. Vipakuliwa au masasisho yoyote ya data ya mtandao wa simu yatawekwa kwenye foleni. Sasisha mada moja kwa moja - \nMada zilizopakuliwa ambazo zina maudhui mapya zinazopatikana zitasasishwa moja kwa moja. + Mada zilizopakuliwa ambazo zina maudhui mapya zinazopatikana zitasasishwa moja kwa moja. Maelezo ya programu. Toleo la Programu - \nVitendo vya akaunti + Vitendo vya akaunti Toka Ghairi Sawa Je,una uhakika unataka kutoka kwenye wasifu wako? - \nToleo la Programu %s + Toleo la Programu %s Sasisho la mwisho lilisakinishwa kwenye %s. Tumia nambari ya toleo iliyo hapo juu kutuma maoni kuhusu hitilafu. Toleo la Programu Lugha ya Programu Lugha chaguomsingi ya Sauti Ukubwa wa Maandishi ya Kusoma Ukubwa wa Maandishi ya Kusoma - \nNakala ya hadithi itaonekana hivi. + Nakala ya hadithi itaonekana hivi. A - \nSauti Chaguomsingi + Sauti Chaguomsingi Lugha ya Programu Ukubwa wa Maandishi ya Kusoma Ndogo Kati Kubwa - \nKubwa Zaidi - \nTelezesha upau wa utafutaji ili kudhibiti ukubwa wa maandishi. - \nWasifu - \nHadithi 2 + Kubwa Zaidi + Telezesha upau wa utafutaji ili kudhibiti ukubwa wa maandishi. + Wasifu + Hadithi 2 Mada zinazoendelea Mada zinazoendelea Hadithi Zimekamilika @@ -348,39 +348,39 @@ Chaguzi Hadithi Zimekamilika Mwongozo wa programu - \nJifunze ujuzi mpya wa hesabu katika hadithi zinazokuonyesha jinsi ya kuzitumia katika maisha yako ya kila siku - \n\"Karibu %s!\" + Jifunze ujuzi mpya wa hesabu katika hadithi zinazokuonyesha jinsi ya kuzitumia katika maisha yako ya kila siku + \"Karibu %s!\" Unataka kujifunza nini? - \nMakuu - Hebu \ntuanze. + Makuu + Hebu tuanze. Ndiyo - \nHapana… - \nChagua \nmada tofauti. + Hapana… + Chagua \nmada tofauti. Je, unavutiwa na:\n%s? Kidokezo kipya kinapatikana - \nOnyesha vidokezo na suluhisho + Onyesha vidokezo na suluhisho Nenda juu Vidokezo Fichua Suluhisho - \nFichua Kidokezo + Fichua Kidokezo Onyesha/Ficha orodha ya vidokezo ya %s - \nOnyesha/Ficha suluhisho + Onyesha/Ficha suluhisho Suluhisho pekee ni: Hii itafichua suluhisho. Una uhakika? Fichua sasa hivi - \nhivi karibuni + hivi karibuni %s iliyopita - \njana + jana Rudi kwenye mada Ufafanuzi: - \nIkiwa vitu viwili ni sawa, viunganishe. + Ikiwa vitu viwili ni sawa, viunganishe. Unganisha na kipengee %s Tenganisha vipengee katika %s Hamisha kipengee chini hadi %s - \nHamisha kipengee juu hadi %s + Hamisha kipengee juu hadi %s Juu - \nChini + Chini %s %s dakika @@ -395,16 +395,16 @@ siku %s mada_marudio_mtazamo wa kuchakata tena_tag - \nInaendelea_kuchakata tena_mtazamo_tag - \nTafadhali chagua angalau chaguo moja. - \nToleo la programu lisilotumika + Inaendelea_kuchakata tena_mtazamo_tag + Tafadhali chagua angalau chaguo moja. + Toleo la programu lisilotumika Toleo hili la programu halitumiki tena. Tafadhali isasishe kupitia hifadhi ya michezo. Funga programu - \nkwa - \nWeka uwiano katika fomu x:y. + kwa + Weka uwiano katika fomu x:y. Maandishi madogo zaidi Maandishi kubwa zaidi - \nInakuja Hivi Karibuni + Inakuja Hivi Karibuni Hadithi Zinazopendekezwa Hadithi Kwa Ajili Yako Hali ya Mazoezi @@ -416,28 +416,28 @@ Jibu sahihi lililowasilishwa Jibu sahihi lililowasilishwa: %s Jibu lililowasilishwa lisilo sahihi - \nJibu lililowasilishwa lisilo sahihi: %s + Jibu lililowasilishwa lisilo sahihi: %s Tegemeo ya mhusika wa tatu toleo %s Leseni za Hakimiliki - \nMtazamo wa Leseni ya Hakimiliki - \nNenda nyuma hadi %s - \norodha ya tegemezi ya mhusika wa tatu + Mtazamo wa Leseni ya Hakimiliki + Nenda nyuma hadi %s + orodha ya tegemezi ya mhusika wa tatu orodha ya leseni za hakimiliki - \nRejesha Somo + Rejesha Somo Endelea - \nAnza tena - \nHabari ya asubuhi, - \nHabari ya mchana, + Anza tena + Habari ya asubuhi, + Habari ya mchana, Habari ya jioni, - \nNinawezaje kuunda wasifu mpya? - \nNinawezaje kufuta wasifu? + Ninawezaje kuunda wasifu mpya? + Ninawezaje kufuta wasifu? Nitabadilisha aje barua pepe/nambari yangu ya simu? - \n%s ni nini? + %s ni nini? Msimamizi ni nani? - \nKwa nini kicheza Uchunguzi hakipakii? + Kwa nini kicheza Uchunguzi hakipakii? Kwa nini sauti yangu haichezwi? - \nNitapakua aje Mada? + Nitapakua aje Mada? Sijapata swali langu hapa. Nini sasa? <p>Ikiwa ni mara yako ya kwanza kuunda wasifu na huna Nambari ya Siri: </p> <p> 1. Kutoka kwa Kichagua Wasifu, gusa <strong>Weka Wasifu Nyingi</strong>. </p> <p> 2. Unda Nambari ya Siri na <strong>Hifadhi</strong>. </p> <p> 3. Jaza sehemu zote za wasifu. </p> <ol> <li> (Si lazima) Pakia picha. </li> <li> Weka jina. </li> <li> (Si lazima) Weka Nambari ya Siri yenye tarakimu 3. </li> </ol> <p> 4. Gusa <strong>Unda</strong>. Wasifu huu umeongezwa kwa Kichagua Wasifu wako! <br/> <br/> Ikiwa umeunda wasifu hapo awali na una Nambari ya Siri: </p> <p> 1. Kutoka kwa Kichagua Wasifu, gusa <strong>Ongeza Wasifu</strong>. </p> <p> 2. Weka Nambari yako ya Siri na uguse <strong>Wasilisha</strong>. </p> <p> 3. Jaza sehemu zote za wasifu. </p> <ol> <li> (Si lazima) Pakia picha. </li> <li> Weka jina. </li> <li> (Si lazima) Weka Nambari ya Siri yenye tarakimu 3. </li> </ol> <p> 4. Gusa <strong>Unda</strong>. Wasifu huu umeongezwa kwa Kichagua Wasifu wako! <br/> <br/> Kumbuka: <u>Msimamizi pekee</u> ndiye anayeweza kudhibiti wasifu.</p> <p>Wasifu unapofutwa:</p> <p><br></p> <p> <li> Wasifu hauwezi kurejeshwa. </li> </p> <p> <li> Taarifa ya wasifu kama vile jina, picha na maendeleo yatafutwa kabisa. </li> </p> <p><br></p> <p>Ili kufuta wasifu (bila kujumuisha <u>Msimamizi</u>):</p> <p>1. Kutoka kwa Ukurasa wa Mwanzo wa Msimamizi, gusa kitufe cha menyu kilicho upande wa juu kushoto.</p> <p>2. Gusa <strong>Vidhibiti vya Msimamizi</strong>.</p> <p>3. Gusa <strong>Hariri Wasifu</strong>.</p> <p>4. Gonga Wasifu ambao ungependa kufuta.</p> <p>5. Katika sehemu ya chini ya skrini, gusa <strong>Ufutaji wa Wasifu</strong>.</p> <p>6. Gusa <strong>Futa</strong> ili kuthibitisha kufuta.</p><p><br></p><p>Kumbuka: <u>Msimamizi</u> pekee ndiye anayeweza kudhibiti wasifu.</ p> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dbb8d131dbb..aa93e2de865 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -422,7 +422,7 @@ What do you want to learn? Great - Let’s get \nstarted. + Let’s get started. Yes No… Pick a \ndifferent topic. diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt index 274df0fe198..96780a251ff 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt @@ -69,9 +69,9 @@ private fun computeInconsistenciesBetween( val baseLines = baseString.split("\\n") val translatedLines = translatedString.split("\\n") return@mapNotNull if (baseLines.size != translatedLines.size) { - "string $stringName: original translation uses ${baseLines.size + 1} line(s) but" + - " translation uses ${translatedLines.size + 1} line(s). Please remove any extra lines or" + - " add any that are missing." + "string $stringName: original translation uses ${baseLines.size} line(s) but translation" + + " uses ${translatedLines.size} line(s). Please remove any extra lines or add any that are" + + " missing." } else null // The number of lines match. } } From 636f8452ccf558cc3c3a0f291d53ba7abc8785b2 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Wed, 7 Sep 2022 02:29:28 -0500 Subject: [PATCH 15/19] Add tests & fix static checks. --- .../android/scripts/common/RepositoryFile.kt | 1 - .../org/oppia/android/scripts/xml/BUILD.bazel | 3 +- .../xml/StringLanguageTranslationCheck.kt | 10 +- .../scripts/xml/StringResourceParser.kt | 79 +++-- .../xml/StringResourceValidationCheck.kt | 12 +- .../org/oppia/android/scripts/xml/BUILD.bazel | 35 ++- .../xml/StringLanguageTranslationCheckTest.kt | 270 +++++++++++++++++ .../scripts/xml/StringResourceParserTest.kt | 279 ++++++++++++++++++ .../xml/StringResourceValidationCheckTest.kt | 259 ++++++++++++++++ 9 files changed, 910 insertions(+), 38 deletions(-) create mode 100644 scripts/src/javatests/org/oppia/android/scripts/xml/StringLanguageTranslationCheckTest.kt create mode 100644 scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceParserTest.kt create mode 100644 scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceValidationCheckTest.kt diff --git a/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt b/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt index 5587dcc50a7..14962f3ee0b 100644 --- a/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt +++ b/scripts/src/java/org/oppia/android/scripts/common/RepositoryFile.kt @@ -1,7 +1,6 @@ package org.oppia.android.scripts.common import java.io.File -import java.nio.file.FileVisitOption import java.nio.file.Files import java.nio.file.Path import kotlin.streams.asSequence diff --git a/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel b/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel index 76e457f3774..e31ea659e02 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel +++ b/scripts/src/java/org/oppia/android/scripts/xml/BUILD.bazel @@ -1,6 +1,6 @@ """ Libraries corresponding to XML syntax based check to ensure that all the XML files in the codebase -are syntactically correct. +are syntactically correct and consistent. """ load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_library") @@ -19,6 +19,7 @@ kt_jvm_library( name = "string_resource_parser", testonly = True, srcs = ["StringResourceParser.kt"], + visibility = ["//scripts:oppia_script_test_visibility"], deps = [ "//scripts/src/java/org/oppia/android/scripts/common:repository_file", ], diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt index 550ed9614ed..35f7e41514b 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringLanguageTranslationCheck.kt @@ -33,11 +33,13 @@ fun main(vararg args: String) { println() println("Missing translations:") missingTranslations.forEach { (language, translations) -> - println("${language.name} (${translations.size}/$missingTranslationCount):") - translations.forEach { translation -> - println("- $translation") + if (translations.isNotEmpty()) { + println("${language.name} (${translations.size}/$missingTranslationCount):") + translations.forEach { translation -> + println("- $translation") + } + println() } - println() } } } diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt index 63b731d073d..955138b337b 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceParser.kt @@ -1,33 +1,32 @@ package org.oppia.android.scripts.xml -import java.io.File -import javax.xml.parsers.DocumentBuilderFactory import org.oppia.android.scripts.common.RepositoryFile import org.w3c.dom.Node import org.w3c.dom.NodeList +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory +/** + * Parser and processor for all UI-facing string resources, for use in validation and analysis + * scripts. + * + * @property repoRoot the root of the Oppia Android repository being processed + */ class StringResourceParser(private val repoRoot: File) { private val translations by lazy { parseTranslations() } private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } - enum class TranslationLanguage(val valuesDirectoryName: String) { - ARABIC(valuesDirectoryName = "values-ar"), - BRAZILIAN_PORTUGUESE(valuesDirectoryName = "values-pt-rBR"), - ENGLISH(valuesDirectoryName = "values"), - SWAHILI(valuesDirectoryName = "values-sw"); - } - - data class StringFile( - val language: TranslationLanguage, val file: File, val strings: Map - ) - - fun retrieveBaseStringFile(): StringFile = retrieveTranslations(TranslationLanguage.ENGLISH) + /** Returns the [StringFile] corresponding to the base (i.e. untranslated English) strings. */ + fun retrieveBaseStringFile(): StringFile = translations.getValue(TranslationLanguage.ENGLISH) + /** Returns the [Set] of all string keys contained within the base strings file. */ fun retrieveBaseStringNames(): Set = retrieveBaseStringFile().strings.keys - fun retrieveTranslations(language: TranslationLanguage): StringFile = - translations.getValue(language) - + /** + * Returns a map of all [StringFile]s (keyed by their [StringFile.language]) which represent + * actual translations (i.e. all non-base files--see [retrieveBaseStringFile] for the base + * strings). + */ fun retrieveAllNonEnglishTranslations(): Map = translations.filter { (language, _) -> language != TranslationLanguage.ENGLISH } @@ -37,16 +36,17 @@ class StringResourceParser(private val repoRoot: File) { repoPath = repoRoot.absolutePath, expectedExtension = ".xml" ).filter { - it.relativeTo(repoRoot).startsWith("app/") && it.nameWithoutExtension == "strings" + it.toRelativeString(repoRoot).startsWith("app/") && it.nameWithoutExtension == "strings" }.associateBy { checkNotNull(it.parentFile?.name?.let(::findTranslationLanguage)) { - "Parent directory of ${it.relativeTo(repoRoot)} does not correspond to a known" + - " translation: ${it.parentFile?.name}" + "Strings file '${it.toRelativeString(repoRoot)}' does not correspond to a known language:" + + " ${it.parentFile?.name}" } - } + }.toSortedMap() // Sorted for consistent output. val expectedLanguages = TranslationLanguage.values().toSet() check(expectedLanguages == stringFiles.keys) { - "Missing translation strings for language(s): ${expectedLanguages - stringFiles.keys}" + "Missing translation strings for language(s):" + + " ${(expectedLanguages - stringFiles.keys).joinToString() }" } return stringFiles.map { (language, file) -> language to StringFile(language, file, file.parseStrings()) @@ -62,8 +62,41 @@ class StringResourceParser(private val repoRoot: File) { } } - private companion object { + /** + * The language given strings have been translated to/are being represented in. + * + * @property valuesDirectoryName the name of the resource values directory that is expected to + * contain a strings.xml file for strings related to this language + */ + enum class TranslationLanguage(val valuesDirectoryName: String) { + /** Corresponds to Arabic (ar) translations. */ + ARABIC(valuesDirectoryName = "values-ar"), + + /** Corresponds to Brazilian Portuguese (pt-rBR) translations. */ + BRAZILIAN_PORTUGUESE(valuesDirectoryName = "values-pt-rBR"), + + /** Corresponds to English (en) translations. */ + ENGLISH(valuesDirectoryName = "values"), + /** Corresponds to Swahili (sw) translations. */ + SWAHILI(valuesDirectoryName = "values-sw"); + } + + /** + * A record of a specific set of translations corresponding to one language. + * + * @property language the language of this string file + * @property file the direct [File] to the strings.xml containing the translations + * @property strings a map with keys of string names and values of the actual strings retrieved + * from the strings.xml file + */ + data class StringFile( + val language: TranslationLanguage, + val file: File, + val strings: Map + ) + + private companion object { private fun DocumentBuilderFactory.parseXmlFile(file: File) = newDocumentBuilder().parse(file) private fun Node.getChildSequence() = childNodes.asSequence() diff --git a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt index 96780a251ff..5619474b23a 100644 --- a/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt +++ b/scripts/src/java/org/oppia/android/scripts/xml/StringResourceValidationCheck.kt @@ -1,13 +1,8 @@ package org.oppia.android.scripts.xml -import java.io.File -import javax.xml.parsers.DocumentBuilderFactory -import org.oppia.android.scripts.common.RepositoryFile import org.oppia.android.scripts.xml.StringResourceParser.StringFile import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage -import org.w3c.dom.Document -import org.w3c.dom.Node -import org.w3c.dom.NodeList +import java.io.File /** * Script for validating consistency between translated and base string resources. @@ -49,7 +44,7 @@ fun main(vararg args: String) { val (language, file) = context println( "${errorLines.size} consistency error(s) were found for ${language.name} strings (file:" + - " ${file.relativeTo(repoRoot)}):" + " ${file.toRelativeString(repoRoot)}):" ) errorLines.forEach { println("- $it") } println() @@ -59,7 +54,8 @@ fun main(vararg args: String) { } private fun computeInconsistenciesBetween( - baseFile: StringFile, translatedFile: StringFile + baseFile: StringFile, + translatedFile: StringFile ): List { val commonTranslations = baseFile.strings.intersectWith(translatedFile.strings) diff --git a/scripts/src/javatests/org/oppia/android/scripts/xml/BUILD.bazel b/scripts/src/javatests/org/oppia/android/scripts/xml/BUILD.bazel index 445e7b6d76d..d1170300f87 100644 --- a/scripts/src/javatests/org/oppia/android/scripts/xml/BUILD.bazel +++ b/scripts/src/javatests/org/oppia/android/scripts/xml/BUILD.bazel @@ -1,10 +1,43 @@ """ Tests corresponding to XML syntax based check to ensure that all the XML files in the codebase are -syntactically correct. +syntactically correct and consistent. """ load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_jvm_test") +kt_jvm_test( + name = "StringLanguageTranslationCheckTest", + srcs = ["StringLanguageTranslationCheckTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/xml:string_language_translation_check_lib", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + ], +) + +kt_jvm_test( + name = "StringResourceParserTest", + srcs = ["StringResourceParserTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/xml:string_resource_parser", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + ], +) + +kt_jvm_test( + name = "StringResourceValidationCheckTest", + srcs = ["StringResourceValidationCheckTest.kt"], + deps = [ + "//scripts/src/java/org/oppia/android/scripts/xml:string_resource_validation_check_lib", + "//testing:assertion_helpers", + "//third_party:com_google_truth_truth", + "//third_party:org_jetbrains_kotlin_kotlin-test-junit", + ], +) + kt_jvm_test( name = "XmlSyntaxCheckTest", srcs = ["XmlSyntaxCheckTest.kt"], diff --git a/scripts/src/javatests/org/oppia/android/scripts/xml/StringLanguageTranslationCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/xml/StringLanguageTranslationCheckTest.kt new file mode 100644 index 00000000000..ebf36a61a72 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/xml/StringLanguageTranslationCheckTest.kt @@ -0,0 +1,270 @@ +package org.oppia.android.scripts.xml + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.testing.assertThrows +import org.w3c.dom.Document +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.io.StringWriter +import java.lang.IllegalArgumentException +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +/** Tests for the string_language_translation_check test. */ +// FunctionName: test names are conventionally named with underscores. +@Suppress("FunctionName") +class StringLanguageTranslationCheckTest { + private companion object { + private val ARABIC_STRINGS_SHARED = mapOf("shared_string" to "مشغل رحلة الاستكشاف") + private val ARABIC_STRINGS_EXTRAS = mapOf("arabic_only_string" to "خيارات") + + private val BRAZILIAN_PORTUGUESE_STRINGS_SHARED = mapOf("shared_string" to "Meus Downloads") + private val BRAZILIAN_PORTUGUESE_STRINGS_EXTRAS = mapOf( + "brazilian_portuguese_only_string" to "Reprodutor de Exploração" + ) + + private val ENGLISH_STRINGS_SHARED = mapOf("shared_string" to "Exploration Player") + private val ENGLISH_STRINGS_EXTRAS = mapOf("english_only_string" to "Help") + + private val SWAHILI_STRINGS_SHARED = mapOf("shared_string" to "Kicheza Ugunduzi") + private val SWAHILI_STRINGS_EXTRAS = mapOf("swahili_only_string" to "Badili Wasifu") + } + + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val originalOut: PrintStream = System.out + private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } + private val transformerFactory by lazy { TransformerFactory.newInstance() } + + private lateinit var outContent: ByteArrayOutputStream + private lateinit var appResources: File + + @Before + fun setUp() { + outContent = ByteArrayOutputStream() + appResources = tempFolder.newFolder("app", "src", "main", "res") + System.setOut(PrintStream(outContent)) + } + + @After + fun restoreStreams() { + System.setOut(originalOut) + } + + @Test + fun testScript_missingPath_throwsException() { + val exception = assertThrows(IllegalArgumentException::class) { runScript(/* With no path. */) } + + assertThat(exception) + .hasMessageThat() + .contains("Expected: bazel run //scripts:string_language_translation_check -- ") + } + + @Test + fun testScript_validPath_noStringFiles_throwsException() { + val exception = assertThrows(IllegalStateException::class) { + runScript(tempFolder.root.absolutePath) + } + + assertThat(exception).hasMessageThat().contains("Missing translation strings for language(s)") + } + + @Test + fun testScript_presentTranslations_allMatch_outputsNoneFoundMissing() { + populateArabicTranslations(ARABIC_STRINGS_SHARED) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_SHARED) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED) + populateSwahiliTranslations(SWAHILI_STRINGS_SHARED) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString()).contains("0 translation(s) were found missing") + } + + @Test + fun testScript_presentTranslations_missingSomeArabic_outputsMissingTranslations() { + populateArabicTranslations(ARABIC_STRINGS_EXTRAS) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_SHARED) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED) + populateSwahiliTranslations(SWAHILI_STRINGS_SHARED) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 translation(s) were found missing. + + Missing translations: + ARABIC (1/1): + - shared_string + """.trimIndent().trim() + ) + } + + @Test + fun testScript_presentTranslations_missingSomeBrazilianPortuguese_outputsMissingTranslations() { + populateArabicTranslations(ARABIC_STRINGS_SHARED) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_EXTRAS) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED) + populateSwahiliTranslations(SWAHILI_STRINGS_SHARED) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 translation(s) were found missing. + + Missing translations: + BRAZILIAN_PORTUGUESE (1/1): + - shared_string + """.trimIndent().trim() + ) + } + + @Test + fun testScript_presentTranslations_missingSomeSwahili_outputsMissingTranslations() { + populateArabicTranslations(ARABIC_STRINGS_SHARED) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_SHARED) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED) + populateSwahiliTranslations(SWAHILI_STRINGS_EXTRAS) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 translation(s) were found missing. + + Missing translations: + SWAHILI (1/1): + - shared_string + """.trimIndent().trim() + ) + } + + @Test + fun testScript_presentTranslations_missingMultiple_outputsMissingTranslationsWithTotalCount() { + populateArabicTranslations(ARABIC_STRINGS_EXTRAS) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_EXTRAS) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED + ENGLISH_STRINGS_EXTRAS) + populateSwahiliTranslations(SWAHILI_STRINGS_EXTRAS) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString().trim()).isEqualTo( + """ + 6 translation(s) were found missing. + + Missing translations: + ARABIC (2/6): + - shared_string + - english_only_string + + BRAZILIAN_PORTUGUESE (2/6): + - shared_string + - english_only_string + + SWAHILI (2/6): + - shared_string + - english_only_string + """.trimIndent().trim() + ) + } + + @Test + fun testScript_presentTranslations_missingMultiple_someShared_outputsMissingXlationsWithCount() { + populateArabicTranslations(ARABIC_STRINGS_SHARED + ARABIC_STRINGS_EXTRAS) + populateBrazilianPortugueseTranslations( + BRAZILIAN_PORTUGUESE_STRINGS_SHARED + BRAZILIAN_PORTUGUESE_STRINGS_EXTRAS + ) + populateEnglishTranslations(ENGLISH_STRINGS_SHARED + ENGLISH_STRINGS_EXTRAS) + populateSwahiliTranslations(SWAHILI_STRINGS_SHARED + SWAHILI_STRINGS_EXTRAS) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString().trim()).isEqualTo( + """ + 3 translation(s) were found missing. + + Missing translations: + ARABIC (1/3): + - english_only_string + + BRAZILIAN_PORTUGUESE (1/3): + - english_only_string + + SWAHILI (1/3): + - english_only_string + """.trimIndent().trim() + ) + } + + @Test + fun testScript_missingEnglishTranslations_outputsNoneFoundMissing() { + populateArabicTranslations(ARABIC_STRINGS_SHARED) + populateBrazilianPortugueseTranslations(BRAZILIAN_PORTUGUESE_STRINGS_SHARED) + populateEnglishTranslations(mapOf()) + populateSwahiliTranslations(SWAHILI_STRINGS_SHARED) + + runScript(tempFolder.root.absolutePath) + + // No translations should be found missing if the string is everywhere except the base file. + assertThat(outContent.asString()).contains("0 translation(s) were found missing") + } + + private fun runScript(vararg args: String) = main(*args) + + private fun populateArabicTranslations(strings: Map) { + populateTranslations(appResources, "values-ar", strings) + } + + private fun populateBrazilianPortugueseTranslations(strings: Map) { + populateTranslations(appResources, "values-pt-rBR", strings) + } + + private fun populateEnglishTranslations(strings: Map) { + populateTranslations(appResources, "values", strings) + } + + private fun populateSwahiliTranslations(strings: Map) { + populateTranslations(appResources, "values-sw", strings) + } + + private fun populateTranslations( + resourceDir: File, + valuesDirName: String, + translations: Map + ) { + val document = documentBuilderFactory.newDocumentBuilder().newDocument() + val resourcesRoot = document.createElement("resources").also { document.appendChild(it) } + translations.map { (name, value) -> + document.createElement("string").also { + it.setAttribute("name", name) + it.textContent = value + } + }.forEach(resourcesRoot::appendChild) + writeTranslationsFile(resourceDir, valuesDirName, document.toSource()) + } + + private fun writeTranslationsFile(resourceDir: File, valuesDirName: String, contents: String) { + val valuesDir = File(resourceDir, valuesDirName).also { check(it.mkdir()) } + File(valuesDir, "strings.xml").writeText(contents) + } + + private fun Document.toSource(): String { + // Reference: https://stackoverflow.com/a/5456836. + val transformer = transformerFactory.newTransformer() + return StringWriter().apply { + transformer.transform(DOMSource(this@toSource), StreamResult(this@apply)) + }.toString() + } + + private fun ByteArrayOutputStream.asString() = toString(Charsets.UTF_8.name()) +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceParserTest.kt b/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceParserTest.kt new file mode 100644 index 00000000000..474b0ceba83 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceParserTest.kt @@ -0,0 +1,279 @@ +package org.oppia.android.scripts.xml + +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage.ARABIC +import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage.BRAZILIAN_PORTUGUESE +import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage.ENGLISH +import org.oppia.android.scripts.xml.StringResourceParser.TranslationLanguage.SWAHILI +import org.oppia.android.testing.assertThrows +import org.w3c.dom.Document +import org.xml.sax.SAXParseException +import java.io.File +import java.io.StringWriter +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +/** Tests for [StringResourceParser]. */ +// FunctionName: test names are conventionally named with underscores. +@Suppress("FunctionName") +class StringResourceParserTest { + @field:[Rule JvmField] val tempFolder = TemporaryFolder() + + private companion object { + private val ARABIC_STRINGS = mapOf( + "shared_string" to "مشغل رحلة الاستكشاف", + "arabic_only_string" to "خيارات" + ) + + private val BRAZILIAN_PORTUGUESE_STRINGS = mapOf( + "shared_string" to "Meus Downloads", + "brazilian_portuguese_only_string" to "Reprodutor de Exploração" + ) + + private val ENGLISH_STRINGS = mapOf( + "shared_string" to "Exploration Player", + "english_only_string" to "Help" + ) + + private val SWAHILI_STRINGS = mapOf( + "shared_string" to "Kicheza Ugunduzi", + "swahili_only_string" to "Badili Wasifu" + ) + } + + private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } + private val transformerFactory by lazy { TransformerFactory.newInstance() } + + private lateinit var appResources: File + private lateinit var utilityResources: File + + @Before + fun setUp() { + // Ensure there are directories for string resources. + appResources = tempFolder.newFolder("app", "src", "main", "res") + utilityResources = tempFolder.newFolder("utility", "src", "main", "res") + } + + @Test + fun testRetrieveBaseStringFile_noStrings_throwsException() { + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains( + "Missing translation strings for language(s): ARABIC, BRAZILIAN_PORTUGUESE, ENGLISH," + + " SWAHILI" + ) + } + + @Test + fun testRetrieveBaseStringFile_noBaseEnglishStrings_throwsException() { + populateArabicTranslations() + populateBrazilianPortugueseTranslations() + populateSwahiliTranslations() + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains("Missing translation strings for language(s): ENGLISH") + } + + @Test + fun testRetrieveBaseStringFile_noArabicStrings_throwsException() { + populateBrazilianPortugueseTranslations() + populateEnglishTranslations() + populateSwahiliTranslations() + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains("Missing translation strings for language(s): ARABIC") + } + + @Test + fun testRetrieveBaseStringFile_noBrazilianPortugueseStrings_throwsException() { + populateArabicTranslations() + populateEnglishTranslations() + populateSwahiliTranslations() + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains("Missing translation strings for language(s): BRAZILIAN_PORTUGUESE") + } + + @Test + fun testRetrieveBaseStringFile_noSwahiliStrings_throwsException() { + populateArabicTranslations() + populateBrazilianPortugueseTranslations() + populateEnglishTranslations() + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains("Missing translation strings for language(s): SWAHILI") + } + + @Test + fun testRetrieveBaseStringFile_extraStringsDirectory_throwsException() { + populateAllAppTranslations() + populateTranslations(appResources, "values-fake", mapOf()) + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + assertThat(exception) + .hasMessageThat() + .contains( + "Strings file 'app/src/main/res/values-fake/strings.xml' does not correspond to a known" + + " language: values-fake" + ) + } + + @Test + fun testRetrieveBaseStringFile_stringsOutsideAppDirectory_areIgnored() { + populateArabicTranslations() + populateBrazilianPortugueseTranslations() + populateSwahiliTranslations() + populateTranslations(utilityResources, "values", mapOf()) + val parser = StringResourceParser(tempFolder.root) + + val exception = assertThrows(IllegalStateException::class) { parser.retrieveBaseStringFile() } + + // An exception is still thrown since resources outside the app directory are ignored. + assertThat(exception) + .hasMessageThat() + .contains("Missing translation strings for language(s): ENGLISH") + } + + @Test + fun testRetrieveBaseStringFile_allStringsPresent_baseStringsInvalidXml_throwsException() { + populateArabicTranslations() + populateBrazilianPortugueseTranslations() + populateSwahiliTranslations() + writeTranslationsFile(appResources, "values", "") + val parser = StringResourceParser(tempFolder.root) + + assertThrows(SAXParseException::class) { parser.retrieveBaseStringFile() } + } + + @Test + fun testRetrieveBaseStringFile_allStringsPresentAndValid_returnsBaseStringFile() { + populateAllAppTranslations() + val parser = StringResourceParser(tempFolder.root) + + val stringFile = parser.retrieveBaseStringFile() + + assertThat(stringFile.language).isEqualTo(ENGLISH) + assertThat(stringFile.file.toRelativeString(tempFolder.root)) + .isEqualTo("app/src/main/res/values/strings.xml") + assertThat(stringFile.strings).containsExactlyEntriesIn(ENGLISH_STRINGS) + } + + @Test + fun testRetrieveBaseStringNames_allStringsPresentAndValid_returnsBaseStringNames() { + populateAllAppTranslations() + val parser = StringResourceParser(tempFolder.root) + + val stringNames = parser.retrieveBaseStringNames() + + assertThat(stringNames).containsExactly("shared_string", "english_only_string") + } + + @Test + fun retrieveAllNonEnglishTranslations_allStringsPresentAndValid_returnsNonEnglishStringFiles() { + populateAllAppTranslations() + val parser = StringResourceParser(tempFolder.root) + + val nonEnglishTranslations = parser.retrieveAllNonEnglishTranslations() + + assertThat(nonEnglishTranslations).hasSize(3) + assertThat(nonEnglishTranslations).containsKey(ARABIC) + assertThat(nonEnglishTranslations).containsKey(BRAZILIAN_PORTUGUESE) + assertThat(nonEnglishTranslations).containsKey(SWAHILI) + assertThat(nonEnglishTranslations).doesNotContainKey(ENGLISH) // Only non-English are included. + val arFile = nonEnglishTranslations[ARABIC] + assertThat(arFile?.language).isEqualTo(ARABIC) + assertThat(arFile?.file?.toRelativeString(tempFolder.root)) + .isEqualTo("app/src/main/res/values-ar/strings.xml") + assertThat(arFile?.strings).containsExactlyEntriesIn(ARABIC_STRINGS) + val ptBrFile = nonEnglishTranslations[BRAZILIAN_PORTUGUESE] + assertThat(ptBrFile?.language).isEqualTo(BRAZILIAN_PORTUGUESE) + assertThat(ptBrFile?.file?.toRelativeString(tempFolder.root)) + .isEqualTo("app/src/main/res/values-pt-rBR/strings.xml") + assertThat(ptBrFile?.strings).containsExactlyEntriesIn(BRAZILIAN_PORTUGUESE_STRINGS) + val swFile = nonEnglishTranslations[SWAHILI] + assertThat(swFile?.language).isEqualTo(SWAHILI) + assertThat(swFile?.file?.toRelativeString(tempFolder.root)) + .isEqualTo("app/src/main/res/values-sw/strings.xml") + assertThat(swFile?.strings).containsExactlyEntriesIn(SWAHILI_STRINGS) + } + + private fun populateAllAppTranslations() { + populateArabicTranslations() + populateBrazilianPortugueseTranslations() + populateEnglishTranslations() + populateSwahiliTranslations() + } + + private fun populateArabicTranslations() { + populateTranslations(appResources, "values-ar", ARABIC_STRINGS) + } + + private fun populateBrazilianPortugueseTranslations() { + populateTranslations(appResources, "values-pt-rBR", BRAZILIAN_PORTUGUESE_STRINGS) + } + + private fun populateEnglishTranslations() { + populateTranslations(appResources, "values", ENGLISH_STRINGS) + } + + private fun populateSwahiliTranslations() { + populateTranslations(appResources, "values-sw", SWAHILI_STRINGS) + } + + private fun populateTranslations( + resourceDir: File, + valuesDirName: String, + translations: Map + ) { + val document = documentBuilderFactory.newDocumentBuilder().newDocument() + val resourcesRoot = document.createElement("resources").also { document.appendChild(it) } + translations.map { (name, value) -> + document.createElement("string").also { + it.setAttribute("name", name) + it.textContent = value + } + }.forEach(resourcesRoot::appendChild) + writeTranslationsFile(resourceDir, valuesDirName, document.toSource()) + } + + private fun writeTranslationsFile(resourceDir: File, valuesDirName: String, contents: String) { + val valuesDir = File(resourceDir, valuesDirName).also { check(it.mkdir()) } + File(valuesDir, "strings.xml").writeText(contents) + } + + private fun Document.toSource(): String { + // Reference: https://stackoverflow.com/a/5456836. + val transformer = transformerFactory.newTransformer() + return StringWriter().apply { + transformer.transform(DOMSource(this@toSource), StreamResult(this@apply)) + }.toString() + } +} diff --git a/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceValidationCheckTest.kt b/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceValidationCheckTest.kt new file mode 100644 index 00000000000..c7fc526f687 --- /dev/null +++ b/scripts/src/javatests/org/oppia/android/scripts/xml/StringResourceValidationCheckTest.kt @@ -0,0 +1,259 @@ +package org.oppia.android.scripts.xml + +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.oppia.android.testing.assertThrows +import org.w3c.dom.Document +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.io.StringWriter +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +/** Tests for the string_resource_validation_check test. */ +// FunctionName: test names are conventionally named with underscores. +@Suppress("FunctionName") +class StringResourceValidationCheckTest { + private companion object { + private const val AR_STRING_NO_NEWLINES = "مساعدة" + private const val AR_STRING_ONE_NEWLINE = "مساعدة\\n" + private const val AR_STRING_TWO_NEWLINES = "\\nمساعدة\\n" + + private const val PT_BR_STRING_NO_NEWLINES = "Ajuda" + private const val PT_BR_STRING_ONE_NEWLINE = "\\nAjuda" + private const val PT_BR_STRING_TWO_NEWLINES = "\\nAjuda\\n" + + private const val EN_STRING_ONE_NEWLINE = "\\nHelp" + + private const val SW_STRING_NO_NEWLINES = "Msaada" + private const val SW_STRING_ONE_NEWLINE = "\\nMsaada" + private const val SW_STRING_TWO_NEWLINES = "\\nMsaada\\n" + } + + @field:[Rule JvmField] var tempFolder = TemporaryFolder() + + private val originalOut: PrintStream = System.out + private val documentBuilderFactory by lazy { DocumentBuilderFactory.newInstance() } + private val transformerFactory by lazy { TransformerFactory.newInstance() } + + private lateinit var outContent: ByteArrayOutputStream + private lateinit var appResources: File + + @Before + fun setUp() { + outContent = ByteArrayOutputStream() + appResources = tempFolder.newFolder("app", "src", "main", "res") + System.setOut(PrintStream(outContent)) + } + + @After + fun restoreStreams() { + System.setOut(originalOut) + } + + @Test + fun testScript_missingPath_throwsException() { + val exception = assertThrows(IllegalArgumentException::class) { runScript(/* With no path. */) } + + assertThat(exception) + .hasMessageThat() + .contains("Expected: bazel run //scripts:string_resource_validation_check -- ") + } + + @Test + fun testScript_validPath_noStringFiles_fails() { + val exception = assertThrows(IllegalStateException::class) { + runScript(tempFolder.root.absolutePath) + } + + assertThat(exception).hasMessageThat().contains("Missing translation strings for language(s)") + } + + @Test + fun testScript_allMatch_succeeds() { + populateArabicTranslations(mapOf("str1" to AR_STRING_ONE_NEWLINE)) + populateBrazilianPortugueseTranslations(mapOf("str1" to PT_BR_STRING_ONE_NEWLINE)) + populateEnglishTranslations(mapOf("str1" to EN_STRING_ONE_NEWLINE)) + populateSwahiliTranslations(mapOf("str1" to SW_STRING_ONE_NEWLINE)) + + runScript(tempFolder.root.absolutePath) + + assertThat(outContent.asString()).contains("STRING RESOURCE VALIDATION CHECKS PASSED") + } + + @Test + fun testScript_inconsistentLines_arabic_failsWithFindings() { + populateArabicTranslations( + mapOf("str1" to AR_STRING_NO_NEWLINES, "str2" to AR_STRING_TWO_NEWLINES) + ) + populateBrazilianPortugueseTranslations(mapOf("str1" to PT_BR_STRING_ONE_NEWLINE)) + populateEnglishTranslations( + mapOf("str1" to EN_STRING_ONE_NEWLINE, "str2" to EN_STRING_ONE_NEWLINE) + ) + populateSwahiliTranslations(mapOf("str1" to SW_STRING_ONE_NEWLINE)) + + val exception = assertThrows(Exception::class) { runScript(tempFolder.root.absolutePath) } + + // This output check also inadvertently verifies that the script doesn't care about missing + // strings in translated string files. + assertThat(exception).hasMessageThat().contains("STRING RESOURCE VALIDATION CHECKS FAILED") + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 language(s) were found with string consistency errors. + + 2 consistency error(s) were found for ARABIC strings (file: app/src/main/res/values-ar/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + """.trimIndent().trim() + ) + } + + @Test + fun testScript_inconsistentLines_brazilianPortuguese_failsWithFindings() { + populateArabicTranslations(mapOf("str1" to AR_STRING_ONE_NEWLINE)) + populateBrazilianPortugueseTranslations( + mapOf("str1" to PT_BR_STRING_NO_NEWLINES, "str2" to PT_BR_STRING_TWO_NEWLINES) + ) + populateEnglishTranslations( + mapOf("str1" to EN_STRING_ONE_NEWLINE, "str2" to EN_STRING_ONE_NEWLINE) + ) + populateSwahiliTranslations(mapOf("str1" to SW_STRING_ONE_NEWLINE)) + + val exception = assertThrows(Exception::class) { runScript(tempFolder.root.absolutePath) } + + // This output check also inadvertently verifies that the script doesn't care about missing + // strings in translated string files. + assertThat(exception).hasMessageThat().contains("STRING RESOURCE VALIDATION CHECKS FAILED") + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 language(s) were found with string consistency errors. + + 2 consistency error(s) were found for BRAZILIAN_PORTUGUESE strings (file: app/src/main/res/values-pt-rBR/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + """.trimIndent().trim() + ) + } + + @Test + fun testScript_inconsistentLines_swahili_failsWithFindings() { + populateArabicTranslations(mapOf("str1" to AR_STRING_ONE_NEWLINE)) + populateBrazilianPortugueseTranslations(mapOf("str1" to PT_BR_STRING_ONE_NEWLINE)) + populateEnglishTranslations( + mapOf("str1" to EN_STRING_ONE_NEWLINE, "str2" to EN_STRING_ONE_NEWLINE) + ) + populateSwahiliTranslations( + mapOf("str1" to SW_STRING_NO_NEWLINES, "str2" to SW_STRING_TWO_NEWLINES) + ) + + val exception = assertThrows(Exception::class) { runScript(tempFolder.root.absolutePath) } + + // This output check also inadvertently verifies that the script doesn't care about missing + // strings in translated string files. + assertThat(exception).hasMessageThat().contains("STRING RESOURCE VALIDATION CHECKS FAILED") + assertThat(outContent.asString().trim()).isEqualTo( + """ + 1 language(s) were found with string consistency errors. + + 2 consistency error(s) were found for SWAHILI strings (file: app/src/main/res/values-sw/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + """.trimIndent().trim() + ) + } + + @Test + fun testScript_inconsistentLines_allLanguages_failsWithFindings() { + populateArabicTranslations( + mapOf("str1" to AR_STRING_NO_NEWLINES, "str2" to AR_STRING_TWO_NEWLINES) + ) + populateBrazilianPortugueseTranslations( + mapOf("str1" to PT_BR_STRING_NO_NEWLINES, "str2" to PT_BR_STRING_TWO_NEWLINES) + ) + populateEnglishTranslations( + mapOf("str1" to EN_STRING_ONE_NEWLINE, "str2" to EN_STRING_ONE_NEWLINE) + ) + populateSwahiliTranslations( + mapOf("str1" to SW_STRING_NO_NEWLINES, "str2" to SW_STRING_TWO_NEWLINES) + ) + + val exception = assertThrows(Exception::class) { runScript(tempFolder.root.absolutePath) } + + // This output check also inadvertently verifies that the script doesn't care about missing + // strings in translated string files. + assertThat(exception).hasMessageThat().contains("STRING RESOURCE VALIDATION CHECKS FAILED") + assertThat(outContent.asString().trim()).isEqualTo( + """ + 3 language(s) were found with string consistency errors. + + 2 consistency error(s) were found for ARABIC strings (file: app/src/main/res/values-ar/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + + 2 consistency error(s) were found for BRAZILIAN_PORTUGUESE strings (file: app/src/main/res/values-pt-rBR/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + + 2 consistency error(s) were found for SWAHILI strings (file: app/src/main/res/values-sw/strings.xml): + - string str1: original translation uses 2 line(s) but translation uses 1 line(s). Please remove any extra lines or add any that are missing. + - string str2: original translation uses 2 line(s) but translation uses 3 line(s). Please remove any extra lines or add any that are missing. + """.trimIndent().trim() + ) + } + + private fun runScript(vararg args: String) = main(*args) + + private fun populateArabicTranslations(strings: Map) { + populateTranslations(appResources, "values-ar", strings) + } + + private fun populateBrazilianPortugueseTranslations(strings: Map) { + populateTranslations(appResources, "values-pt-rBR", strings) + } + + private fun populateEnglishTranslations(strings: Map) { + populateTranslations(appResources, "values", strings) + } + + private fun populateSwahiliTranslations(strings: Map) { + populateTranslations(appResources, "values-sw", strings) + } + + private fun populateTranslations( + resourceDir: File, + valuesDirName: String, + translations: Map + ) { + val document = documentBuilderFactory.newDocumentBuilder().newDocument() + val resourcesRoot = document.createElement("resources").also { document.appendChild(it) } + translations.map { (name, value) -> + document.createElement("string").also { + it.setAttribute("name", name) + it.textContent = value + } + }.forEach(resourcesRoot::appendChild) + writeTranslationsFile(resourceDir, valuesDirName, document.toSource()) + } + + private fun writeTranslationsFile(resourceDir: File, valuesDirName: String, contents: String) { + val valuesDir = File(resourceDir, valuesDirName).also { check(it.mkdir()) } + File(valuesDir, "strings.xml").writeText(contents) + } + + private fun Document.toSource(): String { + // Reference: https://stackoverflow.com/a/5456836. + val transformer = transformerFactory.newTransformer() + return StringWriter().apply { + transformer.transform(DOMSource(this@toSource), StreamResult(this@apply)) + }.toString() + } + + private fun ByteArrayOutputStream.asString() = toString(Charsets.UTF_8.name()) +} From aa51154c96844b4b46a5c7e7bd45d78d29d6aac3 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Wed, 7 Sep 2022 05:01:34 -0500 Subject: [PATCH 16/19] Update version codes & pt-BR strings. The strings were manually pulled Translatewiki. --- app/src/main/res/values-pt-rBR/strings.xml | 28 +++++++++++++++------- version.bzl | 17 +++++++------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 3284685561e..53c259a23d6 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -2,6 +2,7 @@