From 18507ec24c50744e7763dc8cb53d8d398880dca1 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Fri, 9 Apr 2021 10:25:44 -0700 Subject: [PATCH] For #18836: add isHotStartForStartedActivity and tests. --- .../fenix/perf/StartupStateProvider.kt | 31 ++++++++++ .../fenix/perf/StartupStateProviderTest.kt | 56 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/app/src/main/java/org/mozilla/fenix/perf/StartupStateProvider.kt b/app/src/main/java/org/mozilla/fenix/perf/StartupStateProvider.kt index 76a5fde75..c033e9e25 100644 --- a/app/src/main/java/org/mozilla/fenix/perf/StartupStateProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/perf/StartupStateProvider.kt @@ -89,4 +89,35 @@ class StartupStateProvider( ) return isLastActivityCreatedStillStarted } + + /** + * Returns true if the current startup state is HOT and the currently started activity is the + * first started activity for this start up (i.e. we can use it for performance measurements). + * + * This method must be called after the foreground activity is STARTED. + */ + fun isHotStartForStartedActivity(activityClass: Class): Boolean { + // A hot start means: + // - the app was backgrounded and has since been started + // - the first started activity since the app was started is still active. + // - that activity was not created before being started + // + // For the activity log, we expect: + // [... App-STOPPED, ... Activity-STARTED, App-STARTED] + // where: + // - App-STOPPED is the last STOPPED seen + // - App-CREATED is NOT called for this activity + // - we're assuming App-STARTED will only be last if one activity is started (as observed) + if (!startupLog.log.contains(LogEntry.AppStopped)) { + return false // if the app hasn't been stopped, it's not a hot start. + } + val afterLastStopped = startupLog.log.takeLastWhile { it != LogEntry.AppStopped } + + val isLastActivityStartedStillStarted = afterLastStopped.takeLast(2) == listOf( + LogEntry.ActivityStarted(activityClass), + LogEntry.AppStarted + ) + return !afterLastStopped.contains(LogEntry.ActivityCreated(activityClass)) && + isLastActivityStartedStillStarted + } } diff --git a/app/src/test/java/org/mozilla/fenix/perf/StartupStateProviderTest.kt b/app/src/test/java/org/mozilla/fenix/perf/StartupStateProviderTest.kt index d7b822e8d..347846c35 100644 --- a/app/src/test/java/org/mozilla/fenix/perf/StartupStateProviderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/perf/StartupStateProviderTest.kt @@ -81,6 +81,27 @@ class StartupStateProviderTest { } } + @Test + fun `GIVEN the app started for an activity WHEN is cold start THEN hot start is false`() { + forEachColdStartEntries { index -> + assertFalse("$index", provider.isHotStartForStartedActivity(homeActivityClass)) + } + } + + @Test + fun `GIVEN the app started for an activity WHEN is warm start THEN hot start is false`() { + forEachWarmStartEntries { index -> + assertFalse("$index", provider.isHotStartForStartedActivity(homeActivityClass)) + } + } + + @Test + fun `GIVEN the app started for an activity WHEN is hot start THEN hot start is true` () { + forEachHotStartEntries { index -> + assertTrue("$index", provider.isHotStartForStartedActivity(homeActivityClass)) + } + } + @Test fun `GIVEN the app started for an activity WHEN we launched HA through a drawing IntentRA THEN start up is not cold`() { // These entries mimic observed behavior for local code changes. @@ -111,6 +132,21 @@ class StartupStateProviderTest { assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass)) } + @Test + fun `GIVEN the app started for an activity WHEN we launched HA through a drawing IntentRA THEN start up is not hot`() { + // These entries mimic observed behavior for local code changes. + logEntries.addAll(listOf( + LogEntry.AppStopped, + LogEntry.ActivityStopped(homeActivityClass), + LogEntry.ActivityCreated(irActivityClass), + LogEntry.ActivityStarted(irActivityClass), + LogEntry.AppStarted, + LogEntry.ActivityStarted(homeActivityClass), + LogEntry.ActivityStopped(irActivityClass) + )) + assertFalse(provider.isHotStartForStartedActivity(homeActivityClass)) + } + @Test fun `GIVEN the app started for an activity WHEN two HomeActivities are created THEN start up is not cold`() { // We're making an assumption about how this would work based on previous observed patterns. @@ -181,6 +217,26 @@ class StartupStateProviderTest { assertFalse(provider.isWarmStartForStartedActivity(homeActivityClass)) } + @Test + fun `GIVEN the app has not been stopped WHEN an activity has not been created THEN it's not a hot start`() { + assertFalse(provider.isHotStartForStartedActivity(homeActivityClass)) + } + + @Test + fun `GIVEN the app has been stopped WHEN an activity has not been created THEN it's not a hot start`() { + logEntries.add(LogEntry.AppStopped) + assertFalse(provider.isHotStartForStartedActivity(homeActivityClass)) + } + + @Test + fun `GIVEN the app has been stopped WHEN an activity has not been started THEN it's not a hot start`() { + logEntries.addAll(listOf( + LogEntry.AppStopped, + LogEntry.ActivityCreated(homeActivityClass) + )) + assertFalse(provider.isHotStartForStartedActivity(homeActivityClass)) + } + private fun forEachColdStartEntries(block: (index: Int) -> Unit) { // These entries mimic observed behavior. //