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