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
3 # found in the LICENSE file.
5 # These tests access private methods in the speedindex module.
6 # pylint: disable=W0212
12 from telemetry
.core
import bitmap
13 from telemetry
.timeline
import inspector_timeline_data
14 from telemetry
.timeline
import model
15 from metrics
import speedindex
17 # Sample timeline data in the json format provided by devtools.
18 # The sample events will be used in several tests below.
19 _TEST_DIR
= os
.path
.join(os
.path
.dirname(__file__
), 'unittest_data')
20 _SAMPLE_DATA
= json
.load(open(os
.path
.join(_TEST_DIR
, 'sample_timeline.json')))
21 _SAMPLE_TIMELINE_DATA
= inspector_timeline_data
.InspectorTimelineData(
23 _SAMPLE_EVENTS
= model
.TimelineModel(
24 timeline_data
=_SAMPLE_TIMELINE_DATA
).GetAllEvents()
27 class FakeTimelineModel(object):
32 def SetAllEvents(self
, events
):
35 def GetAllEvents(self
, recursive
=True):
36 assert recursive
== True
40 class FakeVideo(object):
42 def __init__(self
, frames
):
45 def GetVideoFrameIter(self
):
46 for frame
in self
._frames
:
49 class FakeBitmap(object):
51 def __init__(self
, r
, g
, b
):
52 self
._histogram
= bitmap
.ColorHistogram(r
, g
, b
, bitmap
.WHITE
)
54 # pylint: disable=W0613
55 def ColorHistogram(self
, ignore_color
=None, tolerance
=None):
56 return self
._histogram
59 class FakeTab(object):
61 def __init__(self
, video_capture_result
=None):
62 self
._timeline
_model
= FakeTimelineModel()
63 self
._javascript
_result
= None
64 self
._video
_capture
_result
= FakeVideo(video_capture_result
)
67 def timeline_model(self
):
68 return self
._timeline
_model
71 def video_capture_supported(self
):
72 return self
._video
_capture
_result
is not None
74 def SetEvaluateJavaScriptResult(self
, result
):
75 self
._javascript
_result
= result
77 def EvaluateJavaScript(self
, _
):
78 return self
._javascript
_result
80 def StartVideoCapture(self
, min_bitrate_mbps
=1):
81 assert self
.video_capture_supported
82 assert min_bitrate_mbps
> 0
84 def StopVideoCapture(self
):
85 assert self
.video_capture_supported
86 return self
._video
_capture
_result
88 def Highlight(self
, _
):
92 class IncludedPaintEventsTest(unittest
.TestCase
):
93 def testNumberPaintEvents(self
):
94 impl
= speedindex
.PaintRectSpeedIndexImpl()
95 # In the sample data, there's one event that occurs before the layout event,
96 # and one paint event that's not a leaf paint event.
97 events
= impl
._IncludedPaintEvents
(_SAMPLE_EVENTS
)
98 self
.assertEqual(len(events
), 5)
101 class SpeedIndexImplTest(unittest
.TestCase
):
102 def testAdjustedAreaDict(self
):
103 impl
= speedindex
.PaintRectSpeedIndexImpl()
104 paint_events
= impl
._IncludedPaintEvents
(_SAMPLE_EVENTS
)
105 viewport
= 1000, 1000
106 time_area_dict
= impl
._TimeAreaDict
(paint_events
, viewport
)
107 self
.assertEqual(len(time_area_dict
), 4)
108 # The event that ends at time 100 is a fullscreen; it's discounted by half.
109 self
.assertEqual(time_area_dict
[100], 500000)
110 self
.assertEqual(time_area_dict
[300], 100000)
111 self
.assertEqual(time_area_dict
[400], 200000)
112 self
.assertEqual(time_area_dict
[800], 200000)
114 def testVideoCompleteness(self
):
116 (0.0, FakeBitmap([ 0, 0, 0,10], [ 0, 0, 0,10], [ 0, 0, 0,10])),
117 (0.1, FakeBitmap([10, 0, 0, 0], [10, 0, 0, 0], [10, 0, 0, 0])),
118 (0.2, FakeBitmap([ 0, 0, 2, 8], [ 0, 0, 4, 6], [ 0, 0, 1, 9])),
119 (0.3, FakeBitmap([ 0, 3, 2, 5], [ 2, 1, 0, 7], [ 0, 3, 0, 7])),
120 (0.4, FakeBitmap([ 0, 0, 1, 0], [ 0, 0, 1, 0], [ 0, 0, 1, 0])),
121 (0.5, FakeBitmap([ 0, 4, 6, 0], [ 0, 4, 6, 0], [ 0, 4, 6, 0])),
125 tab
= FakeTab(frames
)
126 impl
= speedindex
.VideoSpeedIndexImpl()
129 time_completeness
= impl
.GetTimeCompletenessList(tab
)
130 self
.assertEqual(len(time_completeness
), 6)
131 self
.assertEqual(time_completeness
[0], (0.0, 0))
132 self
.assertTimeCompleteness(
133 time_completeness
[1], 0.1, 1 - (16 + 16 + 16) / max_distance
)
134 self
.assertTimeCompleteness(
135 time_completeness
[2], 0.2, 1 - (12 + 10 + 13) / max_distance
)
136 self
.assertTimeCompleteness(
137 time_completeness
[3], 0.3, 1 - (6 + 10 + 8) / max_distance
)
138 self
.assertTimeCompleteness(
139 time_completeness
[4], 0.4, 1 - (4 + 4 + 4) / max_distance
)
140 self
.assertEqual(time_completeness
[5], (0.5, 1))
142 def testBlankPage(self
):
144 (0.0, FakeBitmap([0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1])),
145 (0.1, FakeBitmap([0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1])),
146 (0.2, FakeBitmap([1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 1])),
147 (0.3, FakeBitmap([0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1])),
149 tab
= FakeTab(frames
)
150 impl
= speedindex
.VideoSpeedIndexImpl()
153 time_completeness
= impl
.GetTimeCompletenessList(tab
)
154 self
.assertEqual(len(time_completeness
), 4)
155 self
.assertEqual(time_completeness
[0], (0.0, 1.0))
156 self
.assertEqual(time_completeness
[1], (0.1, 1.0))
157 self
.assertEqual(time_completeness
[2], (0.2, 0.0))
158 self
.assertEqual(time_completeness
[3], (0.3, 1.0))
160 def assertTimeCompleteness(self
, time_completeness
, time
, completeness
):
161 self
.assertEqual(time_completeness
[0], time
)
162 self
.assertAlmostEqual(time_completeness
[1], completeness
)
165 class SpeedIndexTest(unittest
.TestCase
):
166 def testWithSampleData(self
):
168 impl
= speedindex
.PaintRectSpeedIndexImpl()
169 viewport
= 1000, 1000
170 # Add up the parts of the speed index for each time interval.
171 # Each part is the time interval multiplied by the proportion of the
172 # total area value that is not yet painted for that interval.
174 parts
.append(100 * 1.0)
175 parts
.append(200 * 0.5)
176 parts
.append(100 * 0.4)
177 parts
.append(400 * 0.2)
178 expected
= sum(parts
) # 330.0
179 tab
.timeline_model
.SetAllEvents(_SAMPLE_EVENTS
)
180 tab
.SetEvaluateJavaScriptResult(viewport
)
181 actual
= impl
.CalculateSpeedIndex(tab
)
182 self
.assertEqual(actual
, expected
)
185 class WPTComparisonTest(unittest
.TestCase
):
186 """Compare the speed index results with results given by webpagetest.org.
188 Given the same timeline data, both this speedindex metric and webpagetest.org
189 should both return the same results. Fortunately, webpagetest.org also
190 provides timeline data in json format along with the speed index results.
193 def _TestJsonTimelineExpectation(self
, filename
, viewport
, expected
):
194 """Check whether the result for some timeline data is as expected.
197 filename: Filename of a json file which contains a
198 expected: The result expected based on the WPT result.
201 impl
= speedindex
.PaintRectSpeedIndexImpl()
202 file_path
= os
.path
.join(_TEST_DIR
, filename
)
203 with
open(file_path
) as json_file
:
204 raw_events
= json
.load(json_file
)
205 timeline_data
= inspector_timeline_data
.InspectorTimelineData(raw_events
)
206 tab
.timeline_model
.SetAllEvents(
207 model
.TimelineModel(timeline_data
=timeline_data
).GetAllEvents())
208 tab
.SetEvaluateJavaScriptResult(viewport
)
209 actual
= impl
.CalculateSpeedIndex(tab
)
210 # The result might differ by 1 or more milliseconds due to rounding,
211 # so compare to the nearest 10 milliseconds.
212 self
.assertAlmostEqual(actual
, expected
, places
=-1)
215 # Page: http://info.cern.ch/hypertext/WWW/TheProject.html
216 # This page has only one paint event.
217 self
._TestJsonTimelineExpectation
(
218 'cern_repeat_timeline.json', (1014, 650), 379.0)
221 # Page: http://www.baidu.com/
222 # This page has several paint events, but no nested paint events.
223 self
._TestJsonTimelineExpectation
(
224 'baidu_repeat_timeline.json', (1014, 650), 1761.43)
227 # Page: http://2ch.net/
228 # This page has several paint events, including nested paint events.
229 self
._TestJsonTimelineExpectation
(
230 '2ch_repeat_timeline.json', (997, 650), 674.58)
233 if __name__
== "__main__":