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 |
11 of performance/memory statistics. | 11 of performance/memory statistics. |
12 | 12 |
13 DEEP_MEMORY_PROFILE: Enable the Deep Memory Profiler if it's set to 'True'. | 13 DEEP_MEMORY_PROFILE: Enable the Deep Memory Profiler if it's set to 'True'. |
14 DEEP_MEMORY_PROFILE_SAVE: Don't clean up dump files if it's set to 'True'. | 14 DEEP_MEMORY_PROFILE_SAVE: Don't clean up dump files if it's set to 'True'. |
| 15 |
| 16 ENDURE_NO_WPR: Run tests without Web Page Replay if it's set. |
| 17 WPR_RECORD: Run tests in record mode. If you want to make a fresh |
| 18 archive, make sure to delete the old one, otherwise |
| 19 it will append to the old one. |
| 20 WPR_ARCHIVE_PATH: an alternative archive file to use. |
15 """ | 21 """ |
16 | 22 |
17 from datetime import datetime | 23 from datetime import datetime |
18 import json | 24 import json |
19 import logging | 25 import logging |
20 import os | 26 import os |
21 import re | 27 import re |
22 import subprocess | 28 import subprocess |
23 import tempfile | 29 import tempfile |
24 import time | 30 import time |
25 | 31 |
26 import perf | 32 import perf |
27 import pyauto_functional # Must be imported before pyauto. | 33 import pyauto_functional # Must be imported before pyauto. |
28 import pyauto | 34 import pyauto |
29 import pyauto_errors | 35 import pyauto_errors |
30 import pyauto_utils | 36 import pyauto_utils |
31 import remote_inspector_client | 37 import remote_inspector_client |
32 import selenium.common.exceptions | 38 import selenium.common.exceptions |
33 from selenium.webdriver.support.ui import WebDriverWait | 39 from selenium.webdriver.support.ui import WebDriverWait |
| 40 import webpagereplay |
34 | 41 |
35 | 42 |
36 class NotSupportedEnvironmentError(RuntimeError): | 43 class NotSupportedEnvironmentError(RuntimeError): |
37 """Represent an error raised since the environment (OS) is not supported.""" | 44 """Represent an error raised since the environment (OS) is not supported.""" |
38 pass | 45 pass |
39 | 46 |
| 47 |
40 class ChromeEndureBaseTest(perf.BasePerfTest): | 48 class ChromeEndureBaseTest(perf.BasePerfTest): |
41 """Implements common functionality for all Chrome Endure tests. | 49 """Implements common functionality for all Chrome Endure tests. |
42 | 50 |
43 All Chrome Endure test classes should inherit from this class. | 51 All Chrome Endure test classes should inherit from this class. |
44 """ | 52 """ |
45 | 53 |
46 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. | 54 _DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours. |
47 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. | 55 _GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
48 # TODO(dennisjeffrey): Do we still need to tolerate errors? | 56 # TODO(dennisjeffrey): Do we still need to tolerate errors? |
49 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. | 57 _ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. |
50 _DEEP_MEMORY_PROFILE = False | 58 _DEEP_MEMORY_PROFILE = False |
51 _DEEP_MEMORY_PROFILE_SAVE = False | 59 _DEEP_MEMORY_PROFILE_SAVE = False |
52 | 60 |
53 _DMPROF_DIR_PATH = os.path.join( | 61 _DMPROF_DIR_PATH = os.path.join( |
54 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, | 62 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, |
55 'tools', 'deep_memory_profiler') | 63 'tools', 'deep_memory_profiler') |
56 | 64 |
57 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') | 65 _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof') |
58 | 66 |
59 def setUp(self): | 67 def setUp(self): |
| 68 # The Web Page Replay environment variables must be parsed before |
| 69 # perf.BasePerfTest.setUp() |
| 70 self._ParseReplayEnv() |
60 # The environment variables for the Deep Memory Profiler must be set | 71 # The environment variables for the Deep Memory Profiler must be set |
61 # before perf.BasePerfTest.setUp() to inherit them to Chrome. | 72 # before perf.BasePerfTest.setUp() to inherit them to Chrome. |
62 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( | 73 self._deep_memory_profile = self._GetDeepMemoryProfileEnv( |
63 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 74 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) |
64 | 75 |
65 if self._deep_memory_profile: | 76 if self._deep_memory_profile: |
66 if not self.IsLinux(): | 77 if not self.IsLinux(): |
67 raise NotSupportedEnvironmentError( | 78 raise NotSupportedEnvironmentError( |
68 'Deep Memory Profiler is not supported in this environment (OS).') | 79 'Deep Memory Profiler is not supported in this environment (OS).') |
69 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') | 80 dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') |
(...skipping 27 matching lines...) Expand all Loading... |
97 self._remote_inspector_client = ( | 108 self._remote_inspector_client = ( |
98 remote_inspector_client.RemoteInspectorClient()) | 109 remote_inspector_client.RemoteInspectorClient()) |
99 logging.info('Connection to remote inspector set up successfully.') | 110 logging.info('Connection to remote inspector set up successfully.') |
100 | 111 |
101 self._test_start_time = 0 | 112 self._test_start_time = 0 |
102 self._num_errors = 0 | 113 self._num_errors = 0 |
103 self._events_to_output = [] | 114 self._events_to_output = [] |
104 self._deep_memory_profile_json_file = None | 115 self._deep_memory_profile_json_file = None |
105 self._deep_memory_profile_last_json_filename = '' | 116 self._deep_memory_profile_last_json_filename = '' |
106 self._deep_memory_profile_proc = None | 117 self._deep_memory_profile_proc = None |
| 118 self._StartReplayServerIfNecessary() |
107 | 119 |
108 def tearDown(self): | 120 def tearDown(self): |
109 logging.info('Terminating connection to remote inspector...') | 121 logging.info('Terminating connection to remote inspector...') |
110 self._remote_inspector_client.Stop() | 122 self._remote_inspector_client.Stop() |
111 logging.info('Connection to remote inspector terminated.') | 123 logging.info('Connection to remote inspector terminated.') |
112 if self._deep_memory_profile: | 124 if self._deep_memory_profile: |
113 del os.environ['DEEP_HEAP_PROFILE'] | 125 del os.environ['DEEP_HEAP_PROFILE'] |
114 del os.environ['HEAP_PROFILE_MMAP'] | 126 del os.environ['HEAP_PROFILE_MMAP'] |
115 del os.environ['HEAPPROFILE'] | 127 del os.environ['HEAPPROFILE'] |
116 | 128 |
117 # Must be done at end of this function except for post-cleaning after | 129 # Must be done at end of this function except for post-cleaning after |
118 # Chrome finishes. | 130 # Chrome finishes. |
119 perf.BasePerfTest.tearDown(self) | 131 perf.BasePerfTest.tearDown(self) |
120 | 132 |
121 # Remove the temporary directory after Chrome finishes in tearDown. | 133 # Remove the temporary directory after Chrome finishes in tearDown. |
122 if (self._deep_memory_profile and | 134 if (self._deep_memory_profile and |
123 not self._deep_memory_profile_save and | 135 not self._deep_memory_profile_save and |
124 self._deep_tempdir): | 136 self._deep_tempdir): |
125 pyauto_utils.RemovePath(self._deep_tempdir) | 137 pyauto_utils.RemovePath(self._deep_tempdir) |
| 138 # Must be done after perf.BasePerfTest.tearDown() |
| 139 self._StopReplayServerIfNecessary() |
| 140 |
| 141 def _GetArchiveName(self): |
| 142 """Return the Web Page Replay archive name that corresponds to a test. |
| 143 |
| 144 Override this function to return the name of an archive that |
| 145 corresponds to the test, e.g "ChromeEndureGmailTest.wpr". |
| 146 |
| 147 Returns: |
| 148 None, by default no archive name is provided. |
| 149 """ |
| 150 return None |
| 151 |
| 152 def _ParseReplayEnv(self): |
| 153 """Parse Web Page Replay related envrionment variables.""" |
| 154 if 'ENDURE_NO_WPR' in os.environ: |
| 155 self._use_wpr = False |
| 156 logging.info('Skipping Web Page Replay since ENDURE_NO_WPR is set.') |
| 157 else: |
| 158 self._archive_path = None |
| 159 if 'WPR_ARCHIVE_PATH' in os.environ: |
| 160 self._archive_path = os.environ.get('WPR_ARCHIVE_PATH') |
| 161 else: |
| 162 if self._GetArchiveName(): |
| 163 self._archive_path = ChromeEndureReplay.Path( |
| 164 'archive', archive_name=self._GetArchiveName()) |
| 165 self._is_record_mode = 'WPR_RECORD' in os.environ |
| 166 if self._is_record_mode: |
| 167 if self._archive_path: |
| 168 self._use_wpr = True |
| 169 else: |
| 170 self._use_wpr = False |
| 171 logging.info('Fail to record since a valid archive path can not ' + |
| 172 'be generated. Did you implement ' + |
| 173 '_GetArchiveName() in your test?') |
| 174 else: |
| 175 if self._archive_path and os.path.exists(self._archive_path): |
| 176 self._use_wpr = True |
| 177 else: |
| 178 self._use_wpr = False |
| 179 logging.info( |
| 180 'Skipping Web Page Replay since archive file %sdoes not exist.', |
| 181 self._archive_path + ' ' if self._archive_path else '') |
126 | 182 |
127 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): | 183 def _GetDeepMemoryProfileEnv(self, env_name, converter, default): |
128 """Returns a converted environment variable for the Deep Memory Profiler. | 184 """Returns a converted environment variable for the Deep Memory Profiler. |
129 | 185 |
130 Args: | 186 Args: |
131 env_name: A string name of an environment variable. | 187 env_name: A string name of an environment variable. |
132 converter: A function taking a string to convert an environment variable. | 188 converter: A function taking a string to convert an environment variable. |
133 default: A value used if the environment variable is not specified. | 189 default: A value used if the environment variable is not specified. |
134 | 190 |
135 Returns: | 191 Returns: |
(...skipping 21 matching lines...) Expand all Loading... |
157 # The same with setUp, but need to fetch the environment variable since | 213 # The same with setUp, but need to fetch the environment variable since |
158 # ExtraChromeFlags is called before setUp. | 214 # ExtraChromeFlags is called before setUp. |
159 deep_memory_profile = self._GetDeepMemoryProfileEnv( | 215 deep_memory_profile = self._GetDeepMemoryProfileEnv( |
160 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) | 216 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE) |
161 | 217 |
162 # Ensure Chrome enables remote debugging on port 9222. This is required to | 218 # Ensure Chrome enables remote debugging on port 9222. This is required to |
163 # interact with Chrome's remote inspector. | 219 # interact with Chrome's remote inspector. |
164 extra_flags = ['--remote-debugging-port=9222'] | 220 extra_flags = ['--remote-debugging-port=9222'] |
165 if deep_memory_profile: | 221 if deep_memory_profile: |
166 extra_flags.append('--no-sandbox') | 222 extra_flags.append('--no-sandbox') |
167 return (perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags) | 223 if self._use_wpr: |
| 224 extra_flags.extend(ChromeEndureReplay.CHROME_FLAGS) |
| 225 return perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags |
168 | 226 |
169 def _OnTimelineEvent(self, event_info): | 227 def _OnTimelineEvent(self, event_info): |
170 """Invoked by the Remote Inspector Client when a timeline event occurs. | 228 """Invoked by the Remote Inspector Client when a timeline event occurs. |
171 | 229 |
172 Args: | 230 Args: |
173 event_info: A dictionary containing raw information associated with a | 231 event_info: A dictionary containing raw information associated with a |
174 timeline event received from Chrome's remote inspector. Refer to | 232 timeline event received from Chrome's remote inspector. Refer to |
175 chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json | 233 chrome/src/third_party/WebKit/Source/WebCore/inspector/Inspector.json |
176 for the format of this dictionary. | 234 for the format of this dictionary. |
177 """ | 235 """ |
(...skipping 337 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
515 try: | 573 try: |
516 element = self._GetElement(driver.find_element_by_xpath, xpath) | 574 element = self._GetElement(driver.find_element_by_xpath, xpath) |
517 element.click() | 575 element.click() |
518 except (selenium.common.exceptions.StaleElementReferenceException, | 576 except (selenium.common.exceptions.StaleElementReferenceException, |
519 selenium.common.exceptions.TimeoutException) as e: | 577 selenium.common.exceptions.TimeoutException) as e: |
520 logging.exception('WebDriver exception: %s' % e) | 578 logging.exception('WebDriver exception: %s' % e) |
521 return False | 579 return False |
522 | 580 |
523 return True | 581 return True |
524 | 582 |
| 583 def _StartReplayServerIfNecessary(self): |
| 584 """Start replay server if necessary.""" |
| 585 if self._use_wpr: |
| 586 mode = 'record' if self._is_record_mode else 'replay' |
| 587 self._wpr_server = ChromeEndureReplay.ReplayServer(self._archive_path) |
| 588 self._wpr_server.StartServer() |
| 589 logging.info('Web Page Replay server has started in %s mode.', mode) |
| 590 |
| 591 def _StopReplayServerIfNecessary(self): |
| 592 """Stop the Web Page Replay server if necessary. |
| 593 |
| 594 This method has to be called AFTER all network connections which go |
| 595 through Web Page Replay server have shut down. Otherwise the |
| 596 Web Page Replay server will hang to wait for them. A good |
| 597 place is to call it at the end of the teardown process. |
| 598 """ |
| 599 if self._use_wpr: |
| 600 self._wpr_server.StopServer() |
| 601 logging.info('The Web Page Replay server stopped.') |
| 602 |
525 | 603 |
526 class ChromeEndureControlTest(ChromeEndureBaseTest): | 604 class ChromeEndureControlTest(ChromeEndureBaseTest): |
527 """Control tests for Chrome Endure.""" | 605 """Control tests for Chrome Endure.""" |
528 | 606 |
529 _WEBAPP_NAME = 'Control' | 607 _WEBAPP_NAME = 'Control' |
530 _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test' | 608 _TAB_TITLE_SUBSTRING = 'Chrome Endure Control Test' |
531 | 609 |
532 def testControlAttachDetachDOMTree(self): | 610 def testControlAttachDetachDOMTree(self): |
533 """Continually attach and detach a DOM tree from a basic document.""" | 611 """Continually attach and detach a DOM tree from a basic document.""" |
534 test_description = 'AttachDetachDOMTree' | 612 test_description = 'AttachDetachDOMTree' |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
609 | 687 |
610 # Test whether latency dom element is available. | 688 # Test whether latency dom element is available. |
611 try: | 689 try: |
612 self._GetLatencyDomElement(5000) | 690 self._GetLatencyDomElement(5000) |
613 self._has_latency = True | 691 self._has_latency = True |
614 except pyauto_errors.JSONInterfaceError: | 692 except pyauto_errors.JSONInterfaceError: |
615 logging.info('Skip recording latency as latency ' + | 693 logging.info('Skip recording latency as latency ' + |
616 'dom element is not available.') | 694 'dom element is not available.') |
617 self._has_latency = False | 695 self._has_latency = False |
618 | 696 |
| 697 def _GetArchiveName(self): |
| 698 """Return Web Page Replay archive name.""" |
| 699 return 'ChromeEndureGmailTest.wpr' |
| 700 |
619 def _SwitchToCanvasFrame(self, driver): | 701 def _SwitchToCanvasFrame(self, driver): |
620 """Switch the WebDriver to Gmail's 'canvas_frame', if it's available. | 702 """Switch the WebDriver to Gmail's 'canvas_frame', if it's available. |
621 | 703 |
622 Args: | 704 Args: |
623 driver: A selenium.webdriver.remote.webdriver.WebDriver object. | 705 driver: A selenium.webdriver.remote.webdriver.WebDriver object. |
624 | 706 |
625 Returns: | 707 Returns: |
626 True, if the switch to Gmail's 'canvas_frame' is successful, or | 708 True, if the switch to Gmail's 'canvas_frame' is successful, or |
627 False if not. | 709 False if not. |
628 """ | 710 """ |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
948 self.NavigateToURL(self._GetConfig().get('docs_url')) | 1030 self.NavigateToURL(self._GetConfig().get('docs_url')) |
949 self.assertTrue( | 1031 self.assertTrue( |
950 self.WaitUntil(lambda: self._TAB_TITLE_SUBSTRING in | 1032 self.WaitUntil(lambda: self._TAB_TITLE_SUBSTRING in |
951 self.GetActiveTabTitle(), | 1033 self.GetActiveTabTitle(), |
952 timeout=60, expect_retval=True, retry_sleep=1), | 1034 timeout=60, expect_retval=True, retry_sleep=1), |
953 msg='Timed out waiting for Docs to load. Tab title is: %s' % | 1035 msg='Timed out waiting for Docs to load. Tab title is: %s' % |
954 self.GetActiveTabTitle()) | 1036 self.GetActiveTabTitle()) |
955 | 1037 |
956 self._driver = self.NewWebDriver() | 1038 self._driver = self.NewWebDriver() |
957 | 1039 |
| 1040 def _GetArchiveName(self): |
| 1041 """Return Web Page Replay archive name.""" |
| 1042 return 'ChromeEndureDocsTest.wpr' |
| 1043 |
958 def testDocsAlternatelyClickLists(self): | 1044 def testDocsAlternatelyClickLists(self): |
959 """Alternates between two different document lists. | 1045 """Alternates between two different document lists. |
960 | 1046 |
961 This test alternately clicks the "Shared with me" and "My Drive" buttons in | 1047 This test alternately clicks the "Shared with me" and "My Drive" buttons in |
962 Google Docs, and periodically gathers performance stats that may reveal | 1048 Google Docs, and periodically gathers performance stats that may reveal |
963 memory bloat. | 1049 memory bloat. |
964 """ | 1050 """ |
965 test_description = 'AlternateLists' | 1051 test_description = 'AlternateLists' |
966 | 1052 |
967 def sort_menu_setup(): | 1053 def sort_menu_setup(): |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1018 # Log into a test Google account and open up Google Plus. | 1104 # Log into a test Google account and open up Google Plus. |
1019 self._LoginToGoogleAccount() | 1105 self._LoginToGoogleAccount() |
1020 self.NavigateToURL(self._GetConfig().get('plus_url')) | 1106 self.NavigateToURL(self._GetConfig().get('plus_url')) |
1021 loaded_tab_title = self.GetActiveTabTitle() | 1107 loaded_tab_title = self.GetActiveTabTitle() |
1022 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, | 1108 self.assertTrue(self._TAB_TITLE_SUBSTRING in loaded_tab_title, |
1023 msg='Loaded tab title does not contain "%s": "%s"' % | 1109 msg='Loaded tab title does not contain "%s": "%s"' % |
1024 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) | 1110 (self._TAB_TITLE_SUBSTRING, loaded_tab_title)) |
1025 | 1111 |
1026 self._driver = self.NewWebDriver() | 1112 self._driver = self.NewWebDriver() |
1027 | 1113 |
| 1114 def _GetArchiveName(self): |
| 1115 """Return Web Page Replay archive name.""" |
| 1116 return 'ChromeEndurePlusTest.wpr' |
| 1117 |
1028 def testPlusAlternatelyClickStreams(self): | 1118 def testPlusAlternatelyClickStreams(self): |
1029 """Alternates between two different streams. | 1119 """Alternates between two different streams. |
1030 | 1120 |
1031 This test alternately clicks the "Friends" and "Family" buttons using | 1121 This test alternately clicks the "Friends" and "Family" buttons using |
1032 Google Plus, and periodically gathers performance stats that may reveal | 1122 Google Plus, and periodically gathers performance stats that may reveal |
1033 memory bloat. | 1123 memory bloat. |
1034 """ | 1124 """ |
1035 test_description = 'AlternateStreams' | 1125 test_description = 'AlternateStreams' |
1036 | 1126 |
1037 def scenario(): | 1127 def scenario(): |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1121 except (pyauto_errors.JSONInterfaceError, | 1211 except (pyauto_errors.JSONInterfaceError, |
1122 pyauto_errors.JavascriptRuntimeError): | 1212 pyauto_errors.JavascriptRuntimeError): |
1123 self._num_errors += 1 | 1213 self._num_errors += 1 |
1124 | 1214 |
1125 time.sleep(1) | 1215 time.sleep(1) |
1126 | 1216 |
1127 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, | 1217 self._RunEndureTest(self._WEBAPP_NAME, self._TAB_TITLE_SUBSTRING, |
1128 test_description, scenario) | 1218 test_description, scenario) |
1129 | 1219 |
1130 | 1220 |
| 1221 class ChromeEndureReplay(object): |
| 1222 """Run Chrome Endure tests with network simulation via Web Page Replay.""" |
| 1223 |
| 1224 _PATHS = { |
| 1225 'archive': |
| 1226 'src/chrome/test/data/pyauto_private/webpagereplay/{archive_name}', |
| 1227 'scripts': |
| 1228 'src/chrome/test/data/chrome_endure/webpagereplay/wpr_deterministic.js', |
| 1229 } |
| 1230 CHROME_FLAGS = webpagereplay.CHROME_FLAGS |
| 1231 |
| 1232 @classmethod |
| 1233 def Path(cls, key, **kwargs): |
| 1234 return perf.FormatChromePath(cls._PATHS[key], **kwargs) |
| 1235 |
| 1236 @classmethod |
| 1237 def ReplayServer(cls, archive_path): |
| 1238 """Create a replay server.""" |
| 1239 # Inject customized scripts for Google webapps. |
| 1240 # See the javascript file for details. |
| 1241 scripts = cls.Path('scripts') |
| 1242 if not os.path.exists(scripts): |
| 1243 raise IOError('Injected scripts %s not found.' % scripts) |
| 1244 replay_options = ['--inject_scripts', scripts] |
| 1245 if 'WPR_RECORD' in os.environ: |
| 1246 replay_options.append('--append') |
| 1247 return webpagereplay.ReplayServer(archive_path, replay_options) |
| 1248 |
| 1249 |
1131 if __name__ == '__main__': | 1250 if __name__ == '__main__': |
1132 pyauto_functional.Main() | 1251 pyauto_functional.Main() |
OLD | NEW |