mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:37:43 +00:00
b33dcfdaad
No one was maintaining or using these, and they were stale to the point that they were breaking pkgbuild tests because their dependencies weren't compatible with the latest SDK. This also removes tools/testing/perf_testing, which only tested these two samples. R=kevmoo@google.com Review URL: https://codereview.chromium.org/1576153002 .
391 lines
14 KiB
Python
Executable file
391 lines
14 KiB
Python
Executable file
#!/usr/bin/python
|
|
|
|
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
#
|
|
|
|
"""Script to actually open a browser and perform the test, and reports back with
|
|
the result. It uses Selenium WebDriver when possible for running the tests. It
|
|
uses Selenium RC for Safari.
|
|
|
|
If started with --batch this script runs a batch of in-browser tests in
|
|
the same browser process.
|
|
|
|
Normal mode:
|
|
$ python run_selenium.py --browser=ff --timeout=60 path/to/test.html
|
|
|
|
Exit code indicates pass or fail
|
|
|
|
Batch mode:
|
|
$ python run_selenium.py --batch
|
|
stdin: --browser=ff --timeout=60 path/to/test.html
|
|
stdout: >>> TEST PASS
|
|
stdin: --browser=ff --timeout=60 path/to/test2.html
|
|
stdout: >>> TEST FAIL
|
|
stdin: --terminate
|
|
$
|
|
"""
|
|
|
|
import os
|
|
import optparse
|
|
import platform
|
|
import selenium
|
|
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
|
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
import shutil
|
|
import signal
|
|
import socket
|
|
import sys
|
|
import time
|
|
import urllib2
|
|
import threading
|
|
|
|
TIMEOUT_ERROR_MSG = 'FAIL (timeout)'
|
|
CRASH_ERROR_MSG = 'CRASH'
|
|
|
|
def correctness_test_done(source):
|
|
"""Checks if test has completed."""
|
|
return ('PASS' in source) or ('FAIL' in source)
|
|
|
|
def perf_test_done(source):
|
|
"""Tests to see if our performance test is done by printing a score."""
|
|
#This code is written this way to work around a current instability in the
|
|
# python webdriver bindings if you call driver.get_element_by_id.
|
|
#TODO(efortuna): Access these elements in a nicer way using DOM parser.
|
|
string = '<div id="status">'
|
|
index = source.find(string)
|
|
end_index = source.find('</div>', index+1)
|
|
source = source[index + len(string):end_index]
|
|
return 'Score:' in source
|
|
|
|
# TODO(vsm): Ideally, this wouldn't live in this file.
|
|
CONFIGURATIONS = {
|
|
'correctness': correctness_test_done,
|
|
'perf': perf_test_done
|
|
}
|
|
|
|
def run_test_in_browser(browser, html_out, timeout, mode, refresh):
|
|
"""Run the desired test in the browser using Selenium 2.0 WebDriver syntax,
|
|
and wait for the test to complete. This is the newer syntax, that currently
|
|
supports Firefox, Chrome, IE, Opera (and some mobile browsers)."""
|
|
|
|
if isinstance(browser, selenium.selenium):
|
|
return run_test_in_browser_selenium_rc(browser, html_out, timeout, mode,
|
|
refresh)
|
|
|
|
browser.get(html_out)
|
|
if refresh:
|
|
browser.refresh()
|
|
try:
|
|
def pythonTimeout():
|
|
# The builtin quit call for chrome will call close on the RemoteDriver
|
|
# which may hang. Explicitly call browser.service.stop()
|
|
if (type(browser) is selenium.webdriver.chrome.webdriver.WebDriver):
|
|
# Browser may be dead
|
|
try:
|
|
browser.service.stop()
|
|
except:
|
|
print("Trying to close browser that has already been closed")
|
|
# If the browser is crashing selenium may not time out.
|
|
# Explicitly catch this case with a python timer.
|
|
t = threading.Timer(timeout, pythonTimeout)
|
|
t.start()
|
|
test_done = CONFIGURATIONS[mode]
|
|
element = WebDriverWait(browser, float(timeout)).until(
|
|
lambda driver: test_done(driver.page_source))
|
|
t.cancel()
|
|
return browser.page_source
|
|
except selenium.common.exceptions.TimeoutException:
|
|
return TIMEOUT_ERROR_MSG
|
|
except:
|
|
return CRASH_ERROR_MSG
|
|
|
|
def run_test_in_browser_selenium_rc(sel, html_out, timeout, mode, refresh):
|
|
""" Run the desired test in the browser using Selenium 1.0 syntax, and wait
|
|
for the test to complete. This is used for Safari, since it is not currently
|
|
supported on Selenium 2.0."""
|
|
sel.open(html_out)
|
|
if refresh:
|
|
sel.refresh()
|
|
source = sel.get_html_source()
|
|
end_condition = CONFIGURATIONS[mode]
|
|
|
|
elapsed = 0
|
|
while (not end_condition(source)) and elapsed <= timeout:
|
|
sec = .25
|
|
time.sleep(sec)
|
|
elapsed += sec
|
|
source = sel.get_html_source()
|
|
return source
|
|
|
|
def parse_args(args=None):
|
|
parser = optparse.OptionParser()
|
|
parser.add_option('--out', dest='out',
|
|
help = 'The path for html output file that we will running our test from',
|
|
action = 'store', default = '')
|
|
parser.add_option('--browser', dest='browser',
|
|
help = 'The browser type (default = chrome)',
|
|
action = 'store', default = 'chrome')
|
|
parser.add_option('--executable', dest='executable',
|
|
help = 'The browser executable path (only for browser=dartium)',
|
|
action = 'store', default = None)
|
|
# TODO(efortuna): Put this back up to be more than the default timeout in
|
|
# test.dart. Right now it needs to be less than 60 so that when test.dart
|
|
# times out, this script also closes the browser windows.
|
|
parser.add_option('--timeout', dest = 'timeout',
|
|
help = 'Amount of time (seconds) to wait before timeout', type = 'int',
|
|
action = 'store', default=58)
|
|
parser.add_option('--mode', dest = 'mode',
|
|
help = 'The type of test we are running',
|
|
action = 'store', default='correctness')
|
|
parser.add_option('--force-refresh', dest='refresh',
|
|
help='Force the browser to refresh before getting results from this test '
|
|
'(used for browser multitests).', action='store_true', default=False)
|
|
args, _ = parser.parse_args(args=args)
|
|
args.out = args.out.strip('"')
|
|
if args.executable and args.browser != 'dartium':
|
|
print 'Executable path only supported when browser=dartium.'
|
|
sys.exit(1)
|
|
return (args.out, args.browser, args.executable, args.timeout, args.mode,
|
|
args.refresh)
|
|
|
|
def print_server_error():
|
|
"""Provide the user an informative error message if we attempt to connect to
|
|
the Selenium remote control server, but cannot access it. Then exit the
|
|
program."""
|
|
print ('ERROR: Could not connect to Selenium RC server. Are you running'
|
|
' java -jar tools/testing/selenium-server-standalone-*.jar? If not, '
|
|
'start it before running this test.')
|
|
sys.exit(1)
|
|
|
|
def start_browser(browser, executable_path, html_out):
|
|
if browser == 'chrome' or browser == 'dartium':
|
|
# Note: you need ChromeDriver *in your path* to run Chrome, in addition to
|
|
# installing Chrome. Also note that the build bot runs have a different path
|
|
# from a normal user -- check the build logs.
|
|
options = selenium.webdriver.chrome.options.Options()
|
|
if browser == 'dartium':
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
dartium_dir = os.path.join(script_dir, '..', '..', 'client', 'tests',
|
|
'dartium')
|
|
# enable ShadowDOM and style scoped for Dartium
|
|
options.add_argument('--enable-shadow-dom')
|
|
options.add_argument('--enable-style-scoped')
|
|
if executable_path is not None:
|
|
options.binary_location = executable_path
|
|
elif platform.system() == 'Windows':
|
|
options.binary_location = os.path.join(dartium_dir, 'chrome.exe')
|
|
elif platform.system() == 'Darwin':
|
|
options.binary_location = os.path.join(dartium_dir, 'Chromium.app',
|
|
'Contents', 'MacOS', 'Chromium')
|
|
else:
|
|
options.binary_location = os.path.join(dartium_dir, 'chrome')
|
|
return selenium.webdriver.Chrome(chrome_options=options)
|
|
elif browser == 'ff':
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
profile = selenium.webdriver.firefox.firefox_profile.FirefoxProfile()
|
|
profile.set_preference('dom.max_script_run_time', 0)
|
|
profile.set_preference('dom.max_chrome_script_run_time', 0)
|
|
profile.set_preference('app.update.auto', True)
|
|
profile.set_preference('app.update.enabled', True)
|
|
return selenium.webdriver.Firefox(firefox_profile=profile)
|
|
elif ((browser == 'ie9' or browser == 'ie10') and
|
|
platform.system() == 'Windows'):
|
|
return selenium.webdriver.Ie()
|
|
elif browser == 'safari' and platform.system() == 'Darwin':
|
|
# TODO(efortuna): Ensure our preferences (no pop-up blocking) file is the
|
|
# same (Safari auto-deletes when it has too many "crashes," or in our case,
|
|
# timeouts). Come up with a less hacky way to do this.
|
|
backup_safari_prefs = os.path.dirname(__file__) + '/com.apple.Safari.plist'
|
|
if os.path.exists(backup_safari_prefs):
|
|
shutil.copy(backup_safari_prefs,
|
|
'/Library/Preferences/com.apple.Safari.plist')
|
|
sel = selenium.selenium('localhost', 4444, "*safari", html_out)
|
|
try:
|
|
sel.start()
|
|
return sel
|
|
except socket.error:
|
|
print_server_error()
|
|
elif browser == 'opera':
|
|
try:
|
|
driver = RemoteWebDriver(desired_capabilities=DesiredCapabilities.OPERA)
|
|
# By default, Opera sets their script timeout (the amount of time they
|
|
# expect to hear back from the JavaScript file) to be 10 seconds. We just
|
|
# make it an impossibly large number so that it doesn't time out for this
|
|
# reason, so it behaves like all of the other browser drivers.
|
|
driver.set_script_timeout(9000)
|
|
# If the webpage contains document.onreadystatechanged = function() {...}
|
|
# page load event does not correctly get fired and caught (OperaDriver
|
|
# bug). This is a band-aid.
|
|
driver.set_page_load_timeout(1)
|
|
return driver
|
|
except urllib2.URLError:
|
|
print_server_error()
|
|
else:
|
|
raise Exception('Incompatible browser and platform combination.')
|
|
|
|
def close_browser(browser):
|
|
if browser is None:
|
|
return
|
|
if isinstance(browser, selenium.selenium):
|
|
browser.stop()
|
|
return
|
|
|
|
# A timeout exception is thrown if nothing happens within the time limit.
|
|
if (type(browser) is not selenium.webdriver.chrome.webdriver.WebDriver and
|
|
type(browser) is not selenium.webdriver.ie.webdriver.WebDriver):
|
|
browser.close()
|
|
|
|
browser.quit()
|
|
|
|
def report_results(mode, source, browser):
|
|
if mode != 'correctness':
|
|
# We're running a performance test.
|
|
print source.encode('utf8')
|
|
sys.stdout.flush()
|
|
if 'NaN' in source:
|
|
return 1
|
|
else:
|
|
return 0
|
|
else:
|
|
# We're running a correctness test. Mark test as passing if all individual
|
|
# test cases pass.
|
|
if 'FAIL' not in source and 'PASS' in source:
|
|
print 'Content-Type: text/plain\nPASS'
|
|
return 0
|
|
else:
|
|
#The hacky way to get document.getElementById('body').innerHTML for this
|
|
# webpage, without the JavaScript.
|
|
#TODO(efortuna): Access these elements in a nicer way using DOM parser.
|
|
index = source.find('<body>')
|
|
index += len('<body>')
|
|
end_index = source.find('</body')
|
|
print unicode(source[index : end_index]).encode("utf-8")
|
|
return 1
|
|
|
|
|
|
def run_batch_tests():
|
|
'''
|
|
Runs a batch of in-browser tests in the same browser process. Batching
|
|
gives faster throughput and makes tests less subject to browser starting
|
|
flakiness, issues with too many browser processes running, etc.
|
|
|
|
When running this function, stdin/stdout is used to communicate with the test
|
|
framework. See BatchRunnerProcess in test_runner.dart for the other side of
|
|
this communication channel
|
|
|
|
Example of usage:
|
|
$ python run_selenium.py --batch
|
|
stdin: --browser=ff --timeout=60 path/to/test.html
|
|
stdout: >>> TEST PASS
|
|
stdin: --browser=ff --timeout=60 path/to/test2.html
|
|
stdout: >>> TEST FAIL
|
|
stdin: --terminate
|
|
$
|
|
'''
|
|
|
|
print '>>> BATCH START'
|
|
browser = None
|
|
current_browser_name = None
|
|
|
|
# TODO(jmesserly): It'd be nice to shutdown gracefully in the event of a
|
|
# SIGTERM. Unfortunately dart:io cannot send SIGTERM, see dartbug.com/1756.
|
|
signal.signal(signal.SIGTERM, lambda number, frame: close_browser(browser))
|
|
|
|
try:
|
|
try:
|
|
while True:
|
|
line = sys.stdin.readline()
|
|
if line == '--terminate\n':
|
|
print("Terminating selenium driver")
|
|
break
|
|
|
|
(html_out, browser_name, executable_path,
|
|
timeout, mode, refresh) = parse_args(line.split())
|
|
|
|
# Sanity checks that test.dart is passing flags we can handle.
|
|
if mode != 'correctness':
|
|
print 'Batch test runner not compatible with perf testing'
|
|
return 1
|
|
if browser and current_browser_name != browser_name:
|
|
print('Batch test runner got multiple browsers: %s and %s'
|
|
% (current_browser_name, browser_name))
|
|
return 1
|
|
|
|
# Start the browser on the first run
|
|
if browser is None:
|
|
current_browser_name = browser_name
|
|
browser = start_browser(browser_name, executable_path, html_out)
|
|
|
|
source = run_test_in_browser(browser, html_out, timeout, mode, refresh)
|
|
|
|
# Test is done. Write end token to stderr and flush.
|
|
sys.stderr.write('>>> EOF STDERR\n')
|
|
sys.stderr.flush()
|
|
|
|
# print one of:
|
|
# >>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}
|
|
status = report_results(mode, source, browser)
|
|
if status == 0:
|
|
print '>>> TEST PASS'
|
|
elif source == TIMEOUT_ERROR_MSG:
|
|
print '>>> TEST TIMEOUT'
|
|
elif source == CRASH_ERROR_MSG:
|
|
print '>>> TEST CRASH'
|
|
# The browser crashed, set the browser to None so that we will
|
|
# create a new instance on next iteration.
|
|
browser = None
|
|
else:
|
|
print '>>> TEST FAIL'
|
|
sys.stdout.flush()
|
|
except:
|
|
type, value, traceback = sys.exc_info()
|
|
print "run_selenium.py: Unexpected exception occured: "
|
|
print " type: ", type
|
|
print " value: ", value
|
|
print " traceback: ", traceback
|
|
raise
|
|
finally:
|
|
sys.stdin.close()
|
|
print("Closing browser");
|
|
|
|
def close_output_streams():
|
|
sys.stdout.flush()
|
|
sys.stdout.close()
|
|
sys.stderr.flush()
|
|
sys.stderr.close()
|
|
|
|
def close_and_exit():
|
|
print("Timed out waiting for browser to close")
|
|
close_output_streams()
|
|
exit(1)
|
|
|
|
timer = threading.Timer(5.0, close_and_exit)
|
|
timer.start()
|
|
try:
|
|
close_browser(browser)
|
|
timer.cancel()
|
|
finally:
|
|
close_output_streams()
|
|
|
|
def main(args):
|
|
# Run in batch mode if the --batch flag is passed.
|
|
# TODO(jmesserly): reconcile with the existing args parsing
|
|
if '--batch' in args:
|
|
return run_batch_tests()
|
|
|
|
# Run a single test
|
|
html_out, browser_name, executable_path, timeout, mode, refresh = parse_args()
|
|
browser = start_browser(browser_name, executable_path, html_out)
|
|
|
|
try:
|
|
output = run_test_in_browser(browser, html_out, timeout, mode, refresh)
|
|
return report_results(mode, output, browser)
|
|
finally:
|
|
close_browser(browser)
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|