Skip to content

Fix #5989, #6053, #6059, #6060, #6097, #6200: Rebuild app & WorkManager initialization#6062

Open
BenHenning wants to merge 31 commits intodevelopfrom
fix-work-manager-and-app-init
Open

Fix #5989, #6053, #6059, #6060, #6097, #6200: Rebuild app & WorkManager initialization#6062
BenHenning wants to merge 31 commits intodevelopfrom
fix-work-manager-and-app-init

Conversation

@BenHenning
Copy link
Copy Markdown
Member

@BenHenning BenHenning commented Jan 2, 2026

Explanation

Fixes #5989
Fixes #6053
Fixes #6059
Fixes #6060
Fixes #6097
Fixes #6200

This PR fixes the app's WorkManager configuration by essentially rebuilding how it works on a fundamental level.

Existing behaviors

Previously, the app attempted to provide a custom WorkManager Configuration (via AbstractOppiaApplication) which, per WorkManagerConfigurationModule, added a bunch of custom WorkerFactorys per a provided DelegatingWorkerFactory. However, there are two problems with this setup (#6053 covers both of these):

  1. In order to provide a custom Configuration the app must remove the custom initialization provider (https://developer.android.com/develop/background-work/background-tasks/persistent/configuration/custom-configuration). Technically this is being done in the app today, however it's removing all app initialization which can affect more than just WorkManager.
    • This is expected to be one of the root problems behind [BUG]: WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate() [top crash] #6059 (though it cannot be easily verified since we haven't determined a reproduction of the issue).
    • Note that the fix in the PR for this also involves setting exported to false for the provider. This is technically redundant since WorkManager already declares it as not exported, but the Android Linter is only reviewing the local AndroidManifest.xml and not the fully merged manifest so it's a reasonable way to silence the warning (and be a bit more explicit about safety).
  2. More problematically, the custom WorkerFactorys are not adhering to DelegatingWorkerFactory's contract. The delegating factory expects subsequent factories to return null if they do not match the request worker being created, but Oppia's workers always return non-null. This means that the first worker always wins which is in this case the MetricLogSchedulingWorker (hence why we didn't notice this easily before because the majority of analytics events are successfully being uploaded, and most performance metrics are captured via direct app callbacks). This also wasn't caught in testing because of tests directly validating the jobs and not their scheduled and periodic behaviors. [BUG]: PlatformParemeterSyncUpWorkerFactory and MetricLogSchedulingWorkerFactory are not being initialised correctly #5989 is tracking this issue.

Another issue with the current setup is that platform parameters cannot be safely initialized when the app starts up via a worker which has led to a top crash: #6060. That issue thread plus the documentation in AbstractOppiaApplication go into a lot more detail about the initialization order changes.

New design

BootstrapOppiaWorker

The new design involves introducing a single WorkManager worker: BootstrapOppiaWorker. This introduces a very elegant improvement over the previous solution since it allows us to solve several classes of common issues every worker will face:

  • Renaming the worker causes its previous runs to fail.
  • The worker's factory needs to correctly identify that it is the correct worker to instantiate for the particular task (the original problem leading to this PR), and in a way that doesn't interfere with WorkManager's own internal workers (which run through the same factory).
  • Dependency injection and needs to be correctly supported.
  • Worker logic needs to be interoperative with Oppia's background dispatchers so that they can be tested.
  • Worker failures need to correctly lead to periodic task cancellation.
  • Rogue jobs are correctly cancelled (to handle the rename case above and to ensure that such jobs aren't kept indefinitely running by WorkManager; these now clear themselves). This behavior works retroactively for past Oppia workers, as well, once this code is deployed to user's devices.
  • Platform parameter initialization must be done correctly.

Much of these weren't actually handled in the existing design. Using a common worker allows all of that to be solved in one place, and additional benefits could also be realized:

  • No need to ever change the Configuration implementation since the worker itself never changes.
  • Stricter controls over interacting with WorkManager (which actually, combined with some testing requirements, led to a complete isolation of WorkManager throughout the codebase).
  • Strong typing, automatic subtask support, and simpler worker implementations using a nice OppiaWorker interface with Dagger-managed bindings. In the case of subtasks, this is mainly establishing an architectural pattern around an emergent behavior that was already the case for basically all of the current workers. Each subtask operates as a distinct top-level worker from the eyes of WorkManager.

Note that BootstrapOppiaWorker and WorkManagerConfigurationModule are both being very thoroughly tested to ensure everything built on them can be comfortably relied upon and to cover the specific core cases that led to needing this PR in the first place (to help guard against regressions, however unlikely with these two classes rarely needing to change).

New initialization logic

Beyond that, the recreation of initialization led to some other nice improvements:

  • ApplicationStartupListener was updated to have two different callbacks depending on whether platform parameters are initialized. This actually provides significant more stability in listeners that rely on platform parameters.
  • AnalyticsStartupListener was replaced with StartupWorkerScheduleReadinessListener since that's effectively what it was: an application-bound listener that wanted to run on startup to schedule periodic jobs to run. Now that's done completely in an initialization order-safe way and with a custom scheduler (described later in this description).

New scheduler

A new WorkManagerScheduler has been introduced to simplify interacting with WorkManager when wanting to schedule jobs by reusing common and obvious constraints across jobs and also ensuring that jobs cannot be scheduled in a way where they'd effectively never run (to address the potential of #6097 being an issue). Jobs are enforced to run no more than every 15 minutes (which is also a hard WorkManager rule) and no less often than every 14 days.

This scheduler works in tandem with the new StartupWorkerScheduleReadinessListener to provide an easy-to-use method for defining custom schedulers for a job. StartupWorkerScheduleReadinessMonitor is a new utility (bound itself as an ApplicationStartupListener) which injects all of the StartupWorkerScheduleReadinessListeners and calls each at an initialization order safe time to schedule jobs (shortly after platform parameters are fully initialized). A few things to note:

  • The monitor is actually implemented in a way where it's safe for StartupWorkerScheduleReadinessListener implementations to use platform parameters directly (which is actually necessary since they may use platform parameters for configurations like job periodicity intervals as is the case for MetricLogSchedulingWorkerScheduler.
  • This approach of making the monitor itself an ApplicationStartupListener helps to keep AbstractOppiaApplication as lean as possible. One thing found during the development of this PR was that reasoning about AbstractOppiaApplication's initialization order and nuances was really challenging. Moving responsibilities out of the onCreate() should hopefully help make it much easier to understand and perform the necessary isolation for correctness.

One limitation to note: the current version of WorkManager means that WorkManagerScheduler must use ExistingPeriodicWorkPolicy.KEEP when scheduling jobs whereas ExistingPeriodicWorkPolicy.UPDATE would actually be better since the latter properly supports property changes being applied without needing to cancel and restart the job. Practically this isn't expected to cause any problems for a while but it could potentially in the future (such as if we tweak the periodicity of performance metric captures or platform parameter syncing). #6115 is capturing the upgrade work needed to address this and there's a TODO pointing to that issue in code to ensure the update happens once it's possible.

Finally, actual scheduling logic relies on WorkManager. Even though we attempt to schedule jobs upon startup they may not actually run unless it's time for them to run again (i.e. their constraints are met and they haven't run within their configured interval). In most cases, requesting to schedule a job is a no-op since it's will already be tracked by WorkManager.

OppiaWorker

OppiaWorker is described a bit above but it's meant to be an extremely straightforward way to represent scheduled workers without having to worry about the extra complexity of WorkManager and edge integration-specific quirks. The idea is that all a developer needs to do is:

  • Create a new OppiaWorker implementation with whatever subtasks are desired.
  • Create a new scheduler class that implements StartupWorkerScheduleReadinessListener to schedule the worker's subtasks upon application startup.
  • Create a new module class that binds both the listener and the worker (based on the worker's defined string name).
  • Add tests for each of the previous classes using the new testing functionality (described below).

And that's basically it. An OppiaWorker implementation and its StartupWorkerScheduleReadinessListener implementation can both directly depend on platform parameters and feature flags as needed and perform whatever background logic that needs to be done for the worker. The hope is this significantly simplifies implementing such workers in the future.

Developer reference improvements

To help illustrate how to set up a new OppiaWorker and test it, a developer-only DebugWorker has been introduced along with its scheduler and module classes. This is a fully functioning worker that actually runs in //:oppia_dev builds of the app and can be used as both a coding and maintenance reference (see wiki bits below).

A bunch of updates were made to the WorkManager documentation covering not only the new implementation and testing patterns but also how to actually forcibly run workers using adb. Unfortunately these instructions basically only work when adb can be rooted which is limited for personal devices (but should be fine for emulators). Hopefully these instructions significantly help when debugging workers in the future.

Testing infrastructure changes

Testing workers is surprisingly complex which is likely why existing tests weren't actually validating the periodicity part of workers, and were introducing a lot of "backdoors" to directly access configuration values rather than reading what was passed to WorkManager. A lot of investigation and iteration went into the creation of a new OppiaWorkManagerTestDriver utility which solves all of these (and more) problems:

  • It makes it possible to actually introspect a job's characteristics even as it's running. This gives us high confidence that a job's scheduler is actually correct in what it passes WorkManager.
  • It uses WorkManager's own internal testing library for managing constraints (though WorkManager itself doesn't do a great job here so the utility is limited).
  • It makes it possible to test periodicity. This is actually very complex and requires essentially simulating WorkManager's own behavior in a way that's interoperable with TestCoroutineDispatchers. I suggest looking at the code to understand what this does more.
    • Fortunately, this can be simplified once [Feature Request]: Upgrade work manager to 2.9.0 #6115 is addressed because USE_TIME_BASED_SCHEDULING can be used which allows direct binding to the FakeOppiaClock and seems to actually "just work" with Oppia's test coroutine management (with some other tweaks).
    • Unfortunately, [Feature Request]: Upgrade work manager to 2.9.0 #6115 requires a Kotlin upgrade (possibly two) which is definitely outside the scope of this PR. I tested a forced version of 2.9.0 to come to the above conclusions, and I won't repeat how I did that since it's so bad it should really never be done even for testing purposes.
  • It removed the need for FakeLogScheduler, FakeLogUploader, and FirebaseLogUploader (and FirebaseLogUploaderModule) since workers should actually just be tested directly, instead.

It's worth noting that the configuration test setup for the driver is rather complex and won't be fully described here since the documentation and code do a better job (though one thing to note is that using the driver will change the time management mode of the test away from the standard Oppia Android default). This and the other overall complexity of properly interacting with WorkManager, its driver, or TestCoroutineDispatchers is ultimately why this PR also mandates using this driver over WorkManager or its test utility directly. Trying to make jobs work correctly in a test environment is a complete minefield, so it makes sense to just eliminate whole classes of developer frustration and confusion by requiring using a utility that trivializes the majority of these interactions.

Some other notes:

Changes to worker implementations & simplifications

The event log upload worker, performance metrics capture worker, and platform parameter worker all obviously require substantial reworking (along with their corresponding peer classes) to fit the new architectural patterns. Note that some attempt was also made to ensure naming consistency when it was easy (which is why LogUploadWorker remains inconsistent for now--updating it to be consistent would be a rather large codebase-wide change on top of an already very large PR for minimal gain). Fortunately the new architectural pattern makes renaming these classes trivial in the future.

Note that this change does migrate over the platform parameter work which means it actually should run now. However, it doesn't actually perform syncing logic anymore and that will be addressed in #6061 (which actually prompted the investigation that led to this PR).

Miscellaneous non-test changes

There are a bunch of other changes that were made in passing:

  • ApplicationLifecycleListener has a new KDoc update that comes from the fact that ProcessLifecycleOwner has built-in delays for foreground/background changes to avoid invalid "went to background"/"wen to foreground" subsequent events just for navigating activities normally (since onStop/onStart is called between navigation moments). This was found to cause some initialization order complexities in tests so the documentation is meant to help make this clear for other developers in the future.
  • ApplicationLifecycleObserver had a bunch of changes:
    • It was split into itself and a new ApplicationLifecycleLogger which is responsible for the actual metric logging so that the observer can focus on activity-level state tracking.
    • The observer now makes use a command queue to handle complex ordering: even if the app isn't done initializing state changes still need to be captured immediately upon app start (actually logging events can catch up later). This wasn't something being done before and couldn't be due to the delayed initialization for platform parameters.
      • Changes can't be immediately processed since the logger indirectly depends on platform parameters which is why it needs to be injected as a Provider.
      • The command queue also resulted in some additional synchronizations and broader changes in tests.
  • AbstractOppiaApplication.onCreate was further simplified by abstracting away AppCheckProviderFactory to the class's constructor. This allows it to default to the default production-friendly factory but also allow DeveloperOppiaApplication to use DebugAppCheckProviderFactory as needed. This led to an overall cleanup of no longer needing the build flavor as part of the root application component anymore.
  • A small change in AndroidManifest.xml instructions was added to clarify how to actually enable faster event logs. This isn't directly related to the PR but it was noticed in passing. The previous support article was changed over the last few years and it's much harder to find the actual adb command needed.
  • CpuPerformanceSnapshotter was made injectable to simplify the Dagger graph a bit and which modules are actually needed for certain test scenarios. It's not obvious why it wasn't done this way originally from looking at code history.
  • Timestamps are propagated in a few more log places than before due to changes in time management in certain tests (due to the restrictions on setting time when using OppiaWorkManagerTestDriver).
  • ExceptionsController was noticed in passing that it wasn't a singleton, and this has been fixed. It needs to be to avoid potential race conditions against its PersistentCacheStore if/when more than one is created. This almost certainly has caused unseen problems.
  • UncaughtExceptionLoggerStartupListener was updated in a few ways:
    • It was reworked to only try and log exceptions when it's safe (i.e. when platform parameters can be injected) since otherwise it would silently fail and lead to no exceptions being logged or propagated upon app death.
    • It was also noticed that the utility wasn't correctly swapping out the default uncaught exception handler, only the main thread's. This has been corrected, and Gemini helped with writing the tests in this case to validate that it's actually correct. Many more exceptions will now be caught by the utility.

Miscellaneous test & test infrastructural changes

There are other miscellaneous test and test infrastructure changes outside the main work manager bits:

  • All WorkManager initialization was removed in tests that didn't actually use workers (e.g. ProfileAndDeviceIdActivityTest).
  • ExplorationActivityLocalTest did have an event reordering update but I don't remember precisely why this was correct.
  • ApplicationLifecycleObserverTest was updated to use an actual real activity to validate lifecycle integration (since before it wasn't actually testing that the utility was wired correctly). There's some messy reflection to ensure ProcessLifecycleOwner properly resets across tests because it uses static state internally and the current version being used doesn't allow it to be properly reset. Addressing this is being tracked in [Feature Request]: Migrate to a cleaner method for resetting ProcessLifecycleOwner in tests #6187.
  • CpuPerformanceSnapshotterTest has been updated so that the snapshotter itself is being tested rather than testing happening via ApplicationLifecycleObserver for more correct separations of concern.
  • TestPlatformParameterModule has become even more complex due to a realization during debugging that TestCoroutineDispatchers.runCurrent() cannot be safely called off the main thread (see [BUG]: Fix dispatcher synchronization issues #6116), yet it still being possible via certain worker initialization pathways for platform parameters to now be needed off the main thread. This has been addressed by specifically delegating back to the main thread and correctly blocking on it.
    • Note that the Looper check done is actually an SDK 21-compatible variant to isCurrentThread which is, unfortunately, SDK 23+. This technically doesn't matter for Robolectric tests but Android Lint is unhappy without this approach.
    • Separately, some new parameter overrides have also been added.
  • The various fake event loggers FakeAnalyticsEventLogger, FakeExceptionLogger, FakeFirestoreEventLogger, and FakePerformanceMetricsEventLogger have been updaed to have similar APIs (particularly for event count and exceptions) since this was needed for a bunch of tests. The loggers' tests themselves have also been updated to reflect these changes.

Script, pattern, an exemption updates

Other non-app changes:

  • KdocValidityCheck was updated to also ignore Multibinds (which it should've done before, this was just missed since it's a rarely used annotation). A new test was added to validate this behavior change.
  • A bunch of new regex checks were added to further restrict WorkManager library interaction as mentioned in previous sections.
  • No new tests are being added for:
  • Some exemptions were actually removed since there are tests for them now (such as for MetricLogSchedulingWorkerScheduler). This revealed that the TestFileCheck utility doesn't correctly fail when an exemption is added but a test actually exists. [BUG]: Test file checker doesn't catch existing tests #6188 is tracking fixing this.
  • DebugWorkerDebugModule was exempted for coverage compatibility since it actually caused a failure during the finalization of the PR. I didn't dig into the specifics as to why it has an incompatibility, but this seems to be a reasonable workaround for now.

Disclosure of LLM usage

I did use Gemini pretty heavily for diagnosing the early issues with the work manager integration and how to effectively debug them. This actually all began with #6061 when I was trying to figure out why I couldn't get the jobs to run, and pulling on that thread led (with LLM help) uncovered all of the broader problems that ended up being addressed here. Gemini did help a bit with ideating on an approach but the vast majority of the bootstrapping pattern and all of its corresponding code came from me. I used Gemini CLI a bit for some of the testing for UncaughtExceptionLoggerStartupListener but that was it.

Essential Checklist

  • The PR title and explanation each start with "Fix #bugnum: " (If this PR fixes part of an issue, prefix the title with "Fix part of #bugnum: ...".)
  • Any changes to scripts/assets files have their rationale included in the PR explanation.
  • The PR follows the style guide.
  • The PR does not contain any unnecessary code changes from Android Studio (reference).
  • The PR is made from a branch that's not called "develop" and is up-to-date with "develop".
  • The PR is assigned to the appropriate reviewers (reference).

For UI-specific PRs only

Largely this PR should have zero impact on direct UIs in the app, though it will indirectly affect the overall UX since it will enable platform parameters to actually work correctly (though not entirely, #6061 still needs to be finished). No screens are being updated as part of these changes.

This includes substantial changes to how the app starts up in all cases,
but especially for workers. The new approach is robust against worker
changes (including class renames or input changes), centralizes
intialization so that all workers always run in a sane environment, and
ensures all app initialization is done in a completely robust and safe
way.

Also, re-write much of the work manager wiki page to include new design
details and significant debugging recommendations.

Testing, code, and wiki documentation are not yet complete. This also
has only been tested on the developer build of the app, so more
validation will be necessary.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Jan 2, 2026

Thanks for submitting this pull request! Some main reviewers have taken time off for the next few weeks, so it may take a little while before we can look at this PR. We appreciate your patience while some of our team members recharge. We'll be fully returning on 05 January 2026.

@BenHenning
Copy link
Copy Markdown
Member Author

Note that this is probably in a good enough place for me to work off of it to make progress on #3506 (which was my original goal heading into this), and I'll need to circle back to this PR. It still needs a lot of documentation and testing work, including figuring out hopefully a better testing strategy than using fakes (I'm hoping it's possible to actually properly orchestrate kicking off the jobs but I have a lot more digging to do in order to figure that out fully).

@oppiabot
Copy link
Copy Markdown

oppiabot bot commented Jan 9, 2026

Hi @BenHenning, I'm going to mark this PR as stale because it hasn't had any updates for 7 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue.
If you are still working on this PR, please make a follow-up commit within 3 days (and submit it for review, if applicable). Please also let us know if you are stuck so we can help you!

@oppiabot oppiabot bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Jan 9, 2026
@oppiabot oppiabot bot removed the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Jan 13, 2026
@oppiabot
Copy link
Copy Markdown

oppiabot bot commented Jan 20, 2026

Hi @BenHenning, I'm going to mark this PR as stale because it hasn't had any updates for 7 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue.
If you are still working on this PR, please make a follow-up commit within 3 days (and submit it for review, if applicable). Please also let us know if you are stuck so we can help you!

@oppiabot oppiabot bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Jan 20, 2026
@oppiabot oppiabot bot closed this Jan 27, 2026
Some previous code was missed when converting the jobs, and there were
some API simplifications possible. Also, this ensures that platform
parameter syncing intervals can't be tweaked to the point of essentially
bricking app syncing.
This is needed for testing, though this will likely not be a permanent
upgrade since it requires Kotlin 1.8. For proof-of-concept purposes this
also includes ignoring the metadata check so that tests can run with 1.8
dependencies in the 1.6 environment.
These may not land in this PR but they were discovered as gaps when
trying to make work manager synchronization work in tests.
This includes some visibility changes as well as alterations to
TestPlatformParameterModule to ensure parameters can actually be
initialized correctly with the work manager pathway.

This commit essentially establishes the baseline for work manager job
testing and will be used as the foundation for adding tests for all of
the other work manager jobs.
This introduces new utilities to simplify testing with WorkManager and
one of which (the mixin) actually provides tests with the ability to
test multiple rounds of jobs with just time management by leveraging a
custom scheduler (or, rather, a simple self-scheduler to simulate
WorkManager).
Also refined some of the shared utilities including the fake performance
logger.
Also a bunch more shared utility refinement, plus some integration tests
for MetricLogSchedulingWorker in how it relates to LogUploadWorker.
@BenHenning BenHenning reopened this Feb 23, 2026
@oppiabot oppiabot bot removed the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Feb 23, 2026
Not much to add yet since downloading platform parameters isn't fully
supported yet.
Still many more to go, but this is a good start.
Conflicts:
	domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/ApplicationLifecycleObserver.kt
	domain/src/main/java/org/oppia/android/domain/oppialogger/analytics/LearnerAnalyticsLogger.kt
This makes the new test synching mechanism work correctly for UI tests
(which use a different sync-first pathway).
This fixes the dispatcher logic to actually behave correctly. This will
be moved outside of the PR so that it can be more iterated.
@Multibinds should also be exempted.
These are all managed through the central test utilities, now.
This isolates test WorkManager interactions to just a single utility now
(instead of being across multiple tests and multiple utilities).
The new version is hopefully much more streamlined and resilient to user
error.
Copy link
Copy Markdown
Member Author

@BenHenning BenHenning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-reviewed fixes.

PR should be up-to-date with develop again and seems to be in a pretty good shape. Just need to finish main PR description and it should be ready for full review (assuming CI passes now which I am hoping it will).

@github-actions
Copy link
Copy Markdown

Coverage Report

Results

Number of files assessed: 189
Overall Coverage: 96.19%
Coverage Analysis: PASS

Passing coverage

Files with passing code coverage
File Coverage Lines Hit Status Min Required
ActivityRouter.ktapp/src/main/java/org/oppia/android/app/activity/route/ActivityRouter.kt
100.00% 12 / 12 70%
ActivityLanguageLocaleHandler.ktapp/src/main/java/org/oppia/android/app/translation/ActivityLanguageLocaleHandler.kt
100.00% 11 / 11 70%
AppLanguageWatcherMixin.ktapp/src/main/java/org/oppia/android/app/translation/AppLanguageWatcherMixin.kt
72.22% 26 / 36 70%
RegexPatternValidationCheck.ktscripts/src/java/org/oppia/android/scripts/regex/RegexPatternValidationCheck.kt
100.00% 104 / 104 70%
KdocValidityCheck.ktscripts/src/java/org/oppia/android/scripts/docs/KdocValidityCheck.kt
99.25% 132 / 133 70%
PlatformParameterSyncUpWorker.ktdomain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorker.kt
75.86% 22 / 29 70%
PlatformParameterSyncUpWorkerScheduler.ktdomain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorkerScheduler.kt
100.00% 8 / 8 70%
WorkManagerConfigurationModule.ktdomain/src/main/java/org/oppia/android/domain/workmanager/WorkManagerConfigurationModule.kt
100.00% 8 / 8 70%
WorkManagerScheduler.ktdomain/src/main/java/org/oppia/android/domain/workmanager/WorkManagerScheduler.kt
100.00% 30 / 30 70%
DebugWorkerScheduler.ktdomain/src/main/java/org/oppia/android/domain/workmanager/debug/DebugWorkerScheduler.kt
100.00% 9 / 9 70%
DebugWorker.ktdomain/src/main/java/org/oppia/android/domain/workmanager/debug/DebugWorker.kt
100.00% 19 / 19 70%
BootstrapOppiaWorker.ktdomain/src/main/java/org/oppia/android/domain/workmanager/BootstrapOppiaWorker.kt
100.00% 51 / 51 70%
LogUploadWorker.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/loguploader/LogUploadWorker.kt
98.44% 63 / 64 70%
LogReportWorkerScheduler.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/loguploader/LogReportWorkerScheduler.kt
100.00% 5 / 5 70%
MetricLogSchedulingWorker.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/logscheduler/MetricLogSchedulingWorker.kt
100.00% 31 / 31 70%
MetricLogSchedulingWorkerScheduler.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/logscheduler/MetricLogSchedulingWorkerScheduler.kt
100.00% 19 / 19 70%
FeatureFlagsLogger.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/FeatureFlagsLogger.kt
100.00% 23 / 23 70%
CpuPerformanceSnapshotter.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/CpuPerformanceSnapshotter.kt
87.61% 99 / 113 70%
PerformanceMetricsLogger.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/PerformanceMetricsLogger.kt
100.00% 98 / 98 70%
InitializeDefaultLocaleRule.kttesting/src/main/java/org/oppia/android/testing/junit/InitializeDefaultLocaleRule.kt
100.00% 85 / 85 100% *
ActivityIntentFactories.ktapp/src/main/java/org/oppia/android/app/activity/ActivityIntentFactories.kt
0.00% 0 / 0 0% *
CpuPerformanceSnapshotterModule.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/CpuPerformanceSnapshotterModule.kt
80.00% 4 / 5 26% *

* represents tests with custom overridden pass/fail coverage thresholds

Exempted coverage

Files exempted from coverage
File Exemption Reason
FakeFirestoreEventLogger.kttesting/src/main/java/org/oppia/android/testing/FakeFirestoreEventLogger.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FakeAnalyticsEventLogger.kttesting/src/main/java/org/oppia/android/testing/FakeAnalyticsEventLogger.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TestPlatformParameterModule.kttesting/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt
This file is exempted from having a test file; skipping coverage check.
FakeExceptionLogger.kttesting/src/main/java/org/oppia/android/testing/FakeExceptionLogger.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FakePerformanceMetricsEventLogger.kttesting/src/main/java/org/oppia/android/testing/FakePerformanceMetricsEventLogger.kt
This file is incompatible with code coverage tooling; skipping coverage check.
CompletedStoryListActivity.ktapp/src/main/java/org/oppia/android/app/completedstorylist/CompletedStoryListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OnboardingFragment.ktapp/src/main/java/org/oppia/android/app/onboarding/OnboardingFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
CreateProfileActivity.ktapp/src/main/java/org/oppia/android/app/onboarding/CreateProfileActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OnboardingProfileTypeActivity.ktapp/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdminIntroActivity.ktapp/src/main/java/org/oppia/android/app/onboarding/AdminIntroActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
IntroActivity.ktapp/src/main/java/org/oppia/android/app/onboarding/IntroActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
CreateProfileFragment.ktapp/src/main/java/org/oppia/android/app/onboarding/CreateProfileFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
IntroFragment.ktapp/src/main/java/org/oppia/android/app/onboarding/IntroFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdminIntroFragment.ktapp/src/main/java/org/oppia/android/app/onboarding/AdminIntroFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OnboardingProfileTypeFragment.ktapp/src/main/java/org/oppia/android/app/onboarding/OnboardingProfileTypeFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OnboardingActivity.ktapp/src/main/java/org/oppia/android/app/onboarding/OnboardingActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MathExpressionInteractionsView.ktapp/src/main/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsView.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LessonThumbnailImageView.ktapp/src/main/java/org/oppia/android/app/customview/LessonThumbnailImageView.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OsDeprecationNoticeDialogFragment.ktapp/src/main/java/org/oppia/android/app/notice/OsDeprecationNoticeDialogFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ForcedAppDeprecationNoticeDialogFragment.ktapp/src/main/java/org/oppia/android/app/notice/ForcedAppDeprecationNoticeDialogFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
BetaNoticeDialogFragment.ktapp/src/main/java/org/oppia/android/app/notice/BetaNoticeDialogFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OptionalAppDeprecationNoticeDialogFragment.ktapp/src/main/java/org/oppia/android/app/notice/OptionalAppDeprecationNoticeDialogFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
GeneralAvailabilityUpgradeNoticeDialogFragment.ktapp/src/main/java/org/oppia/android/app/notice/GeneralAvailabilityUpgradeNoticeDialogFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ActivityRouterModule.ktapp/src/main/java/org/oppia/android/app/activity/route/ActivityRouterModule.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AbstractOppiaApplication.ktapp/src/main/java/org/oppia/android/app/application/AbstractOppiaApplication.kt
This file is exempted from having a test file; skipping coverage check.
DeveloperApplicationComponent.ktapp/src/main/java/org/oppia/android/app/application/dev/DeveloperApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.
DeveloperOppiaApplication.ktapp/src/main/java/org/oppia/android/app/application/dev/DeveloperOppiaApplication.kt
This file is exempted from having a test file; skipping coverage check.
GaApplicationComponent.ktapp/src/main/java/org/oppia/android/app/application/ga/GaApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.
ApplicationComponent.ktapp/src/main/java/org/oppia/android/app/application/ApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.
AlphaApplicationComponent.ktapp/src/main/java/org/oppia/android/app/application/alpha/AlphaApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.
BetaApplicationComponent.ktapp/src/main/java/org/oppia/android/app/application/beta/BetaApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.
TopicTestActivityForStory.ktapp/src/main/java/org/oppia/android/app/testing/TopicTestActivityForStory.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TestActivity.ktapp/src/main/java/org/oppia/android/app/testing/activity/TestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FractionInputInteractionViewTestActivity.ktapp/src/main/java/org/oppia/android/app/testing/FractionInputInteractionViewTestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
DragDropTestActivity.ktapp/src/main/java/org/oppia/android/app/testing/DragDropTestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TestFontScaleConfigurationUtilActivity.ktapp/src/main/java/org/oppia/android/app/testing/TestFontScaleConfigurationUtilActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
InputInteractionViewTestActivity.ktapp/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TextInputInteractionViewTestActivity.ktapp/src/main/java/org/oppia/android/app/testing/TextInputInteractionViewTestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RatioInputInteractionViewTestActivity.ktapp/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OngoingTopicListActivity.ktapp/src/main/java/org/oppia/android/app/ongoingtopiclist/OngoingTopicListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
SplashActivity.ktapp/src/main/java/org/oppia/android/app/splash/SplashActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AppLanguageResourceHandler.ktapp/src/main/java/org/oppia/android/app/translation/AppLanguageResourceHandler.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileChooserActivity.ktapp/src/main/java/org/oppia/android/app/profile/ProfileChooserActivity.kt
This file is exempted from having a test file; skipping coverage check.
AddProfileActivity.ktapp/src/main/java/org/oppia/android/app/profile/AddProfileActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileLoginActivity.ktapp/src/main/java/org/oppia/android/app/profile/ProfileLoginActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PinPasswordActivity.ktapp/src/main/java/org/oppia/android/app/profile/PinPasswordActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileLoginFragment.ktapp/src/main/java/org/oppia/android/app/profile/ProfileLoginFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdminPinActivity.ktapp/src/main/java/org/oppia/android/app/profile/AdminPinActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileChooserFragment.ktapp/src/main/java/org/oppia/android/app/profile/ProfileChooserFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdminAuthActivity.ktapp/src/main/java/org/oppia/android/app/profile/AdminAuthActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileAndDeviceIdFragment.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileAndDeviceIdActivity.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/learneranalytics/ProfileAndDeviceIdActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AppVersionActivity.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/appversion/AppVersionActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdministratorControlsActivity.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AdministratorControlsFragment.ktapp/src/main/java/org/oppia/android/app/administratorcontrols/AdministratorControlsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileRenameActivity.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileResetPinActivity.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileResetPinFragment.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileResetPinFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileListActivity.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileRenameFragment.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileRenameFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileEditActivity.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileEditActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileEditFragment.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileEditFragment.kt
This file is exempted from having a test file; skipping coverage check.
ProfileListFragment.ktapp/src/main/java/org/oppia/android/app/settings/profile/ProfileListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
SpotlightFragment.ktapp/src/main/java/org/oppia/android/app/spotlight/SpotlightFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ViewEventLogsActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ViewEventLogsFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/vieweventlogs/ViewEventLogsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PlatformParametersFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PlatformParametersActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/platformparameters/PlatformParametersActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkChaptersCompletedActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkChaptersCompletedFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/markchapterscompleted/MarkChaptersCompletedFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
DeveloperOptionsActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MathExpressionParserActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MathExpressionParserFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/mathexpressionparser/MathExpressionParserFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkTopicsCompletedFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkTopicsCompletedActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/marktopicscompleted/MarkTopicsCompletedActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ForceNetworkTypeActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ForceNetworkTypeFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/forcenetworktype/ForceNetworkTypeFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FeatureFlagsActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FeatureFlagsFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/featureflags/FeatureFlagsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkStoriesCompletedActivity.ktapp/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MarkStoriesCompletedFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/markstoriescompleted/MarkStoriesCompletedFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
DeveloperOptionsFragment.ktapp/src/main/java/org/oppia/android/app/devoptions/DeveloperOptionsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
DateTimeUtil.ktapp/src/main/java/org/oppia/android/app/utility/datetime/DateTimeUtil.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MathExpressionAccessibilityUtil.ktapp/src/main/java/org/oppia/android/app/utility/math/MathExpressionAccessibilityUtil.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RatioExtensions.ktapp/src/main/java/org/oppia/android/app/utility/RatioExtensions.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ResumeLessonActivity.ktapp/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ResumeLessonFragment.ktapp/src/main/java/org/oppia/android/app/resumelesson/ResumeLessonFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ExplorationActivity.ktapp/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
BottomSheetOptionsMenu.ktapp/src/main/java/org/oppia/android/app/player/exploration/BottomSheetOptionsMenu.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AudioFragment.ktapp/src/main/java/org/oppia/android/app/player/audio/AudioFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ImageRegionSelectionInteractionView.ktapp/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt
This file is incompatible with code coverage tooling; skipping coverage check.
StateFragment.ktapp/src/main/java/org/oppia/android/app/player/state/StateFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
SurveyActivity.ktapp/src/main/java/org/oppia/android/app/survey/SurveyActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
SurveyFragment.ktapp/src/main/java/org/oppia/android/app/survey/SurveyFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MyDownloadsFragment.ktapp/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
MyDownloadsActivity.ktapp/src/main/java/org/oppia/android/app/mydownloads/MyDownloadsActivity.kt
This file is exempted from having a test file; skipping coverage check.
StoryActivity.ktapp/src/main/java/org/oppia/android/app/story/StoryActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
StoryFragment.ktapp/src/main/java/org/oppia/android/app/story/StoryFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ClassroomListActivity.ktapp/src/main/java/org/oppia/android/app/classroom/ClassroomListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ClassroomListFragment.ktapp/src/main/java/org/oppia/android/app/classroom/ClassroomListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RevisionCardActivity.ktapp/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RevisionCardFragment.ktapp/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
QuestionPlayerActivity.ktapp/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicRevisionFragment.ktapp/src/main/java/org/oppia/android/app/topic/revision/TopicRevisionFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicFragment.ktapp/src/main/java/org/oppia/android/app/topic/TopicFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicActivity.ktapp/src/main/java/org/oppia/android/app/topic/TopicActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicLessonsFragment.ktapp/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicInfoFragment.ktapp/src/main/java/org/oppia/android/app/topic/info/TopicInfoFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicPracticeFragment.ktapp/src/main/java/org/oppia/android/app/topic/practice/TopicPracticeFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ConceptCardFragment.ktapp/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
HelpFragment.ktapp/src/main/java/org/oppia/android/app/help/HelpFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LicenseTextViewerActivity.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ThirdPartyDependencyListFragment.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ThirdPartyDependencyListActivity.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/ThirdPartyDependencyListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LicenseListActivity.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LicenseListFragment.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/LicenseListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LicenseTextViewerFragment.ktapp/src/main/java/org/oppia/android/app/help/thirdparty/LicenseTextViewerFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FAQListActivity.ktapp/src/main/java/org/oppia/android/app/help/faq/FAQListActivity.kt
This file is exempted from having a test file; skipping coverage check.
FAQSingleActivity.ktapp/src/main/java/org/oppia/android/app/help/faq/faqsingle/FAQSingleActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FAQListFragment.ktapp/src/main/java/org/oppia/android/app/help/faq/FAQListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
HelpActivity.ktapp/src/main/java/org/oppia/android/app/help/HelpActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PoliciesActivity.ktapp/src/main/java/org/oppia/android/app/policies/PoliciesActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PoliciesFragment.ktapp/src/main/java/org/oppia/android/app/policies/PoliciesFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
StringToRatioParser.ktapp/src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt
This file is incompatible with code coverage tooling; skipping coverage check.
FractionParsingUiError.ktapp/src/main/java/org/oppia/android/app/parser/FractionParsingUiError.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TopicSummaryViewModel.ktapp/src/main/java/org/oppia/android/app/home/topiclist/TopicSummaryViewModel.kt
This file is incompatible with code coverage tooling; skipping coverage check.
HomeActivity.ktapp/src/main/java/org/oppia/android/app/home/HomeActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PromotedStoryListViewModel.ktapp/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryListViewModel.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PromotedStoryViewModel.ktapp/src/main/java/org/oppia/android/app/home/promotedlist/PromotedStoryViewModel.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RecentlyPlayedFragment.ktapp/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
RecentlyPlayedActivity.ktapp/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
WelcomeViewModel.ktapp/src/main/java/org/oppia/android/app/home/WelcomeViewModel.kt
This file is incompatible with code coverage tooling; skipping coverage check.
BindableAdapter.ktapp/src/main/java/org/oppia/android/app/recyclerview/BindableAdapter.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileProgressFragment.ktapp/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfileProgressActivity.ktapp/src/main/java/org/oppia/android/app/profileprogress/ProfileProgressActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ProfilePictureActivity.ktapp/src/main/java/org/oppia/android/app/profileprogress/ProfilePictureActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ReadingTextSizeFragment.ktapp/src/main/java/org/oppia/android/app/options/ReadingTextSizeFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OptionsFragment.ktapp/src/main/java/org/oppia/android/app/options/OptionsFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AudioLanguageFragment.ktapp/src/main/java/org/oppia/android/app/options/AudioLanguageFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
OptionsActivity.ktapp/src/main/java/org/oppia/android/app/options/OptionsActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AudioLanguageActivity.ktapp/src/main/java/org/oppia/android/app/options/AudioLanguageActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AppLanguageActivity.ktapp/src/main/java/org/oppia/android/app/options/AppLanguageActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
AppLanguageFragment.ktapp/src/main/java/org/oppia/android/app/options/AppLanguageFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ReadingTextSizeActivity.ktapp/src/main/java/org/oppia/android/app/options/ReadingTextSizeActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
WalkthroughTopicListFragment.ktapp/src/main/java/org/oppia/android/app/walkthrough/topiclist/WalkthroughTopicListFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
WalkthroughFinalFragment.ktapp/src/main/java/org/oppia/android/app/walkthrough/end/WalkthroughFinalFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
WalkthroughActivity.ktapp/src/main/java/org/oppia/android/app/walkthrough/WalkthroughActivity.kt
This file is incompatible with code coverage tooling; skipping coverage check.
WalkthroughWelcomeFragment.ktapp/src/main/java/org/oppia/android/app/walkthrough/welcome/WalkthroughWelcomeFragment.kt
This file is incompatible with code coverage tooling; skipping coverage check.
HtmlParser.ktutility/src/main/java/org/oppia/android/util/parser/html/HtmlParser.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ListItemLeadingMarginSpan.ktutility/src/main/java/org/oppia/android/util/parser/html/ListItemLeadingMarginSpan.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ExplorationActiveTimeController.ktdomain/src/main/java/org/oppia/android/domain/exploration/ExplorationActiveTimeController.kt
This file is incompatible with code coverage tooling; skipping coverage check.
PlatformParameterSyncUpWorkerModule.ktdomain/src/main/java/org/oppia/android/domain/platformparameter/syncup/PlatformParameterSyncUpWorkerModule.kt
This file is exempted from having a test file; skipping coverage check.
OppiaWorkManagerTestDriver.ktdomain/src/main/java/org/oppia/android/domain/workmanager/testing/OppiaWorkManagerTestDriver.kt
This file is exempted from having a test file; skipping coverage check.
OppiaWorker.ktdomain/src/main/java/org/oppia/android/domain/workmanager/OppiaWorker.kt
This file is exempted from having a test file; skipping coverage check.
StartupWorkerScheduleReadinessListener.ktdomain/src/main/java/org/oppia/android/domain/workmanager/StartupWorkerScheduleReadinessListener.kt
This file is exempted from having a test file; skipping coverage check.
StartupWorkerScheduleReadinessMonitor.ktdomain/src/main/java/org/oppia/android/domain/workmanager/StartupWorkerScheduleReadinessMonitor.kt
This file is exempted from having a test file; skipping coverage check.
DebugWorkerDebugModule.ktdomain/src/main/java/org/oppia/android/domain/workmanager/debug/DebugWorkerDebugModule.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LogReportWorkerModule.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/loguploader/LogReportWorkerModule.kt
This file is exempted from having a test file; skipping coverage check.
ApplicationStartupListener.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/ApplicationStartupListener.kt
This file is exempted from having a test file; skipping coverage check.
MetricLogSchedulerModule.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/logscheduler/MetricLogSchedulerModule.kt
This file is incompatible with code coverage tooling; skipping coverage check.
LearnerAnalyticsLogger.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/LearnerAnalyticsLogger.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ApplicationLifecycleObserver.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/ApplicationLifecycleObserver.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ApplicationLifecycleLogger.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/ApplicationLifecycleLogger.kt
This file is exempted from having a test file; skipping coverage check.
ApplicationLifecycleListener.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/analytics/ApplicationLifecycleListener.kt
This file is exempted from having a test file; skipping coverage check.
UncaughtExceptionLoggerStartupListener.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/exceptions/UncaughtExceptionLoggerStartupListener.kt
This file is incompatible with code coverage tooling; skipping coverage check.
ExceptionsController.ktdomain/src/main/java/org/oppia/android/domain/oppialogger/exceptions/ExceptionsController.kt
This file is incompatible with code coverage tooling; skipping coverage check.
TestApplicationComponent.ktinstrumentation/src/java/org/oppia/android/instrumentation/application/TestApplicationComponent.kt
This file is exempted from having a test file; skipping coverage check.

Refer test_file_exemptions.textproto for the comprehensive list of file exemptions and their required coverage percentages.

To learn more, visit the Oppia Android Code Coverage wiki page

@akkicodes-dev
Copy link
Copy Markdown

Hi @BenHenning

This refactor is really impressive — I can see how introducing the Bootstrap worker and centralizing scheduling resolves the failure modes I was exploring earlier around WorkManager’s KEEP policy and permanent sync stalls. It’s much clearer now how the new architecture avoids those dead states.

I especially found the separation of scheduling concerns and the abstraction over WorkManager interactions quite elegant — it makes the system easier to reason about and extend.

I had one question while going through the changes: with the new scheduling flow, are intermittent failures (like transient network issues) primarily handled within individual workers (via retry/backoff), or is there also a mechanism at the scheduler/Bootstrap level that ensures eventual recovery?

Happy to dig deeper into related areas or help validate edge cases if that would be useful. Really enjoyed going through this — thanks for putting together such a thorough redesign!

@BenHenning
Copy link
Copy Markdown
Member Author

@akkicodes-dev the current implementation expects workers (that is, OppiaWorkers) to be responsible for handling errors that they encounter. Otherwise, failures will bubble up to the bootstrap worker and then to WorkManager which will handle the worker accordingly. There's not really a need for back-off at this point, and retry will happen automatically by virtue of these being periodic jobs.

@akkicodes-dev
Copy link
Copy Markdown

Got it, that clarifies things — thanks!

I was thinking in terms of additional retry/backoff handling, but this makes sense given the periodic nature of the workers and existing flow.

Appreciate the explanation!

@BenHenning BenHenning marked this pull request as ready for review April 1, 2026 06:36
@BenHenning BenHenning requested review from a team as code owners April 1, 2026 06:36
@BenHenning BenHenning requested a review from adhiamboperes April 1, 2026 06:36
@BenHenning
Copy link
Copy Markdown
Member Author

PTAL @adhiamboperes. This is now ready for full review (the PR description is finally finished!).

@oppiabot
Copy link
Copy Markdown

oppiabot bot commented Apr 8, 2026

Hi @BenHenning, I'm going to mark this PR as stale because it hasn't had any updates for 7 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue.
If you are still working on this PR, please make a follow-up commit within 3 days (and submit it for review, if applicable). Please also let us know if you are stuck so we can help you! If you're unsure how to reassign this PR to a reviewer, please make sure to review the wiki page that details the Guidance on submitting PRs.

@oppiabot oppiabot bot added the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Apr 8, 2026
@adhiamboperes adhiamboperes removed the stale Corresponds to items that haven't seen a recent update and may be automatically closed. label Apr 8, 2026
Copy link
Copy Markdown
Contributor

@adhiamboperes adhiamboperes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @BenHenning! PR LGTM mostly. PTAL at my comments.

Comment on lines +175 to +180
@Test
fun testFakeExceptionLogger_getMostRecentExceptions_two_noExceptionsLogged_returnsEmptyList() {
val mostRecentExceptions = fakeExceptionLogger.getMostRecentExceptions(count = 2)

assertThat(mostRecentExceptions).isEmpty()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear what this test is checking for.

Comment on lines +192 to +212
@Test
fun testFakeExceptionLogger_getMostRecentExceptions_two_twoExceptionsLogged_returnsInOrder() {
exceptionLogger.logException(exception2)
exceptionLogger.logException(exception1)

val mostRecentExceptions = fakeExceptionLogger.getMostRecentExceptions(count = 2)

val messages = mostRecentExceptions.map { it.message }
assertThat(messages).containsExactly("Second Exception", "First Exception").inOrder()
}

@Test
fun testFakeExceptionLogger_getMostRecentExceptions_one_twoExceptionsLogged_returnsLatest() {
exceptionLogger.logException(exception2)
exceptionLogger.logException(exception1)

val mostRecentExceptions = fakeExceptionLogger.getMostRecentExceptions(count = 1)

assertThat(mostRecentExceptions).hasSize(1)
assertThat(mostRecentExceptions.single()).hasMessageThat().isEqualTo("First Exception")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit on naming -- could these and adjacent tests be better as:
testFakeExceptionLogger_getMostRecentException_twoExceptionsLogged_returnsLatest

and

testFakeExceptionLogger_getTwoMostRecentExceptions_twoExceptionsLogged_returnsInOrder

/**
* A general-purpose, all-in-one test utility when interacting with [WorkManager] in Oppia tests.
*
* This utility contains a bunch of critical setup and interaction pathways that tests needing to
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence seems a bit broken?

@BenHenning BenHenning changed the title Fix #5989, #6053, #6059, #6060, #6097: Rebuild app & WorkManager initialization Fix #5989, #6053, #6059, #6060, #6097, #6200: Rebuild app & WorkManager initialization Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment