[infra] Remove obsolete webdriver/selenium support from testing

After this removal, the only thing in tools/testing is the checked-in
jsshell (Mozilla command-line JavaScript runner) executable.  Discussions
for removing that as well have been started.

Change-Id: I28505154de9fa7df3a2d5f84f3f778c596f52fcc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106087
Reviewed-by: Bob Nystrom <rnystrom@google.com>
This commit is contained in:
William Hesse 2019-06-18 06:59:09 +00:00
parent 770675b668
commit 707d3ccd14
14 changed files with 0 additions and 1136 deletions

View file

@ -1,20 +0,0 @@
# 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.
import test_runner
import utils
# Constants used for test outcomes
SKIP = 'skip'
FAIL = 'fail'
PASS = 'pass'
OKAY = 'okay'
TIMEOUT = 'timeout'
CRASH = 'crash'
SLOW = 'slow'
HOST_CPUS = utils.GuessCpus()
USE_DEFAULT_CPUS = -1

View file

@ -1,29 +0,0 @@
Overview:
These are the instructions to run a wide variety of browser tests using
test.dart or dart/tools/testing/perf_testing/run_perf_tests.py. Currently
the results of run_perf_tests are uploaded to
https://dartperf.googleplex.com/.
========= General Browser Setup ==========
See instructions on:
https://code.google.com/p/dart/wiki/BrowserTestSetup
========= Proceed further only if you also want to run performance tests.======
1) Pull down benchmarks from internal repo (Google only): goto/dartbrowsersetup
2) Create a directory in called appengine-python in third_party. Download the
Linux/Other Platforms .zip file, and place the contents in the directory
you just created.
http://code.google.com/appengine/downloads.html#Google_App_Engine_SDK_for_Python
3) Run the tests! While standing in dart/tools/testing/perf_testing, run
$> python run_perf_tests.py --forever --verbose
to run all the tests (browser performance, language correctness in the
browser, command line performance, and self-hosted compile time and compiled
code size).
You can run individual tests by adding the particular option (such as
--language) when running create_graph.py. Type "create_graph.py -h" for a
full list of the options.

View file

@ -1,90 +0,0 @@
// Copyright (c) 2012, 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.
/**
* This is the background window. It can access the necessary APIs to get
* at the console messages. It can only communicate with the content
* window through message passing.
*
* There is no way to query the console messages, as such, but we can
* set up a handler that is called when there are console messages. This
* will be called with any console messages already present, so it can be set
* up after the fact. However, if there are no messages it won't be called.
* To handle the end of the messages (or no messages) we have to use a
* sentinel message that is logged by the content page.
*/
var version = "1.0";
var messages = []; // An array that we can put messages in.
var debuggeeId; // An object that identifies the browser tab we are talking to.
var callback; // For passing back the response to the content window.
var timer; // To time out if no messages are available.
/**
* When we have collected all the messages, we send them back to the
* content page via the callback, turn off message collection, and
* detach the debugger from the browser tab.
*/
function allDone() {
callback(messages);
chrome.debugger.sendCommand(debuggeeId, "Console.clearMessages", {},
function() {
chrome.debugger.sendCommand(debuggeeId, "Console.disable", {},
function() {});
chrome.debugger.detach(debuggeeId, function() {});
});
messages = [];
}
/**
* Debugger event handler. We only care about console.messageAdded
* events, in which case we add a new message object with the fields
* we care about to our messages array.
*/
function onEvent(debuggeeId, method, params) {
var tabId = debuggeeId.tabId;
if (method == "Console.messageAdded") {
var msg = params.message;
// More fields are available if we want them later. See
// https://developers.google.com/chrome-developer-tools/docs/protocol/1.0/console#type-ConsoleMessage
if (msg.text == 'getMessages/end') {
allDone();
} else {
messages.push({"source":msg.url, "line": msg.line,
"category":msg.source, "message":msg.text });
}
}
}
/**
* Handle requests sent by the content script. We save the callback,
* get the window and tab that is currently active, attach the
* debugger to that tab, and then turn on console message event
* handling, which will result in onEvent calls for each console
* message, including the ones that are already present in the console.
*/
function onRequest(request, sender, sendResponse) {
if (request.command == "getMessages") {
callback = sendResponse;
chrome.windows.getCurrent(function(win) {
chrome.tabs.getSelected(win.id, function(tab) {
debuggeeId = {tabId:tab.id};
chrome.debugger.attach(debuggeeId, version, function() {
if (chrome.extension.lastError) {
// Attach failed; send an empty response.
callback([]);
} else {
chrome.debugger.sendCommand(debuggeeId, "Console.enable", {},
function() {});
//timer = setTimeout(allDone, 1000);
}
});
});
});
}
}
// Set up the general handler for debug events.
chrome.debugger.onEvent.addListener(onEvent);
// Listen for the content script to send a message to the background page.
chrome.extension.onRequest.addListener(onRequest);

View file

@ -1,19 +0,0 @@
// Copyright (c) 2012, 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.
/**
* This is the content page script. This runs in the context of the browser
* page, and communicates with the background page by relaying a getMessages
* request, and the forwarding the messages back to the browser page as a
* gotMessages message.
*/
window.addEventListener("message", function(event) {
if (event.source == window && event.data == "getMessages") {
// Log a special sentinel message to mark the end of the messages.
console.log('getMessages/end');
chrome.extension.sendRequest({command: "getMessages"}, function(messages) {
window.postMessage({ "type": "gotMessages", "messages" : messages}, "*");
});
}
}, false);

View file

@ -1,22 +0,0 @@
{
"name": "Console Collector",
"version": "1.0",
"manifest_version": 2,
"description": "Allow querying of the Javascript console.",
"browser_action": {
"name": "ConsoleCollector"
},
"background": {
"scripts": ["background.js"],
"persistent": true
},
"content_scripts": [
{
"matches": ["http://*/*", "file://*" ],
"js": [ "content.js" ]
}
],
"permissions": [
"tabs", "http://*/*", "file://*", "debugger"
]
}

View file

@ -1,2 +0,0 @@
../ConsoleCollector.xpi: chrome.manifest install.rdf chrome/content/console.js chrome/content/overlay.xul
zip -r ../ConsoleCollector.xpi chrome.manifest install.rdf chrome/content/console.js chrome/content/overlay.xul

View file

@ -1,2 +0,0 @@
content ConsoleCollector chrome/content/
overlay chrome://browser/content/browser.xul chrome://ConsoleCollector/content/overlay.xul

View file

@ -1,104 +0,0 @@
// Copyright (c) 2012, 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.
// This Firefox add-on exposes the Javascript console contents to Javascript
// running in the browser. Once this is installed there will be a new
// window.ConsoleCollector object with read() and clear() functions.
var ConsoleCollector = {};
(function() {
// An array for collecting the messages.
var messages = [];
// Add a console message to the collection.
this.add = function(message) {
messages.push(message);
};
// Read the message collection. As a side effect we clear the message list.
this.read = function(type) {
var rtn = [];
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
if (message.errorMessage) {
rtn.push({ 'time' : message.timeStamp,
'source' : message.sourceName,
'line': message.lineNumber,
'column': message.columnNumber,
'category': message.category,
'message' : message.errorMessage });
}
}
messages = [];
return rtn;
};
// Clear the message list.
this.clear = function() {
messages = [];
};
}).apply(ConsoleCollector);
// A Console Listener.
// See https://developer.mozilla.org/en-US/docs/Console_service for
// details.
(function() {
var consoleService;
var consoleListener = {
observe: function(e) {
try {
var message = e.QueryInterface(Components.interfaces.nsIScriptError);
ConsoleCollector.add(message);
} catch (exception) {
ConsoleCollector.add(e);
}
},
QueryInterface: function (iid) {
if (!iid.equals(Components.interfaces.nsIConsoleListener) &&
!iid.equals(Components.interfaces.nsISupports)) {
throw Components.results.NS_ERROR_NO_INTERFACE;
}
return this;
}
};
// Start collecting console messages.
function initialize(event) {
consoleService = Components.classes['@mozilla.org/consoleservice;1']
.getService(Components.interfaces.nsIConsoleService);
if (consoleService) {
consoleService.registerListener(consoleListener);
}
// Add the handler for hooking in to each page's DOM. This handler
// is for each "gBrowser", representing a tab/window.
window.getBrowser().addEventListener("load", onPageLoad, true);
}
// Stop collecting console messages.
function shutdown(event) {
window.getBrowser().removeEventListener("load", onPageLoad);
consoleService.unregisterListener(consoleListener);
ConsoleCollector.clear();
}
// Hook the ConsoleCollector into the DOM as window.ConsoleCollector.
var onPageLoad = function(e) {
var win = e.originalTarget.defaultView;
if (win) {
win.wrappedJSObject.ConsoleCollector = ConsoleCollector;
}
};
// Add the handlers to initialize the add-on and shut it down.
// These handlers are for the application as a whole.
window.addEventListener('load', initialize, false);
window.addEventListener('unload', shutdown, false);
}());

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<overlay id="ConsoleCollector-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="console.js"/>
</overlay>

View file

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>ConsoleCollector@google.com</em:id>
<em:name>Console collector</em:name>
<em:version>0.2</em:version>
<em:description>Exposes the Javascript console to each browser window.
</em:description>
<em:creator>Graham Wheeler</em:creator>
<em:homepageURL>http://www.dartlang.org</em:homepageURL>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox -->
<em:minVersion>3.0</em:minVersion>
<em:maxVersion>15.0</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View file

@ -1,390 +0,0 @@
#!/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))

View file

@ -1,434 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2012, 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.
# Run to install the necessary components to run webdriver on the buildbots or
# on your local machine.
# Note: The setup steps can be done fairly easily by hand. This script is
# intended to simply and reduce the time for setup since there are a fair number
# of steps.
# TODO(efortuna): Rewrite this script in Dart when the Process module has a
# better high level API.
import HTMLParser
import optparse
import os
import platform
import re
import shutil
import string
import subprocess
import sys
import urllib
import urllib2
import zipfile
def run_cmd(cmd, stdin=None):
"""Run the command on the command line in the shell. We print the output of
the command.
"""
print cmd
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE, shell=True)
output, stderr = p.communicate(input=stdin)
if output:
print output
if stderr:
print stderr
def parse_args():
parser = optparse.OptionParser()
parser.add_option('--firefox', '-f', dest='firefox',
help="Don't install Firefox", action='store_true', default=False)
parser.add_option('--opera', '-o', dest='opera', default=False,
help="Don't install Opera", action='store_true')
parser.add_option('--chromedriver', '-c', dest='chromedriver',
help="Don't install chromedriver.", action='store_true', default=False)
parser.add_option('--iedriver', '-i', dest='iedriver',
help="Don't install iedriver (only used on Windows).",
action='store_true', default=False)
parser.add_option('--seleniumrc', '-s', dest='seleniumrc',
help="Don't install the Selenium RC server (used for Safari and Opera "
"tests).", action='store_true', default=False)
parser.add_option('--python', '-p', dest='python',
help="Don't install Selenium python bindings.", action='store_true',
default=False)
parser.add_option('--buildbot', '-b', dest='buildbot', action='store_true',
help='Perform a buildbot selenium setup (buildbots have a different' +
'location for their python executable).', default=False)
args, _ = parser.parse_args()
return args
def find_depot_tools_location(is_buildbot):
"""Depot_tools is our default install location for chromedriver, so we find
its location on the filesystem.
Arguments:
is_buildbot - True if we are running buildbot machine setup (we can't detect
this automatically because this script is not run at build time).
"""
if is_buildbot:
depot_tools = os.sep + os.path.join('b', 'depot_tools')
if 'win32' in sys.platform or 'cygwin' in sys.platform:
depot_tools = os.path.join('e:', depot_tools)
return depot_tools
else:
path = os.environ['PATH'].split(os.pathsep)
for loc in path:
if 'depot_tools' in loc:
return loc
raise Exception("Could not find depot_tools in your path.")
class GoogleBasedInstaller(object):
"""Install a project from a Google source, pulling latest version."""
def __init__(self, project_name, destination, download_path_func):
"""Create an object that will install the project.
Arguments:
project_name - Google code name of the project, such as "selenium" or
"chromedriver."
destination - Where to download the desired file on our filesystem.
download_path_func - A function that takes a dictionary (currently with keys
"os" and "version", but more can be added) that calculates the string
representing the path of the download we want.
"""
self.project_name = project_name
self.destination = destination
self.download_path_func = download_path_func
@property
def get_os_str(self):
"""The strings to indicate what OS a download is for."""
os_str = 'win'
if 'darwin' in sys.platform:
os_str = 'mac'
elif 'linux' in sys.platform:
os_str = 'linux32'
if '64bit' in platform.architecture()[0]:
os_str = 'linux64'
if self.project_name == 'chromedriver' and (
os_str == 'mac' or os_str == 'win'):
os_str += '32'
return os_str
def run(self):
"""Download and install the project."""
print 'Installing %s' % self.project_name
os_str = self.get_os_str
version = self.find_latest_version()
download_path = self.download_path_func({'os': os_str, 'version': version})
download_name = os.path.basename(download_path)
urllib.urlretrieve(os.path.join(self.source_path(), download_path),
os.path.join(self.destination, download_name))
if download_name.endswith('.zip'):
if platform.system() != 'Windows':
# The Python zip utility does not preserve executable permissions, but
# this does not seem to be a problem for Windows, which does not have a
# built in zip utility. :-/
run_cmd('unzip -u %s -d %s' % (os.path.join(self.destination,
download_name), self.destination), stdin='y')
else:
z = zipfile.ZipFile(os.path.join(self.destination, download_name))
z.extractall(self.destination)
z.close()
os.remove(os.path.join(self.destination, download_name))
chrome_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
'orig-chromedriver')
if self.project_name == 'chromedriver' and os.path.exists(chrome_path):
# We have one additional location to make sure chromedriver is updated.
# TODO(efortuna): Remove this. See move_chrome_driver_if_needed in
# perf_testing/run_perf_tests.py
driver = 'chromedriver'
if platform.system() == 'Windows':
driver += '.exe'
shutil.copy(os.path.join(self.destination, driver),
os.path.join(chrome_path, driver))
class ChromeDriverInstaller(GoogleBasedInstaller):
"""Install chromedriver from Google Storage."""
def __init__(self, destination):
"""Create an object to install ChromeDriver
destination - Where to download the desired file on our filesystem.
"""
super(ChromeDriverInstaller, self).__init__('chromedriver', destination,
lambda x: '%(version)s/chromedriver_%(os)s.zip' % x)
def find_latest_version(self):
"""Find the latest version number of ChromeDriver."""
source_page = urllib2.urlopen(self.source_path())
source_text = source_page.read()
regex = re.compile('(?:<Key>)(\d+\.\d+)')
latest = max(regex.findall(source_text))
return latest
def source_path(self):
return 'http://chromedriver.storage.googleapis.com'
class GoogleCodeInstaller(GoogleBasedInstaller):
"""Install a project from Google Code."""
def google_code_downloads_page(self):
return 'http://code.google.com/p/%s/downloads/list' % self.project_name
def find_latest_version(self):
"""Find the latest version number of some code available for download on a
Google code page. This was unfortunately done in an ad hoc manner because
Google Code does not seem to have an API for their list of current
downloads(!).
"""
google_code_site = self.google_code_downloads_page()
f = urllib2.urlopen(google_code_site)
latest = ''
download_regex_str = self.download_path_func({'os': self.get_os_str,
'version': '.+'})
for line in f.readlines():
if re.search(download_regex_str, line):
suffix_index = line.find(
download_regex_str[download_regex_str.rfind('.'):])
name_end = download_regex_str.rfind('.+')
name = self.download_path_func({'os': self.get_os_str, 'version': ''})
name = name[:name.rfind('.')]
version_str = line[line.find(name) + len(name) : suffix_index]
orig_version_str = version_str
if version_str.count('.') == 0:
version_str = version_str.replace('_', '.')
version_str = re.compile(r'[^\d.]+').sub('', version_str)
if latest == '':
latest = '0.' * version_str.count('.')
latest += '0'
orig_latest_str = latest
else:
orig_latest_str = latest
latest = latest.replace('_', '.')
latest = re.compile(r'[^\d.]+').sub('', latest)
nums = version_str.split('.')
latest_nums = latest.split('.')
for (num, latest_num) in zip(nums, latest_nums):
if int(num) > int(latest_num):
latest = orig_version_str
break
else:
latest = orig_latest_str
if latest == '':
raise Exception("Couldn't find the desired download on " + \
' %s.' % google_code_site)
return latest
def source_path(self):
return 'http://%s.googlecode.com/files/' % self.project_name
class FirefoxInstaller(object):
"""Installs the latest version of Firefox on the machine."""
def ff_download_site(self, os_name):
return 'http://releases.mozilla.org/pub/mozilla.org/firefox/releases/' + \
'latest/%s/en-US/' % os_name
@property
def get_os_str(self):
"""Returns the string that Mozilla uses to denote which operating system a
Firefox binary is for."""
os_str = ('win32', '.exe')
if 'darwin' in sys.platform:
os_str = ('mac', '.dmg')
elif 'linux' in sys.platform:
os_str = ('linux-i686', '.tar.bz2')
if '64bit' in platform.architecture()[0]:
os_str = ('linux-x86_64', '.tar.bz2')
return os_str
def get_download_url(self):
"""Parse the html on the page to determine what is the latest download
appropriate for our system."""
f = urllib2.urlopen(self.ff_download_site(self.get_os_str[0]))
download_name = ''
for line in f.readlines():
suffix = self.get_os_str[1]
if (suffix + '"') in line:
link_str = '<a href="'
download_name = line[line.find(link_str) + len(link_str) : \
line.find(suffix) + len(suffix)]
break
return '%s%s' % (self.ff_download_site(self.get_os_str[0]), download_name)
def run(self):
print 'Installing Firefox'
if 'darwin' in sys.platform:
urllib.urlretrieve(self.get_download_url(), 'firefox.dmg')
run_cmd('hdiutil mount firefox.dmg')
run_cmd('sudo cp -R /Volumes/firefox/Firefox.app /Applications')
run_cmd('hdiutil unmount /Volumes/firefox/')
elif 'win' in sys.platform:
urllib.urlretrieve(self.get_download_url(), 'firefox_install.exe')
run_cmd('firefox_install.exe -ms')
else:
run_cmd('wget -O - %s | tar -C ~ -jxv' % self.get_download_url())
class SeleniumBindingsInstaller(object):
"""Install the Selenium Webdriver bindings for Python."""
SETUPTOOLS_SITE = 'http://python-distribute.org/distribute_setup.py'
PIP_SITE = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py'
def __init__(self, is_buildbot):
self.is_buildbot = is_buildbot
def run(self):
print 'Installing Selenium Python Bindings'
admin_keyword = ''
python_cmd = 'python'
pip_cmd = 'pip'
if 'win32' not in sys.platform and 'cygwin' not in sys.platform:
admin_keyword = 'sudo'
pip_cmd = '/usr/local/bin/pip'
else:
# The python installation is "special" on Windows buildbots.
if self.is_buildbot:
python_loc = os.path.join(
find_depot_tools_location(self.is_buildbot), 'python_bin')
python_cmd = os.path.join(python_loc, 'python')
pip_cmd = os.path.join(python_loc, 'Scripts', pip_cmd)
else:
path = os.environ['PATH'].split(os.pathsep)
for loc in path:
if 'python' in loc or 'Python' in loc:
pip_cmd = os.path.join(loc, 'Scripts', pip_cmd)
break
page = urllib2.urlopen(self.SETUPTOOLS_SITE)
run_cmd('%s %s' % (admin_keyword, python_cmd), page.read())
page = urllib2.urlopen(self.PIP_SITE)
run_cmd('%s %s' % (admin_keyword, python_cmd), page.read())
run_cmd('%s %s install -U selenium' % (admin_keyword, pip_cmd))
class OperaHtmlParser(HTMLParser.HTMLParser):
"""A helper class to parse Opera pages listing available downloads to find the
correct download we want."""
def initialize(self, rejection_func, accept_func):
"""Initialize some state for our parser.
Arguments:
rejection_func: A function that accepts the value of the URL and determines
if it is of the type we are looking for.
accept_func: A function that takes the URL and the "current best" URL and
determines if it is better than our current download url."""
self.latest = 0
self.rejection_func = rejection_func
self.accept_func = accept_func
def handle_starttag(self, tag, attrs):
"""Find the latest version."""
if (tag == 'a' and attrs[0][0] == 'href' and
self.rejection_func(attrs[0][1])):
self.latest = self.accept_func(attrs[0][1], self.latest)
class OperaInstaller(object):
"""Install from the Opera FTP website."""
def find_latest_version(self, download_page, rejection_func, accept_func):
"""Get the latest non-beta version.
Arguments:
download_page: The initial page that lists all the download options.
rejection_func: A function that accepts the value of the URL and determines
if it is of the type we are looking for.
accept_func: A function that takes the URL and the "current best" URL and
determines if it is better than our current download url."""
f = urllib2.urlopen(download_page)
parser = OperaHtmlParser()
parser.initialize(rejection_func, accept_func)
parser.feed(f.read())
return str(parser.latest)
def run(self):
"""Download and install Opera."""
print 'Installing Opera'
os_str = self.get_os_str
download_name = 'http://ftp.opera.com/pub/opera/%s/' % os_str
def higher_revision(new_version_str, current):
version_string = new_version_str[:-1]
if int(version_string) > current:
return int(version_string)
return current
version = self.find_latest_version(
download_name,
lambda x: x[0] in string.digits and 'b' not in x and 'rc' not in x,
higher_revision)
download_name += version
if ('linux' in sys.platform and
platform.linux_distribution()[0] == 'Ubuntu'):
# Last time I tried, the .deb file you download directly from opera was
# not installing correctly on Ubuntu. This installs Opera more nicely.
os.system("sudo sh -c 'wget -O - http://deb.opera.com/archive.key | "
"apt-key add -'")
os.system("""sudo sh -c 'echo "deb http://deb.opera.com/opera/ """
"""stable non-free" > /etc/apt/sources.list.d/opera.list'""")
run_cmd('sudo apt-get update')
run_cmd('sudo apt-get install opera', stdin='y')
else:
if 'darwin' in sys.platform:
dotted_version = '%s.%s' % (version[:2], version[2:])
download_name += '/Opera_%s_Setup_Intel.dmg' % dotted_version
urllib.urlretrieve(download_name, 'opera.dmg')
run_cmd('hdiutil mount opera.dmg', stdin='qY\n')
run_cmd('sudo cp -R /Volumes/Opera/Opera.app /Applications')
run_cmd('hdiutil unmount /Volumes/Opera/')
elif 'win' in sys.platform:
download_name += '/en/Opera_%s_en_Setup.exe' % version
urllib.urlretrieve(download_name, 'opera_install.exe')
run_cmd('opera_install.exe -ms')
else:
# For all other flavors of linux, download the tar.
download_name += '/'
extension = '.tar.bz2'
if '64bit' in platform.architecture()[0]:
platform_str = '.x86_64'
else:
platform_str = '.i386'
def get_acceptable_file(new_version_str, current):
return new_version_str
latest = self.find_latest_version(
download_name,
lambda x: x.startswith('opera') and x.endswith(extension)
and platform_str in x,
get_acceptable_file)
download_name += latest
run_cmd('wget -O - %s | tar -C ~ -jxv' % download_name)
print ('PLEASE MANUALLY RUN "~/%s/install" TO COMPLETE OPERA '
'INSTALLATION' %
download_name[download_name.rfind('/') + 1:-len(extension)])
@property
def get_os_str(self):
"""The strings to indicate what OS a download is."""
os_str = 'win'
if 'darwin' in sys.platform:
os_str = 'mac'
elif 'linux' in sys.platform:
os_str = 'linux'
return os_str
def main():
args = parse_args()
if not args.python:
SeleniumBindingsInstaller(args.buildbot).run()
if not args.chromedriver:
ChromeDriverInstaller(find_depot_tools_location(args.buildbot)).run()
if not args.seleniumrc:
GoogleCodeInstaller('selenium', os.path.dirname(os.path.abspath(__file__)),
lambda x: 'selenium-server-standalone-%(version)s.jar' % x).run()
if not args.iedriver and platform.system() == 'Windows':
GoogleCodeInstaller('selenium', find_depot_tools_location(args.buildbot),
lambda x: 'IEDriverServer_Win32_%(version)s.zip' % x).run()
if not args.firefox:
FirefoxInstaller().run()
if not args.opera:
OperaInstaller().run()
if __name__ == '__main__':
main()