Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Side by Side Diff: tools/testing/run_selenium.py

Issue 9420037: reuse the same browser when running webdriver tests (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: added comment about processQueue Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file 3 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a 4 # for details. All rights reserved. Use of this source code is governed by a
5 # BSD-style license that can be found in the LICENSE file. 5 # BSD-style license that can be found in the LICENSE file.
6 # 6 #
7 7
8 """Script to actually open a browser and perform the test, and reports back with 8 """Script to actually open a browser and perform the test, and reports back with
9 the result. 9 the result. It uses Selenium WebDriver when possible for running the tests. It
10 uses Selenium RC for Safari.
11
12 If started with --batch this script runs a batch of in-browser tests in
13 the same browser process.
14
15 Normal mode:
16 $ python run_selenium.py --browser=ff --timeout=60 path/to/test.html
17
18 Exit code indicates pass or fail
19
20 Batch mode:
21 $ python run_selenium.py --batch
22 stdin: --browser=ff --timeout=60 path/to/test.html
23 stdout: >>> TEST PASS
24 stdin: --browser=ff --timeout=60 path/to/test2.html
25 stdout: >>> TEST FAIL
26 stdin: --terminate
27 $
10 """ 28 """
11 29
12 import os 30 import os
13 import optparse 31 import optparse
14 import platform 32 import platform
15 import selenium 33 import selenium
16 from selenium.webdriver.support.ui import WebDriverWait 34 from selenium.webdriver.support.ui import WebDriverWait
17 import shutil 35 import shutil
18 import socket 36 import socket
19 import sys 37 import sys
20 import time 38 import time
39 import signal
40
41 TIMEOUT_ERROR_MSG = 'FAIL (timeout)'
21 42
22 def perf_test_done(driver): 43 def perf_test_done(driver):
23 """Checks if the performance test has completed.""" 44 """Checks if the performance test has completed."""
24 return perf_test_done_helper(driver.page_source) 45 return perf_test_done_helper(driver.page_source)
25 46
26 def perf_test_done_helper(source): 47 def perf_test_done_helper(source):
27 """Tests to see if our performance test is done by printing a score.""" 48 """Tests to see if our performance test is done by printing a score."""
28 #This code is written this way to work around a current instability in the 49 #This code is written this way to work around a current instability in the
29 # python webdriver bindings if you call driver.get_element_by_id. 50 # python webdriver bindings if you call driver.get_element_by_id.
30 #TODO(efortuna): Access these elements in a nicer way using DOM parser. 51 #TODO(efortuna): Access these elements in a nicer way using DOM parser.
31 string = '<div id="status">' 52 string = '<div id="status">'
32 index = source.find(string) 53 index = source.find(string)
33 end_index = source.find('</div>', index+1) 54 end_index = source.find('</div>', index+1)
34 source = source[index + len(string):end_index] 55 source = source[index + len(string):end_index]
35 return 'Score:' in source 56 return 'Score:' in source
36 57
37 def run_test_in_browser(browser, html_out, timeout, is_perf): 58 def run_test_in_browser(browser, html_out, timeout, is_perf):
38 """Run the desired test in the browser using Selenium 2.0 WebDriver syntax, 59 """Run the desired test in the browser using Selenium 2.0 WebDriver syntax,
39 and wait for the test to complete. This is the newer syntax, that currently 60 and wait for the test to complete. This is the newer syntax, that currently
40 supports Firefox, Chrome, IE, Opera (and some mobile browsers).""" 61 supports Firefox, Chrome, IE, Opera (and some mobile browsers)."""
41 browser.get("file://" + html_out) 62 if isinstance(browser, selenium.selenium):
63 return run_test_in_browser_selenium_rc(browser, html_out, timeout, is_perf)
64
65 browser.get("file://" + html_out)
42 source = '' 66 source = ''
43 try: 67 try:
44 if is_perf: 68 if is_perf:
45 # We're running a performance test. 69 # We're running a performance test.
46 element = WebDriverWait(browser, float(timeout)).until(perf_test_done) 70 element = WebDriverWait(browser, float(timeout)).until(perf_test_done)
47 else: 71 else:
48 element = WebDriverWait(browser, float(timeout)).until( 72 element = WebDriverWait(browser, float(timeout)).until(
49 lambda driver : ('PASS' in driver.page_source) or 73 lambda driver : ('PASS' in driver.page_source) or
50 ('FAIL' in driver.page_source)) 74 ('FAIL' in driver.page_source))
51 source = browser.page_source 75 source = browser.page_source
52 except selenium.common.exceptions.TimeoutException: 76 except selenium.common.exceptions.TimeoutException:
53 source = 'FAIL (timeout)' 77 source = TIMEOUT_ERROR_MSG
54 finally:
55 # A timeout exception is thrown if nothing happens within the time limit.
56 if browser != 'chrome':
57 browser.close()
58 try:
59 browser.quit()
60 except selenium.common.exceptions.WebDriverException:
61 #TODO(efortuna): figure out why this crashes.... and avoid?
62 pass
63 return source 78 return source
64 79
65 def run_test_in_browser_selenium1(sel, html_out, timeout, is_perf): 80 def run_test_in_browser_selenium_rc(sel, html_out, timeout, is_perf):
66 """ Run the desired test in the browser using Selenium 1.0 syntax, and wait 81 """ Run the desired test in the browser using Selenium 1.0 syntax, and wait
67 for the test to complete. This is used for Safari, since it is not currently 82 for the test to complete. This is used for Safari, since it is not currently
68 supported on Selenium 2.0.""" 83 supported on Selenium 2.0."""
69 sel.open('file://' + html_out) 84 sel.open('file://' + html_out)
70 source = sel.get_html_source() 85 source = sel.get_html_source()
71 def end_condition(source): 86 def end_condition(source):
72 return 'PASS' in source or 'FAIL' in source 87 return 'PASS' in source or 'FAIL' in source
73 if is_perf: 88 if is_perf:
74 end_condition = perf_test_done_helper 89 end_condition = perf_test_done_helper
75 90
76 elapsed = 0 91 elapsed = 0
77 while (not end_condition(source)) and elapsed <= timeout: 92 while (not end_condition(source)) and elapsed <= timeout:
78 sec = .25 93 sec = .25
79 time.sleep(sec) 94 time.sleep(sec)
80 elapsed += sec 95 elapsed += sec
81 source = sel.get_html_source() 96 source = sel.get_html_source()
82 sel.stop()
83 return source 97 return source
84 98
85 def parse_args(): 99 def parse_args(args=None):
86 parser = optparse.OptionParser() 100 parser = optparse.OptionParser()
87 parser.add_option('--out', dest='out', 101 parser.add_option('--out', dest='out',
88 help = 'The path for html output file that we will running our test from', 102 help = 'The path for html output file that we will running our test from',
89 action = 'store', default = '') 103 action = 'store', default = '')
90 parser.add_option('--browser', dest='browser', 104 parser.add_option('--browser', dest='browser',
91 help = 'The browser type (default = chrome)', 105 help = 'The browser type (default = chrome)',
92 action = 'store', default = 'chrome') 106 action = 'store', default = 'chrome')
93 # TODO(efortuna): Put this back up to be more than the default timeout in 107 # TODO(efortuna): Put this back up to be more than the default timeout in
94 # test.dart. Right now it needs to be less than 60 so that when test.dart 108 # test.dart. Right now it needs to be less than 60 so that when test.dart
95 # times out, this script also closes the browser windows. 109 # times out, this script also closes the browser windows.
96 parser.add_option('--timeout', dest = 'timeout', 110 parser.add_option('--timeout', dest = 'timeout',
97 help = 'Amount of time (seconds) to wait before timeout', type = 'int', 111 help = 'Amount of time (seconds) to wait before timeout', type = 'int',
98 action = 'store', default=58) 112 action = 'store', default=58)
99 parser.add_option('--perf', dest = 'is_perf', 113 parser.add_option('--perf', dest = 'is_perf',
100 help = 'Add this flag if we are running a browser performance test', 114 help = 'Add this flag if we are running a browser performance test',
101 action = 'store_true', default=False) 115 action = 'store_true', default=False)
102 args, ignored = parser.parse_args() 116 args, ignored = parser.parse_args(args=args)
103 return args.out, args.browser, args.timeout, args.is_perf 117 return args.out, args.browser, args.timeout, args.is_perf
104 118
105 def Main(): 119 def start_browser(browser, html_out):
106 # Note: you need ChromeDriver *in your path* to run Chrome, in addition to
107 # installing Chrome.
108 browser = None
109 html_out, browser, timeout, is_perf = parse_args()
110
111 if browser == 'chrome': 120 if browser == 'chrome':
112 browser = selenium.webdriver.Chrome() 121 # Note: you need ChromeDriver *in your path* to run Chrome, in addition to
122 # installing Chrome. Also note that the build bot runs have a different path
123 # from a normal user -- check the build logs.
124 return selenium.webdriver.Chrome()
113 elif browser == 'ff': 125 elif browser == 'ff':
114 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile() 126 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile()
115 profile.set_preference('dom.max_script_run_time', 0) 127 profile.set_preference('dom.max_script_run_time', 0)
116 profile.set_preference('dom.max_chrome_script_run_time', 0) 128 profile.set_preference('dom.max_chrome_script_run_time', 0)
117 browser = selenium.webdriver.Firefox(firefox_profile=profile) 129 return selenium.webdriver.Firefox(firefox_profile=profile)
118 elif browser == 'ie' and platform.system() == 'Windows': 130 elif browser == 'ie' and platform.system() == 'Windows':
119 browser = selenium.webdriver.Ie() 131 return selenium.webdriver.Ie()
120 elif browser == 'safari' and platform.system() == 'Darwin': 132 elif browser == 'safari' and platform.system() == 'Darwin':
121 # TODO(efortuna): Ensure our preferences (no pop-up blocking) file is the 133 # TODO(efortuna): Ensure our preferences (no pop-up blocking) file is the
122 # same (Safari auto-deletes when it has too many "crashes," or in our case, 134 # same (Safari auto-deletes when it has too many "crashes," or in our case,
123 # timeouts). Come up with a less hacky way to do this. 135 # timeouts). Come up with a less hacky way to do this.
124 shutil.copy(os.path.dirname(__file__) + '/com.apple.Safari.plist', 136 backup_safari_prefs = os.path.dirname(__file__) + '/com.apple.Safari.plist'
125 '/Library/Preferences/com.apple.Safari.plist') 137 if os.path.exists(backup_safari_prefs):
138 shutil.copy(backup_safari_prefs,
139 '/Library/Preferences/com.apple.Safari.plist')
126 sel = selenium.selenium('localhost', 4444, "*safari", 'file://' + html_out) 140 sel = selenium.selenium('localhost', 4444, "*safari", 'file://' + html_out)
127 try: 141 try:
128 sel.start() 142 sel.start()
143 return sel
129 except socket.error: 144 except socket.error:
130 print 'ERROR: Could not connect to Selenium RC server. Are you running' +\ 145 print 'ERROR: Could not connect to Selenium RC server. Are you running' +\
131 ' java -jar selenium-server-standalone-2.15.0.jar? If not, start ' + \ 146 ' java -jar selenium-server-standalone-2.15.0.jar? If not, start ' + \
132 'it before running this test.' 147 'it before running this test.'
133 return 1 148 sys.exit(1)
134 else: 149 else:
135 raise Exception('Incompatible browser and platform combination.') 150 raise Exception('Incompatible browser and platform combination.')
136 source = ''
137 if browser == 'safari':
138 source = run_test_in_browser_selenium1(sel, html_out, timeout, is_perf)
139 else:
140 source = run_test_in_browser(browser, html_out, timeout, is_perf)
141 151
152 def close_browser(browser):
153 if browser is None:
154 return
155 if isinstance(browser, selenium.selenium):
156 browser.stop()
157 return
158
159 # A timeout exception is thrown if nothing happens within the time limit.
160 if browser != 'chrome':
161 browser.close()
162 try:
163 browser.quit()
164 except selenium.common.exceptions.WebDriverException:
165 # TODO(efortuna): Figure out why this crashes.... and avoid?
166 pass
167
168 def report_results(is_perf, source):
142 if is_perf: 169 if is_perf:
143 # We're running a performance test. 170 # We're running a performance test.
144 print source 171 print source
145 if 'NaN' in source: 172 if 'NaN' in source:
146 return 1 173 return 1
147 else: 174 else:
148 return 0 175 return 0
149 else: 176 else:
150 # We're running a correctness test. Mark test as passing if all individual 177 # We're running a correctness test. Mark test as passing if all individual
151 # test cases pass. 178 # test cases pass.
152 if 'FAIL' not in source and 'PASS' in source: 179 if 'FAIL' not in source and 'PASS' in source:
153 print 'Content-Type: text/plain\nPASS' 180 print 'Content-Type: text/plain\nPASS'
154 return 0 181 return 0
155 else: 182 else:
156 #The hacky way to get document.getElementById('body').innerHTML for this 183 #The hacky way to get document.getElementById('body').innerHTML for this
157 # webpage, without the JavaScript. 184 # webpage, without the JavaScript.
158 #TODO(efortuna): Access these elements in a nicer way using DOM parser. 185 #TODO(efortuna): Access these elements in a nicer way using DOM parser.
159 index = source.find('<body>') 186 index = source.find('<body>')
160 index += len('<body>') 187 index += len('<body>')
161 end_index = source.find('<script') 188 end_index = source.find('<script')
162 print source[index : end_index] 189 print unicode(source[index : end_index]).encode("utf-8")
163 return 1 190 return 1
164 191
165 192
193 def run_batch_tests():
194 '''
195 Runs a batch of in-browser tests in the same browser process. Batching
196 gives faster throughput and makes tests less subject to browser starting
197 flakiness, issues with too many browser processes running, etc.
198
199 When running this function, stdin/stdout is used to communicate with the test
200 framework. See BatchRunnerProcess in test_runner.dart for the other side of
201 this communication channel
202
203 Example of usage:
204 $ python run_selenium.py --batch
205 stdin: --browser=ff --timeout=60 path/to/test.html
206 stdout: >>> TEST PASS
207 stdin: --browser=ff --timeout=60 path/to/test2.html
208 stdout: >>> TEST FAIL
209 stdin: --terminate
210 $
211 '''
212
213 print '>>> BATCH START'
214 browser = None
215 current_browser_name = None
216
217 # TODO(jmesserly): It'd be nice to shutdown gracefully in the event of a
218 # SIGTERM. Unfortunately dart:io cannot send SIGTERM, see dartbug.com/1756.
219 signal.signal(signal.SIGTERM, lambda number, frame: close_browser(browser))
220
221 try:
222 while True:
223 line = sys.stdin.readline()
224 if line == '--terminate\n':
225 break
226
227 html_out, browser_name, timeout, is_perf = parse_args(line.split())
228
229 # Sanity checks that test.dart is passing flags we can handle.
230 if is_perf:
231 print 'Batch test runner not compatible with perf testing'
232 return 1
233 if browser and current_browser_name != browser_name:
234 print('Batch test runner got multiple browsers: %s and %s'
235 % (current_browser_name, browser_name))
236 return 1
237
238 # Start the browser on the first run
239 if browser is None:
240 current_browser_name = browser_name
241 browser = start_browser(browser_name, html_out)
242
243 source = run_test_in_browser(browser, html_out, timeout, is_perf)
244
245 # print one of:
246 # >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}
247 status = report_results(is_perf, source)
248 if status == 0:
249 print '>>> TEST PASS'
250 elif source == TIMEOUT_ERROR_MSG:
251 print '>>> TEST TIMEOUT'
252 else:
253 print '>>> TEST FAIL'
254 sys.stdout.flush()
255 finally:
256 close_browser(browser)
257
258
259 def main(args):
260 # Run in batch mode if the --batch flag is passed.
261 # TODO(jmesserly): reconcile with the existing args parsing
262 if '--batch' in args:
263 return run_batch_tests()
264
265 # Run a single test
266 html_out, browser_name, timeout, is_perf = parse_args()
267 browser = start_browser(browser_name, html_out)
268
269 try:
270 output = run_test_in_browser(browser, html_out, timeout, is_perf)
271 return report_results(is_perf, output)
272 finally:
273 close_browser(browser)
274
166 if __name__ == "__main__": 275 if __name__ == "__main__":
167 sys.exit(Main()) 276 sys.exit(main(sys.argv))
OLDNEW
« tools/testing/dart/test_runner.dart ('K') | « tools/testing/dart/test_suite.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698