Commit e8a2f088 authored by John Chen's avatar John Chen Committed by Commit Bot

[ChromeDriver] Remove waterfall related code

Clean up code that has become obsolete due to decomission of
ChromeDriver waterfall.

Change-Id: Ifbdb43872111ec3e575ec6e4aa1b28b76b663756
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1509095
Commit-Queue: John Chen <johnchen@chromium.org>
Reviewed-by: default avatarCaleb Rouleau <crouleau@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638687}
parent f1181050
This file contains high-level info about how ChromeDriver works and how to This file contains high-level info about how ChromeDriver works and how to
contribute. contribute.
ChromeDriver is an implementation of the WebDriver standard, ChromeDriver is an implementation of the WebDriver standard
which allows users to automate testing of their website across browsers. (https://w3c.github.io/webdriver/), which allows users to automate testing of
their website across browsers.
See the user site at https://sites.google.com/a/chromium.org/chromedriver/ See the user site at https://sites.google.com/a/chromium.org/chromedriver/
...@@ -11,7 +12,10 @@ Build ChromeDriver by building the 'chromedriver' target. This will ...@@ -11,7 +12,10 @@ Build ChromeDriver by building the 'chromedriver' target. This will
create an executable binary in the build folder named create an executable binary in the build folder named
'chromedriver[.exe]'. 'chromedriver[.exe]'.
Once built, ChromeDriver can be used interactively with python. Once built, ChromeDriver can be used with various third-party libraries that
support WebDriver protocol, including language bindings provided by Selenium.
For testing purposes, ChromeDriver can be used interactively with python.
$ export PYTHONPATH=<THIS_DIR>/server:<THIS_DIR>/client $ export PYTHONPATH=<THIS_DIR>/server:<THIS_DIR>/client
$ python $ python
...@@ -25,7 +29,7 @@ $ python ...@@ -25,7 +29,7 @@ $ python
ChromeDriver will use the system installed Chrome by default. ChromeDriver will use the system installed Chrome by default.
To use ChromeDriver2 with Chrome on Android pass the Android package name in the To use ChromeDriver with Chrome on Android pass the Android package name in the
chromeOptions.androidPackage capability when creating the driver. The path to chromeOptions.androidPackage capability when creating the driver. The path to
adb_commands.py and the adb tool from the Android SDK must be set in PATH. For adb_commands.py and the adb tool from the Android SDK must be set in PATH. For
more detailed instructions see the user site. more detailed instructions see the user site.
...@@ -79,30 +83,25 @@ Integration tests. ...@@ -79,30 +83,25 @@ Integration tests.
Third party libraries used by chromedriver. Third party libraries used by chromedriver.
=====Testing===== =====Testing=====
See the ChromeDriver waterfall at: There are several test suites for verifying ChromeDriver's correctness:
http://build.chromium.org/p/chromium.chromedriver/waterfall
There are 4 test suites for verifying ChromeDriver's correctness:
1) chromedriver_unittests (chrome/chrome_tests.gypi)
This is the unittest target, which runs on the main waterfall on win/mac/linux
and can close the tree. It is also run on the commit queue and try bots by
default. Tests should take a few milliseconds and be very stable.
2) chromedriver_tests (chrome/chrome_tests.gypi) * chromedriver_unittests
This is a collection of C++ medium sized tests which can be run optionally This is the unittest target, which runs on the commit queue on win/mac/linux.
on the trybots. Tests should take a few milliseconds and be very stable.
3) python integration tests * python integration tests (test/run_py_tests.py)
Run test/run_py_tests.py --help for more info. These are only run on the These tests are maintained by the ChromeDriver team, and are intended to verify
ChromeDriver waterfall. that ChromeDriver works correctly with Chrome. Run test/run_py_tests.py --help
for more info. These are run on the commit queue on win/mac/linux.
4) WebDriver Java acceptance tests * WebDriver Java acceptance tests (test/run_java_tests.py)
These are integration tests from the WebDriver open source project which can These are integration tests from the WebDriver open source project which can
be run via test/run_java_tests.py. They are only run on the ChromeDriver be run via test/run_java_tests.py. They are not currently run on any bots, but
bots. Run with --help for more info. will be included in the commit queue in the future. Run with --help for more
info.
=====Contributing===== =====Contributing=====
Find an open issue and submit a patch for review by an individual listed in Find an open issue and submit a patch for review by an individual listed in
the OWNERS file in this directory. Issues are tracked in chromedriver's issue the OWNERS file in this directory. Issues are tracked in chromedriver's issue
tracker: tracker:
https://code.google.com/p/chromedriver/issues/list https://bugs.chromium.org/p/chromedriver/issues/list
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Downloads items from the Chromium snapshot archive."""
import json
import os
import re
import urllib
import urllib2
import util
_SITE = 'http://commondatastorage.googleapis.com'
GS_GIT_LOG_URL = (
'https://chromium.googlesource.com/chromium/src/+/%s?format=json')
GS_SEARCH_PATTERN = r'Cr-Commit-Position: refs/heads/master@{#(\d+)}'
CR_REV_URL = 'https://cr-rev.appspot.com/_ah/api/crrev/v1/redirect/%s'
class Site(object):
CHROMIUM_SNAPSHOT = _SITE + '/chromium-browser-snapshots'
def GetLatestRevision():
"""Returns the latest revision (as a string) available for this platform.
Args:
site: the archive site to check against, default to the snapshot one.
"""
url = '%s/%s/LAST_CHANGE' % (GetDownloadSite(), _GetDownloadPlatform())
return urllib.urlopen(url).read()
def DownloadChrome(revision, dest_dir, site=Site.CHROMIUM_SNAPSHOT):
"""Downloads the packaged Chrome from the archive to the given directory.
Args:
revision: the revision of Chrome to download.
dest_dir: the directory to download Chrome to.
site: the archive site to download from, default to the snapshot one.
Returns:
The path to the unzipped Chrome binary.
"""
def GetZipName(revision):
if util.IsWindows():
return revision + (
'/chrome-win.zip' if int(revision) > 591478 else '/chrome-win32.zip')
elif util.IsMac():
return revision + '/chrome-mac.zip'
elif util.IsLinux():
return revision + '/chrome-linux.zip'
def GetDirName(revision):
if util.IsWindows():
return 'chrome-win' if int(revision) > 591478 else 'chrome-win32'
elif util.IsMac():
return 'chrome-mac'
elif util.IsLinux():
return 'chrome-linux'
def GetChromePathFromPackage():
if util.IsWindows():
return 'chrome.exe'
elif util.IsMac():
return 'Chromium.app/Contents/MacOS/Chromium'
elif util.IsLinux():
return 'chrome-wrapper'
zip_path = os.path.join(dest_dir, 'chrome-%s.zip' % revision)
if not os.path.exists(zip_path):
url = site + '/%s/%s' % (_GetDownloadPlatform(), GetZipName(revision))
print 'Downloading', url, '...'
urllib.urlretrieve(url, zip_path)
util.Unzip(zip_path, dest_dir)
return os.path.join(dest_dir, GetDirName(revision),
GetChromePathFromPackage())
def _GetDownloadPlatform():
"""Returns the name for this platform on the archive site."""
if util.IsWindows():
return 'Win'
elif util.IsMac():
return 'Mac'
elif util.IsLinux():
return 'Linux_x64'
def GetLatestSnapshotPosition():
"""Returns the latest commit position of snapshot build."""
latest_revision = GetLatestRevision()
return latest_revision
def GetDownloadSite():
"""Returns the site to download snapshot build according to the platform."""
return Site.CHROMIUM_SNAPSHOT
def GetCommitPositionFromGitHash(snapshot_hashcode):
json_url = GS_GIT_LOG_URL % snapshot_hashcode
try:
response = urllib2.urlopen(json_url)
except urllib2.HTTPError as error:
util.PrintAndFlush('HTTP Error %d' % error.getcode())
return None
except urllib2.URLError as error:
util.PrintAndFlush('URL Error %s' % error.message)
return None
data = json.loads(response.read()[4:])
if 'message' in data:
message = data['message'].split('\n')
message = [line for line in message if line.strip()]
search_pattern = re.compile(GS_SEARCH_PATTERN)
result = search_pattern.search(message[len(message)-1])
if result:
return result.group(1)
util.PrintAndFlush('Failed to get commit position number for %s' %
snapshot_hashcode)
return None
def _GetGitHashFromCommitPosition(commit_position):
json_url = CR_REV_URL % commit_position
try:
response = urllib2.urlopen(json_url)
except urllib2.HTTPError as error:
util.PrintAndFlush('HTTP Error %d' % error.getcode())
return None
except urllib2.URLError as error:
util.PrintAndFlush('URL Error %s' % error.message)
return None
data = json.loads(response.read())
if 'git_sha' in data:
return data['git_sha']
util.PrintAndFlush('Failed to get git hash for %s' % commit_position)
return None
def _GetFirstBuildAfterBranch(branch_position):
latest_revision = GetLatestSnapshotPosition()
for commit_position in range(int(branch_position), int(latest_revision)):
git_hash = _GetGitHashFromCommitPosition(commit_position)
try:
_ = DownloadChrome(git_hash, util.MakeTempDir(), GetDownloadSite())
return git_hash
except:
continue
return None
#!/usr/bin/env python
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Runs all the buildbot steps for ChromeDriver except for update/compile."""
import glob
import json
import optparse
import os
import sys
import tempfile
import time
_THIS_DIR = os.path.abspath(os.path.dirname(__file__))
GS_CHROMEDRIVER_DATA_BUCKET = 'gs://chromedriver-data'
GS_CONTINUOUS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/continuous'
GS_PREBUILTS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/prebuilts'
GS_SERVER_LOGS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/server_logs'
SERVER_LOGS_LINK = (
'http://chromedriver-data.storage.googleapis.com/server_logs')
TEST_LOG_FORMAT = '%s_log.json'
SCRIPT_DIR = os.path.join(_THIS_DIR, os.pardir, os.pardir, os.pardir, os.pardir,
os.pardir, os.pardir, os.pardir, 'scripts')
SITE_CONFIG_DIR = os.path.join(_THIS_DIR, os.pardir, os.pardir, os.pardir,
os.pardir, os.pardir, os.pardir, os.pardir,
'site_config')
sys.path.append(SCRIPT_DIR)
sys.path.append(SITE_CONFIG_DIR)
import archive
import chrome_paths
from slave import gsutil_download
from slave import slave_utils
import util
def _ArchivePrebuilts(commit_position):
"""Uploads the prebuilts to google storage."""
util.MarkBuildStepStart('archive prebuilts')
zip_path = util.Zip(os.path.join(chrome_paths.GetBuildDir(['chromedriver']),
'chromedriver'))
if slave_utils.GSUtilCopy(
zip_path,
'%s/%s' % (GS_PREBUILTS_URL, 'r%s.zip' % commit_position)):
util.MarkBuildStepError()
def _ArchiveServerLogs():
"""Uploads chromedriver server logs to google storage."""
util.MarkBuildStepStart('archive chromedriver server logs')
pathname_pattern = os.path.join(tempfile.gettempdir(), 'chromedriver_log_*')
print 'archiving logs from: %s' % pathname_pattern
for server_log in glob.glob(pathname_pattern):
if os.path.isfile(server_log):
base_name = os.path.basename(server_log)
util.AddLink(base_name, '%s/%s' % (SERVER_LOGS_LINK, base_name))
slave_utils.GSUtilCopy(
server_log,
'%s/%s' % (GS_SERVER_LOGS_URL, base_name),
mimetype='text/plain')
def _DownloadPrebuilts():
"""Downloads the most recent prebuilts from google storage."""
util.MarkBuildStepStart('Download latest chromedriver')
zip_path = os.path.join(util.MakeTempDir(), 'build.zip')
if gsutil_download.DownloadLatestFile(GS_PREBUILTS_URL,
GS_PREBUILTS_URL + '/r',
zip_path):
util.MarkBuildStepError()
util.Unzip(zip_path, chrome_paths.GetBuildDir(['host_forwarder']))
def _GetTestResultsLog(platform):
"""Gets the test results log for the given platform.
Args:
platform: The platform that the test results log is for.
Returns:
A dictionary where the keys are commit positions and the values are booleans
indicating whether the tests passed.
"""
(temp_fd, temp_log) = tempfile.mkstemp()
os.close(temp_fd)
log_name = TEST_LOG_FORMAT % platform
result = slave_utils.GSUtilDownloadFile(
'%s/%s' % (GS_CHROMEDRIVER_DATA_BUCKET, log_name), temp_log)
if result:
return {}
with open(temp_log, 'rb') as log_file:
json_dict = json.load(log_file)
# Workaround for json encoding dictionary keys as strings.
return dict([(int(v[0]), v[1]) for v in json_dict.items()])
def _PutTestResultsLog(platform, test_results_log):
"""Pushes the given test results log to google storage."""
temp_dir = util.MakeTempDir()
log_name = TEST_LOG_FORMAT % platform
log_path = os.path.join(temp_dir, log_name)
with open(log_path, 'wb') as log_file:
json.dump(test_results_log, log_file)
if slave_utils.GSUtilCopyFile(log_path, GS_CHROMEDRIVER_DATA_BUCKET):
raise Exception('Failed to upload test results log to google storage')
def _UpdateTestResultsLog(platform, commit_position, passed):
"""Updates the test results log for the given platform.
Args:
platform: The platform name.
commit_position: The commit position number.
passed: Boolean indicating whether the tests passed at this commit position.
"""
assert commit_position.isdigit(), 'The commit position must be a number'
commit_position = int(commit_position)
log = _GetTestResultsLog(platform)
if len(log) > 500:
del log[min(log.keys())]
assert commit_position not in log, \
'Results already exist for commit position %s' % commit_position
log[commit_position] = bool(passed)
_PutTestResultsLog(platform, log)
def _GetSupportedChromeVersions():
"""Get the minimum and maximum supported Chrome versions.
Returns:
A tuple of the form (min_version, max_version).
"""
# Minimum supported Chrome version is embedded as:
# const int kMinimumSupportedChromeVersion[] = {27, 0, 1453, 0};
with open(os.path.join(_THIS_DIR, 'chrome', 'version.cc'), 'r') as f:
lines = f.readlines()
chrome_min_version_line = [
x for x in lines if 'kMinimumSupportedChromeVersion' in x]
chrome_min_version = chrome_min_version_line[0].split('{')[1].split(',')[0]
with open(os.path.join(chrome_paths.GetSrc(), 'chrome', 'VERSION'), 'r') as f:
chrome_max_version = f.readlines()[0].split('=')[1].strip()
return (chrome_min_version, chrome_max_version)
def _ArchiveGoodBuild(platform, commit_position):
"""Archive chromedriver binary if the build is green."""
assert platform != 'android'
util.MarkBuildStepStart('archive build')
server_name = 'chromedriver'
if util.IsWindows():
server_name += '.exe'
zip_path = util.Zip(os.path.join(chrome_paths.GetBuildDir([server_name]),
server_name))
build_name = 'chromedriver_%s_%s.zip' % (
platform, commit_position)
build_url = '%s/%s' % (GS_CONTINUOUS_URL, build_name)
if slave_utils.GSUtilCopy(zip_path, build_url):
util.MarkBuildStepError()
if util.IsWindows():
zip_path = util.Zip(os.path.join(
chrome_paths.GetBuildDir([server_name + '.pdb']), server_name + '.pdb'))
pdb_name = 'chromedriver_%s_pdb_%s.zip' % (
platform, commit_position)
pdb_url = '%s/%s' % (GS_CONTINUOUS_URL, pdb_name)
if slave_utils.GSUtilCopy(zip_path, pdb_url):
util.MarkBuildStepError()
(latest_fd, latest_file) = tempfile.mkstemp()
os.write(latest_fd, build_name)
os.close(latest_fd)
latest_url = '%s/latest_%s' % (GS_CONTINUOUS_URL, platform)
if slave_utils.GSUtilCopy(latest_file, latest_url, mimetype='text/plain'):
util.MarkBuildStepError()
os.remove(latest_file)
def _WaitForLatestSnapshot(commit_position):
util.MarkBuildStepStart('wait_for_snapshot')
for attempt in range(0, 200):
snapshot_position = archive.GetLatestSnapshotPosition()
if commit_position is not None and snapshot_position is not None:
if int(snapshot_position) >= int(commit_position):
break
util.PrintAndFlush('Waiting for snapshot >= %s, found %s' %
(commit_position, snapshot_position))
time.sleep(60)
if int(snapshot_position) < int(commit_position):
raise Exception('Failed to find a snapshot version >= %s' % commit_position)
util.PrintAndFlush('Got snapshot commit position %s' % snapshot_position)
def _AddToolsToPath(platform_name):
"""Add some tools like Ant and Java to PATH for testing steps to use."""
paths = []
error_message = ''
if platform_name == 'win32':
paths = [
# Path to Ant and Java, required for the java acceptance tests.
'C:\\Program Files (x86)\\Java\\ant\\bin',
'C:\\Program Files (x86)\\Java\\jre\\bin',
]
error_message = ('Java test steps will fail as expected and '
'they can be ignored.\n'
'Ant, Java or others might not be installed on bot.\n'
'Please refer to page "WATERFALL" on site '
'go/chromedriver.')
if paths:
util.MarkBuildStepStart('Add tools to PATH')
path_missing = False
for path in paths:
if not os.path.isdir(path) or not os.listdir(path):
print 'Directory "%s" is not found or empty.' % path
path_missing = True
if path_missing:
print error_message
util.MarkBuildStepError()
return
os.environ['PATH'] += os.pathsep + os.pathsep.join(paths)
def main():
parser = optparse.OptionParser()
parser.add_option(
'', '--android-packages',
help=('Comma separated list of application package names, '
'if running tests on Android.'))
parser.add_option(
'-r', '--revision', help='Chromium git revision hash')
parser.add_option(
'', '--update-log', action='store_true',
help='Update the test results log (only applicable to Android)')
options, _ = parser.parse_args()
bitness = '32'
if util.Is64Bit():
bitness = '64'
platform = '%s%s' % (util.GetPlatformName(), bitness)
if options.android_packages:
platform = 'android'
if not options.revision:
commit_position = None
else:
commit_position = archive.GetCommitPositionFromGitHash(options.revision)
if commit_position is None:
raise Exception('Failed to convert revision to commit position')
if platform == 'android':
if not options.revision and options.update_log:
parser.error('Must supply a --revision with --update-log')
_DownloadPrebuilts()
else:
if not options.revision:
parser.error('Must supply a --revision')
if platform == 'linux64':
_ArchivePrebuilts(commit_position)
_WaitForLatestSnapshot(commit_position)
_AddToolsToPath(platform)
cmd = [
sys.executable,
os.path.join(_THIS_DIR, 'test', 'run_all_tests.py'),
]
if platform == 'android':
cmd.append('--android-packages=' + options.android_packages)
passed = (util.RunCommand(cmd) == 0)
_ArchiveServerLogs()
if platform == 'android':
if options.update_log:
util.MarkBuildStepStart('update test result log')
_UpdateTestResultsLog(platform, commit_position, passed)
elif passed:
_ArchiveGoodBuild(platform, commit_position)
if not passed:
# Make sure the build is red if there is some uncaught exception during
# running run_all_tests.py.
util.MarkBuildStepStart('run_all_tests.py')
util.MarkBuildStepError()
# Add a "cleanup" step so that errors from runtest.py or bb_device_steps.py
# (which invoke this script) are kept in their own build step.
util.MarkBuildStepStart('cleanup')
return 0 if passed else 1
if __name__ == '__main__':
sys.exit(main())
#!/usr/bin/env python
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Runs all ChromeDriver end to end tests."""
import optparse
import os
import platform
import shutil
import sys
import tempfile
import traceback
_THIS_DIR = os.path.abspath(os.path.dirname(__file__))
_PARENT_DIR = os.path.join(_THIS_DIR, os.pardir)
sys.path.insert(0, _PARENT_DIR)
import archive
import chrome_paths
import util
sys.path.insert(0, os.path.join(chrome_paths.GetSrc(), 'build', 'android'))
from pylib import constants
def _GenerateTestCommand(script,
chromedriver,
chrome=None,
android_package=None,
verbose=False):
_, log_path = tempfile.mkstemp(prefix='chromedriver_log_')
print 'chromedriver server log: %s' % log_path
cmd = [
sys.executable,
os.path.join(_THIS_DIR, script),
'--chromedriver=%s' % chromedriver,
'--log-path=%s' % log_path,
]
if chrome:
cmd.append('--chrome=' + chrome)
if verbose:
cmd.append('--verbose')
if android_package:
cmd = ['xvfb-run', '-a'] + cmd
cmd.append('--android-package=' + android_package)
return cmd
def RunReplayTests(chromedriver, chrome):
util.MarkBuildStepStart('replay_tests')
_, log_path = tempfile.mkstemp(prefix='chromedriver_log_')
print 'chromedriver server log: %s' % log_path
cmd = [
sys.executable,
os.path.join(_PARENT_DIR, 'log_replay', 'client_replay_test.py'),
chromedriver,
chrome,
'--output-log-path=%s' % log_path
]
code = util.RunCommand(cmd)
if code:
util.MarkBuildStepError()
return code
def RunPythonTests(chromedriver,
chrome=None,
android_package=None):
util.MarkBuildStepStart('python_tests')
code = util.RunCommand(
_GenerateTestCommand('run_py_tests.py',
chromedriver,
chrome=chrome,
android_package=android_package))
if code:
util.MarkBuildStepError()
return code
def RunJavaTests(chromedriver, chrome=None,
android_package=None,
verbose=False):
util.MarkBuildStepStart('java_tests')
code = util.RunCommand(
_GenerateTestCommand('run_java_tests.py',
chromedriver,
chrome=chrome,
android_package=android_package,
verbose=verbose))
if code:
util.MarkBuildStepError()
return code
def RunCppTests(cpp_tests):
util.MarkBuildStepStart('chromedriver_tests')
code = util.RunCommand([cpp_tests])
if code:
util.MarkBuildStepError()
return code
def DownloadChrome(version_name, revision, download_site):
util.MarkBuildStepStart('download %s' % version_name)
try:
temp_dir = util.MakeTempDir()
return (temp_dir, archive.DownloadChrome(revision, temp_dir, download_site))
except Exception:
traceback.print_exc()
util.AddBuildStepText('Skip Java and Python tests')
util.MarkBuildStepError()
return (None, None)
def _KillChromes():
chrome_map = {
'win': 'chrome.exe',
'mac': 'Chromium',
'linux': 'chrome',
}
if util.IsWindows():
cmd = ['taskkill', '/F', '/IM']
else:
cmd = ['killall', '-9']
cmd.append(chrome_map[util.GetPlatformName()])
util.RunCommand(cmd)
def main():
parser = optparse.OptionParser()
parser.add_option(
'', '--android-packages',
help='Comma separated list of application package names, '
'if running tests on Android.')
options, _ = parser.parse_args()
exe_postfix = ''
if util.IsWindows():
exe_postfix = '.exe'
cpp_tests_name = 'chromedriver_tests' + exe_postfix
server_name = 'chromedriver' + exe_postfix
required_build_outputs = [server_name]
if not options.android_packages:
required_build_outputs += [cpp_tests_name]
try:
build_dir = chrome_paths.GetBuildDir(required_build_outputs)
except RuntimeError:
util.MarkBuildStepStart('check required binaries')
traceback.print_exc()
util.MarkBuildStepError()
constants.SetBuildType(os.path.basename(build_dir))
print 'Using build outputs from', build_dir
chromedriver = os.path.join(build_dir, server_name)
platform_name = util.GetPlatformName()
if util.IsLinux() and util.Is64Bit():
platform_name += '64'
if options.android_packages:
os.environ['PATH'] += os.pathsep + os.path.join(
_THIS_DIR, os.pardir, 'chrome')
code = 0
for package in options.android_packages.split(','):
code1 = RunPythonTests(chromedriver,
chrome_version_name=package,
android_package=package)
code2 = RunJavaTests(chromedriver,
chrome_version_name=package,
android_package=package,
verbose=True)
code = code or code1 or code2
return code
else:
code = 0
download_site = archive.GetDownloadSite()
revision = archive.GetLatestRevision()
temp_dir, chrome_path = DownloadChrome(revision, revision,
download_site)
if not chrome_path:
code = 1
code1 = RunPythonTests(chromedriver,
chrome=chrome_path)
code2 = RunJavaTests(chromedriver,
verbose=True,
chrome=chrome_path)
code3 = RunReplayTests(chromedriver,
chrome=chrome_path)
code = code or code1 or code2 or code3
_KillChromes()
shutil.rmtree(temp_dir)
cpp_tests = os.path.join(build_dir, cpp_tests_name)
return RunCppTests(cpp_tests) or code
if __name__ == '__main__':
sys.exit(main())
...@@ -3,13 +3,7 @@ ...@@ -3,13 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""Runs the WebDriver Java acceptance tests. """Runs the WebDriver Java acceptance tests."""
This script is called from chrome/test/chromedriver/run_all_tests.py and reports
results using the buildbot annotation scheme.
For ChromeDriver documentation, refer to http://code.google.com/p/chromedriver.
"""
import optparse import optparse
import os import os
......
{
"build_info": {
"win7" : {
"builder_url" : "http://build.chromium.org/p/chromium.chromedriver/builders/Win7/builds/",
"json_url" : "http://build.chromium.org/p/chromium.chromedriver/json/builders/Win7/builds/%d?as_text=1",
"builds_url": "http://build.chromium.org/p/chromium.chromedriver/json/builders/Win7/builds/_all?as_text=1"
},
"mac_10_6" : {
"builder_url" : "http://build.chromium.org/p/chromium.chromedriver/builders/Mac%2010.6/builds/",
"json_url" : "http://build.chromium.org/p/chromium.chromedriver/json/builders/Mac 10.6/builds/%d?as_text=1",
"builds_url": "http://build.chromium.org/p/chromium.chromedriver/json/builders/Mac 10.6/builds/_all?as_text=1"
},
"linux" : {
"builder_url" : "http://build.chromium.org/p/chromium.chromedriver/builders/Linux/builds/",
"json_url" : "http://build.chromium.org/p/chromium.chromedriver/json/builders/Linux/builds/%d?as_text=1",
"builds_url": "http://build.chromium.org/p/chromium.chromedriver/json/builders/Linux/builds/_all?as_text=1"
},
"linux32" : {
"builder_url" : "http://build.chromium.org/p/chromium.chromedriver/builders/Linux32/builds/",
"json_url" : "http://build.chromium.org/p/chromium.chromedriver/json/builders/Linux32/builds/%d?as_text=1",
"builds_url": "http://build.chromium.org/p/chromium.chromedriver/json/builders/Linux32/builds/_all?as_text=1"
},
"android_chromedriver_test" : {
"builder_url" : "http://build.chromium.org/p/chromium.fyi/builders/Android%20ChromeDriver%20Tests%20%28dbg%29/builds/",
"json_url" : "http://build.chromium.org/p/chromium.fyi/json/builders/Android ChromeDriver Tests (dbg)/builds/%d?as_text=1",
"builds_url": "http://build.chromium.org/p/chromium.fyi/json/builders/Android ChromeDriver Tests (dbg)/builds/_all?as_text=1"
}
},
"recipient_emails": "pshenoy@chromium.org",
"sender_email": "pshenoy@chromium.org",
"old_build_days": 2
}
#!/usr/bin/env python
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Waterfall monitoring script.
This script checks all builders specified in the config file and sends
status email about any step failures in these builders. This also
reports a build as failure if the latest build on that builder was built
2 days back. (Number of days can be configured in the config file)
This script can be run as cronjob on a linux machine once a day and
get email notification for any waterfall specified in the config file.
Sample cronjob entry below. This entry will run the script everyday at 9 AM.
Include this in the crontab file.
0 9 * * * <Path to script> --config <Path to json file>
"""
import datetime
import json
import optparse
import sys
import time
import traceback
import urllib
from datetime import timedelta
from email.mime.text import MIMEText
from subprocess import Popen, PIPE
SUCCESS_SUBJECT = ('[CHROME TESTING]: Builder status %s: PASSED.')
FAILURE_SUBJECT = ('[CHROME TESTING]: Builder status %s: FAILED %d out of %d')
EXCEPTION_SUBJECT = ('Exception occurred running waterfall_builder_monitor.py '
'script')
def GetTimeDelta(date, days):
if isinstance(date, datetime.datetime):
return date + timedelta(days)
def GetDateFromEpochFormat(epoch_time):
last_build_date = time.localtime(epoch_time)
last_build_date = datetime.datetime(int(last_build_date.tm_year),
int(last_build_date.tm_mon),
int(last_build_date.tm_mday),
int(last_build_date.tm_hour),
int(last_build_date.tm_min),
int(last_build_date.tm_sec))
return last_build_date
def GetJSONData(json_url):
response = urllib.urlopen(json_url)
if response.getcode() == 200:
try:
data = json.loads(response.read())
except ValueError:
print 'ValueError for JSON URL: %s' % json_url
raise
else:
raise Exception('Error from URL: %s' % json_url)
response.close()
return data
def SendEmailViaSendmailCommand(sender_email, recipient_emails,
subject, email_body):
msg = MIMEText(email_body)
msg["From"] = sender_email
msg["To"] = recipient_emails
msg["Subject"] = subject
pipe = Popen(["/usr/sbin/sendmail", "-t"], stdin=PIPE)
pipe.communicate(msg.as_string())
def SendStatusEmailViaSendmailCommand(consolidated_results,
recipient_emails,
sender_email):
failure_count = 0
for result in consolidated_results:
if result['error'] != 'passed' and not result['build_too_old']:
failure_count += 1
today = str(datetime.date.today()).replace('-', '/')[5:]
if failure_count == 0:
subject = SUCCESS_SUBJECT % today
else:
subject = FAILURE_SUBJECT % (today,
failure_count,
len(consolidated_results))
email_body = ''
for result in consolidated_results:
if result['error'] != 'passed' or result['build_too_old']:
if result['build_date'] is not None:
email_body += result['platform'] + ': ' +\
result['build_link'] + ' ( Build too old: ' +\
result['build_date'] + ' ) ' +'\n\n'
else:
email_body += result['platform'] + ': ' +\
result['build_link'] + '\n\n'
SendEmailViaSendmailCommand(sender_email, recipient_emails,
subject, email_body)
def SendExceptionEmailViaSendmailCommand(exception_message_lines,
recipient_emails,
sender_email):
subject = EXCEPTION_SUBJECT
email_body = ''
email_body = '\n'.join(exception_message_lines)
SendEmailViaSendmailCommand(sender_email, recipient_emails,
subject, email_body)
class OfficialBuilderParser(object):
"""This class implements basic utility functions on a specified builder."""
def __init__(self, builder_type, build_info):
self.platform = builder_type
self.builder_info = build_info
self.builder_url = build_info['builder_url']
self.build_json_url = build_info['json_url']
self.build = self._GetLatestBuildNumber()
def _GetLatestBuildNumber(self):
json_url = self.builder_info['builds_url']
data = GetJSONData(json_url)
# Get a sorted list of all the keys in the json data.
keys = sorted(map(int, data.keys()))
return self._GetLatestCompletedBuild(keys)
def _GetLatestCompletedBuild(self, keys):
reversed_list = keys[::-1]
for build in reversed_list:
data = self._GetJSONDataForBuild(build)
if data is not None:
if 'text' in data:
return build
return None
def _GetJSONDataForBuild(self, build):
if build is None:
return build
json_url = self.build_json_url % build
return GetJSONData(json_url)
class GetBuilderStatus(OfficialBuilderParser):
def __init__(self, builder_type, build_info):
OfficialBuilderParser.__init__(self, builder_type, build_info)
def CheckForFailedSteps(self, days):
if self.build is None:
return {}
result = {'platform': self.platform,
'build_number': self.build,
'build_link': self.builder_url + str(self.build),
'build_date': None,
'build_too_old': False,
'error': 'unknown'}
data = self._GetJSONDataForBuild(self.build)
if data is not None:
if 'text' in data:
if 'build' in data['text'] and 'successful' in data['text']:
result['error'] = 'passed'
else:
if 'failed' in data['text'] or\
'exception' in data['text'] or\
'interrupted' in data['text']:
result['error'] = 'failed'
if 'times' in data:
old_date = GetTimeDelta(datetime.datetime.now(), days)
last_build_date = GetDateFromEpochFormat(data['times'][0])
if last_build_date < old_date:
result['build_too_old'] = True
result['build_date'] = str(last_build_date).split(' ')[0]
else:
raise Exception('There was some problem getting JSON data '
'from URL: %s' % result['build_link'])
return result
def main():
parser = optparse.OptionParser()
parser.add_option('--config', type='str',
help='Absolute path to the config file.')
(options, _) = parser.parse_args()
if not options.config:
print 'Error: missing required parameter: --config'
parser.print_help()
return 1
try:
with open(options.config, 'r') as config_file:
try:
json_data = json.loads(config_file.read())
except ValueError:
print 'ValueError for loading JSON data from : %s' % options.config
raise ValueError
old_build_days = -2
if 'old_build_days' in json_data:
old_build_days = - json_data['old_build_days']
consolidated_results = []
for key in json_data['build_info'].keys():
builder_status = GetBuilderStatus(key, json_data['build_info'][key])
builder_result = builder_status.CheckForFailedSteps(old_build_days)
consolidated_results.append(builder_result)
SendStatusEmailViaSendmailCommand(consolidated_results,
json_data['recipient_emails'],
json_data['sender_email'])
return 0
except Exception:
formatted_lines = traceback.format_exc().splitlines()
SendExceptionEmailViaSendmailCommand(formatted_lines,
json_data['recipient_emails'],
json_data['sender_email'])
return 1
if __name__ == '__main__':
sys.exit(main())
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2007-2009 Google Inc.
Copyright 2007-2009 WebDriver committers
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Name: Google code support upload script
Short Name: googlecode_upload
URL: http://support.googlecode.com/svn/trunk/scripts/googlecode_upload.py
Version: r681
Security Critical: no
License: Apache 2
License File: NOT_SHIPPED
Description:
A simple script from Google Code support for automating uploads.
Local modifications:
-Added comment describing license. They have a comment about the license, but
it uses an atypical format that isn't recognized by chromium's checklicense
script.
-Instead of using the default netrc lookup which requires HOME to be set
(which normally isn't on Windows), specify the file using os.path.expanduser.
-Fail instead of prompting for username/password if none is given.
#!/usr/bin/env python
#
# Copyright 2006, 2007 Google Inc. All Rights Reserved.
# Author: danderson@google.com (David Anderson)
#
# Script for uploading files to a Google Code project.
#
# This is intended to be both a useful script for people who want to
# streamline project uploads and a reference implementation for
# uploading files to Google Code projects.
#
# To upload a file to Google Code, you need to provide a path to the
# file on your local machine, a small summary of what the file is, a
# project name, and a valid account that is a member or owner of that
# project. You can optionally provide a list of labels that apply to
# the file. The file will be uploaded under the same name that it has
# in your local filesystem (that is, the "basename" or last path
# component). Run the script with '--help' to get the exact syntax
# and available options.
#
# Note that the upload script requests that you enter your
# googlecode.com password. This is NOT your Gmail account password!
# This is the password you use on googlecode.com for committing to
# Subversion and uploading files. You can find your password by going
# to http://code.google.com/hosting/settings when logged in with your
# Gmail account. If you have already committed to your project's
# Subversion repository, the script will automatically retrieve your
# credentials from there (unless disabled, see the output of '--help'
# for details).
#
# If you are looking at this script as a reference for implementing
# your own Google Code file uploader, then you should take a look at
# the upload() function, which is the meat of the uploader. You
# basically need to build a multipart/form-data POST request with the
# right fields and send it to https://PROJECT.googlecode.com/files .
# Authenticate the request using HTTP Basic authentication, as is
# shown below.
#
# Licensed under the terms of the Apache Software License 2.0:
# http://www.apache.org/licenses/LICENSE-2.0
#
# Questions, comments, feature requests and patches are most welcome.
# Please direct all of these to the Google Code users group:
# http://groups.google.com/group/google-code-hosting
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
"""Google Code file uploader script.
"""
__author__ = 'danderson@google.com (David Anderson)'
import httplib
import os.path
import optparse
import getpass
import base64
import sys
def upload(file, project_name, user_name, password, summary, labels=None):
"""Upload a file to a Google Code project's file server.
Args:
file: The local path to the file.
project_name: The name of your project on Google Code.
user_name: Your Google account name.
password: The googlecode.com password for your account.
Note that this is NOT your global Google Account password!
summary: A small description for the file.
labels: an optional list of label strings with which to tag the file.
Returns: a tuple:
http_status: 201 if the upload succeeded, something else if an
error occured.
http_reason: The human-readable string associated with http_status
file_url: If the upload succeeded, the URL of the file on Google
Code, None otherwise.
"""
# The login is the user part of user@gmail.com. If the login provided
# is in the full user@domain form, strip it down.
if user_name.endswith('@gmail.com'):
user_name = user_name[:user_name.index('@gmail.com')]
form_fields = [('summary', summary)]
if labels is not None:
form_fields.extend([('label', l.strip()) for l in labels])
content_type, body = encode_upload_request(form_fields, file)
upload_host = '%s.googlecode.com' % project_name
upload_uri = '/files'
auth_token = base64.b64encode('%s:%s'% (user_name, password))
headers = {
'Authorization': 'Basic %s' % auth_token,
'User-Agent': 'Googlecode.com uploader v0.9.4',
'Content-Type': content_type,
}
server = httplib.HTTPSConnection(upload_host)
server.request('POST', upload_uri, body, headers)
resp = server.getresponse()
server.close()
if resp.status == 201:
location = resp.getheader('Location', None)
else:
location = None
return resp.status, resp.reason, location
def encode_upload_request(fields, file_path):
"""Encode the given fields and file into a multipart form body.
fields is a sequence of (name, value) pairs. file is the path of
the file to upload. The file will be uploaded to Google Code with
the same file name.
Returns: (content_type, body) ready for httplib.HTTP instance
"""
BOUNDARY = '----------Googlecode_boundary_reindeer_flotilla'
CRLF = '\r\n'
body = []
# Add the metadata about the upload first
for key, value in fields:
body.extend(
['--' + BOUNDARY,
'Content-Disposition: form-data; name="%s"' % key,
'',
value,
])
# Now add the file itself
file_name = os.path.basename(file_path)
f = open(file_path, 'rb')
file_content = f.read()
f.close()
body.extend(
['--' + BOUNDARY,
'Content-Disposition: form-data; name="filename"; filename="%s"'
% file_name,
# The upload server determines the mime-type, no need to set it.
'Content-Type: application/octet-stream',
'',
file_content,
])
# Finalize the form body
body.extend(['--' + BOUNDARY + '--', ''])
return 'multipart/form-data; boundary=%s' % BOUNDARY, CRLF.join(body)
def upload_find_auth(file_path, project_name, summary, labels=None,
user_name=None, password=None, tries=3):
"""Find credentials and upload a file to a Google Code project's file server.
file_path, project_name, summary, and labels are passed as-is to upload.
Args:
file_path: The local path to the file.
project_name: The name of your project on Google Code.
summary: A small description for the file.
labels: an optional list of label strings with which to tag the file.
config_dir: Path to Subversion configuration directory, 'none', or None.
user_name: Your Google account name.
tries: How many attempts to make.
"""
if user_name is None or password is None:
from netrc import netrc
# Chromium edit: Works on windows without requiring HOME to be set.
netrc_path = os.path.join(os.path.expanduser('~'), '.netrc')
authenticators = netrc(netrc_path).authenticators("code.google.com")
if authenticators:
if user_name is None:
user_name = authenticators[0]
if password is None:
password = authenticators[2]
if user_name is None or password is None:
raise RuntimeError('Missing user credentials for upload')
return upload(file_path, project_name, user_name, password, summary, labels)
def main():
parser = optparse.OptionParser(usage='googlecode-upload.py -s SUMMARY '
'-p PROJECT [options] FILE')
parser.add_option('-s', '--summary', dest='summary',
help='Short description of the file')
parser.add_option('-p', '--project', dest='project',
help='Google Code project name')
parser.add_option('-u', '--user', dest='user',
help='Your Google Code username')
parser.add_option('-w', '--password', dest='password',
help='Your Google Code password')
parser.add_option('-l', '--labels', dest='labels',
help='An optional list of comma-separated labels to attach '
'to the file')
options, args = parser.parse_args()
if not options.summary:
parser.error('File summary is missing.')
elif not options.project:
parser.error('Project name is missing.')
elif len(args) < 1:
parser.error('File to upload not provided.')
elif len(args) > 1:
parser.error('Only one file may be specified.')
file_path = args[0]
if options.labels:
labels = options.labels.split(',')
else:
labels = None
status, reason, url = upload_find_auth(file_path, options.project,
options.summary, labels,
options.user, options.password)
if url:
print 'The file was uploaded successfully.'
print 'URL: %s' % url
return 0
else:
print 'An error occurred. Your file was not uploaded.'
print 'Google Code upload server said: %s (%s)' % (reason, status)
return 1
if __name__ == '__main__':
sys.exit(main())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment