• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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