OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import random | 5 import random |
6 import unittest | 6 import unittest |
7 | 7 |
| 8 from metrics.rendering_stats import UI_COMP_NAME, BEGIN_COMP_NAME, END_COMP_NAME |
| 9 from metrics.rendering_stats import GetScrollInputLatencyEvents |
| 10 from metrics.rendering_stats import ComputeMouseWheelScrollLatency |
| 11 from metrics.rendering_stats import ComputeTouchScrollLatency |
8 from metrics.rendering_stats import RenderingStats | 12 from metrics.rendering_stats import RenderingStats |
9 import telemetry.core.timeline.bounds as timeline_bounds | 13 import telemetry.core.timeline.bounds as timeline_bounds |
10 from telemetry.core.timeline import model | 14 from telemetry.core.timeline import model |
| 15 import telemetry.core.timeline.async_slice as tracing_async_slice |
11 | 16 |
12 | 17 |
13 class MockTimer(object): | 18 class MockTimer(object): |
14 """A mock timer class which can generate random durations. | 19 """A mock timer class which can generate random durations. |
15 | 20 |
16 An instance of this class is used as a global timer to generate random | 21 An instance of this class is used as a global timer to generate random |
17 durations for stats and consistent timestamps for all mock trace events. | 22 durations for stats and consistent timestamps for all mock trace events. |
18 The unit of time is milliseconds. | 23 The unit of time is milliseconds. |
19 """ | 24 """ |
20 def __init__(self): | 25 def __init__(self): |
(...skipping 23 matching lines...) Expand all Loading... |
44 def AppendNewRange(self): | 49 def AppendNewRange(self): |
45 self.frame_timestamps.append([]) | 50 self.frame_timestamps.append([]) |
46 self.frame_times.append([]) | 51 self.frame_times.append([]) |
47 self.paint_times.append([]) | 52 self.paint_times.append([]) |
48 self.painted_pixel_counts.append([]) | 53 self.painted_pixel_counts.append([]) |
49 self.record_times.append([]) | 54 self.record_times.append([]) |
50 self.recorded_pixel_counts.append([]) | 55 self.recorded_pixel_counts.append([]) |
51 self.rasterize_times.append([]) | 56 self.rasterize_times.append([]) |
52 self.rasterized_pixel_counts.append([]) | 57 self.rasterized_pixel_counts.append([]) |
53 | 58 |
| 59 class ReferenceInputLatencyStats(object): |
| 60 """ Stores expected data for comparison with actual input latency stats """ |
| 61 def __init__(self): |
| 62 self.mouse_wheel_scroll_latency = [] |
| 63 self.touch_scroll_latency = [] |
| 64 self.mouse_wheel_scroll_events = [] |
| 65 self.touch_scroll_events = [] |
54 | 66 |
55 def AddMainThreadRenderingStats(mock_timer, thread, first_frame, | 67 def AddMainThreadRenderingStats(mock_timer, thread, first_frame, |
56 ref_stats = None): | 68 ref_stats = None): |
57 """ Adds a random main thread rendering stats event. | 69 """ Adds a random main thread rendering stats event. |
58 | 70 |
59 thread: The timeline model thread to which the event will be added. | 71 thread: The timeline model thread to which the event will be added. |
60 first_frame: Is this the first frame within the bounds of an action? | 72 first_frame: Is this the first frame within the bounds of an action? |
61 ref_stats: A ReferenceRenderingStats object to record expected values. | 73 ref_stats: A ReferenceRenderingStats object to record expected values. |
62 """ | 74 """ |
63 # Create randonm data and timestap for main thread rendering stats. | 75 # Create randonm data and timestap for main thread rendering stats. |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 # Add frame_time if this is not the first frame in within the bounds of an | 133 # Add frame_time if this is not the first frame in within the bounds of an |
122 # action. | 134 # action. |
123 prev_timestamp = ref_stats.frame_timestamps[-1][-1] | 135 prev_timestamp = ref_stats.frame_timestamps[-1][-1] |
124 ref_stats.frame_times[-1].append(round(timestamp - prev_timestamp, 2)) | 136 ref_stats.frame_times[-1].append(round(timestamp - prev_timestamp, 2)) |
125 ref_stats.frame_timestamps[-1].append(timestamp) | 137 ref_stats.frame_timestamps[-1].append(timestamp) |
126 | 138 |
127 ref_stats.rasterize_times[-1].append(data['rasterize_time'] * 1000.0) | 139 ref_stats.rasterize_times[-1].append(data['rasterize_time'] * 1000.0) |
128 ref_stats.rasterized_pixel_counts[-1].append(data['rasterized_pixel_count']) | 140 ref_stats.rasterized_pixel_counts[-1].append(data['rasterized_pixel_count']) |
129 | 141 |
130 | 142 |
| 143 def AddInputLatencyStats(mock_timer, input_type, start_thread, end_thread, |
| 144 ref_latency_stats = None): |
| 145 """ Adds a random input latency stats event. |
| 146 |
| 147 input_type: The input type for which the latency slice is generated. |
| 148 start_thread: The start thread on which the async slice is added. |
| 149 end_thread: The end thread on which the async slice is ended. |
| 150 ref_latency_stats: A ReferenceInputLatencyStats object for expected values. |
| 151 """ |
| 152 |
| 153 mock_timer.Advance() |
| 154 ui_comp_time = mock_timer.Get() * 1000.0 |
| 155 mock_timer.Advance() |
| 156 begin_comp_time = mock_timer.Get() * 1000.0 |
| 157 mock_timer.Advance(10, 20) |
| 158 end_comp_time = mock_timer.Get() * 1000.0 |
| 159 |
| 160 data = { UI_COMP_NAME: {'time': ui_comp_time}, |
| 161 BEGIN_COMP_NAME: {'time': begin_comp_time}, |
| 162 END_COMP_NAME: {'time': end_comp_time} } |
| 163 |
| 164 timestamp = mock_timer.Get() |
| 165 |
| 166 async_slice = tracing_async_slice.AsyncSlice( |
| 167 'benchmark', 'InputLatency', timestamp) |
| 168 |
| 169 async_sub_slice = tracing_async_slice.AsyncSlice( |
| 170 'benchmark', 'InputLatency', timestamp) |
| 171 async_sub_slice.args = {'data': data, 'step': input_type} |
| 172 async_sub_slice.parent_slice = async_slice |
| 173 async_sub_slice.start_thread = start_thread |
| 174 async_sub_slice.end_thread = end_thread |
| 175 |
| 176 async_slice.sub_slices.append(async_sub_slice) |
| 177 async_slice.start_thread = start_thread |
| 178 async_slice.end_thread = end_thread |
| 179 start_thread.AddAsyncSlice(async_slice) |
| 180 |
| 181 if not ref_latency_stats: |
| 182 return |
| 183 |
| 184 if input_type == 'MouseWheel': |
| 185 ref_latency_stats.mouse_wheel_scroll_events.append(async_sub_slice) |
| 186 ref_latency_stats.mouse_wheel_scroll_latency.append( |
| 187 (data[END_COMP_NAME]['time'] - data[BEGIN_COMP_NAME]['time']) / 1000.0) |
| 188 |
| 189 |
| 190 if input_type == 'GestureScrollUpdate': |
| 191 ref_latency_stats.touch_scroll_events.append(async_sub_slice) |
| 192 ref_latency_stats.touch_scroll_latency.append( |
| 193 (data[END_COMP_NAME]['time'] - data[UI_COMP_NAME]['time']) / 1000.0) |
| 194 |
131 class RenderingStatsUnitTest(unittest.TestCase): | 195 class RenderingStatsUnitTest(unittest.TestCase): |
132 def testFromTimeline(self): | 196 def testFromTimeline(self): |
133 timeline = model.TimelineModel() | 197 timeline = model.TimelineModel() |
134 | 198 |
135 # Create a browser process and a renderer process, and a main thread and | 199 # Create a browser process and a renderer process, and a main thread and |
136 # impl thread for each. | 200 # impl thread for each. |
137 browser = timeline.GetOrCreateProcess(pid = 1) | 201 browser = timeline.GetOrCreateProcess(pid = 1) |
138 browser_main = browser.GetOrCreateThread(tid = 11) | 202 browser_main = browser.GetOrCreateThread(tid = 11) |
139 browser_compositor = browser.GetOrCreateThread(tid = 12) | 203 browser_compositor = browser.GetOrCreateThread(tid = 12) |
140 renderer = timeline.GetOrCreateProcess(pid = 2) | 204 renderer = timeline.GetOrCreateProcess(pid = 2) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 AddImplThreadRenderingStats(timer, browser_compositor, first, None) | 252 AddImplThreadRenderingStats(timer, browser_compositor, first, None) |
189 renderer_main.EndSlice(timer.Get()) | 253 renderer_main.EndSlice(timer.Get()) |
190 | 254 |
191 renderer_main.FinalizeImport() | 255 renderer_main.FinalizeImport() |
192 renderer_compositor.FinalizeImport() | 256 renderer_compositor.FinalizeImport() |
193 | 257 |
194 timeline_markers = timeline.FindTimelineMarkers( | 258 timeline_markers = timeline.FindTimelineMarkers( |
195 ['ActionA', 'ActionB', 'ActionA']) | 259 ['ActionA', 'ActionB', 'ActionA']) |
196 timeline_ranges = [ timeline_bounds.Bounds.CreateFromEvent(marker) | 260 timeline_ranges = [ timeline_bounds.Bounds.CreateFromEvent(marker) |
197 for marker in timeline_markers ] | 261 for marker in timeline_markers ] |
198 stats = RenderingStats(renderer, timeline_ranges) | 262 stats = RenderingStats(renderer, browser, timeline_ranges) |
199 | 263 |
200 # Compare rendering stats to reference. | 264 # Compare rendering stats to reference. |
201 self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) | 265 self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) |
202 self.assertEquals(stats.frame_times, ref_stats.frame_times) | 266 self.assertEquals(stats.frame_times, ref_stats.frame_times) |
203 self.assertEquals(stats.rasterize_times, ref_stats.rasterize_times) | 267 self.assertEquals(stats.rasterize_times, ref_stats.rasterize_times) |
204 self.assertEquals(stats.rasterized_pixel_counts, | 268 self.assertEquals(stats.rasterized_pixel_counts, |
205 ref_stats.rasterized_pixel_counts) | 269 ref_stats.rasterized_pixel_counts) |
206 self.assertEquals(stats.paint_times, ref_stats.paint_times) | 270 self.assertEquals(stats.paint_times, ref_stats.paint_times) |
207 self.assertEquals(stats.painted_pixel_counts, | 271 self.assertEquals(stats.painted_pixel_counts, |
208 ref_stats.painted_pixel_counts) | 272 ref_stats.painted_pixel_counts) |
209 self.assertEquals(stats.record_times, ref_stats.record_times) | 273 self.assertEquals(stats.record_times, ref_stats.record_times) |
210 self.assertEquals(stats.recorded_pixel_counts, | 274 self.assertEquals(stats.recorded_pixel_counts, |
211 ref_stats.recorded_pixel_counts) | 275 ref_stats.recorded_pixel_counts) |
| 276 |
| 277 def testScrollLatencyFromTimeline(self): |
| 278 timeline = model.TimelineModel() |
| 279 |
| 280 # Create a browser process and a renderer process. |
| 281 browser = timeline.GetOrCreateProcess(pid = 1) |
| 282 browser_main = browser.GetOrCreateThread(tid = 11) |
| 283 renderer = timeline.GetOrCreateProcess(pid = 2) |
| 284 renderer_main = renderer.GetOrCreateThread(tid = 21) |
| 285 |
| 286 timer = MockTimer() |
| 287 ref_latency_stats = ReferenceInputLatencyStats() |
| 288 |
| 289 # Create 10 input latency stats events for Action A. |
| 290 timer.Advance() |
| 291 renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') |
| 292 for _ in xrange(0, 10): |
| 293 AddInputLatencyStats(timer, 'MouseWheel', browser_main, |
| 294 renderer_main, ref_latency_stats) |
| 295 AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main, |
| 296 renderer_main, ref_latency_stats) |
| 297 renderer_main.EndSlice(timer.Get()) |
| 298 |
| 299 # Create 5 input latency stats events not within any action. |
| 300 for _ in xrange(0, 5): |
| 301 AddInputLatencyStats(timer, 'MouseWheel', browser_main, |
| 302 renderer_main, None) |
| 303 AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main, |
| 304 renderer_main, None) |
| 305 |
| 306 # Create 10 input latency stats events for Action B. |
| 307 timer.Advance() |
| 308 renderer_main.BeginSlice('webkit.console', 'ActionB', timer.Get(), '') |
| 309 for _ in xrange(0, 10): |
| 310 AddInputLatencyStats(timer, 'MouseWheel', browser_main, |
| 311 renderer_main, ref_latency_stats) |
| 312 AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main, |
| 313 renderer_main, ref_latency_stats) |
| 314 renderer_main.EndSlice(timer.Get()) |
| 315 |
| 316 # Create 10 input latency stats events for Action A. |
| 317 timer.Advance() |
| 318 renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') |
| 319 for _ in xrange(0, 10): |
| 320 AddInputLatencyStats(timer, 'MouseWheel', browser_main, |
| 321 renderer_main, ref_latency_stats) |
| 322 AddInputLatencyStats(timer, 'GestureScrollUpdate', browser_main, |
| 323 renderer_main, ref_latency_stats) |
| 324 renderer_main.EndSlice(timer.Get()) |
| 325 |
| 326 browser_main.FinalizeImport() |
| 327 renderer_main.FinalizeImport() |
| 328 |
| 329 mouse_wheel_scroll_events = [] |
| 330 touch_scroll_events = [] |
| 331 |
| 332 timeline_markers = timeline.FindTimelineMarkers( |
| 333 ['ActionA', 'ActionB', 'ActionA']) |
| 334 for timeline_range in [ timeline_bounds.Bounds.CreateFromEvent(marker) |
| 335 for marker in timeline_markers ]: |
| 336 if timeline_range.is_empty: |
| 337 continue |
| 338 tmp_mouse_events, tmp_touch_events = GetScrollInputLatencyEvents( |
| 339 browser, timeline_range) |
| 340 mouse_wheel_scroll_events.extend(tmp_mouse_events) |
| 341 touch_scroll_events.extend(tmp_touch_events) |
| 342 |
| 343 self.assertEquals(mouse_wheel_scroll_events, |
| 344 ref_latency_stats.mouse_wheel_scroll_events) |
| 345 self.assertEquals(touch_scroll_events, |
| 346 ref_latency_stats.touch_scroll_events) |
| 347 self.assertEquals(ComputeMouseWheelScrollLatency(mouse_wheel_scroll_events), |
| 348 ref_latency_stats.mouse_wheel_scroll_latency) |
| 349 self.assertEquals(ComputeTouchScrollLatency(touch_scroll_events), |
| 350 ref_latency_stats.touch_scroll_latency) |
OLD | NEW |