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 os 6import unittest 7 8from telemetry import story 9from telemetry.internal.results import page_test_results 10from telemetry.page import page as page_module 11from telemetry.timeline import async_slice 12from telemetry.timeline import model as model_module 13from telemetry.value import improvement_direction 14from telemetry.value import scalar 15from telemetry.web_perf.metrics import timeline_based_metric 16from telemetry.web_perf import timeline_based_measurement as tbm_module 17 18 19class FakeSmoothMetric(timeline_based_metric.TimelineBasedMetric): 20 21 def AddResults(self, model, renderer_thread, interaction_records, results): 22 results.AddValue(scalar.ScalarValue( 23 results.current_page, 'FakeSmoothMetric', 'ms', 1, 24 improvement_direction=improvement_direction.DOWN)) 25 results.AddValue(scalar.ScalarValue( 26 results.current_page, 'SmoothMetricRecords', 'count', 27 len(interaction_records), 28 improvement_direction=improvement_direction.DOWN)) 29 30 31class FakeLoadingMetric(timeline_based_metric.TimelineBasedMetric): 32 33 def AddResults(self, model, renderer_thread, interaction_records, results): 34 results.AddValue(scalar.ScalarValue( 35 results.current_page, 'FakeLoadingMetric', 'ms', 2, 36 improvement_direction=improvement_direction.DOWN)) 37 results.AddValue(scalar.ScalarValue( 38 results.current_page, 'LoadingMetricRecords', 'count', 39 len(interaction_records), 40 improvement_direction=improvement_direction.DOWN)) 41 42 43class FakeStartupMetric(timeline_based_metric.TimelineBasedMetric): 44 45 def AddResults(self, model, renderer_thread, interaction_records, results): 46 pass 47 48 def AddWholeTraceResults(self, model, results): 49 results.AddValue(scalar.ScalarValue( 50 results.current_page, 'FakeStartupMetric', 'ms', 3, 51 improvement_direction=improvement_direction.DOWN)) 52 53 54class TimelineBasedMetricTestData(object): 55 56 def __init__(self, options): 57 self._model = model_module.TimelineModel() 58 renderer_process = self._model.GetOrCreateProcess(1) 59 self._renderer_thread = renderer_process.GetOrCreateThread(2) 60 self._renderer_thread.name = 'CrRendererMain' 61 self._foo_thread = renderer_process.GetOrCreateThread(3) 62 self._foo_thread.name = 'CrFoo' 63 64 self._results_wrapper = tbm_module._TBMResultWrapper() 65 self._results = page_test_results.PageTestResults() 66 self._story_set = None 67 self._threads_to_records_map = None 68 self._tbm_options = options 69 70 @property 71 def model(self): 72 return self._model 73 74 @property 75 def renderer_thread(self): 76 return self._renderer_thread 77 78 @property 79 def foo_thread(self): 80 return self._foo_thread 81 82 @property 83 def threads_to_records_map(self): 84 return self._threads_to_records_map 85 86 @property 87 def results(self): 88 return self._results 89 90 def AddInteraction(self, thread, marker='', ts=0, duration=5): 91 assert thread in (self._renderer_thread, self._foo_thread) 92 thread.async_slices.append(async_slice.AsyncSlice( 93 'category', marker, timestamp=ts, duration=duration, 94 start_thread=self._renderer_thread, end_thread=self._renderer_thread, 95 thread_start=ts, thread_duration=duration)) 96 97 def FinalizeImport(self): 98 self._model.FinalizeImport() 99 self._threads_to_records_map = ( 100 tbm_module._GetRendererThreadsToInteractionRecordsMap(self._model)) 101 self._story_set = story.StorySet(base_dir=os.path.dirname(__file__)) 102 self._story_set.AddStory(page_module.Page( 103 'http://www.bar.com/', self._story_set, self._story_set.base_dir)) 104 self._results.WillRunPage(self._story_set.stories[0]) 105 106 def AddResults(self): 107 all_metrics = self._tbm_options.GetLegacyTimelineBasedMetrics() 108 109 for thread, records in self._threads_to_records_map.iteritems(): 110 # pylint: disable=protected-access 111 metric = tbm_module._TimelineBasedMetrics( 112 self._model, thread, records, self._results_wrapper, all_metrics) 113 metric.AddResults(self._results) 114 115 for metric in all_metrics: 116 metric.AddWholeTraceResults(self._model, self._results) 117 118 self._results.DidRunPage(self._story_set.stories[0]) 119 120 121class TimelineBasedMetricsTests(unittest.TestCase): 122 123 def setUp(self): 124 self.actual_get_all_tbm_metrics = ( 125 tbm_module._GetAllLegacyTimelineBasedMetrics) 126 self._options = tbm_module.Options() 127 self._options.SetLegacyTimelineBasedMetrics( 128 (FakeSmoothMetric(), FakeLoadingMetric(), FakeStartupMetric())) 129 130 def tearDown(self): 131 tbm_module._GetAllLegacyTimelineBasedMetrics = ( 132 self.actual_get_all_tbm_metrics) 133 134 def testGetRendererThreadsToInteractionRecordsMap(self): 135 d = TimelineBasedMetricTestData(self._options) 136 # Insert 2 interaction records to renderer_thread and 1 to foo_thread 137 d.AddInteraction(d.renderer_thread, ts=0, duration=20, 138 marker='Interaction.LogicalName1') 139 d.AddInteraction(d.renderer_thread, ts=25, duration=5, 140 marker='Interaction.LogicalName2') 141 d.AddInteraction(d.foo_thread, ts=50, duration=15, 142 marker='Interaction.LogicalName3') 143 d.FinalizeImport() 144 145 self.assertEquals(2, len(d.threads_to_records_map)) 146 147 # Assert the 2 interaction records of renderer_thread are in the map. 148 self.assertIn(d.renderer_thread, d.threads_to_records_map) 149 interactions = d.threads_to_records_map[d.renderer_thread] 150 self.assertEquals(2, len(interactions)) 151 self.assertEquals(0, interactions[0].start) 152 self.assertEquals(20, interactions[0].end) 153 154 self.assertEquals(25, interactions[1].start) 155 self.assertEquals(30, interactions[1].end) 156 157 # Assert the 1 interaction records of foo_thread is in the map. 158 self.assertIn(d.foo_thread, d.threads_to_records_map) 159 interactions = d.threads_to_records_map[d.foo_thread] 160 self.assertEquals(1, len(interactions)) 161 self.assertEquals(50, interactions[0].start) 162 self.assertEquals(65, interactions[0].end) 163 164 def testAddResults(self): 165 d = TimelineBasedMetricTestData(self._options) 166 d.AddInteraction(d.renderer_thread, ts=0, duration=20, 167 marker='Interaction.LogicalName1') 168 d.AddInteraction(d.foo_thread, ts=25, duration=5, 169 marker='Interaction.LogicalName2') 170 d.FinalizeImport() 171 d.AddResults() 172 self.assertEquals(1, len(d.results.FindAllPageSpecificValuesFromIRNamed( 173 'LogicalName1', 'FakeSmoothMetric'))) 174 self.assertEquals(1, len(d.results.FindAllPageSpecificValuesFromIRNamed( 175 'LogicalName2', 'FakeLoadingMetric'))) 176 self.assertEquals(1, len(d.results.FindAllPageSpecificValuesNamed( 177 'FakeStartupMetric'))) 178 179 def testDuplicateInteractionsInDifferentThreads(self): 180 d = TimelineBasedMetricTestData(self._options) 181 d.AddInteraction(d.renderer_thread, ts=10, duration=5, 182 marker='Interaction.LogicalName/repeatable') 183 d.AddInteraction(d.foo_thread, ts=20, duration=5, 184 marker='Interaction.LogicalName') 185 self.assertRaises(tbm_module.InvalidInteractions, d.FinalizeImport) 186 187 def testDuplicateRepeatableInteractionsInDifferentThreads(self): 188 d = TimelineBasedMetricTestData(self._options) 189 d.AddInteraction(d.renderer_thread, ts=10, duration=5, 190 marker='Interaction.LogicalName/repeatable') 191 d.AddInteraction(d.foo_thread, ts=20, duration=5, 192 marker='Interaction.LogicalName/repeatable') 193 self.assertRaises(tbm_module.InvalidInteractions, d.FinalizeImport) 194 195 def testDuplicateUnrepeatableInteractionsInSameThread(self): 196 d = TimelineBasedMetricTestData(self._options) 197 d.AddInteraction(d.renderer_thread, ts=10, duration=5, 198 marker='Interaction.LogicalName') 199 d.AddInteraction(d.renderer_thread, ts=20, duration=5, 200 marker='Interaction.LogicalName') 201 d.FinalizeImport() 202 self.assertRaises(tbm_module.InvalidInteractions, d.AddResults) 203 204 def testDuplicateRepeatableInteractions(self): 205 d = TimelineBasedMetricTestData(self._options) 206 d.AddInteraction(d.renderer_thread, ts=10, duration=5, 207 marker='Interaction.LogicalName/repeatable') 208 d.AddInteraction(d.renderer_thread, ts=20, duration=5, 209 marker='Interaction.LogicalName/repeatable') 210 d.FinalizeImport() 211 d.AddResults() 212 self.assertEquals(1, len(d.results.pages_that_succeeded)) 213