OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Performance tests for Chrome Endure (long-running perf tests on Chrome). | 6 """Performance tests for Chrome Endure (long-running perf tests on Chrome). |
7 | 7 |
8 This module accepts the following environment variable inputs: | 8 This module accepts the following environment variable inputs: |
9 TEST_LENGTH: The number of seconds in which to run each test. | 9 TEST_LENGTH: The number of seconds in which to run each test. |
10 PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling | 10 PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling |
(...skipping 15 matching lines...) Expand all Loading... | |
26 import time | 26 import time |
27 | 27 |
28 import perf | 28 import perf |
29 import pyauto_functional # Must be imported before pyauto. | 29 import pyauto_functional # Must be imported before pyauto. |
30 import pyauto | 30 import pyauto |
31 import pyauto_errors | 31 import pyauto_errors |
32 import pyauto_utils | 32 import pyauto_utils |
33 import remote_inspector_client | 33 import remote_inspector_client |
34 import selenium.common.exceptions | 34 import selenium.common.exceptions |
35 from selenium.webdriver.support.ui import WebDriverWait | 35 from selenium.webdriver.support.ui import WebDriverWait |
36 import webpagereplay | |
36 | 37 |
37 | 38 |
38 class NotSupportedEnvironmentError(RuntimeError): | 39 class NotSupportedEnvironmentError(RuntimeError): |
39 """Represent an error raised since the environment (OS) is not supported.""" | 40 """Represent an error raised since the environment (OS) is not supported.""" |
40 pass | 41 pass |
41 | 42 |
43 | |
42 class ChromeEndureBaseTest(perf.BasePerfTest): | 44 class ChromeEndureBaseTest(perf.BasePerfTest): |
43 """Implements common functionality for all Chrome Endure tests. | 45 """Implements common functionality for all Chrome Endure tests. |
44 | 46 |
45 All Chrome Endure test classes should inherit from this class. | 47 All Chrome Endure test classes should inherit from this class. |
46 """ | 48 """ |
47 | 49 |
48 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. | 50 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. |
49 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. | 51 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
50 # TODO(dennisjeffrey): Do we still need to tolerate errors? | 52 # TODO(dennisjeffrey): Do we still need to tolerate errors? |
51 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. | 53 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. |
52 _DEEP_MEMORY_PROFILE = False | 54 _DEEP_MEMORY_PROFILE = False |
53 _DEEP_MEMORY_PROFILE_INTERVAL = _GET_PERF_STATS_INTERVAL | 55 _DEEP_MEMORY_PROFILE_INTERVAL = _GET_PERF_STATS_INTERVAL |
54 _DEEP_MEMORY_PROFILE_SAVE = False | 56 _DEEP_MEMORY_PROFILE_SAVE = False |
55 | 57 |
56 _DMPROF_DIR_PATH = os.path.join( | 58 _DMPROF_DIR_PATH = os.path.join( |
57 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, | 59 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, |
58 'tools', 'deep_memory_profiler') | 60 'tools', 'deep_memory_profiler') |
59 | 61 |
60 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') | 62 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') |
61 | 63 |
62 _CHROME_BIN_PATH = os.path.join(perf.BasePerfTest.BrowserPath(), 'chrome') | 64 _CHROME_BIN_PATH = os.path.join(perf.BasePerfTest.BrowserPath(), 'chrome') |
63 | 65 |
64 def setUp(self): | 66 def setUp(self): |
67 # The environment variable for the usage of Web Page Replay. | |
68 # It must be parsed before perf.BasePerfTest.setUp() | |
69 self._use_wpr = self._NeedReplayServer() | |
70 # Do NOT mannually set WPR_RECORD for the purpose of | |
71 # making recording archive for endure tests. Run record_endure.py instead. | |
72 if self._use_wpr: | |
73 self._is_replay_mode = 'WPR_RECORD' not in os.environ | |
74 | |
65 # The environment variables for the Deep Memory Profiler must be set | 75 # The environment variables for the Deep Memory Profiler must be set |
66 # before perf.BasePerfTest.setUp() to inherit them to Chrome. | 76 # before perf.BasePerfTest.setUp() to inherit them to Chrome. |
67 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( | 77 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( |
68 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 78 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) |
69 | 79 |
70 self._deep_memory_profile_interval = self._GetDeepMemoryProfileEnv( | 80 self._deep_memory_profile_interval = self._GetDeepMemoryProfileEnv( |
71 'DEEP_MEMORY_PROFILE_INTERVAL', int, self._DEEP_MEMORY_PROFILE_INTERVAL) | 81 'DEEP_MEMORY_PROFILE_INTERVAL', int, self._DEEP_MEMORY_PROFILE_INTERVAL) |
72 | 82 |
73 if self._deep_memory_profile: | 83 if self._deep_memory_profile: |
74 if not self.IsLinux(): | 84 if not self.IsLinux(): |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
110 self._remote_inspector_client = ( | 120 self._remote_inspector_client = ( |
111 remote_inspector_client.RemoteInspectorClient()) | 121 remote_inspector_client.RemoteInspectorClient()) |
112 logging.info('Connection to remote inspector set up successfully.') | 122 logging.info('Connection to remote inspector set up successfully.') |
113 | 123 |
114 self._test_start_time = 0 | 124 self._test_start_time = 0 |
115 self._num_errors = 0 | 125 self._num_errors = 0 |
116 self._events_to_output = [] | 126 self._events_to_output = [] |
117 self._deep_memory_profile_json_file = None | 127 self._deep_memory_profile_json_file = None |
118 self._deep_memory_profile_last_json_filename = '' | 128 self._deep_memory_profile_last_json_filename = '' |
119 self._deep_memory_profile_proc = None | 129 self._deep_memory_profile_proc = None |
130 self._StartReplayServerIfNecessary() | |
120 | 131 |
121 def tearDown(self): | 132 def tearDown(self): |
122 logging.info('Terminating connection to remote inspector...') | 133 logging.info('Terminating connection to remote inspector...') |
123 self._remote_inspector_client.Stop() | 134 self._remote_inspector_client.Stop() |
124 logging.info('Connection to remote inspector terminated.') | 135 logging.info('Connection to remote inspector terminated.') |
125 if self._deep_memory_profile: | 136 if self._deep_memory_profile: |
126 # TODO(dmikurube): Stop to set HEAP_PROFILE_TIME_INTERVAL in setUp when | 137 # TODO(dmikurube): Stop to set HEAP_PROFILE_TIME_INTERVAL in setUp when |
127 # PyAuto supports to dump renderer heap profile. | 138 # PyAuto supports to dump renderer heap profile. |
128 del os.environ['HEAP_PROFILE_TIME_INTERVAL'] | 139 del os.environ['HEAP_PROFILE_TIME_INTERVAL'] |
129 del os.environ['DEEP_HEAP_PROFILE'] | 140 del os.environ['DEEP_HEAP_PROFILE'] |
130 del os.environ['HEAP_PROFILE_MMAP'] | 141 del os.environ['HEAP_PROFILE_MMAP'] |
131 del os.environ['HEAPPROFILE'] | 142 del os.environ['HEAPPROFILE'] |
132 | 143 |
133 # Must be done at end of this function except for post-cleaning after | 144 # Must be done at end of this function except for post-cleaning after |
134 # Chrome finishes. | 145 # Chrome finishes. |
135 perf.BasePerfTest.tearDown(self) | 146 perf.BasePerfTest.tearDown(self) |
136 | 147 |
137 # Remove the temporary directory after Chrome finishes in tearDown. | 148 # Remove the temporary directory after Chrome finishes in tearDown. |
138 if (self._deep_memory_profile and | 149 if (self._deep_memory_profile and |
139 not self._deep_memory_profile_save and | 150 not self._deep_memory_profile_save and |
140 self._deep_tempdir): | 151 self._deep_tempdir): |
141 pyauto_utils.RemovePath(self._deep_tempdir) | 152 pyauto_utils.RemovePath(self._deep_tempdir) |
153 # Must be done after perf.BasePerfTest.tearDown() | |
154 self._StopReplayServerIfNecessary() | |
fdeng1
2012/08/02 22:57:30
StopReplayServerIfNecessary()/StartReplayServerIfN
| |
155 | |
156 def _NeedReplayServer(self): | |
157 """Check whether the test needs to run against Web Page Replay server. | |
158 | |
159 Override this function to return False if you want to permanently | |
160 turn off Web Page Replay for a test. | |
161 | |
162 Returns: | |
163 True if need replay server otherwise False. | |
164 | |
165 Environment variable: | |
166 ENDURE_NO_WPR: if set, do not need replay server | |
167 """ | |
168 if 'ENDURE_NO_WPR' in os.environ: | |
169 return False | |
170 else: | |
171 return True | |
172 | |
fdeng1
2012/08/02 22:57:30
Two tests are offline and don't need WPR. These te
| |
173 def _GenArchiveName(self): | |
174 """Returns the Web Page Replay archive name that corresponds to a test. | |
175 | |
176 Use test class name as archive name, e.g. ChromeEndureGmailTest.wpr | |
177 Override this function to allow other names. | |
178 | |
179 Returns: | |
180 An archive name generated using test class name. | |
181 """ | |
182 # Use class name as archive name. | |
183 return self.id().split('.')[1] + '.wpr' | |
142 | 184 |
fdeng1
2012/08/02 22:57:30
I add a function called _GenArchiveName which uses
| |
143 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): | 185 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): |
144 """Returns a converted environment variable for the Deep Memory Profiler. | 186 """Returns a converted environment variable for the Deep Memory Profiler. |
145 | 187 |
146 Args: | 188 Args: |
147 env_name: A string name of an environment variable. | 189 env_name: A string name of an environment variable. |
148 converter: A function taking a string to convert an environment variable. | 190 converter: A function taking a string to convert an environment variable. |
149 default: A value used if the environment variable is not specified. | 191 default: A value used if the environment variable is not specified. |
150 | 192 |
151 Returns: | 193 Returns: |
152 A value converted from the environment variable with 'converter'. | 194 A value converted from the environment variable with 'converter'. |
(...skipping 20 matching lines...) Expand all Loading... | |
173 # The same with setUp, but need to fetch the environment variable since | 215 # The same with setUp, but need to fetch the environment variable since |
174 # ExtraChromeFlags is called before setUp. | 216 # ExtraChromeFlags is called before setUp. |
175 deep_memory_profile = self._GetDeepMemoryProfileEnv( | 217 deep_memory_profile = self._GetDeepMemoryProfileEnv( |
176 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 218 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) |
177 | 219 |
178 # Ensure Chrome enables remote debugging on port 9222. This is required to | 220 # Ensure Chrome enables remote debugging on port 9222. This is required to |
179 # interact with Chrome's remote inspector. | 221 # interact with Chrome's remote inspector. |
180 extra_flags = ['--remote-debugging-port=9222'] | 222 extra_flags = ['--remote-debugging-port=9222'] |
181 if deep_memory_profile: | 223 if deep_memory_profile: |
182 extra_flags.append('--no-sandbox') | 224 extra_flags.append('--no-sandbox') |
183 return (perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags) | 225 if self._use_wpr: |
226 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) | |
227 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags | |
184 | 228 |
185 def _OnTimelineEvent(self, event_info): | 229 def _OnTimelineEvent(self, event_info): |
186 """Invoked by the Remote Inspector Client when a timeline event occurs. | 230 """Invoked by the Remote Inspector Client when a timeline event occurs. |
187 | 231 |
188 Args: | 232 Args: |
189 event_info: A dictionary containing raw information associated with a | 233 event_info: A dictionary containing raw information associated with a |
190 timeline event received from Chrome's remote inspector. Refer to | 234 timeline event received from Chrome's remote inspector. Refer to |
191 chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json | 235 chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json |
192 for the format of this dictionary. | 236 for the format of this dictionary. |
193 """ | 237 """ |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
533 try: | 577 try: |
534 element = self._GetElement(driver.find_element_by_xpath, xpath) | 578 element = self._GetElement(driver.find_element_by_xpath, xpath) |
535 element.click() | 579 element.click() |
536 except (selenium.common.exceptions.StaleElementReferenceException, | 580 except (selenium.common.exceptions.StaleElementReferenceException, |
537 selenium.common.exceptions.TimeoutException) as e: | 581 selenium.common.exceptions.TimeoutException) as e: |
538 logging.exception('WebDriver exception: %s' % e) | 582 logging.exception('WebDriver exception: %s' % e) |
539 return False | 583 return False |
540 | 584 |
541 return True | 585 return True |
542 | 586 |
587 def _StartReplayServerIfNecessary(self): | |
588 """Start replay server if necessary. | |
589 | |
590 This method needs to be called BEFORE any connection (which is supposed | |
591 to go through the Web Page Replay server) occurs. | |
592 """ | |
593 if self._use_wpr and self._is_replay_mode: | |
594 archive_name = self._GenArchiveName() | |
595 self._wpr_server = ChromeEndureReplay.ReplayServer(archive_name) | |
596 self._wpr_server.StartServer() | |
597 logging.info('Web Page Replay server has started in Replay mode.') | |
598 | |
599 def _StopReplayServerIfNecessary(self): | |
600 """Stop the Web Page Replay server if necessary. | |
601 | |
602 This method has to be called AFTER all network connections which go | |
603 through Web Page Replay server have shut down. Otherwise the | |
604 Web Page Replay server will hang to wait for them. A good | |
605 place is to call it at the end of the teardown process. | |
606 """ | |
607 if self._use_wpr and self._is_replay_mode: | |
608 self._wpr_server.StopServer() | |
609 logging.info('The Web Page Replay server stopped.') | |
610 | |
543 | 611 |
544 class ChromeEndureControlTest(ChromeEndureBaseTest): | 612 class ChromeEndureControlTest(ChromeEndureBaseTest): |
545 """Control tests for Chrome Endure.""" | 613 """Control tests for Chrome Endure.""" |
546 | 614 |
547 _WEBAPP_NAME = 'Control' | 615 _WEBAPP_NAME = 'Control' |
548 _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test' | 616 _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test' |
549 | 617 |
618 def _NeedReplayServer(self): | |
619 """Override parent's function to remove replay support.""" | |
620 return False | |
621 | |
550 def testControlAttachDetachDOMTree(self): | 622 def testControlAttachDetachDOMTree(self): |
551 """Continually attach and detach a DOM tree from a basic document.""" | 623 """Continually attach and detach a DOM tree from a basic document.""" |
552 test_description = 'AttachDetachDOMTree' | 624 test_description = 'AttachDetachDOMTree' |
553 url = self.GetHttpURLForDataPath('chrome_endure', 'endurance_control.html') | 625 url = self.GetHttpURLForDataPath('chrome_endure', 'endurance_control.html') |
554 self.NavigateToURL(url) | 626 self.NavigateToURL(url) |
555 loaded_tab_title = self.GetActiveTabTitle() | 627 loaded_tab_title = self.GetActiveTabTitle() |
556 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, | 628 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, |
557 msg='Loaded tab title does not contain "%s": "%s"' % | 629 msg='Loaded tab title does not contain "%s": "%s"' % |
558 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) | 630 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) |
559 | 631 |
(...skipping 519 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1079 | 1151 |
1080 url = self.GetHttpURLForDataPath('indexeddb', 'endure', 'app.html') | 1152 url = self.GetHttpURLForDataPath('indexeddb', 'endure', 'app.html') |
1081 self.NavigateToURL(url) | 1153 self.NavigateToURL(url) |
1082 loaded_tab_title = self.GetActiveTabTitle() | 1154 loaded_tab_title = self.GetActiveTabTitle() |
1083 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, | 1155 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, |
1084 msg='Loaded tab title does not contain "%s": "%s"' % | 1156 msg='Loaded tab title does not contain "%s": "%s"' % |
1085 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) | 1157 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) |
1086 | 1158 |
1087 self._driver = self.NewWebDriver() | 1159 self._driver = self.NewWebDriver() |
1088 | 1160 |
1161 def _NeedReplayServer(self): | |
1162 """Override parent's function to remove replay support.""" | |
1163 return False | |
1164 | |
1089 def testOfflineOnline(self): | 1165 def testOfflineOnline(self): |
1090 """Simulates user input while offline and sync while online. | 1166 """Simulates user input while offline and sync while online. |
1091 | 1167 |
1092 This test alternates between a simulated "Offline" state (where user | 1168 This test alternates between a simulated "Offline" state (where user |
1093 input events are queued) and an "Online" state (where user input events | 1169 input events are queued) and an "Online" state (where user input events |
1094 are dequeued, sync data is staged, and sync data is unstaged). | 1170 are dequeued, sync data is staged, and sync data is unstaged). |
1095 """ | 1171 """ |
1096 test_description = 'OnlineOfflineSync' | 1172 test_description = 'OnlineOfflineSync' |
1097 | 1173 |
1098 def scenario(): | 1174 def scenario(): |
(...skipping 18 matching lines...) Expand all Loading... | |
1117 except (pyauto_errors.JSONInterfaceError, | 1193 except (pyauto_errors.JSONInterfaceError, |
1118 pyauto_errors.JavascriptRuntimeError): | 1194 pyauto_errors.JavascriptRuntimeError): |
1119 self._num_errors += 1 | 1195 self._num_errors += 1 |
1120 | 1196 |
1121 time.sleep(1) | 1197 time.sleep(1) |
1122 | 1198 |
1123 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, | 1199 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, |
1124 test_description, scenario) | 1200 test_description, scenario) |
1125 | 1201 |
1126 | 1202 |
1203 class ChromeEndureReplay(object): | |
1204 """Run Chrome Endure tests with network simulation via Web Page Replay.""" | |
1205 | |
1206 _PATHS = { | |
1207 'archive': | |
1208 'src/chrome/test/data/pyauto_private/webpagereplay/{archive_name}', | |
1209 'scripts': | |
1210 'src/chrome/test/data/chrome_endure/webpagereplay/wpr_deterministic.js', | |
1211 } | |
1212 CHROME_FLAGS = webpagereplay.CHROME_FLAGS | |
1213 | |
1214 @classmethod | |
1215 def Path(cls, key, **kwargs): | |
1216 return perf.FormatChromePath(cls._PATHS[key], **kwargs) | |
1217 | |
1218 @classmethod | |
1219 def ReplayServer(cls, archive_name): | |
1220 """Creat a replay server.""" | |
1221 # Inject customized scripts for Google webapps. | |
1222 # See the java script file for details. | |
1223 scripts = cls.Path('scripts') | |
1224 if not os.path.exists(scripts): | |
1225 raise webpagereplay.ReplayNotFoundError('injected scripts', scripts) | |
1226 replay_options = ['--inject_scripts', scripts] | |
1227 archive_path = cls.Path('archive', archive_name=archive_name) | |
1228 return webpagereplay.ReplayServer(archive_path, replay_options) | |
1229 | |
1230 | |
1127 if __name__ == '__main__': | 1231 if __name__ == '__main__': |
1128 pyauto_functional.Main() | 1232 pyauto_functional.Main() |
OLD | NEW |