1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.jank_tracker; 6 7 import static org.mockito.Mockito.verify; 8 import static org.mockito.Mockito.verifyNoMoreInteractions; 9 10 import android.os.Handler; 11 import android.os.Looper; 12 13 import org.junit.Assert; 14 import org.junit.Before; 15 import org.junit.Rule; 16 import org.junit.Test; 17 import org.junit.runner.RunWith; 18 import org.mockito.Mock; 19 import org.mockito.Mockito; 20 import org.mockito.MockitoAnnotations; 21 import org.robolectric.shadows.ShadowLooper; 22 23 import org.chromium.base.TimeUtils; 24 import org.chromium.base.test.BaseRobolectricTestRunner; 25 import org.chromium.base.test.util.JniMocker; 26 27 /** Tests for JankReportingRunnable. */ 28 @RunWith(BaseRobolectricTestRunner.class) 29 public class JankReportingRunnableTest { 30 ShadowLooper mShadowLooper; 31 Handler mHandler; 32 @Rule public JniMocker mocker = new JniMocker(); 33 34 @Mock JankMetricUMARecorder.Natives mNativeMock; 35 36 @Before setUp()37 public void setUp() { 38 MockitoAnnotations.initMocks(this); 39 mocker.mock(JankMetricUMARecorderJni.TEST_HOOKS, mNativeMock); 40 mShadowLooper = ShadowLooper.shadowMainLooper(); 41 mHandler = new Handler(Looper.getMainLooper()); 42 } 43 44 @Test testStartTracking()45 public void testStartTracking() { 46 FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore()); 47 metricsStore.initialize(); 48 49 JankReportingRunnable reportingRunnable = 50 new JankReportingRunnable( 51 metricsStore, 52 JankScenario.TAB_SWITCHER, 53 /* isStartingTracking= */ true, 54 mHandler, 55 null); 56 reportingRunnable.run(); 57 58 verify(metricsStore).initialize(); 59 verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER); 60 verifyNoMoreInteractions(metricsStore); 61 } 62 63 @Test testStopTracking_withoutDelay()64 public void testStopTracking_withoutDelay() { 65 FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore()); 66 metricsStore.initialize(); 67 68 JankReportingRunnable startReportingRunnable = 69 new JankReportingRunnable( 70 metricsStore, 71 JankScenario.TAB_SWITCHER, 72 /* isStartingTracking= */ true, 73 mHandler, 74 null); 75 startReportingRunnable.run(); 76 77 metricsStore.addFrameMeasurement(1_000_000L, 2, 1); 78 79 JankReportingRunnable stopReportingRunnable = 80 new JankReportingRunnable( 81 metricsStore, 82 JankScenario.TAB_SWITCHER, 83 /* isStartingTracking= */ false, 84 mHandler, 85 null); 86 stopReportingRunnable.run(); 87 88 verify(metricsStore).initialize(); 89 verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER); 90 verify(metricsStore).stopTrackingScenario(JankScenario.TAB_SWITCHER); 91 92 verify(mNativeMock) 93 .recordJankMetrics( 94 new long[] {1_000_000L}, new int[] {2}, 0L, 1L, JankScenario.TAB_SWITCHER); 95 } 96 97 @Test testStopTracking_withDelay()98 public void testStopTracking_withDelay() { 99 final long frameTime = 50L * TimeUtils.NANOSECONDS_PER_MILLISECOND; 100 FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore()); 101 metricsStore.initialize(); 102 103 JankEndScenarioTime endScenarioTime = JankEndScenarioTime.endAt(frameTime); 104 Assert.assertTrue(endScenarioTime != null); 105 Assert.assertEquals(endScenarioTime.endScenarioTimeNs, frameTime); 106 107 JankReportingRunnable startReportingRunnable = 108 new JankReportingRunnable( 109 metricsStore, 110 JankScenario.TAB_SWITCHER, 111 /* isStartingTracking= */ true, 112 mHandler, 113 endScenarioTime); 114 startReportingRunnable.run(); 115 116 metricsStore.addFrameMeasurement(1_000_000L, 2, 1 * TimeUtils.NANOSECONDS_PER_MILLISECOND); 117 118 JankReportingRunnable stopReportingRunnable = 119 new JankReportingRunnable( 120 metricsStore, 121 JankScenario.TAB_SWITCHER, 122 /* isStartingTracking= */ false, 123 mHandler, 124 endScenarioTime); 125 stopReportingRunnable.run(); 126 127 // Add two frames, one added before the frame time of 50ms above and one after. The first 128 // should be included and the second ignored. 129 metricsStore.addFrameMeasurement(1_000_001L, 0, 5 * TimeUtils.NANOSECONDS_PER_MILLISECOND); 130 metricsStore.addFrameMeasurement( 131 1_000_002L, 1, (frameTime + 5) * TimeUtils.NANOSECONDS_PER_MILLISECOND); 132 133 mShadowLooper.runOneTask(); 134 135 verify(metricsStore).initialize(); 136 verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER); 137 verify(metricsStore).stopTrackingScenario(JankScenario.TAB_SWITCHER, frameTime); 138 139 verify(mNativeMock) 140 .recordJankMetrics( 141 new long[] {1_000_000L, 1_000_001L}, 142 new int[] {2, 0}, 143 1L, 144 5L, 145 JankScenario.TAB_SWITCHER); 146 } 147 148 @Test testStopTracking_emptyStoreShouldntRecordAnything()149 public void testStopTracking_emptyStoreShouldntRecordAnything() { 150 // Create a store but don't add any measurements. 151 FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore()); 152 metricsStore.initialize(); 153 154 JankReportingRunnable startReportingRunnable = 155 new JankReportingRunnable( 156 metricsStore, 157 JankScenario.TAB_SWITCHER, 158 /* isStartingTracking= */ true, 159 mHandler, 160 null); 161 startReportingRunnable.run(); 162 163 JankReportingRunnable stopReportingRunnable = 164 new JankReportingRunnable( 165 metricsStore, 166 JankScenario.TAB_SWITCHER, 167 /* isStartingTracking= */ false, 168 mHandler, 169 null); 170 stopReportingRunnable.run(); 171 172 verify(metricsStore).initialize(); 173 verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER); 174 verify(metricsStore).stopTrackingScenario(JankScenario.TAB_SWITCHER); 175 176 // Native shouldn't be called when there are no measurements. 177 verifyNoMoreInteractions(mNativeMock); 178 } 179 } 180