| OLD | NEW |
| 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. It uses Selenium WebDriver when possible for running the tests. It | 9 the result. It uses Selenium WebDriver when possible for running the tests. It |
| 10 uses Selenium RC for Safari. | 10 uses Selenium RC for Safari. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 stdin: --terminate | 26 stdin: --terminate |
| 27 $ | 27 $ |
| 28 """ | 28 """ |
| 29 | 29 |
| 30 import os | 30 import os |
| 31 import optparse | 31 import optparse |
| 32 import platform | 32 import platform |
| 33 import selenium | 33 import selenium |
| 34 from selenium.webdriver.support.ui import WebDriverWait | 34 from selenium.webdriver.support.ui import WebDriverWait |
| 35 import shutil | 35 import shutil |
| 36 import signal |
| 36 import socket | 37 import socket |
| 37 import sys | 38 import sys |
| 38 import time | 39 import time |
| 39 import signal | |
| 40 | 40 |
| 41 TIMEOUT_ERROR_MSG = 'FAIL (timeout)' | 41 TIMEOUT_ERROR_MSG = 'FAIL (timeout)' |
| 42 | 42 |
| 43 def perf_test_done(driver): | 43 def correctness_test_done(source): |
| 44 """Checks if the performance test has completed.""" | 44 """Checks if test has completed.""" |
| 45 return perf_test_done_helper(driver.page_source) | 45 return ('PASS' in source) or ('FAIL' in source) |
| 46 | 46 |
| 47 def perf_test_done_helper(source): | 47 def perf_test_done(source): |
| 48 """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.""" |
| 49 #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 |
| 50 # python webdriver bindings if you call driver.get_element_by_id. | 50 # python webdriver bindings if you call driver.get_element_by_id. |
| 51 #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. |
| 52 string = '<div id="status">' | 52 string = '<div id="status">' |
| 53 index = source.find(string) | 53 index = source.find(string) |
| 54 end_index = source.find('</div>', index+1) | 54 end_index = source.find('</div>', index+1) |
| 55 source = source[index + len(string):end_index] | 55 source = source[index + len(string):end_index] |
| 56 return 'Score:' in source | 56 return 'Score:' in source |
| 57 | 57 |
| 58 def run_test_in_browser(browser, html_out, timeout, is_perf): | 58 def dromaeo_test_done(source): |
| 59 """Tests to see if our performance test is done by printing a score.""" |
| 60 #TODO(efortuna): Access these elements in a nicer way using DOM parser. |
| 61 string = '<span class="left">' |
| 62 index = source.find(string) |
| 63 end_index = source.find('</span>', index+1) |
| 64 source = source[index + len(string):end_index] |
| 65 return '0:00' in source |
| 66 |
| 67 # TODO(vsm): Ideally, this wouldn't live in this file. |
| 68 CONFIGURATIONS = { |
| 69 'correctness': correctness_test_done, |
| 70 'perf': perf_test_done, |
| 71 'dromaeo': dromaeo_test_done |
| 72 } |
| 73 |
| 74 def run_test_in_browser(browser, html_out, timeout, config): |
| 59 """Run the desired test in the browser using Selenium 2.0 WebDriver syntax, | 75 """Run the desired test in the browser using Selenium 2.0 WebDriver syntax, |
| 60 and wait for the test to complete. This is the newer syntax, that currently | 76 and wait for the test to complete. This is the newer syntax, that currently |
| 61 supports Firefox, Chrome, IE, Opera (and some mobile browsers).""" | 77 supports Firefox, Chrome, IE, Opera (and some mobile browsers).""" |
| 62 if isinstance(browser, selenium.selenium): | 78 if isinstance(browser, selenium.selenium): |
| 63 return run_test_in_browser_selenium_rc(browser, html_out, timeout, is_perf) | 79 return run_test_in_browser_selenium_rc(browser, html_out, timeout, config) |
| 64 | 80 |
| 65 browser.get("file://" + html_out) | 81 browser.get("file://" + html_out) |
| 66 source = '' | 82 source = '' |
| 67 try: | 83 try: |
| 68 if is_perf: | 84 test_done = CONFIGURATIONS[config] |
| 69 # We're running a performance test. | 85 element = WebDriverWait(browser, float(timeout)).until( |
| 70 element = WebDriverWait(browser, float(timeout)).until(perf_test_done) | 86 lambda driver: test_done(driver.page_source)) |
| 71 else: | |
| 72 element = WebDriverWait(browser, float(timeout)).until( | |
| 73 lambda driver : ('PASS' in driver.page_source) or | |
| 74 ('FAIL' in driver.page_source)) | |
| 75 source = browser.page_source | 87 source = browser.page_source |
| 76 except selenium.common.exceptions.TimeoutException: | 88 except selenium.common.exceptions.TimeoutException: |
| 77 source = TIMEOUT_ERROR_MSG | 89 source = TIMEOUT_ERROR_MSG |
| 78 return source | 90 return source |
| 79 | 91 |
| 80 def run_test_in_browser_selenium_rc(sel, html_out, timeout, is_perf): | 92 def run_test_in_browser_selenium_rc(sel, html_out, timeout, config): |
| 81 """ Run the desired test in the browser using Selenium 1.0 syntax, and wait | 93 """ Run the desired test in the browser using Selenium 1.0 syntax, and wait |
| 82 for the test to complete. This is used for Safari, since it is not currently | 94 for the test to complete. This is used for Safari, since it is not currently |
| 83 supported on Selenium 2.0.""" | 95 supported on Selenium 2.0.""" |
| 84 sel.open('file://' + html_out) | 96 sel.open('file://' + html_out) |
| 85 source = sel.get_html_source() | 97 source = sel.get_html_source() |
| 86 def end_condition(source): | 98 end_condition = CONFIGURATIONS[config] |
| 87 return 'PASS' in source or 'FAIL' in source | |
| 88 if is_perf: | |
| 89 end_condition = perf_test_done_helper | |
| 90 | 99 |
| 91 elapsed = 0 | 100 elapsed = 0 |
| 92 while (not end_condition(source)) and elapsed <= timeout: | 101 while (not end_condition(source)) and elapsed <= timeout: |
| 93 sec = .25 | 102 sec = .25 |
| 94 time.sleep(sec) | 103 time.sleep(sec) |
| 95 elapsed += sec | 104 elapsed += sec |
| 96 source = sel.get_html_source() | 105 source = sel.get_html_source() |
| 97 return source | 106 return source |
| 98 | 107 |
| 99 def parse_args(args=None): | 108 def parse_args(args=None): |
| 100 parser = optparse.OptionParser() | 109 parser = optparse.OptionParser() |
| 101 parser.add_option('--out', dest='out', | 110 parser.add_option('--out', dest='out', |
| 102 help = 'The path for html output file that we will running our test from', | 111 help = 'The path for html output file that we will running our test from', |
| 103 action = 'store', default = '') | 112 action = 'store', default = '') |
| 104 parser.add_option('--browser', dest='browser', | 113 parser.add_option('--browser', dest='browser', |
| 105 help = 'The browser type (default = chrome)', | 114 help = 'The browser type (default = chrome)', |
| 106 action = 'store', default = 'chrome') | 115 action = 'store', default = 'chrome') |
| 107 # TODO(efortuna): Put this back up to be more than the default timeout in | 116 # TODO(efortuna): Put this back up to be more than the default timeout in |
| 108 # test.dart. Right now it needs to be less than 60 so that when test.dart | 117 # test.dart. Right now it needs to be less than 60 so that when test.dart |
| 109 # times out, this script also closes the browser windows. | 118 # times out, this script also closes the browser windows. |
| 110 parser.add_option('--timeout', dest = 'timeout', | 119 parser.add_option('--timeout', dest = 'timeout', |
| 111 help = 'Amount of time (seconds) to wait before timeout', type = 'int', | 120 help = 'Amount of time (seconds) to wait before timeout', type = 'int', |
| 112 action = 'store', default=58) | 121 action = 'store', default=58) |
| 113 parser.add_option('--perf', dest = 'is_perf', | 122 parser.add_option('--perf', dest = 'is_perf', |
| 114 help = 'Add this flag if we are running a browser performance test', | 123 help = 'Add this flag if we are running a browser performance test', |
| 115 action = 'store_true', default=False) | 124 action = 'store_true', default=False) |
| 125 # TODO(vsm): Abstract this out better. |
| 126 parser.add_option('--dromaeo', dest = 'is_dromaeo', |
| 127 help = 'Add this flag if we are running a browser performance test', |
| 128 action = 'store_true', default=False) |
| 116 args, ignored = parser.parse_args(args=args) | 129 args, ignored = parser.parse_args(args=args) |
| 117 return args.out, args.browser, args.timeout, args.is_perf | 130 if args.is_perf: |
| 131 config = 'perf' |
| 132 elif args.is_dromaeo: |
| 133 config = 'dromaeo' |
| 134 else: |
| 135 config = 'correctness' |
| 136 return args.out, args.browser, args.timeout, config |
| 118 | 137 |
| 119 def start_browser(browser, html_out): | 138 def start_browser(browser, html_out): |
| 120 if browser == 'chrome': | 139 if browser == 'chrome': |
| 121 # Note: you need ChromeDriver *in your path* to run Chrome, in addition to | 140 # 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 | 141 # installing Chrome. Also note that the build bot runs have a different path |
| 123 # from a normal user -- check the build logs. | 142 # from a normal user -- check the build logs. |
| 124 return selenium.webdriver.Chrome() | 143 return selenium.webdriver.Chrome() |
| 125 elif browser == 'ff': | 144 elif browser == 'ff': |
| 126 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile() | 145 profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile() |
| 127 profile.set_preference('dom.max_script_run_time', 0) | 146 profile.set_preference('dom.max_script_run_time', 0) |
| (...skipping 30 matching lines...) Expand all Loading... |
| 158 | 177 |
| 159 # A timeout exception is thrown if nothing happens within the time limit. | 178 # A timeout exception is thrown if nothing happens within the time limit. |
| 160 if browser != 'chrome': | 179 if browser != 'chrome': |
| 161 browser.close() | 180 browser.close() |
| 162 try: | 181 try: |
| 163 browser.quit() | 182 browser.quit() |
| 164 except selenium.common.exceptions.WebDriverException: | 183 except selenium.common.exceptions.WebDriverException: |
| 165 # TODO(efortuna): Figure out why this crashes.... and avoid? | 184 # TODO(efortuna): Figure out why this crashes.... and avoid? |
| 166 pass | 185 pass |
| 167 | 186 |
| 168 def report_results(is_perf, source): | 187 def report_results(config, source): |
| 169 if is_perf: | 188 # TODO(vsm): Add a failure check for Dromaeo. |
| 189 if config != 'correctness': |
| 170 # We're running a performance test. | 190 # We're running a performance test. |
| 171 print source | 191 print source.encode('utf8') |
| 192 sys.stdout.flush() |
| 172 if 'NaN' in source: | 193 if 'NaN' in source: |
| 173 return 1 | 194 return 1 |
| 174 else: | 195 else: |
| 175 return 0 | 196 return 0 |
| 176 else: | 197 else: |
| 177 # We're running a correctness test. Mark test as passing if all individual | 198 # We're running a correctness test. Mark test as passing if all individual |
| 178 # test cases pass. | 199 # test cases pass. |
| 179 if 'FAIL' not in source and 'PASS' in source: | 200 if 'FAIL' not in source and 'PASS' in source: |
| 180 print 'Content-Type: text/plain\nPASS' | 201 print 'Content-Type: text/plain\nPASS' |
| 181 return 0 | 202 return 0 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 # TODO(jmesserly): It'd be nice to shutdown gracefully in the event of a | 238 # 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. | 239 # SIGTERM. Unfortunately dart:io cannot send SIGTERM, see dartbug.com/1756. |
| 219 signal.signal(signal.SIGTERM, lambda number, frame: close_browser(browser)) | 240 signal.signal(signal.SIGTERM, lambda number, frame: close_browser(browser)) |
| 220 | 241 |
| 221 try: | 242 try: |
| 222 while True: | 243 while True: |
| 223 line = sys.stdin.readline() | 244 line = sys.stdin.readline() |
| 224 if line == '--terminate\n': | 245 if line == '--terminate\n': |
| 225 break | 246 break |
| 226 | 247 |
| 227 html_out, browser_name, timeout, is_perf = parse_args(line.split()) | 248 html_out, browser_name, timeout, config = parse_args(line.split()) |
| 228 | 249 |
| 229 # Sanity checks that test.dart is passing flags we can handle. | 250 # Sanity checks that test.dart is passing flags we can handle. |
| 230 if is_perf: | 251 if config != 'correctness': |
| 231 print 'Batch test runner not compatible with perf testing' | 252 print 'Batch test runner not compatible with perf testing' |
| 232 return 1 | 253 return 1 |
| 233 if browser and current_browser_name != browser_name: | 254 if browser and current_browser_name != browser_name: |
| 234 print('Batch test runner got multiple browsers: %s and %s' | 255 print('Batch test runner got multiple browsers: %s and %s' |
| 235 % (current_browser_name, browser_name)) | 256 % (current_browser_name, browser_name)) |
| 236 return 1 | 257 return 1 |
| 237 | 258 |
| 238 # Start the browser on the first run | 259 # Start the browser on the first run |
| 239 if browser is None: | 260 if browser is None: |
| 240 current_browser_name = browser_name | 261 current_browser_name = browser_name |
| 241 browser = start_browser(browser_name, html_out) | 262 browser = start_browser(browser_name, html_out) |
| 242 | 263 |
| 243 source = run_test_in_browser(browser, html_out, timeout, is_perf) | 264 source = run_test_in_browser(browser, html_out, timeout, config) |
| 244 | 265 |
| 245 # Test is done. Write end token to stderr and flush. | 266 # Test is done. Write end token to stderr and flush. |
| 246 sys.stderr.write('>>> EOF STDERR\n') | 267 sys.stderr.write('>>> EOF STDERR\n') |
| 247 sys.stderr.flush() | 268 sys.stderr.flush() |
| 248 | 269 |
| 249 # print one of: | 270 # print one of: |
| 250 # >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT} | 271 # >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT} |
| 251 status = report_results(is_perf, source) | 272 status = report_results(config, source) |
| 252 if status == 0: | 273 if status == 0: |
| 253 print '>>> TEST PASS' | 274 print '>>> TEST PASS' |
| 254 elif source == TIMEOUT_ERROR_MSG: | 275 elif source == TIMEOUT_ERROR_MSG: |
| 255 print '>>> TEST TIMEOUT' | 276 print '>>> TEST TIMEOUT' |
| 256 else: | 277 else: |
| 257 print '>>> TEST FAIL' | 278 print '>>> TEST FAIL' |
| 258 sys.stdout.flush() | 279 sys.stdout.flush() |
| 259 finally: | 280 finally: |
| 260 close_browser(browser) | 281 close_browser(browser) |
| 261 | 282 |
| 262 | 283 |
| 263 def main(args): | 284 def main(args): |
| 264 # Run in batch mode if the --batch flag is passed. | 285 # Run in batch mode if the --batch flag is passed. |
| 265 # TODO(jmesserly): reconcile with the existing args parsing | 286 # TODO(jmesserly): reconcile with the existing args parsing |
| 266 if '--batch' in args: | 287 if '--batch' in args: |
| 267 return run_batch_tests() | 288 return run_batch_tests() |
| 268 | 289 |
| 269 # Run a single test | 290 # Run a single test |
| 270 html_out, browser_name, timeout, is_perf = parse_args() | 291 html_out, browser_name, timeout, config = parse_args() |
| 271 browser = start_browser(browser_name, html_out) | 292 browser = start_browser(browser_name, html_out) |
| 272 | 293 |
| 273 try: | 294 try: |
| 274 output = run_test_in_browser(browser, html_out, timeout, is_perf) | 295 output = run_test_in_browser(browser, html_out, timeout, config) |
| 275 return report_results(is_perf, output) | 296 return report_results(config, output) |
| 276 finally: | 297 finally: |
| 277 close_browser(browser) | 298 close_browser(browser) |
| 278 | 299 |
| 279 if __name__ == "__main__": | 300 if __name__ == "__main__": |
| 280 sys.exit(main(sys.argv)) | 301 sys.exit(main(sys.argv)) |
| OLD | NEW |