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

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: updated 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 for running the tests.
Emily Fortuna 2012/02/21 18:19:35 Selenium RC and Selenium Webdriver (Selenium Webdr
Jennifer Messerly 2012/02/21 18:58:43 Done.
10
11 If started without arguments, this script runs a batch of in-browser tests in
12 the same browser process. Batching gives faster throughput and makes tests less subject to browser starting flakiness, issues with too many browser processes ru nning, etc.
Emily Fortuna 2012/02/21 18:19:35 line > 80 char. Also maybe give an example how the
Jennifer Messerly 2012/02/21 18:58:43 Done.
10 """ 13 """
11 14
12 import os 15 import os
13 import optparse 16 import optparse
14 import platform 17 import platform
15 import selenium 18 import selenium
16 from selenium.webdriver.support.ui import WebDriverWait 19 from selenium.webdriver.support.ui import WebDriverWait
17 import shutil 20 import shutil
18 import socket 21 import socket
19 import sys 22 import sys
20 import time 23 import time
24 import signal
25
26 TIMEOUT_ERROR_MSG = 'FAIL (timeout)'
21 27
22 def perf_test_done(driver): 28 def perf_test_done(driver):
23 """Checks if the performance test has completed.""" 29 """Checks if the performance test has completed."""
24 return perf_test_done_helper(driver.page_source) 30 return perf_test_done_helper(driver.page_source)
25 31
26 def perf_test_done_helper(source): 32 def perf_test_done_helper(source):
27 """Tests to see if our performance test is done by printing a score.""" 33 """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 34 #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. 35 # python webdriver bindings if you call driver.get_element_by_id.
30 #TODO(efortuna): Access these elements in a nicer way using DOM parser. 36 #TODO(efortuna): Access these elements in a nicer way using DOM parser.
31 string = '<div id="status">' 37 string = '<div id="status">'
32 index = source.find(string) 38 index = source.find(string)
33 end_index = source.find('</div>', index+1) 39 end_index = source.find('</div>', index+1)
34 source = source[index + len(string):end_index] 40 source = source[index + len(string):end_index]
35 return 'Score:' in source 41 return 'Score:' in source
36 42
37 def run_test_in_browser(browser, html_out, timeout, is_perf): 43 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, 44 """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 45 and wait for the test to complete. This is the newer syntax, that currently
40 supports Firefox, Chrome, IE, Opera (and some mobile browsers).""" 46 supports Firefox, Chrome, IE, Opera (and some mobile browsers)."""
41 browser.get("file://" + html_out) 47 if isinstance(browser, selenium.selenium):
48 return run_test_in_browser_selenium1(browser, html_out, timeout, is_perf)
Emily Fortuna 2012/02/21 18:19:35 I know I came up with the name originally here, b
Jennifer Messerly 2012/02/21 18:58:43 Done.
49
50 browser.get("file://" + html_out)
42 source = '' 51 source = ''
43 try: 52 try:
44 if is_perf: 53 if is_perf:
45 # We're running a performance test. 54 # We're running a performance test.
46 element = WebDriverWait(browser, float(timeout)).until(perf_test_done) 55 element = WebDriverWait(browser, float(timeout)).until(perf_test_done)
47 else: 56 else:
48 element = WebDriverWait(browser, float(timeout)).until( 57 element = WebDriverWait(browser, float(timeout)).until(
49 lambda driver : ('PASS' in driver.page_source) or 58 lambda driver : ('PASS' in driver.page_source) or
50 ('FAIL' in driver.page_source)) 59 ('FAIL' in driver.page_source))
51 source = browser.page_source 60 source = browser.page_source
52 except selenium.common.exceptions.TimeoutException: 61 except selenium.common.exceptions.TimeoutException:
53 source = 'FAIL (timeout)' 62 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 63 return source
64 64
65 def run_test_in_browser_selenium1(sel, html_out, timeout, is_perf): 65 def run_test_in_browser_selenium1(sel, html_out, timeout, is_perf):
66 """ Run the desired test in the browser using Selenium 1.0 syntax, and wait 66 """ 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 67 for the test to complete. This is used for Safari, since it is not currently
68 supported on Selenium 2.0.""" 68 supported on Selenium 2.0."""
69 sel.open('file://' + html_out) 69 sel.open('file://' + html_out)
70 source = sel.get_html_source() 70 source = sel.get_html_source()
71 def end_condition(source): 71 def end_condition(source):
72 return 'PASS' in source or 'FAIL' in source 72 return 'PASS' in source or 'FAIL' in source
73 if is_perf: 73 if is_perf:
74 end_condition = perf_test_done_helper 74 end_condition = perf_test_done_helper
75 75
76 elapsed = 0 76 elapsed = 0
77 while (not end_condition(source)) and elapsed <= timeout: 77 while (not end_condition(source)) and elapsed <= timeout:
78 sec = .25 78 sec = .25
79 time.sleep(sec) 79 time.sleep(sec)
80 elapsed += sec 80 elapsed += sec
81 source = sel.get_html_source() 81 source = sel.get_html_source()
82 sel.stop()
83 return source 82 return source
84 83
85 def parse_args(): 84 def parse_args(args=None):
86 parser = optparse.OptionParser() 85 parser = optparse.OptionParser()
87 parser.add_option('--out', dest='out', 86 parser.add_option('--out', dest='out',
88 help = 'The path for html output file that we will running our test from', 87 help = 'The path for html output file that we will running our test from',
89 action = 'store', default = '') 88 action = 'store', default = '')
90 parser.add_option('--browser', dest='browser', 89 parser.add_option('--browser', dest='browser',
91 help = 'The browser type (default = chrome)', 90 help = 'The browser type (default = chrome)',
92 action = 'store', default = 'chrome') 91 action = 'store', default = 'chrome')
93 # TODO(efortuna): Put this back up to be more than the default timeout in 92 # 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 93 # 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. 94 # times out, this script also closes the browser windows.
96 parser.add_option('--timeout', dest = 'timeout', 95 parser.add_option('--timeout', dest = 'timeout',
97 help = 'Amount of time (seconds) to wait before timeout', type = 'int', 96 help = 'Amount of time (seconds) to wait before timeout', type = 'int',
98 action = 'store', default=58) 97 action = 'store', default=58)
99 parser.add_option('--perf', dest = 'is_perf', 98 parser.add_option('--perf', dest = 'is_perf',
100 help = 'Add this flag if we are running a browser performance test', 99 help = 'Add this flag if we are running a browser performance test',
101 action = 'store_true', default=False) 100 action = 'store_true', default=False)
102 args, ignored = parser.parse_args() 101 args, ignored = parser.parse_args(args=args)
103 return args.out, args.browser, args.timeout, args.is_perf 102 return args.out, args.browser, args.timeout, args.is_perf
104 103
105 def Main(): 104 def start_browser(browser, html_out):
Emily Fortuna 2012/02/21 18:19:35 Maybe call this get_browser instead of start_brows
Jennifer Messerly 2012/02/21 18:58:43 I worry about calling it "get" in that it sounds l
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': 105 if browser == 'chrome':
112 browser = selenium.webdriver.Chrome() 106 # Note: you need ChromeDriver *in your path* to run Chrome, in addition to
107 # installing Chrome. Also note that the build bot runs have a different path
108 # from a normal user -- check the build logs.
109 return selenium.webdriver.Chrome()
113 elif browser == 'ff': 110 elif browser == 'ff':
114 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile() 111 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile()
115 profile.set_preference('dom.max_script_run_time', 0) 112 profile.set_preference('dom.max_script_run_time', 0)
116 profile.set_preference('dom.max_chrome_script_run_time', 0) 113 profile.set_preference('dom.max_chrome_script_run_time', 0)
117 browser = selenium.webdriver.Firefox(firefox_profile=profile) 114 return selenium.webdriver.Firefox(firefox_profile=profile)
118 elif browser == 'ie' and platform.system() == 'Windows': 115 elif browser == 'ie' and platform.system() == 'Windows':
119 browser = selenium.webdriver.Ie() 116 return selenium.webdriver.Ie()
120 elif browser == 'safari' and platform.system() == 'Darwin': 117 elif browser == 'safari' and platform.system() == 'Darwin':
121 # TODO(efortuna): Ensure our preferences (no pop-up blocking) file is the 118 # 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, 119 # 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. 120 # timeouts). Come up with a less hacky way to do this.
124 shutil.copy(os.path.dirname(__file__) + '/com.apple.Safari.plist', 121 # !!!!
Emily Fortuna 2012/02/21 18:19:35 Do we have a workaround for this yet? We probably
Jennifer Messerly 2012/02/21 18:58:43 Oops! Thanks for catching that. Fixed--now checks
125 '/Library/Preferences/com.apple.Safari.plist') 122 #shutil.copy(os.path.dirname(__file__) + '/com.apple.Safari.plist',
123 # '/Library/Preferences/com.apple.Safari.plist')
126 sel = selenium.selenium('localhost', 4444, "*safari", 'file://' + html_out) 124 sel = selenium.selenium('localhost', 4444, "*safari", 'file://' + html_out)
127 try: 125 try:
128 sel.start() 126 sel.start()
127 return sel
129 except socket.error: 128 except socket.error:
130 print 'ERROR: Could not connect to Selenium RC server. Are you running' +\ 129 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 ' + \ 130 ' java -jar selenium-server-standalone-2.15.0.jar? If not, start ' + \
132 'it before running this test.' 131 'it before running this test.'
133 return 1 132 sys.exit(1)
134 else: 133 else:
135 raise Exception('Incompatible browser and platform combination.') 134 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 135
136 def close_browser(browser):
137 if browser is None:
138 return
139 if isinstance(browser, selenium.selenium):
140 browser.stop()
141 return
142
143 # A timeout exception is thrown if nothing happens within the time limit.
144 print '!!! trying to close browser !!!'
Emily Fortuna 2012/02/21 18:19:35 Do we always want to print this? Perhaps a more in
Jennifer Messerly 2012/02/21 18:58:43 Oops. removed.
145 if browser != 'chrome':
146 browser.close()
147 try:
148 browser.quit()
149 except selenium.common.exceptions.WebDriverException:
150 # TODO(efortuna): figure out why this crashes.... and avoid?
Emily Fortuna 2012/02/21 18:19:35 nit: Yes, this was my mistake, but let's make that
Jennifer Messerly 2012/02/21 18:58:43 Done.
151 pass
152
153 def report_results(is_perf, source):
142 if is_perf: 154 if is_perf:
143 # We're running a performance test. 155 # We're running a performance test.
144 print source 156 print source
145 if 'NaN' in source: 157 if 'NaN' in source:
146 return 1 158 return 1
147 else: 159 else:
148 return 0 160 return 0
149 else: 161 else:
150 # We're running a correctness test. Mark test as passing if all individual 162 # We're running a correctness test. Mark test as passing if all individual
151 # test cases pass. 163 # test cases pass.
152 if 'FAIL' not in source and 'PASS' in source: 164 if 'FAIL' not in source and 'PASS' in source:
153 print 'Content-Type: text/plain\nPASS' 165 print 'Content-Type: text/plain\nPASS'
154 return 0 166 return 0
155 else: 167 else:
156 #The hacky way to get document.getElementById('body').innerHTML for this 168 #The hacky way to get document.getElementById('body').innerHTML for this
157 # webpage, without the JavaScript. 169 # webpage, without the JavaScript.
158 #TODO(efortuna): Access these elements in a nicer way using DOM parser. 170 #TODO(efortuna): Access these elements in a nicer way using DOM parser.
159 index = source.find('<body>') 171 index = source.find('<body>')
160 index += len('<body>') 172 index += len('<body>')
161 end_index = source.find('<script') 173 end_index = source.find('<script')
162 print source[index : end_index] 174 print unicode(source[index : end_index]).encode("utf-8")
Emily Fortuna 2012/02/21 18:19:35 good catch here.
Jennifer Messerly 2012/02/21 18:58:43 Done.
163 return 1 175 return 1
164 176
177 def run_batch_tests():
Emily Fortuna 2012/02/21 18:19:35 Add some comments saying what's going on in this m
Jennifer Messerly 2012/02/21 18:58:43 Done.
178 print '>>> BATCH START'
179 browser = None
180 current_browser_name = None
181
182 # test.dart doesn't give us a chance to shut down gracefully, so handle
183 # SIGTERM instead. TODO(jmesserly): make this more robust
Emily Fortuna 2012/02/21 18:19:35 FYI: I've filed a feature request about this here:
Jennifer Messerly 2012/02/21 18:58:43 Done.
184 def sigterm(number, frame):
185 close_browser(browser)
186 signal.signal(signal.SIGTERM, sigterm)
187
188 try:
189 while True:
190 line = sys.stdin.readline()
191 if line == '--terminate\n':
192 break
193
194 html_out, browser_name, timeout, is_perf = parse_args(line.split())
195
196 # Sanity checks that test.dart is passing flags we can handle.
197 if is_perf:
198 print 'Batch test runner not compatible with perf testing'
199 return 1
200 if browser and current_browser_name != browser_name:
201 print('Batch test runner got multiple browsers: %s and %s'
202 % (current_browser_name, browser_name))
203 return 1
204
205 # Start the browser on the first run
206 if browser is None:
207 current_browser_name = browser_name
208 browser = start_browser(browser_name, html_out)
209
210 source = run_test_in_browser(browser, html_out, timeout, is_perf)
211
212 # print one of:
213 # >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}
214 status = report_results(is_perf, source)
215 if status == 0:
216 print '>>> TEST PASS'
Emily Fortuna 2012/02/21 18:19:35 I believe you need to print out the following "mag
Jennifer Messerly 2012/02/21 18:58:43 This is printed still by report_results. But I'm n
Emily Fortuna 2012/02/21 19:35:33 Oh, I see. Seems fine then.
217 elif source == TIMEOUT_ERROR_MSG:
218 print '>>> TEST TIMEOUT'
219 else:
220 print '>>> TEST FAIL'
221 sys.stdout.flush()
222 finally:
223 close_browser(browser)
224
225
226 def main(args):
227 # Run in batch mode if the --batch flag is passed.
228 # TODO(jmesserly): reconcile with the existing args parsing
229 if '--batch' in args:
230 return run_batch_tests()
231
232 # Run a single test
233 html_out, browser_name, timeout, is_perf = parse_args()
234 browser = start_browser(browser_name, html_out)
235
236 try:
237 output = run_test_in_browser(browser, html_out, timeout, is_perf)
238 return report_results(is_perf, output)
239 finally:
240 close_browser(browser)
165 241
166 if __name__ == "__main__": 242 if __name__ == "__main__":
167 sys.exit(Main()) 243 sys.exit(main(sys.argv))
OLDNEW
« tools/testing/dart/test_suite.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