15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import unittest
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)from metrics import test_page_test_results
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from metrics import timeline
9f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)from telemetry.timeline import model as model_module
10a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochfrom telemetry.web_perf import timeline_interaction_record as tir_module
11a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
12a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochdef _GetInteractionRecord(start, end):
130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  return tir_module.TimelineInteractionRecord("test-record", start, end)
14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class LoadTimesTimelineMetric(unittest.TestCase):
170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  def GetResults(self, metric, model, renderer_thread, interaction_records):
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    results = test_page_test_results.TestPageTestResults(self)
190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    metric.AddResults(model, renderer_thread, interaction_records, results)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return results
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def testSanitizing(self):
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model = model_module.TimelineModel()
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.name = 'CrRendererMain'
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # [      X       ]
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    #      [  Y  ]
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat1', 'x.y', 10, 0)
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.EndSlice(20, 20)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model.FinalizeImport()
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
33a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.LoadTimesTimelineMetric()
34a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results = self.GetResults(
35a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      metric, model=model, renderer_thread=renderer_main,
360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      interaction_records=[_GetInteractionRecord(0, float('inf'))])
37a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results.AssertHasPageSpecificScalarValue(
38a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'CrRendererMain|x_y', 'ms', 10)
39a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results.AssertHasPageSpecificScalarValue(
40a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'CrRendererMain|x_y_max', 'ms', 10)
41a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results.AssertHasPageSpecificScalarValue(
42a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      'CrRendererMain|x_y_avg', 'ms', 10)
43a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
44a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  def testTimelineBetweenRange(self):
45a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    model = model_module.TimelineModel()
46a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
47a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main.name = 'CrRendererMain'
48a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
49a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    #   [          X         ]    [       Z      ]
50a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    #           [  Y  ]               [   T    ]
51a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    #   [ interaction record ]
52a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main.BeginSlice('cat1', 'x.y', 10, 0)
53a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main.EndSlice(20, 20)
54a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main.BeginSlice('cat1', 'z.t', 30, 0)
55a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    renderer_main.EndSlice(35, 35)
56a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    model.FinalizeImport()
57a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
58a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.LoadTimesTimelineMetric()
590529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch    results = self.GetResults(
600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      metric, model=model, renderer_thread=renderer_main,
610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      interaction_records=[_GetInteractionRecord(10, 20)])
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    results.AssertHasPageSpecificScalarValue(
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      'CrRendererMain|x_y', 'ms', 10)
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    results.AssertHasPageSpecificScalarValue(
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      'CrRendererMain|x_y_max', 'ms', 10)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    results.AssertHasPageSpecificScalarValue(
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      'CrRendererMain|x_y_avg', 'ms', 10)
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
69a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def testCounterSanitizing(self):
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model = model_module.TimelineModel()
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.name = 'CrRendererMain'
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    x_counter = renderer_main.parent.GetOrCreateCounter('cat', 'x.y')
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    x_counter.samples += [1, 2]
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    x_counter.series_names += ['a']
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    x_counter.timestamps += [0, 1]
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model.FinalizeImport()
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
81a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.LoadTimesTimelineMetric()
82a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results = self.GetResults(
83a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      metric, model=model, renderer_thread=renderer_main,
840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      interaction_records=[_GetInteractionRecord(0, float('inf'))])
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    results.AssertHasPageSpecificScalarValue(
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      'cat_x_y', 'count', 3)
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    results.AssertHasPageSpecificScalarValue(
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      'cat_x_y_avg', 'count', 1.5)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class ThreadTimesTimelineMetricUnittest(unittest.TestCase):
92a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  def GetResults(self, metric, model, renderer_thread, interaction_record):
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    results = test_page_test_results.TestPageTestResults(self)
94a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric.AddResults(model, renderer_thread, interaction_record,
95a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                results)
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return results
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def testResults(self):
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model = model_module.TimelineModel()
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.name = 'CrRendererMain'
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
103a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.ThreadTimesTimelineMetric()
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    metric.details_to_report = timeline.ReportMainThreadOnly
105a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results = self.GetResults(metric, model, renderer_main.parent,
1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                              [_GetInteractionRecord(1,2)])
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Test that all result thread categories exist
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for name in timeline.TimelineThreadCategories.values():
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      results.GetPageSpecificValueNamed(timeline.ThreadCpuTimeResultName(name))
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def testBasic(self):
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model = model_module.TimelineModel()
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.name = 'CrRendererMain'
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Create two frame swaps (Results times should be divided by two)
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main = model.GetOrCreateProcess(1).GetOrCreateThread(3)
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.name = 'Compositor'
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.BeginSlice('cc_cat', timeline.FrameTraceName, 10, 10)
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.EndSlice(11, 11)
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.BeginSlice('cc_cat', timeline.FrameTraceName, 12, 12)
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.EndSlice(13, 13)
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # [      X       ]   [ Z ]
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    #      [  Y  ]
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat1', 'X', 10, 0)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat2', 'Y', 15, 5)
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.EndSlice(16, 5.5)
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.EndSlice(30, 19.5)
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat1', 'Z', 31, 20)
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat1', 'Z', 33, 21)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model.FinalizeImport()
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Exclude 'Z' using an action-range.
136a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.ThreadTimesTimelineMetric()
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    metric.details_to_report = timeline.ReportMainThreadOnly
138a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results = self.GetResults(metric, model, renderer_main.parent,
1390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                              [_GetInteractionRecord(10, 30)])
140a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Test a couple specific results.
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert_results = {
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      timeline.ThreadCpuTimeResultName('renderer_main') : 9.75,
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      timeline.ThreadDetailResultName('renderer_main','cat1') : 9.5,
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      timeline.ThreadDetailResultName('renderer_main','cat2') : 0.5,
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      timeline.ThreadDetailResultName('renderer_main','idle') : 0
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for name, value in assert_results.iteritems():
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      results.AssertHasPageSpecificScalarValue(name, 'ms', value)
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def testOverheadIsRemoved(self):
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model = model_module.TimelineModel()
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main = model.GetOrCreateProcess(1).GetOrCreateThread(2)
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.name = 'CrRendererMain'
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Create one frame swap.
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main = model.GetOrCreateProcess(1).GetOrCreateThread(3)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.name = 'Compositor'
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.BeginSlice('cc_cat', timeline.FrameTraceName, 10, 10)
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    cc_main.EndSlice(11, 11)
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # [      X       ]
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    #    [Overhead]
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    overhead_category = timeline.OverheadTraceCategory
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    overhead_name = timeline.OverheadTraceName
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice('cat1', 'X', 10, 0)
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.BeginSlice(overhead_category, overhead_name, 15, 5)
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.EndSlice(16, 6)
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    renderer_main.EndSlice(30, 10)
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model.FinalizeImport()
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Include everything in an action-range.
174a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    metric = timeline.ThreadTimesTimelineMetric()
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    metric.details_to_report = timeline.ReportMainThreadOnly
176a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    results = self.GetResults(metric, model, renderer_main.parent,
1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch                              [_GetInteractionRecord(10, 30)])
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # Test a couple specific results.
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert_results = {
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      timeline.ThreadCpuTimeResultName('renderer_main') : 9.0,
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for name, value in assert_results.iteritems():
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      results.AssertHasPageSpecificScalarValue(name, 'ms', value)
185