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.junit.Assert.assertArrayEquals; 8 import static org.junit.Assert.assertEquals; 9 10 import org.junit.Rule; 11 import org.junit.Test; 12 import org.junit.runner.RunWith; 13 import org.robolectric.annotation.Config; 14 15 import org.chromium.base.FakeTimeTestRule; 16 import org.chromium.base.TimeUtils; 17 import org.chromium.base.test.BaseRobolectricTestRunner; 18 19 /** Tests for FrameMetricsStore. */ 20 @RunWith(BaseRobolectricTestRunner.class) 21 @Config(manifest = Config.NONE) 22 public class FrameMetricsStoreTest { 23 @Rule public FakeTimeTestRule mFakeTimeTestRule = new FakeTimeTestRule(); 24 25 @Test addFrameMeasurementTest()26 public void addFrameMeasurementTest() { 27 FrameMetricsStore store = new FrameMetricsStore(); 28 store.initialize(); 29 30 store.startTrackingScenario(JankScenario.NEW_TAB_PAGE); 31 32 long frame_start_vsync_ts = 0; 33 store.addFrameMeasurement(10_000_000L, 0, frame_start_vsync_ts); 34 store.addFrameMeasurement(12_000_000L, 0, frame_start_vsync_ts); 35 store.addFrameMeasurement(20_000_000L, 1, frame_start_vsync_ts); 36 store.addFrameMeasurement(8_000_000L, 2, frame_start_vsync_ts); 37 38 JankMetrics metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 39 40 assertArrayEquals( 41 new long[] {10_000_000L, 12_000_000L, 20_000_000L, 8_000_000L}, 42 metrics.durationsNs); 43 assertArrayEquals(new int[] {0, 0, 1, 2}, metrics.missedVsyncs); 44 45 metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 46 assertEquals(0, metrics.durationsNs.length); 47 } 48 49 @Test takeMetrics_getMetricsWithoutAnyFrames()50 public void takeMetrics_getMetricsWithoutAnyFrames() { 51 FrameMetricsStore store = new FrameMetricsStore(); 52 store.initialize(); 53 store.startTrackingScenario(JankScenario.NEW_TAB_PAGE); 54 JankMetrics metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 55 56 assertEquals(0, metrics.durationsNs.length); 57 } 58 59 @Test concurrentScenarios()60 public void concurrentScenarios() { 61 // We want to test 2 things. 62 // 1) that concurrent scenarios get the correct frames 63 // 2) that the deletion logic runs correctly. Note however that deletion logic is not 64 // actually public behaviour but we just want this test to explicitly exercise it to 65 // uncover potential bugs. 66 FrameMetricsStore store = new FrameMetricsStore(); 67 store.initialize(); 68 69 store.startTrackingScenario(JankScenario.NEW_TAB_PAGE); 70 71 long frame_start_vsync_ts = 1_000_000L; 72 store.addFrameMeasurement(10_000_000L, 0, frame_start_vsync_ts); 73 store.addFrameMeasurement(12_000_000L, 0, frame_start_vsync_ts + 1); 74 store.startTrackingScenario(JankScenario.FEED_SCROLLING); 75 store.addFrameMeasurement(20_000_000L, 2, frame_start_vsync_ts + 2); 76 store.addFrameMeasurement(8_000_000L, 1, frame_start_vsync_ts + 3); 77 78 // Stop NEW_TAB_PAGE and now the first two frames will be deleted from the 79 // FrameMetricsStore(). 80 JankMetrics metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 81 82 assertArrayEquals( 83 new long[] {10_000_000L, 12_000_000L, 20_000_000L, 8_000_000L}, 84 metrics.durationsNs); 85 assertArrayEquals(new int[] {0, 0, 2, 1}, metrics.missedVsyncs); 86 87 metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 88 assertEquals(0, metrics.durationsNs.length); 89 90 // Only after that will we stop FEED_SCROLLING and we should only see the last two frames. 91 metrics = store.stopTrackingScenario(JankScenario.FEED_SCROLLING); 92 assertArrayEquals(new long[] {20_000_000L, 8_000_000L}, metrics.durationsNs); 93 assertArrayEquals(new int[] {2, 1}, metrics.missedVsyncs); 94 } 95 96 @Test restartScenario()97 public void restartScenario() { 98 // This test is setup to mainly test removeUnusedFrames() method codepaths. 99 FrameMetricsStore store = new FrameMetricsStore(); 100 store.initialize(); 101 102 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 103 104 long frame_start_vsync_ts = 1_000_000L; 105 store.addFrameMeasurement(10_000_000L, 0, frame_start_vsync_ts); 106 store.addFrameMeasurement(12_000_000L, 1, frame_start_vsync_ts + 1); 107 108 JankMetrics metrics = store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 109 110 assertArrayEquals(new long[] {10_000_000L, 12_000_000L}, metrics.durationsNs); 111 assertArrayEquals(new int[] {0, 1}, metrics.missedVsyncs); 112 113 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 114 115 store.addFrameMeasurement(10_000_100L, 2, frame_start_vsync_ts + 2); 116 store.addFrameMeasurement(11_000_100L, 0, frame_start_vsync_ts + 3); 117 118 store.startTrackingScenario(JankScenario.NEW_TAB_PAGE); 119 120 store.addFrameMeasurement(12_000_100L, 0, frame_start_vsync_ts + 4); 121 122 metrics = store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 123 assertArrayEquals(new long[] {10_000_100L, 11_000_100L, 12_000_100L}, metrics.durationsNs); 124 assertArrayEquals(new int[] {2, 0, 0}, metrics.missedVsyncs); 125 126 metrics = store.stopTrackingScenario(JankScenario.NEW_TAB_PAGE); 127 assertArrayEquals(new long[] {12_000_100L}, metrics.durationsNs); 128 assertArrayEquals(new int[] {0}, metrics.missedVsyncs); 129 } 130 131 @Test startPendingScenarioBeforeScenarioEnd()132 public void startPendingScenarioBeforeScenarioEnd() { 133 FrameMetricsStore store = new FrameMetricsStore(); 134 store.initialize(); 135 136 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 137 138 long now = TimeUtils.uptimeMillis(); 139 140 long[] frame_timestamps_ns = 141 new long[] { 142 now * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame1 start time 143 (now + 1) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame2 start time 144 (now + 2) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // scenario end time 145 (now + 3) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // new scenario start time 146 (now + 4) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame3 start time 147 (now + 5) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame4 start time 148 }; 149 150 store.addFrameMeasurement(10_000_000L, 0, frame_timestamps_ns[0]); 151 store.addFrameMeasurement(12_000_000L, 3, frame_timestamps_ns[1]); 152 153 // This start scenario shouldn't be blanket ignored instead this should 154 // trigger start of the scenario after stop scenario call. 155 mFakeTimeTestRule.advanceMillis( 156 (frame_timestamps_ns[3] / TimeUtils.NANOSECONDS_PER_MILLISECOND) - now); 157 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 158 159 // The end time of scenario is before the start time of last start calls, 160 // this can occur in the field as well since we delay stopTrackingScenario 161 // to receive metrics for all the frames in given time range. 162 JankMetrics metrics = 163 store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING, frame_timestamps_ns[2]); 164 165 assertArrayEquals(new long[] {10_000_000L, 12_000_000L}, metrics.durationsNs); 166 assertArrayEquals(new int[] {0, 3}, metrics.missedVsyncs); 167 168 store.addFrameMeasurement(10_000_100L, 2, frame_timestamps_ns[4]); 169 store.addFrameMeasurement(11_000_100L, 0, frame_timestamps_ns[5]); 170 171 metrics = store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 172 173 assertArrayEquals(new long[] {10_000_100L, 11_000_100L}, metrics.durationsNs); 174 assertArrayEquals(new int[] {2, 0}, metrics.missedVsyncs); 175 } 176 177 @Test multipleStartScenarioBeforeScenarioEnd()178 public void multipleStartScenarioBeforeScenarioEnd() { 179 /* 180 * Testing a scenario where might have a complete scroll within the delay period of 181 * processing a finishTrackingScenario request. We typically wait for ~100ms to receive 182 * frame metrics corresponding to last few frames in scenario. This test simulates 183 * scenario depicted below. '|______|' is the actual scroll start and end, while 184 * '-----------|' is the delay in processing finishTrackingScenario call i.e. when we 185 * call stopTrackingScenario. 186 * 187 * Scroll1: |________________________|-----------| 188 * Scroll2: |____|-----------| 189 * Scroll3: |_____________|-----------| 190 * 191 * As per current architecture expectation is we will loose metrics for scroll2 and scroll3, 192 * but this should be fine since the scenario is pretty unrealistic so shouldn't occur often 193 * in field data. 194 */ 195 FrameMetricsStore store = new FrameMetricsStore(); 196 store.initialize(); 197 198 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); 199 200 long now = TimeUtils.uptimeMillis(); 201 202 long[] frame_timestamps_ns = 203 new long[] { 204 now * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame1 start time 205 (now + 1) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame2 start time 206 (now + 2) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Scroll1 end time 207 (now + 3) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Scroll2 start time 208 (now + 4) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame3 start time 209 (now + 5) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame4 start time 210 (now + 6) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Scroll2 end time 211 (now + 7) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Scroll3 start time 212 (now + 8) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame5 start time 213 (now + 9) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Frame6 start time 214 (now + 10) * TimeUtils.NANOSECONDS_PER_MILLISECOND, // Scroll3 end time 215 }; 216 217 store.addFrameMeasurement(10_000_000L, 0, frame_timestamps_ns[0]); // Frame1 218 store.addFrameMeasurement(12_000_000L, 1, frame_timestamps_ns[1]); // Frame2 219 220 mFakeTimeTestRule.advanceMillis( 221 (frame_timestamps_ns[3] / TimeUtils.NANOSECONDS_PER_MILLISECOND) - now); 222 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); // Scroll2 start 223 store.addFrameMeasurement(10_000_100L, 2, frame_timestamps_ns[4]); // Frame3 224 store.addFrameMeasurement(11_000_100L, 0, frame_timestamps_ns[5]); // Frame4 225 226 mFakeTimeTestRule.advanceMillis( 227 (frame_timestamps_ns[7] / TimeUtils.NANOSECONDS_PER_MILLISECOND) 228 - (frame_timestamps_ns[3] / TimeUtils.NANOSECONDS_PER_MILLISECOND)); 229 store.startTrackingScenario(JankScenario.WEBVIEW_SCROLLING); // Scroll3 start 230 store.addFrameMeasurement(10_000_100L, 1, frame_timestamps_ns[8]); // Frame5 231 store.addFrameMeasurement(11_000_100L, 0, frame_timestamps_ns[9]); // Frame6 232 233 // Scroll1 end. 234 JankMetrics metrics = 235 store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING, frame_timestamps_ns[2]); 236 237 assertArrayEquals(new long[] {10_000_000L, 12_000_000L}, metrics.durationsNs); 238 assertArrayEquals(new int[] {0, 1}, metrics.missedVsyncs); 239 240 // Scroll2 end. 241 metrics = 242 store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING, frame_timestamps_ns[6]); 243 244 assertArrayEquals(new long[] {}, metrics.durationsNs); 245 assertArrayEquals(new int[] {}, metrics.missedVsyncs); 246 247 // Scroll3 end. 248 metrics = 249 store.stopTrackingScenario(JankScenario.WEBVIEW_SCROLLING, frame_timestamps_ns[10]); 250 251 assertArrayEquals(new long[] {}, metrics.durationsNs); 252 assertArrayEquals(new int[] {}, metrics.missedVsyncs); 253 } 254 } 255