1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import random 6import unittest 7 8from telemetry.timeline import async_slice 9from telemetry.timeline import bounds 10from telemetry.timeline import model 11from telemetry.util import perf_tests_helper 12from telemetry.util import statistics 13from telemetry.web_perf.metrics import rendering_stats 14 15 16class MockTimer(object): 17 """A mock timer class which can generate random durations. 18 19 An instance of this class is used as a global timer to generate random 20 durations for stats and consistent timestamps for all mock trace events. 21 The unit of time is milliseconds. 22 """ 23 24 def __init__(self): 25 self.milliseconds = 0 26 27 def Advance(self, low=0.1, high=1): 28 delta = random.uniform(low, high) 29 self.milliseconds += delta 30 return delta 31 32 def AdvanceAndGet(self, low=0.1, high=1): 33 self.Advance(low, high) 34 return self.milliseconds 35 36 37class MockVblankTimer(object): 38 """A mock vblank timer class which can generate random durations. 39 40 An instance of this class is used as a vblank timer to generate random 41 durations for drm stats and consistent timeval for mock trace drm events. 42 The unit of time is microseconds. 43 """ 44 45 def __init__(self): 46 self.microseconds = 200000000 47 48 def TvAdvance(self, low=100, high=1000): 49 delta = random.randint(low, high) 50 self.microseconds += delta 51 return delta 52 53 def TvAdvanceAndGet(self, low=100, high=1000): 54 self.TvAdvance(low, high) 55 return self.microseconds 56 57 58class ReferenceRenderingStats(object): 59 """ Stores expected data for comparison with actual RenderingStats """ 60 61 def __init__(self): 62 self.frame_timestamps = [] 63 self.frame_times = [] 64 self.approximated_pixel_percentages = [] 65 self.checkerboarded_pixel_percentages = [] 66 67 def AppendNewRange(self): 68 self.frame_timestamps.append([]) 69 self.frame_times.append([]) 70 self.approximated_pixel_percentages.append([]) 71 self.checkerboarded_pixel_percentages.append([]) 72 73 74class ReferenceInputLatencyStats(object): 75 """ Stores expected data for comparison with actual input latency stats """ 76 77 def __init__(self): 78 self.input_event_latency = [] 79 self.input_event = [] 80 81 82def AddSurfaceFlingerStats(mock_timer, thread, first_frame, 83 ref_stats=None): 84 """ Adds a random surface flinger stats event. 85 86 thread: The timeline model thread to which the event will be added. 87 first_frame: Is this the first frame within the bounds of an action? 88 ref_stats: A ReferenceRenderingStats object to record expected values. 89 """ 90 # Create random data and timestamp for impl thread rendering stats. 91 data = {'frame_count': 1, 92 'refresh_period': 16.6666} 93 timestamp = mock_timer.AdvanceAndGet() 94 95 # Add a slice with the event data to the given thread. 96 thread.PushCompleteSlice( 97 'SurfaceFlinger', 'vsync_before', 98 timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, 99 args={'data': data}) 100 101 if not ref_stats: 102 return 103 104 # Add timestamp only if a frame was output 105 if data['frame_count'] == 1: 106 if not first_frame: 107 # Add frame_time if this is not the first frame in within the bounds of an 108 # action. 109 prev_timestamp = ref_stats.frame_timestamps[-1][-1] 110 ref_stats.frame_times[-1].append(timestamp - prev_timestamp) 111 ref_stats.frame_timestamps[-1].append(timestamp) 112 113 114def AddDrmEventFlipStats(mock_timer, vblank_timer, thread, 115 first_frame, ref_stats=None): 116 """ Adds a random drm flip complete event. 117 118 thread: The timeline model thread to which the event will be added. 119 first_frame: Is this the first frame within the bounds of an action? 120 ref_stats: A ReferenceRenderingStats object to record expected values. 121 """ 122 # Create random data and timestamp for drm thread flip complete stats. 123 vblank_timeval = vblank_timer.TvAdvanceAndGet() 124 vblank_tv_sec = vblank_timeval / 1000000 125 vblank_tv_usec = vblank_timeval % 1000000 126 data = {'frame_count': 1, 127 'vblank.tv_usec': vblank_tv_usec, 128 'vblank.tv_sec': vblank_tv_sec} 129 timestamp = mock_timer.AdvanceAndGet() 130 131 # Add a slice with the event data to the given thread. 132 thread.PushCompleteSlice( 133 'benchmark,drm', 'DrmEventFlipComplete', 134 timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, 135 args={'data': data}) 136 137 if not ref_stats: 138 return 139 140 # Add vblank timeval only if a frame was output. 141 cur_timestamp = vblank_tv_sec * 1000.0 + vblank_tv_usec / 1000.0 142 if not first_frame: 143 # Add frame_time if this is not the first frame in within the bounds of an 144 # action. 145 prev_timestamp = ref_stats.frame_timestamps[-1][-1] 146 ref_stats.frame_times[-1].append(cur_timestamp - prev_timestamp) 147 ref_stats.frame_timestamps[-1].append(cur_timestamp) 148 149 150def AddDisplayRenderingStats(mock_timer, thread, first_frame, 151 ref_stats=None): 152 """ Adds a random display rendering stats event. 153 154 thread: The timeline model thread to which the event will be added. 155 first_frame: Is this the first frame within the bounds of an action? 156 ref_stats: A ReferenceRenderingStats object to record expected values. 157 """ 158 # Create random data and timestamp for main thread rendering stats. 159 data = {'frame_count': 1} 160 timestamp = mock_timer.AdvanceAndGet() 161 162 # Add a slice with the event data to the given thread. 163 thread.PushCompleteSlice( 164 'benchmark', 'BenchmarkInstrumentation::DisplayRenderingStats', 165 timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, 166 args={'data': data}) 167 168 if not ref_stats: 169 return 170 171 # Add timestamp only if a frame was output 172 if not first_frame: 173 # Add frame_time if this is not the first frame in within the bounds of an 174 # action. 175 prev_timestamp = ref_stats.frame_timestamps[-1][-1] 176 ref_stats.frame_times[-1].append(timestamp - prev_timestamp) 177 ref_stats.frame_timestamps[-1].append(timestamp) 178 179 180def AddImplThreadRenderingStats(mock_timer, thread, first_frame, 181 ref_stats=None): 182 """ Adds a random impl thread rendering stats event. 183 184 thread: The timeline model thread to which the event will be added. 185 first_frame: Is this the first frame within the bounds of an action? 186 ref_stats: A ReferenceRenderingStats object to record expected values. 187 """ 188 # Create random data and timestamp for impl thread rendering stats. 189 data = {'frame_count': 1, 190 'visible_content_area': random.uniform(0, 100), 191 'approximated_visible_content_area': random.uniform(0, 5), 192 'checkerboarded_visible_content_area': random.uniform(0, 5)} 193 timestamp = mock_timer.AdvanceAndGet() 194 195 # Add a slice with the event data to the given thread. 196 thread.PushCompleteSlice( 197 'benchmark', 'BenchmarkInstrumentation::ImplThreadRenderingStats', 198 timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, 199 args={'data': data}) 200 201 if not ref_stats: 202 return 203 204 # Add timestamp only if a frame was output 205 if data['frame_count'] == 1: 206 if not first_frame: 207 # Add frame_time if this is not the first frame in within the bounds of an 208 # action. 209 prev_timestamp = ref_stats.frame_timestamps[-1][-1] 210 ref_stats.frame_times[-1].append(timestamp - prev_timestamp) 211 ref_stats.frame_timestamps[-1].append(timestamp) 212 213 ref_stats.approximated_pixel_percentages[-1].append( 214 round(statistics.DivideIfPossibleOrZero( 215 data['approximated_visible_content_area'], 216 data['visible_content_area']) * 100.0, 3)) 217 218 ref_stats.checkerboarded_pixel_percentages[-1].append( 219 round(statistics.DivideIfPossibleOrZero( 220 data['checkerboarded_visible_content_area'], 221 data['visible_content_area']) * 100.0, 3)) 222 223def AddInputLatencyStats(mock_timer, start_thread, end_thread, 224 ref_latency_stats=None): 225 """ Adds a random input latency stats event. 226 227 start_thread: The start thread on which the async slice is added. 228 end_thread: The end thread on which the async slice is ended. 229 ref_latency_stats: A ReferenceInputLatencyStats object for expected values. 230 """ 231 232 original_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0 233 ui_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0 234 begin_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0 235 forward_comp_time = mock_timer.AdvanceAndGet(2, 4) * 1000.0 236 end_comp_time = mock_timer.AdvanceAndGet(10, 20) * 1000.0 237 238 data = {rendering_stats.ORIGINAL_COMP_NAME: {'time': original_comp_time}, 239 rendering_stats.UI_COMP_NAME: {'time': ui_comp_time}, 240 rendering_stats.BEGIN_COMP_NAME: {'time': begin_comp_time}, 241 rendering_stats.END_COMP_NAME: {'time': end_comp_time}} 242 243 timestamp = mock_timer.AdvanceAndGet(2, 4) 244 245 tracing_async_slice = async_slice.AsyncSlice( 246 'benchmark', 'InputLatency', timestamp) 247 248 async_sub_slice = async_slice.AsyncSlice( 249 'benchmark', rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME, timestamp) 250 async_sub_slice.args = {'data': data} 251 async_sub_slice.parent_slice = tracing_async_slice 252 async_sub_slice.start_thread = start_thread 253 async_sub_slice.end_thread = end_thread 254 255 tracing_async_slice.sub_slices.append(async_sub_slice) 256 tracing_async_slice.start_thread = start_thread 257 tracing_async_slice.end_thread = end_thread 258 start_thread.AddAsyncSlice(tracing_async_slice) 259 260 # Add scroll update latency info. 261 scroll_update_data = { 262 rendering_stats.BEGIN_SCROLL_UPDATE_COMP_NAME: {'time': begin_comp_time}, 263 rendering_stats.FORWARD_SCROLL_UPDATE_COMP_NAME: 264 {'time': forward_comp_time}, 265 rendering_stats.END_COMP_NAME: {'time': end_comp_time} 266 } 267 268 scroll_async_slice = async_slice.AsyncSlice( 269 'benchmark', 'InputLatency', timestamp) 270 271 scroll_async_sub_slice = async_slice.AsyncSlice( 272 'benchmark', rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME, 273 timestamp) 274 scroll_async_sub_slice.args = {'data': scroll_update_data} 275 scroll_async_sub_slice.parent_slice = scroll_async_slice 276 scroll_async_sub_slice.start_thread = start_thread 277 scroll_async_sub_slice.end_thread = end_thread 278 279 scroll_async_slice.sub_slices.append(scroll_async_sub_slice) 280 scroll_async_slice.start_thread = start_thread 281 scroll_async_slice.end_thread = end_thread 282 start_thread.AddAsyncSlice(scroll_async_slice) 283 284 # Also add some dummy frame statistics so we can feed the resulting timeline 285 # to RenderingStats. 286 AddImplThreadRenderingStats(mock_timer, end_thread, False) 287 288 if not ref_latency_stats: 289 return 290 291 ref_latency_stats.input_event.append(async_sub_slice) 292 ref_latency_stats.input_event.append(scroll_async_sub_slice) 293 ref_latency_stats.input_event_latency.append(( 294 rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME, 295 (data[rendering_stats.END_COMP_NAME]['time'] - 296 data[rendering_stats.ORIGINAL_COMP_NAME]['time']) / 1000.0)) 297 scroll_update_time = ( 298 scroll_update_data[rendering_stats.END_COMP_NAME]['time'] - 299 scroll_update_data[rendering_stats.BEGIN_SCROLL_UPDATE_COMP_NAME]['time']) 300 ref_latency_stats.input_event_latency.append(( 301 rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME, 302 scroll_update_time / 1000.0)) 303 304 305class RenderingStatsUnitTest(unittest.TestCase): 306 307 def testHasRenderingStats(self): 308 timeline = model.TimelineModel() 309 timer = MockTimer() 310 311 # A process without rendering stats 312 process_without_stats = timeline.GetOrCreateProcess(pid=1) 313 thread_without_stats = process_without_stats.GetOrCreateThread(tid=11) 314 process_without_stats.FinalizeImport() 315 self.assertFalse(rendering_stats.HasRenderingStats(thread_without_stats)) 316 317 # A process with rendering stats, but no frames in them 318 process_without_frames = timeline.GetOrCreateProcess(pid=2) 319 thread_without_frames = process_without_frames.GetOrCreateThread(tid=21) 320 process_without_frames.FinalizeImport() 321 self.assertFalse(rendering_stats.HasRenderingStats(thread_without_frames)) 322 323 # A process with rendering stats and frames in them 324 process_with_frames = timeline.GetOrCreateProcess(pid=3) 325 thread_with_frames = process_with_frames.GetOrCreateThread(tid=31) 326 AddImplThreadRenderingStats(timer, thread_with_frames, True, None) 327 process_with_frames.FinalizeImport() 328 self.assertTrue(rendering_stats.HasRenderingStats(thread_with_frames)) 329 330 def testHasDrmStats(self): 331 timeline = model.TimelineModel() 332 timer = MockTimer() 333 vblank_timer = MockVblankTimer() 334 335 # A process without drm stats 336 process_without_stats = timeline.GetOrCreateProcess(pid=5) 337 thread_without_stats = process_without_stats.GetOrCreateThread(tid=51) 338 process_without_stats.FinalizeImport() 339 self.assertFalse(rendering_stats.HasDrmStats(thread_without_stats)) 340 341 # A process with drm stats and frames in them 342 process_with_frames = timeline.GetOrCreateProcess(pid=6) 343 thread_with_frames = process_with_frames.GetOrCreateThread(tid=61) 344 AddDrmEventFlipStats(timer, vblank_timer, thread_with_frames, True, None) 345 process_with_frames.FinalizeImport() 346 self.assertTrue(rendering_stats.HasDrmStats(thread_with_frames)) 347 348 def testBothSurfaceFlingerAndDisplayStats(self): 349 timeline = model.TimelineModel() 350 timer = MockTimer() 351 352 ref_stats = ReferenceRenderingStats() 353 ref_stats.AppendNewRange() 354 surface_flinger = timeline.GetOrCreateProcess(pid=4) 355 surface_flinger.name = 'SurfaceFlinger' 356 surface_flinger_thread = surface_flinger.GetOrCreateThread(tid=41) 357 renderer = timeline.GetOrCreateProcess(pid=2) 358 browser = timeline.GetOrCreateProcess(pid=3) 359 browser_main = browser.GetOrCreateThread(tid=31) 360 browser_main.BeginSlice('webkit.console', 'ActionA', 361 timer.AdvanceAndGet(2, 4), '') 362 363 # Create SurfaceFlinger stats and display rendering stats. 364 for i in xrange(0, 10): 365 first = (i == 0) 366 AddSurfaceFlingerStats(timer, surface_flinger_thread, first, ref_stats) 367 timer.Advance(2, 4) 368 369 for i in xrange(0, 10): 370 first = (i == 0) 371 AddDisplayRenderingStats(timer, browser_main, first, None) 372 timer.Advance(5, 10) 373 374 browser_main.EndSlice(timer.AdvanceAndGet()) 375 timer.Advance(2, 4) 376 377 browser.FinalizeImport() 378 renderer.FinalizeImport() 379 timeline_markers = timeline.FindTimelineMarkers(['ActionA']) 380 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 381 for marker in timeline_markers] 382 stats = rendering_stats.RenderingStats( 383 renderer, browser, surface_flinger, None, timeline_ranges) 384 385 # Compare rendering stats to reference - Only SurfaceFlinger stats should 386 # count 387 self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) 388 self.assertEquals(stats.frame_times, ref_stats.frame_times) 389 390 def testBothDrmAndDisplayStats(self): 391 timeline = model.TimelineModel() 392 timer = MockTimer() 393 vblank_timer = MockVblankTimer() 394 395 ref_stats = ReferenceRenderingStats() 396 ref_stats.AppendNewRange() 397 gpu = timeline.GetOrCreateProcess(pid=6) 398 gpu.name = 'GPU Process' 399 gpu_drm_thread = gpu.GetOrCreateThread(tid=61) 400 renderer = timeline.GetOrCreateProcess(pid=2) 401 browser = timeline.GetOrCreateProcess(pid=3) 402 browser_main = browser.GetOrCreateThread(tid=31) 403 browser_main.BeginSlice('webkit.console', 'ActionA', 404 timer.AdvanceAndGet(2, 4), '') 405 vblank_timer.TvAdvance(2000, 4000) 406 407 # Create drm flip stats and display rendering stats. 408 for i in xrange(0, 10): 409 first = (i == 0) 410 AddDrmEventFlipStats(timer, vblank_timer, gpu_drm_thread, 411 first, ref_stats) 412 timer.Advance(2, 4) 413 vblank_timer.TvAdvance(2000, 4000) 414 415 for i in xrange(0, 10): 416 first = (i == 0) 417 AddDisplayRenderingStats(timer, browser_main, first, None) 418 timer.Advance(5, 10) 419 vblank_timer.TvAdvance(5000, 10000) 420 421 browser_main.EndSlice(timer.AdvanceAndGet()) 422 timer.Advance(2, 4) 423 vblank_timer.TvAdvance(2000, 4000) 424 425 browser.FinalizeImport() 426 renderer.FinalizeImport() 427 timeline_markers = timeline.FindTimelineMarkers(['ActionA']) 428 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 429 for marker in timeline_markers] 430 stats = rendering_stats.RenderingStats( 431 renderer, browser, None, gpu, timeline_ranges) 432 433 # Compare rendering stats to reference - Only drm flip stats should 434 # count 435 self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) 436 self.assertEquals(stats.frame_times, ref_stats.frame_times) 437 438 def testBothDisplayAndImplStats(self): 439 timeline = model.TimelineModel() 440 timer = MockTimer() 441 442 ref_stats = ReferenceRenderingStats() 443 ref_stats.AppendNewRange() 444 renderer = timeline.GetOrCreateProcess(pid=2) 445 browser = timeline.GetOrCreateProcess(pid=3) 446 browser_main = browser.GetOrCreateThread(tid=31) 447 browser_main.BeginSlice('webkit.console', 'ActionA', 448 timer.AdvanceAndGet(2, 4), '') 449 450 # Create main, impl, and display rendering stats. 451 for i in xrange(0, 10): 452 first = (i == 0) 453 AddImplThreadRenderingStats(timer, browser_main, first, None) 454 timer.Advance(2, 4) 455 456 for i in xrange(0, 10): 457 first = (i == 0) 458 AddDisplayRenderingStats(timer, browser_main, first, ref_stats) 459 timer.Advance(5, 10) 460 461 browser_main.EndSlice(timer.AdvanceAndGet()) 462 timer.Advance(2, 4) 463 464 browser.FinalizeImport() 465 renderer.FinalizeImport() 466 timeline_markers = timeline.FindTimelineMarkers(['ActionA']) 467 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 468 for marker in timeline_markers] 469 stats = rendering_stats.RenderingStats( 470 renderer, browser, None, None, timeline_ranges) 471 472 # Compare rendering stats to reference - Only display stats should count 473 self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) 474 self.assertEquals(stats.frame_times, ref_stats.frame_times) 475 476 def testRangeWithoutFrames(self): 477 timer = MockTimer() 478 timeline = model.TimelineModel() 479 480 # Create a renderer process, with a main thread and impl thread. 481 renderer = timeline.GetOrCreateProcess(pid=2) 482 renderer_main = renderer.GetOrCreateThread(tid=21) 483 renderer_compositor = renderer.GetOrCreateThread(tid=22) 484 485 # Create 10 main and impl rendering stats events for Action A. 486 renderer_main.BeginSlice('webkit.console', 'ActionA', 487 timer.AdvanceAndGet(2, 4), '') 488 for i in xrange(0, 10): 489 first = (i == 0) 490 AddImplThreadRenderingStats(timer, renderer_compositor, first, None) 491 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 492 timer.Advance(2, 4) 493 494 # Create 5 main and impl rendering stats events not within any action. 495 for i in xrange(0, 5): 496 first = (i == 0) 497 AddImplThreadRenderingStats(timer, renderer_compositor, first, None) 498 499 # Create Action B without any frames. This should trigger 500 # NotEnoughFramesError when the RenderingStats object is created. 501 renderer_main.BeginSlice('webkit.console', 'ActionB', 502 timer.AdvanceAndGet(2, 4), '') 503 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 504 505 renderer.FinalizeImport() 506 507 timeline_markers = timeline.FindTimelineMarkers(['ActionA', 'ActionB']) 508 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 509 for marker in timeline_markers] 510 511 stats = rendering_stats.RenderingStats( 512 renderer, None, None, None, timeline_ranges) 513 self.assertEquals(0, len(stats.frame_timestamps[1])) 514 515 def testFromTimeline(self): 516 timeline = model.TimelineModel() 517 518 # Create a browser process and a renderer process, and a main thread and 519 # impl thread for each. 520 browser = timeline.GetOrCreateProcess(pid=1) 521 browser_compositor = browser.GetOrCreateThread(tid=12) 522 renderer = timeline.GetOrCreateProcess(pid=2) 523 renderer_main = renderer.GetOrCreateThread(tid=21) 524 renderer_compositor = renderer.GetOrCreateThread(tid=22) 525 526 timer = MockTimer() 527 renderer_ref_stats = ReferenceRenderingStats() 528 browser_ref_stats = ReferenceRenderingStats() 529 530 # Create 10 main and impl rendering stats events for Action A. 531 renderer_main.BeginSlice('webkit.console', 'ActionA', 532 timer.AdvanceAndGet(2, 4), '') 533 renderer_ref_stats.AppendNewRange() 534 browser_ref_stats.AppendNewRange() 535 for i in xrange(0, 10): 536 first = (i == 0) 537 AddImplThreadRenderingStats( 538 timer, renderer_compositor, first, renderer_ref_stats) 539 AddImplThreadRenderingStats( 540 timer, browser_compositor, first, browser_ref_stats) 541 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 542 543 # Create 5 main and impl rendering stats events not within any action. 544 for i in xrange(0, 5): 545 first = (i == 0) 546 AddImplThreadRenderingStats(timer, renderer_compositor, first, None) 547 AddImplThreadRenderingStats(timer, browser_compositor, first, None) 548 549 # Create 10 main and impl rendering stats events for Action B. 550 renderer_main.BeginSlice('webkit.console', 'ActionB', 551 timer.AdvanceAndGet(2, 4), '') 552 renderer_ref_stats.AppendNewRange() 553 browser_ref_stats.AppendNewRange() 554 for i in xrange(0, 10): 555 first = (i == 0) 556 AddImplThreadRenderingStats( 557 timer, renderer_compositor, first, renderer_ref_stats) 558 AddImplThreadRenderingStats( 559 timer, browser_compositor, first, browser_ref_stats) 560 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 561 562 # Create 10 main and impl rendering stats events for Action A. 563 renderer_main.BeginSlice('webkit.console', 'ActionA', 564 timer.AdvanceAndGet(2, 4), '') 565 renderer_ref_stats.AppendNewRange() 566 browser_ref_stats.AppendNewRange() 567 for i in xrange(0, 10): 568 first = (i == 0) 569 AddImplThreadRenderingStats( 570 timer, renderer_compositor, first, renderer_ref_stats) 571 AddImplThreadRenderingStats( 572 timer, browser_compositor, first, browser_ref_stats) 573 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 574 timer.Advance(2, 4) 575 576 browser.FinalizeImport() 577 renderer.FinalizeImport() 578 579 timeline_markers = timeline.FindTimelineMarkers( 580 ['ActionA', 'ActionB', 'ActionA']) 581 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 582 for marker in timeline_markers] 583 stats = rendering_stats.RenderingStats( 584 renderer, browser, None, None, timeline_ranges) 585 586 # Compare rendering stats to reference. 587 self.assertEquals(stats.frame_timestamps, 588 browser_ref_stats.frame_timestamps) 589 self.assertEquals(stats.frame_times, browser_ref_stats.frame_times) 590 self.assertEquals(stats.approximated_pixel_percentages, 591 renderer_ref_stats.approximated_pixel_percentages) 592 self.assertEquals(stats.checkerboarded_pixel_percentages, 593 renderer_ref_stats.checkerboarded_pixel_percentages) 594 595 def testInputLatencyFromTimeline(self): 596 timeline = model.TimelineModel() 597 598 # Create a browser process and a renderer process. 599 browser = timeline.GetOrCreateProcess(pid=1) 600 browser_main = browser.GetOrCreateThread(tid=11) 601 renderer = timeline.GetOrCreateProcess(pid=2) 602 renderer_main = renderer.GetOrCreateThread(tid=21) 603 604 timer = MockTimer() 605 ref_latency = ReferenceInputLatencyStats() 606 607 # Create 10 input latency stats events for Action A. 608 renderer_main.BeginSlice('webkit.console', 'ActionA', 609 timer.AdvanceAndGet(2, 4), '') 610 for _ in xrange(0, 10): 611 AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency) 612 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 613 614 # Create 5 input latency stats events not within any action. 615 timer.Advance(2, 4) 616 for _ in xrange(0, 5): 617 AddInputLatencyStats(timer, browser_main, renderer_main, None) 618 619 # Create 10 input latency stats events for Action B. 620 renderer_main.BeginSlice('webkit.console', 'ActionB', 621 timer.AdvanceAndGet(2, 4), '') 622 for _ in xrange(0, 10): 623 AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency) 624 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 625 626 # Create 10 input latency stats events for Action A. 627 renderer_main.BeginSlice('webkit.console', 'ActionA', 628 timer.AdvanceAndGet(2, 4), '') 629 for _ in xrange(0, 10): 630 AddInputLatencyStats(timer, browser_main, renderer_main, ref_latency) 631 renderer_main.EndSlice(timer.AdvanceAndGet(2, 4)) 632 633 browser.FinalizeImport() 634 renderer.FinalizeImport() 635 636 latency_events = [] 637 638 timeline_markers = timeline.FindTimelineMarkers( 639 ['ActionA', 'ActionB', 'ActionA']) 640 timeline_ranges = [bounds.Bounds.CreateFromEvent(marker) 641 for marker in timeline_markers] 642 for timeline_range in timeline_ranges: 643 if timeline_range.is_empty: 644 continue 645 latency_events.extend(rendering_stats.GetLatencyEvents( 646 browser, timeline_range)) 647 648 self.assertEquals(latency_events, ref_latency.input_event) 649 event_latency_result = rendering_stats.ComputeEventLatencies(latency_events) 650 self.assertEquals(event_latency_result, 651 ref_latency.input_event_latency) 652 653 stats = rendering_stats.RenderingStats( 654 renderer, browser, None, None, timeline_ranges) 655 self.assertEquals( 656 perf_tests_helper.FlattenList(stats.input_event_latency), 657 [latency for name, latency in ref_latency.input_event_latency 658 if name != rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME]) 659 self.assertEquals( 660 perf_tests_helper.FlattenList(stats.main_thread_scroll_latency), 661 [latency for name, latency in ref_latency.input_event_latency 662 if name == rendering_stats.MAIN_THREAD_SCROLL_UPDATE_EVENT_NAME]) 663 self.assertEquals( 664 perf_tests_helper.FlattenList(stats.gesture_scroll_update_latency), 665 [latency for name, latency in ref_latency.input_event_latency 666 if name == rendering_stats.GESTURE_SCROLL_UPDATE_EVENT_NAME]) 667