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 unittest
6
7import telemetry.timeline.bounds as timeline_bounds
8import telemetry.timeline.slice as tracing_slice
9from telemetry.timeline import model
10from telemetry.web_perf.metrics. \
11    rendering_frame import GetFrameEventsInsideRange
12from telemetry.web_perf.metrics.rendering_frame import MissingData
13from telemetry.web_perf.metrics.rendering_frame import RenderingFrame
14
15
16class RenderingFrameTestData(object):
17
18  def __init__(self):
19    self._begin_frame_id = 0
20    self._events = []
21    self._renderer_process = model.TimelineModel().GetOrCreateProcess(pid=1)
22    self._main_thread = self._renderer_process.GetOrCreateThread(tid=11)
23    self._compositor_thread = self._renderer_process.GetOrCreateThread(tid=12)
24
25  @property
26  def events(self):
27    return self._events
28
29  @property
30  def renderer_process(self):
31    return self._renderer_process
32
33  def AddSendEvent(self, ts=0, duration=1):
34    self._begin_frame_id += 1
35    event = self._CreateEvent(
36        RenderingFrame.send_begin_frame_event, ts, duration)
37    self._compositor_thread.PushSlice(event)
38
39  def AddBeginMainFrameEvent(self, ts=0, duration=1):
40    event = self._CreateEvent(
41        RenderingFrame.begin_main_frame_event, ts, duration)
42    self._main_thread.PushSlice(event)
43
44  def FinalizeImport(self):
45    self._renderer_process.FinalizeImport()
46
47  def _CreateEvent(self, event_name, ts, duration):
48    event = tracing_slice.Slice(None, 'cc,benchmark', event_name, ts,
49        duration=duration, args={'begin_frame_id': self._begin_frame_id})
50    self._events.append(event)
51    return event
52
53
54def GenerateTimelineRange(start=0, end=100):
55  timeline_range = timeline_bounds.Bounds()
56  timeline_range.AddValue(start)
57  timeline_range.AddValue(end)
58  return timeline_range
59
60
61class RenderingFrameUnitTest(unittest.TestCase):
62
63  def testRenderingFrame(self):
64    d = RenderingFrameTestData()
65    d.AddSendEvent(ts=10)
66    d.AddBeginMainFrameEvent(ts=20)
67    d.FinalizeImport()
68
69    frame = RenderingFrame(d.events)
70    self.assertEquals(10, frame.queueing_duration)
71
72  def testRenderingFrameMissingSendBeginFrameEvents(self):
73    d = RenderingFrameTestData()
74    d.AddBeginMainFrameEvent(ts=10)
75    d.FinalizeImport()
76
77    self.assertRaises(MissingData, RenderingFrame, d.events)
78
79  def testRenderingFrameDuplicateSendBeginFrameEvents(self):
80    d = RenderingFrameTestData()
81    d.AddSendEvent(ts=10)
82    d.AddBeginMainFrameEvent(ts=20)
83    d.AddSendEvent(ts=30)
84    d.FinalizeImport()
85
86    self.assertRaises(MissingData, RenderingFrame, d.events)
87
88  def testRenderingFrameMissingBeginMainFrameEvents(self):
89    d = RenderingFrameTestData()
90    d.AddSendEvent(ts=10)
91    d.FinalizeImport()
92
93    self.assertRaises(MissingData, RenderingFrame, d.events)
94
95  def testRenderingFrameDuplicateBeginMainFrameEvents(self):
96    d = RenderingFrameTestData()
97    d.AddSendEvent(ts=10)
98    d.AddBeginMainFrameEvent(ts=20)
99    d.AddBeginMainFrameEvent(ts=30)
100    d.AddBeginMainFrameEvent(ts=40)
101    d.FinalizeImport()
102
103    frame = RenderingFrame(d.events)
104    self.assertEquals(30, frame.queueing_duration)
105
106  def testFrameEventMissingBeginFrameId(self):
107    timeline = model.TimelineModel()
108    process = timeline.GetOrCreateProcess(pid=1)
109    main_thread = process.GetOrCreateThread(tid=11)
110    timeline_range = timeline_bounds.Bounds()
111
112    # Create an event without the begin_frame_id argument
113    event = tracing_slice.Slice(
114        None, 'cc,benchmark', RenderingFrame.begin_main_frame_event, 0)
115    main_thread.PushSlice(event)
116    process.FinalizeImport()
117    self.assertRaises(Exception, GetFrameEventsInsideRange, process,
118                      timeline_range)
119
120  def testGetFrameEventsInsideRange(self):
121    """Test a basic sequenece, with expected frame queueing delays A and B.
122
123                 |----A----|    |--B--|
124         Main:        [1]  [1]        [2]
125
126    Compositor:  [1]            [2]
127    """
128    d = RenderingFrameTestData()
129    d.AddSendEvent(ts=10)
130    d.AddBeginMainFrameEvent(ts=20)
131    d.AddBeginMainFrameEvent(ts=30)
132    d.AddSendEvent(ts=40)
133    d.AddBeginMainFrameEvent(ts=50)
134    d.FinalizeImport()
135
136    timeline_range = GenerateTimelineRange()
137    frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range)
138
139    self.assertEquals(2, len(frame_events))
140    self.assertEquals(20, frame_events[0].queueing_duration)
141    self.assertEquals(10, frame_events[1].queueing_duration)
142
143  def testFrameEventsMissingDataNotIncluded(self):
144    """Test a sequenece missing an initial SendBeginFrame.
145
146    Only one frame should be returned, with expected frame queueing delay A.
147                           |--A--|
148          Main:  [0]  [0]        [2]
149
150    Compositor:            [2]
151    """
152    d = RenderingFrameTestData()
153    d.AddBeginMainFrameEvent(ts=20)
154    d.AddBeginMainFrameEvent(ts=30)
155    d.AddSendEvent(ts=40)
156    d.AddBeginMainFrameEvent(ts=50)
157    d.FinalizeImport()
158
159    timeline_range = GenerateTimelineRange()
160    frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range)
161
162    self.assertEquals(1, len(frame_events))
163    self.assertEquals(10, frame_events[0].queueing_duration)
164