# Copyright 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import random import unittest from metrics.rendering_stats import RenderingStats from telemetry.core.timeline import model class MockTimer(object): """A mock timer class which can generate random durations. An instance of this class is used as a global timer to generate random durations for stats and consistent timestamps for all mock trace events. The unit of time is milliseconds. """ def __init__(self): self.milliseconds = 0 def Get(self): return self.milliseconds def Advance(self, low=0, high=1): delta = random.uniform(low, high) self.milliseconds += delta return delta class ReferenceRenderingStats(object): """ Stores expected data for comparison with actual RenderingStats """ def __init__(self): self.frame_timestamps = [] self.frame_times = [] self.paint_time = [] self.painted_pixel_count = [] self.record_time = [] self.recorded_pixel_count = [] self.rasterize_time = [] self.rasterized_pixel_count = [] def AddMainThreadRenderingStats(mock_timer, thread, first_frame, ref_stats = None): """ Adds a random main thread rendering stats event. thread: The timeline model thread to which the event will be added. first_frame: Is this the first frame within the bounds of an action? ref_stats: A ReferenceRenderingStats object to record expected values. """ # Create randonm data and timestap for main thread rendering stats. data = { 'frame_count': 0, 'paint_time': 0.0, 'painted_pixel_count': 0, 'record_time': mock_timer.Advance(2, 4) / 1000.0, 'recorded_pixel_count': 3000*3000 } timestamp = mock_timer.Get() # Add a slice with the event data to the given thread. thread.PushCompleteSlice( 'benchmark', 'BenchmarkInstrumentation::MainThreadRenderingStats', timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, args={'data': data}) if not ref_stats: return # Add timestamp only if a frame was output if data['frame_count'] == 1: if not first_frame: # Add frame_time if this is not the first frame in within the bounds of an # action. prev_timestamp = ref_stats.frame_timestamps[-1] ref_stats.frame_times.append(round(timestamp - prev_timestamp, 2)) ref_stats.frame_timestamps.append(timestamp) ref_stats.paint_time.append(data['paint_time'] * 1000.0) ref_stats.painted_pixel_count.append(data['painted_pixel_count']) ref_stats.record_time.append(data['record_time'] * 1000.0) ref_stats.recorded_pixel_count.append(data['recorded_pixel_count']) def AddImplThreadRenderingStats(mock_timer, thread, first_frame, ref_stats = None): """ Adds a random impl thread rendering stats event. thread: The timeline model thread to which the event will be added. first_frame: Is this the first frame within the bounds of an action? ref_stats: A ReferenceRenderingStats object to record expected values. """ # Create randonm data and timestap for impl thread rendering stats. data = { 'frame_count': 1, 'rasterize_time': mock_timer.Advance(5, 10) / 1000.0, 'rasterized_pixel_count': 1280*720 } timestamp = mock_timer.Get() # Add a slice with the event data to the given thread. thread.PushCompleteSlice( 'benchmark', 'BenchmarkInstrumentation::ImplThreadRenderingStats', timestamp, duration=0.0, thread_timestamp=None, thread_duration=None, args={'data': data}) if not ref_stats: return # Add timestamp only if a frame was output if data['frame_count'] == 1: if not first_frame: # Add frame_time if this is not the first frame in within the bounds of an # action. prev_timestamp = ref_stats.frame_timestamps[-1] ref_stats.frame_times.append(round(timestamp - prev_timestamp, 2)) ref_stats.frame_timestamps.append(timestamp) ref_stats.rasterize_time.append(data['rasterize_time'] * 1000.0) ref_stats.rasterized_pixel_count.append(data['rasterized_pixel_count']) class RenderingStatsUnitTest(unittest.TestCase): def testFromTimeline(self): timeline = model.TimelineModel() # Create a browser process and a renderer process, and a main thread and # impl thread for each. browser = timeline.GetOrCreateProcess(pid = 1) browser_main = browser.GetOrCreateThread(tid = 11) browser_compositor = browser.GetOrCreateThread(tid = 12) renderer = timeline.GetOrCreateProcess(pid = 2) renderer_main = renderer.GetOrCreateThread(tid = 21) renderer_compositor = renderer.GetOrCreateThread(tid = 22) timer = MockTimer() ref_stats = ReferenceRenderingStats() # Create 10 main and impl rendering stats events for Action A. timer.Advance() renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') for i in xrange(0, 10): first = (i == 0) AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) AddMainThreadRenderingStats(timer, browser_main, first, None) AddImplThreadRenderingStats(timer, browser_compositor, first, None) renderer_main.EndSlice(timer.Get()) # Create 5 main and impl rendering stats events not within any action. for i in xrange(0, 5): first = (i == 0) AddMainThreadRenderingStats(timer, renderer_main, first, None) AddImplThreadRenderingStats(timer, renderer_compositor, first, None) AddMainThreadRenderingStats(timer, browser_main, first, None) AddImplThreadRenderingStats(timer, browser_compositor, first, None) # Create 10 main and impl rendering stats events for Action B. timer.Advance() renderer_main.BeginSlice('webkit.console', 'ActionB', timer.Get(), '') for i in xrange(0, 10): first = (i == 0) AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) AddMainThreadRenderingStats(timer, browser_main, first, None) AddImplThreadRenderingStats(timer, browser_compositor, first, None) renderer_main.EndSlice(timer.Get()) # Create 10 main and impl rendering stats events for Action A. timer.Advance() renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') for i in xrange(0, 10): first = (i == 0) AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) AddMainThreadRenderingStats(timer, browser_main, first, None) AddImplThreadRenderingStats(timer, browser_compositor, first, None) renderer_main.EndSlice(timer.Get()) renderer_main.FinalizeImport() renderer_compositor.FinalizeImport() timeline_markers = timeline.FindTimelineMarkers( ['ActionA', 'ActionB', 'ActionA']) stats = RenderingStats(renderer, timeline_markers) # Compare rendering stats to reference. self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) self.assertEquals(stats.frame_times, ref_stats.frame_times) self.assertEquals(stats.rasterize_time, ref_stats.rasterize_time) self.assertEquals(stats.rasterized_pixel_count, ref_stats.rasterized_pixel_count) self.assertEquals(stats.paint_time, ref_stats.paint_time) self.assertEquals(stats.painted_pixel_count, ref_stats.painted_pixel_count) self.assertEquals(stats.record_time, ref_stats.record_time) self.assertEquals(stats.recorded_pixel_count, ref_stats.recorded_pixel_count)