| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 import sys | |
| 6 import unittest | |
| 7 | |
| 8 from telemetry.internal.browser import browser_options | |
| 9 from telemetry.internal.results import page_test_results | |
| 10 from telemetry.internal import story_runner | |
| 11 from telemetry import page as page_module | |
| 12 from telemetry.testing import simple_mock | |
| 13 | |
| 14 from measurements import page_cycler | |
| 15 from metrics import keychain_metric | |
| 16 | |
| 17 | |
| 18 # Allow testing protected members in the unit test. | |
| 19 # pylint: disable=protected-access | |
| 20 | |
| 21 class MockMemoryMetric(object): | |
| 22 """Used instead of simple_mock.MockObject so that the precise order and | |
| 23 number of calls need not be specified.""" | |
| 24 | |
| 25 def __init__(self): | |
| 26 pass | |
| 27 | |
| 28 def Start(self, page, tab): | |
| 29 pass | |
| 30 | |
| 31 def Stop(self, page, tab): | |
| 32 pass | |
| 33 | |
| 34 def AddResults(self, tab, results): | |
| 35 pass | |
| 36 | |
| 37 def AddSummaryResults(self, tab, results): | |
| 38 pass | |
| 39 | |
| 40 | |
| 41 class FakePage(page_module.Page): | |
| 42 """Used to mock loading a page.""" | |
| 43 | |
| 44 def __init__(self, url): | |
| 45 super(FakePage, self).__init__(url=url) | |
| 46 | |
| 47 @property | |
| 48 def is_file(self): | |
| 49 return self._url.startswith('file://') | |
| 50 | |
| 51 | |
| 52 class FakeTab(object): | |
| 53 """Used to mock a browser tab.""" | |
| 54 | |
| 55 def __init__(self): | |
| 56 self.clear_cache_calls = 0 | |
| 57 self.navigated_urls = [] | |
| 58 | |
| 59 def ClearCache(self, force=False): | |
| 60 assert force | |
| 61 self.clear_cache_calls += 1 | |
| 62 | |
| 63 def EvaluateJavaScript(self, script): | |
| 64 # If the page cycler invokes javascript to measure the number of keychain | |
| 65 # accesses, return a valid JSON dictionary. | |
| 66 keychain_histogram_name = keychain_metric.KeychainMetric.HISTOGRAM_NAME | |
| 67 | |
| 68 # Fake data for keychain metric. | |
| 69 if keychain_histogram_name in script: | |
| 70 return '{{ "{0}" : 0 }}'.format(keychain_histogram_name) | |
| 71 | |
| 72 return 1 | |
| 73 | |
| 74 def Navigate(self, url): | |
| 75 self.navigated_urls.append(url) | |
| 76 | |
| 77 def WaitForJavaScriptExpression(self, _, __): | |
| 78 pass | |
| 79 | |
| 80 @property | |
| 81 def browser(self): | |
| 82 return FakeBrowser() | |
| 83 | |
| 84 | |
| 85 class FakeBrowser(object): | |
| 86 _iteration = 0 | |
| 87 | |
| 88 @property | |
| 89 def cpu_stats(self): | |
| 90 FakeBrowser._iteration += 1 | |
| 91 return { | |
| 92 'Browser': {'CpuProcessTime': FakeBrowser._iteration, | |
| 93 'TotalTime': FakeBrowser._iteration * 2}, | |
| 94 'Renderer': {'CpuProcessTime': FakeBrowser._iteration, | |
| 95 'TotalTime': FakeBrowser._iteration * 3}, | |
| 96 'Gpu': {'CpuProcessTime': FakeBrowser._iteration, | |
| 97 'TotalTime': FakeBrowser._iteration * 4} | |
| 98 } | |
| 99 | |
| 100 @property | |
| 101 def platform(self): | |
| 102 return FakePlatform() | |
| 103 | |
| 104 @property | |
| 105 def supports_cpu_metrics(self): | |
| 106 return True | |
| 107 | |
| 108 @property | |
| 109 def supports_memory_metrics(self): | |
| 110 return True | |
| 111 | |
| 112 @property | |
| 113 def supports_power_metrics(self): | |
| 114 return True | |
| 115 | |
| 116 | |
| 117 class FakePlatform(object): | |
| 118 | |
| 119 def GetOSName(self): | |
| 120 return 'fake' | |
| 121 | |
| 122 def CanMonitorPower(self): | |
| 123 return False | |
| 124 | |
| 125 @property | |
| 126 def http_server(self): | |
| 127 class FakeHttpServer(object): | |
| 128 | |
| 129 def UrlOf(self, url_path): | |
| 130 return 'http://fakeserver:99999/%s' % url_path | |
| 131 return FakeHttpServer() | |
| 132 | |
| 133 | |
| 134 class PageCyclerUnitTest(unittest.TestCase): | |
| 135 | |
| 136 def SetUpCycler(self, page_repeat=1, pageset_repeat=10, cold_load_percent=50, | |
| 137 report_speed_index=False, setup_memory_module=False): | |
| 138 cycler = page_cycler.PageCycler( | |
| 139 page_repeat=page_repeat, | |
| 140 pageset_repeat=pageset_repeat, | |
| 141 cold_load_percent=cold_load_percent, | |
| 142 report_speed_index=report_speed_index) | |
| 143 options = browser_options.BrowserFinderOptions() | |
| 144 options.browser_options.platform = FakePlatform() | |
| 145 parser = options.CreateParser() | |
| 146 story_runner.AddCommandLineArgs(parser) | |
| 147 args = ['--page-repeat=%i' % page_repeat, | |
| 148 '--pageset-repeat=%i' % pageset_repeat] | |
| 149 parser.parse_args(args) | |
| 150 story_runner.ProcessCommandLineArgs(parser, options) | |
| 151 cycler.CustomizeBrowserOptions(options.browser_options) | |
| 152 | |
| 153 if setup_memory_module: | |
| 154 # Mock out memory metrics; the real ones require a real browser. | |
| 155 mock_memory_metric = MockMemoryMetric() | |
| 156 | |
| 157 mock_memory_module = simple_mock.MockObject() | |
| 158 mock_memory_module.ExpectCall( | |
| 159 'MemoryMetric').WithArgs(simple_mock.DONT_CARE).WillReturn( | |
| 160 mock_memory_metric) | |
| 161 | |
| 162 real_memory_module = page_cycler.memory | |
| 163 try: | |
| 164 page_cycler.memory = mock_memory_module | |
| 165 browser = FakeBrowser() | |
| 166 cycler.WillStartBrowser(options.browser_options.platform) | |
| 167 cycler.DidStartBrowser(browser) | |
| 168 finally: | |
| 169 page_cycler.memory = real_memory_module | |
| 170 | |
| 171 return cycler | |
| 172 | |
| 173 def testOptionsColdLoadNoArgs(self): | |
| 174 cycler = self.SetUpCycler() | |
| 175 | |
| 176 self.assertEquals(cycler._cold_run_start_index, 5) | |
| 177 | |
| 178 def testOptionsColdLoadPagesetRepeat(self): | |
| 179 cycler = self.SetUpCycler(pageset_repeat=20, page_repeat=2) | |
| 180 | |
| 181 self.assertEquals(cycler._cold_run_start_index, 20) | |
| 182 | |
| 183 def testOptionsColdLoadRequested(self): | |
| 184 cycler = self.SetUpCycler(pageset_repeat=21, page_repeat=2, | |
| 185 cold_load_percent=40) | |
| 186 | |
| 187 self.assertEquals(cycler._cold_run_start_index, 26) | |
| 188 | |
| 189 def testCacheHandled(self): | |
| 190 cycler = self.SetUpCycler(pageset_repeat=5, | |
| 191 cold_load_percent=50, | |
| 192 setup_memory_module=True) | |
| 193 | |
| 194 url_name = 'http://fakepage.com' | |
| 195 page = FakePage(url_name) | |
| 196 tab = FakeTab() | |
| 197 | |
| 198 for i in range(5): | |
| 199 results = page_test_results.PageTestResults() | |
| 200 results.WillRunPage(page) | |
| 201 cycler.WillNavigateToPage(page, tab) | |
| 202 self.assertEqual(max(0, i - 2), tab.clear_cache_calls, | |
| 203 'Iteration %d tab.clear_cache_calls %d' % | |
| 204 (i, tab.clear_cache_calls)) | |
| 205 cycler.ValidateAndMeasurePage(page, tab, results) | |
| 206 results.DidRunPage(page) | |
| 207 | |
| 208 values = results.all_page_specific_values | |
| 209 self.assertGreater(len(values), 2) | |
| 210 | |
| 211 self.assertEqual(values[0].page, page) | |
| 212 chart_name = 'cold_times' if i == 0 or i > 2 else 'warm_times' | |
| 213 self.assertEqual(values[0].name, '%s-page_load_time' % chart_name) | |
| 214 self.assertEqual(values[0].units, 'ms') | |
| 215 | |
| 216 cycler.DidNavigateToPage(page, tab) | |
| 217 | |
| 218 def testColdWarm(self): | |
| 219 cycler = self.SetUpCycler(pageset_repeat=3, setup_memory_module=True) | |
| 220 pages = [FakePage('http://fakepage1.com'), FakePage('http://fakepage2.com')] | |
| 221 tab = FakeTab() | |
| 222 for i in range(3): | |
| 223 for page in pages: | |
| 224 results = page_test_results.PageTestResults() | |
| 225 results.WillRunPage(page) | |
| 226 cycler.WillNavigateToPage(page, tab) | |
| 227 cycler.ValidateAndMeasurePage(page, tab, results) | |
| 228 results.DidRunPage(page) | |
| 229 | |
| 230 values = results.all_page_specific_values | |
| 231 self.assertGreater(len(values), 2) | |
| 232 | |
| 233 self.assertEqual(values[0].page, page) | |
| 234 | |
| 235 chart_name = 'cold_times' if i == 0 or i > 1 else 'warm_times' | |
| 236 self.assertEqual(values[0].name, '%s-page_load_time' % chart_name) | |
| 237 self.assertEqual(values[0].units, 'ms') | |
| 238 | |
| 239 cycler.DidNavigateToPage(page, tab) | |
| 240 | |
| 241 def testResults(self): | |
| 242 cycler = self.SetUpCycler(setup_memory_module=True) | |
| 243 | |
| 244 pages = [FakePage('http://fakepage1.com'), FakePage('http://fakepage2.com')] | |
| 245 tab = FakeTab() | |
| 246 | |
| 247 for i in range(2): | |
| 248 for page in pages: | |
| 249 results = page_test_results.PageTestResults() | |
| 250 results.WillRunPage(page) | |
| 251 cycler.WillNavigateToPage(page, tab) | |
| 252 cycler.ValidateAndMeasurePage(page, tab, results) | |
| 253 results.DidRunPage(page) | |
| 254 | |
| 255 values = results.all_page_specific_values | |
| 256 | |
| 257 # On Mac, there is an additional measurement: the number of keychain | |
| 258 # accesses. | |
| 259 value_count = 6 | |
| 260 if sys.platform == 'darwin': | |
| 261 value_count += 1 | |
| 262 self.assertEqual(value_count, len(values)) | |
| 263 | |
| 264 self.assertEqual(values[0].page, page) | |
| 265 chart_name = 'cold_times' if i == 0 else 'warm_times' | |
| 266 self.assertEqual(values[0].name, '%s-page_load_time' % chart_name) | |
| 267 self.assertEqual(values[0].units, 'ms') | |
| 268 self.assertEqual(values[1].name, '%s-time_to_onload' % chart_name) | |
| 269 self.assertEqual(values[1].units, 'ms') | |
| 270 | |
| 271 expected_values = ['gpu', 'browser'] | |
| 272 for value, expected in zip(values[4:len(expected_values) + 1], | |
| 273 expected_values): | |
| 274 self.assertEqual(value.page, page) | |
| 275 self.assertEqual(value.name, | |
| 276 'cpu_utilization.cpu_utilization_%s' % expected) | |
| 277 self.assertEqual(value.units, '%') | |
| 278 | |
| 279 cycler.DidNavigateToPage(page, tab) | |
| 280 | |
| 281 def testLegacyPagesAvoidCrossRenderNavigation(self): | |
| 282 # For legacy page cyclers with file URLs, verify that WillNavigateToPage | |
| 283 # does an initial navigate to avoid paying for a cross-renderer navigation. | |
| 284 cycler = self.SetUpCycler(setup_memory_module=True) | |
| 285 pages = [FakePage('file://fakepage1.com'), FakePage('file://fakepage2.com')] | |
| 286 tab = FakeTab() | |
| 287 | |
| 288 self.assertEqual([], tab.navigated_urls) | |
| 289 for page in pages * 2: | |
| 290 cycler.WillNavigateToPage(page, tab) | |
| 291 self.assertEqual( | |
| 292 ['http://fakeserver:99999/nonexistent.html'], tab.navigated_urls) | |
| OLD | NEW |