@@ -537,4 +537,127 @@ class AppStartMetricsTest {
537537
538538 assertEquals(secondActivity, CurrentActivityHolder .getInstance().activity)
539539 }
540+
541+ @Test
542+ fun `firstPostUptimeMillis is properly cleared` () {
543+ val metrics = AppStartMetrics .getInstance()
544+ metrics.registerLifecycleCallbacks(mock<Application >())
545+ Shadows .shadowOf(Looper .getMainLooper()).idle()
546+
547+ val reflectionField = AppStartMetrics ::class .java.getDeclaredField(" firstPostUptimeMillis" )
548+ reflectionField.isAccessible = true
549+ val firstPostValue = reflectionField.getLong(metrics)
550+ assertTrue(firstPostValue > 0 )
551+
552+ metrics.clear()
553+
554+ val clearedValue = reflectionField.getLong(metrics)
555+ assertEquals(- 1 , clearedValue)
556+ }
557+
558+ @Test
559+ fun `firstPostUptimeMillis is set when registerLifecycleCallbacks is called` () {
560+ val metrics = AppStartMetrics .getInstance()
561+ val beforeRegister = SystemClock .uptimeMillis()
562+
563+ metrics.registerLifecycleCallbacks(mock<Application >())
564+ Shadows .shadowOf(Looper .getMainLooper()).idle()
565+
566+ val afterIdle = SystemClock .uptimeMillis()
567+
568+ val reflectionField = AppStartMetrics ::class .java.getDeclaredField(" firstPostUptimeMillis" )
569+ reflectionField.isAccessible = true
570+ val firstPostValue = reflectionField.getLong(metrics)
571+
572+ assertTrue(firstPostValue >= beforeRegister)
573+ assertTrue(firstPostValue <= afterIdle)
574+ }
575+
576+ @Test
577+ fun `Sets app launch type to WARM when activity created after firstPost` () {
578+ val metrics = AppStartMetrics .getInstance()
579+ assertEquals(AppStartMetrics .AppStartType .UNKNOWN , metrics.appStartType)
580+
581+ metrics.registerLifecycleCallbacks(mock<Application >())
582+ Shadows .shadowOf(Looper .getMainLooper()).idle()
583+
584+ SystemClock .setCurrentTimeMillis(SystemClock .uptimeMillis() + 100 )
585+ metrics.onActivityCreated(mock<Activity >(), null )
586+
587+ assertEquals(AppStartMetrics .AppStartType .WARM , metrics.appStartType)
588+ }
589+
590+ @Test
591+ fun `Sets app launch type to COLD when activity created before firstPost executes` () {
592+ val metrics = AppStartMetrics .getInstance()
593+ assertEquals(AppStartMetrics .AppStartType .UNKNOWN , metrics.appStartType)
594+
595+ metrics.registerLifecycleCallbacks(mock<Application >())
596+ metrics.onActivityCreated(mock<Activity >(), null )
597+
598+ assertEquals(AppStartMetrics .AppStartType .COLD , metrics.appStartType)
599+
600+ Shadows .shadowOf(Looper .getMainLooper()).idle()
601+
602+ assertEquals(AppStartMetrics .AppStartType .COLD , metrics.appStartType)
603+ }
604+
605+ @Test
606+ fun `Sets app launch type to COLD when activity created at same time as firstPost` () {
607+ val metrics = AppStartMetrics .getInstance()
608+
609+ val now = SystemClock .uptimeMillis()
610+ val reflectionField = AppStartMetrics ::class .java.getDeclaredField(" firstPostUptimeMillis" )
611+ reflectionField.isAccessible = true
612+ reflectionField.setLong(metrics, now)
613+
614+ SystemClock .setCurrentTimeMillis(now)
615+ metrics.onActivityCreated(mock<Activity >(), null )
616+
617+ assertEquals(AppStartMetrics .AppStartType .COLD , metrics.appStartType)
618+ }
619+
620+ @Test
621+ fun `savedInstanceState check takes precedence over firstPost timing` () {
622+ val metrics = AppStartMetrics .getInstance()
623+
624+ metrics.registerLifecycleCallbacks(mock<Application >())
625+ Shadows .shadowOf(Looper .getMainLooper()).idle()
626+
627+ SystemClock .setCurrentTimeMillis(SystemClock .uptimeMillis() + 100 )
628+ metrics.onActivityCreated(mock<Activity >(), mock<Bundle >())
629+
630+ assertEquals(AppStartMetrics .AppStartType .WARM , metrics.appStartType)
631+ }
632+
633+ @Test
634+ fun `timeout check takes precedence over firstPost timing` () {
635+ val metrics = AppStartMetrics .getInstance()
636+
637+ metrics.registerLifecycleCallbacks(mock<Application >())
638+ Shadows .shadowOf(Looper .getMainLooper()).idle()
639+
640+ val futureTime = SystemClock .uptimeMillis() + TimeUnit .MINUTES .toMillis(2 )
641+ SystemClock .setCurrentTimeMillis(futureTime)
642+ metrics.onActivityCreated(mock<Activity >(), null )
643+
644+ assertEquals(AppStartMetrics .AppStartType .WARM , metrics.appStartType)
645+ assertTrue(metrics.appStartTimeSpan.hasStarted())
646+ assertEquals(futureTime, metrics.appStartTimeSpan.startUptimeMs)
647+ }
648+
649+ @Test
650+ fun `firstPost timing does not affect subsequent activity creations` () {
651+ val metrics = AppStartMetrics .getInstance()
652+
653+ metrics.registerLifecycleCallbacks(mock<Application >())
654+ Shadows .shadowOf(Looper .getMainLooper()).idle()
655+
656+ SystemClock .setCurrentTimeMillis(SystemClock .uptimeMillis() + 100 )
657+ metrics.onActivityCreated(mock<Activity >(), null )
658+ assertEquals(AppStartMetrics .AppStartType .WARM , metrics.appStartType)
659+
660+ metrics.onActivityCreated(mock<Activity >(), mock<Bundle >())
661+ assertEquals(AppStartMetrics .AppStartType .WARM , metrics.appStartType)
662+ }
540663}
0 commit comments