import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

4
testing/tps/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# These files are added by running the TPS test suite.
build/
dist/
tps.egg-info/

4
testing/tps/.hgignore Normal file
View file

@ -0,0 +1,4 @@
# These files are added by running the TPS test suite.
build/
dist/
tps.egg-info/

42
testing/tps/README Normal file
View file

@ -0,0 +1,42 @@
TPS is a test automation framework for Firefox Sync. See
https://developer.mozilla.org/en/TPS for documentation.
Installation
============
TPS requires several packages to operate properly. To install TPS and
required packages, use the INSTALL.sh script, provided:
./INSTALL.sh /path/to/create/virtualenv
This script will create a virtalenv and install TPS into it. TPS can then
be run by activating the virtualenv and executing:
runtps --binary=/path/to/firefox
Configuration
=============
To edit the TPS configuration, do not edit config/config.json.in in the tree.
Instead, edit config.json inside your virtualenv; it will be located at
something like:
(linux): /path/to/virtualenv/lib/python2.6/site-packages/tps-0.2.40-py2.6.egg/tps/config.json
(win): /path/to/virtualenv/Lib/site-packages/tps-0.2.40-py2.6.egg/tps/config.json
Setting Up Test Accounts
========================
Firefox Accounts
----------------
To create a test account for using the Firefox Account authentication perform the
following steps:
1. Go to a URL like http://restmail.net/mail/%account_prefix%@restmail.net
2. Go to https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v1
3. Sign in with the previous chosen email address and a password
4. Go back to the Restmail URL, reload the page
5. Search for the verification link and open that page
Now you will be able to use your setup Firefox Account for Sync.

View file

@ -0,0 +1,24 @@
{
"sync_account": {
"username": "__SYNC_ACCOUNT_USERNAME__",
"password": "__SYNC_ACCOUNT_PASSWORD__",
"passphrase": "__SYNC_ACCOUNT_PASSPHRASE__"
},
"fx_account": {
"username": "__FX_ACCOUNT_USERNAME__",
"password": "__FX_ACCOUNT_PASSWORD__"
},
"email": {
"username": "crossweave@mozilla.com",
"password": "",
"passednotificationlist": ["crossweave@mozilla.com"],
"notificationlist": ["crossweave@mozilla.com"]
},
"auth_type": "fx_account",
"es": "localhost:9200",
"os": "Ubuntu",
"platform": "linux64",
"serverURL": null,
"extensiondir": "__EXTENSIONDIR__",
"testdir": "__TESTDIR__"
}

194
testing/tps/create_venv.py Normal file
View file

@ -0,0 +1,194 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
This scripts sets up a virtualenv and installs TPS into it.
It's probably best to specify a path NOT inside the repo, otherwise
all the virtualenv files will show up in e.g. hg status.
"""
import optparse
import os
import shutil
import subprocess
import sys
import urllib2
import zipfile
here = os.path.dirname(os.path.abspath(__file__))
usage_message = """
***********************************************************************
To run TPS, activate the virtualenv using:
source {TARGET}/{BIN_NAME}
To change your TPS config, please edit the file:
{TARGET}/config.json
To execute tps use:
runtps --binary=/path/to/firefox
See runtps --help for all options
***********************************************************************
"""
# Link to the folder, which contains the zip archives of virtualenv
URL_VIRTUALENV = 'https://codeload.github.com/pypa/virtualenv/zip/'
VERSION_VIRTUALENV = '1.11.6'
if sys.platform == 'win32':
bin_name = os.path.join('Scripts', 'activate.bat')
activate_env = os.path.join('Scripts', 'activate_this.py')
python_env = os.path.join('Scripts', 'python.exe')
else:
bin_name = os.path.join('bin', 'activate')
activate_env = os.path.join('bin', 'activate_this.py')
python_env = os.path.join('bin', 'python')
def download(url, target):
"""Downloads the specified url to the given target."""
response = urllib2.urlopen(url)
with open(target, 'wb') as f:
f.write(response.read())
return target
def setup_virtualenv(target, python_bin=None):
script_path = os.path.join(here, 'virtualenv-%s' % VERSION_VIRTUALENV,
'virtualenv.py')
print 'Downloading virtualenv %s' % VERSION_VIRTUALENV
zip_path = download(URL_VIRTUALENV + VERSION_VIRTUALENV,
os.path.join(here, 'virtualenv.zip'))
try:
with zipfile.ZipFile(zip_path, 'r') as f:
f.extractall(here)
print 'Creating new virtual environment'
cmd_args = [sys.executable, script_path, target]
if python_bin:
cmd_args.extend(['-p', python_bin])
subprocess.check_call(cmd_args)
finally:
try:
os.remove(zip_path)
except OSError:
pass
shutil.rmtree(os.path.dirname(script_path), ignore_errors=True)
def update_configfile(source, target, replacements):
lines = []
with open(source) as config:
for line in config:
for source_string, target_string in replacements.iteritems():
if target_string:
line = line.replace(source_string, target_string)
lines.append(line)
with open(target, 'w') as config:
for line in lines:
config.write(line)
def main():
parser = optparse.OptionParser('Usage: %prog [options] path_to_venv')
parser.add_option('--password',
type='string',
dest='password',
metavar='FX_ACCOUNT_PASSWORD',
default=None,
help='The Firefox Account password.')
parser.add_option('-p', '--python',
type='string',
dest='python',
metavar='PYTHON_BIN',
default=None,
help='The Python interpreter to use.')
parser.add_option('--sync-passphrase',
type='string',
dest='sync_passphrase',
metavar='SYNC_ACCOUNT_PASSPHRASE',
default=None,
help='The old Firefox Sync account passphrase.')
parser.add_option('--sync-password',
type='string',
dest='sync_password',
metavar='SYNC_ACCOUNT_PASSWORD',
default=None,
help='The old Firefox Sync account password.')
parser.add_option('--sync-username',
type='string',
dest='sync_username',
metavar='SYNC_ACCOUNT_USERNAME',
default=None,
help='The old Firefox Sync account username.')
parser.add_option('--username',
type='string',
dest='username',
metavar='FX_ACCOUNT_USERNAME',
default=None,
help='The Firefox Account username.')
(options, args) = parser.parse_args(args=None, values=None)
if len(args) != 1:
parser.error('Path to the environment has to be specified')
target = args[0]
assert(target)
setup_virtualenv(target, python_bin=options.python)
# Activate tps environment
tps_env = os.path.join(target, activate_env)
execfile(tps_env, dict(__file__=tps_env))
# Install TPS in environment
subprocess.check_call([os.path.join(target, python_env),
os.path.join(here, 'setup.py'), 'install'])
# Get the path to tests and extensions directory by checking check where
# the tests and extensions directories are located
sync_dir = os.path.abspath(os.path.join(here, '..', '..', 'services',
'sync'))
if os.path.exists(sync_dir):
testdir = os.path.join(sync_dir, 'tests', 'tps')
extdir = os.path.join(sync_dir, 'tps', 'extensions')
else:
testdir = os.path.join(here, 'tests')
extdir = os.path.join(here, 'extensions')
update_configfile(os.path.join(here, 'config', 'config.json.in'),
os.path.join(target, 'config.json'),
replacements={
'__TESTDIR__': testdir.replace('\\','/'),
'__EXTENSIONDIR__': extdir.replace('\\','/'),
'__FX_ACCOUNT_USERNAME__': options.username,
'__FX_ACCOUNT_PASSWORD__': options.password,
'__SYNC_ACCOUNT_USERNAME__': options.sync_username,
'__SYNC_ACCOUNT_PASSWORD__': options.sync_password,
'__SYNC_ACCOUNT_PASSPHRASE__': options.sync_passphrase})
if not (options.username and options.password):
print '\nFirefox Account credentials not specified.'
if not (options.sync_username and options.sync_password and options.passphrase):
print '\nFirefox Sync account credentials not specified.'
# Print the user instructions
print usage_message.format(TARGET=target,
BIN_NAME=bin_name)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
Static microsummary #1

View file

@ -0,0 +1 @@
Static microsummary #2

View file

@ -0,0 +1 @@
Static microsummary #3

View file

@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<title>Crossweave Test Page 1</title>
</head>
<body>
<p>
Crossweave Test Page 1
</p>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<title>Crossweave Test Page 2</title>
</head>
<body>
<p>
Crossweave Test Page 2
</p>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<title>Crossweave Test Page 3</title>
</head>
<body>
<p>
Crossweave Test Page 3
</p>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<title>Crossweave Test Page 4</title>
</head>
<body>
<p>
Crossweave Test Page 4
</p>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<title>Crossweave Test Page 5</title>
</head>
<body>
<p>
Crossweave Test Page 5
</p>
</body>
</html>

48
testing/tps/setup.py Normal file
View file

@ -0,0 +1,48 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from setuptools import setup, find_packages
import sys
version = '0.5'
deps = ['httplib2 == 0.9.2',
'mozfile == 1.2',
'mozhttpd == 0.7',
'mozinfo == 0.9',
'mozinstall == 1.12',
'mozprocess == 0.23',
'mozprofile == 0.28',
'mozrunner == 6.12',
'mozversion == 1.4',
]
# we only support python 2.6+ right now
assert sys.version_info[0] == 2
assert sys.version_info[1] >= 6
setup(name='tps',
version=version,
description='run automated multi-profile sync tests',
long_description="""\
""",
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='',
author='Mozilla Automation and Tools team',
author_email='tools@lists.mozilla.org',
url='https://developer.mozilla.org/en-US/docs/TPS',
license='MPL 2.0',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=deps,
entry_points="""
# -*- Entry points: -*-
[console_scripts]
runtps = tps.cli:main
""",
data_files=[
('tps', ['config/config.json.in']),
],
)

View file

@ -0,0 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from . firefoxrunner import TPSFirefoxRunner
from . testrunner import TPSTestRunner

128
testing/tps/tps/cli.py Normal file
View file

@ -0,0 +1,128 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
import optparse
import os
import re
import sys
from threading import RLock
from tps import TPSTestRunner
def main():
parser = optparse.OptionParser()
parser.add_option('--binary',
action='store',
type='string',
dest='binary',
default=None,
help='path to the Firefox binary, specified either as '
'a local file or a url; if omitted, the PATH '
'will be searched;')
parser.add_option('--configfile',
action='store',
type='string',
dest='configfile',
default=None,
help='path to the config file to use default: %default]')
parser.add_option('--debug',
action='store_true',
dest='debug',
default=False,
help='run in debug mode')
parser.add_option('--ignore-unused-engines',
default=False,
action='store_true',
dest='ignore_unused_engines',
help='If defined, do not load unused engines in individual tests.'
' Has no effect for pulse monitor.')
parser.add_option('--logfile',
action='store',
type='string',
dest='logfile',
default='tps.log',
help='path to the log file [default: %default]')
parser.add_option('--mobile',
action='store_true',
dest='mobile',
default=False,
help='run with mobile settings')
parser.add_option('--pulsefile',
action='store',
type='string',
dest='pulsefile',
default=None,
help='path to file containing a pulse message in '
'json format that you want to inject into the monitor')
parser.add_option('--resultfile',
action='store',
type='string',
dest='resultfile',
default='tps_result.json',
help='path to the result file [default: %default]')
parser.add_option('--testfile',
action='store',
type='string',
dest='testfile',
default='all_tests.json',
help='path to the test file to run [default: %default]')
parser.add_option('--stop-on-error',
action='store_true',
dest='stop_on_error',
help='stop running tests after the first failure')
(options, args) = parser.parse_args()
configfile = options.configfile
if configfile is None:
virtual_env = os.environ.get('VIRTUAL_ENV')
if virtual_env:
configfile = os.path.join(virtual_env, 'config.json')
if configfile is None or not os.access(configfile, os.F_OK):
raise Exception('Unable to find config.json in a VIRTUAL_ENV; you must '
'specify a config file using the --configfile option')
# load the config file
f = open(configfile, 'r')
configcontent = f.read()
f.close()
config = json.loads(configcontent)
testfile = os.path.join(config.get('testdir', ''), options.testfile)
rlock = RLock()
print 'using result file', options.resultfile
extensionDir = config.get('extensiondir')
if not extensionDir or extensionDir == '__EXTENSIONDIR__':
extensionDir = os.path.join(os.getcwd(), '..', '..',
'services', 'sync', 'tps', 'extensions')
else:
if sys.platform == 'win32':
# replace msys-style paths with proper Windows paths
m = re.match('^\/\w\/', extensionDir)
if m:
extensionDir = '%s:/%s' % (m.group(0)[1:2], extensionDir[3:])
extensionDir = extensionDir.replace('/', '\\')
TPS = TPSTestRunner(extensionDir,
binary=options.binary,
config=config,
debug=options.debug,
ignore_unused_engines=options.ignore_unused_engines,
logfile=options.logfile,
mobile=options.mobile,
resultfile=options.resultfile,
rlock=rlock,
testfile=testfile,
stop_on_error=options.stop_on_error,
)
TPS.run_tests()
if TPS.numfailed > 0 or TPS.numpassed == 0:
sys.exit(1)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,84 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import copy
import httplib2
import os
import mozfile
import mozinstall
from mozprofile import Profile
from mozrunner import FirefoxRunner
class TPSFirefoxRunner(object):
PROCESS_TIMEOUT = 240
def __init__(self, binary):
if binary is not None and ('http://' in binary or 'ftp://' in binary):
self.url = binary
self.binary = None
else:
self.url = None
self.binary = binary
self.installdir = None
def __del__(self):
if self.installdir:
mozfile.remove(self.installdir, True)
def download_url(self, url, dest=None):
h = httplib2.Http()
resp, content = h.request(url, 'GET')
if dest == None:
dest = os.path.basename(url)
local = open(dest, 'wb')
local.write(content)
local.close()
return dest
def download_build(self, installdir='downloadedbuild', appname='firefox'):
self.installdir = os.path.abspath(installdir)
buildName = os.path.basename(self.url)
pathToBuild = os.path.join(os.path.dirname(os.path.abspath(__file__)),
buildName)
# delete the build if it already exists
if os.access(pathToBuild, os.F_OK):
os.remove(pathToBuild)
# download the build
print 'downloading build'
self.download_url(self.url, pathToBuild)
# install the build
print 'installing %s' % pathToBuild
mozfile.remove(self.installdir, True)
binary = mozinstall.install(src=pathToBuild, dest=self.installdir)
# remove the downloaded archive
os.remove(pathToBuild)
return binary
def run(self, profile=None, timeout=PROCESS_TIMEOUT, env=None, args=None):
"""Runs the given FirefoxRunner with the given Profile, waits
for completion, then returns the process exit code
"""
if profile is None:
profile = Profile()
self.profile = profile
if self.binary is None and self.url:
self.binary = self.download_build()
runner = FirefoxRunner(profile=self.profile, binary=self.binary,
env=env, cmdargs=args)
runner.start(timeout=timeout)
return runner.wait()

69
testing/tps/tps/phase.py Normal file
View file

@ -0,0 +1,69 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import re
class TPSTestPhase(object):
lineRe = re.compile(
r'^(.*?)test phase (?P<matchphase>[^\s]+): (?P<matchstatus>.*)$')
def __init__(self, phase, profile, testname, testpath, logfile, env,
firefoxRunner, logfn, ignore_unused_engines=False):
self.phase = phase
self.profile = profile
self.testname = str(testname) # this might be passed in as unicode
self.testpath = testpath
self.logfile = logfile
self.env = env
self.firefoxRunner = firefoxRunner
self.log = logfn
self.ignore_unused_engines = ignore_unused_engines
self._status = None
self.errline = ''
@property
def status(self):
return self._status if self._status else 'unknown'
def run(self):
# launch Firefox
args = [ '-tps', self.testpath,
'-tpsphase', self.phase,
'-tpslogfile', self.logfile ]
if self.ignore_unused_engines:
args.append('--ignore-unused-engines')
self.log('\nLaunching Firefox for phase %s with args %s\n' %
(self.phase, str(args)))
self.firefoxRunner.run(env=self.env,
args=args,
profile=self.profile)
# parse the logfile and look for results from the current test phase
found_test = False
f = open(self.logfile, 'r')
for line in f:
# skip to the part of the log file that deals with the test we're running
if not found_test:
if line.find('Running test %s' % self.testname) > -1:
found_test = True
else:
continue
# look for the status of the current phase
match = self.lineRe.match(line)
if match:
if match.group('matchphase') == self.phase:
self._status = match.group('matchstatus')
break
# set the status to FAIL if there is TPS error
if line.find('CROSSWEAVE ERROR: ') > -1 and not self._status:
self._status = 'FAIL'
self.errline = line[line.find('CROSSWEAVE ERROR: ') + len('CROSSWEAVE ERROR: '):]
f.close()

View file

@ -0,0 +1,491 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
import os
import platform
import random
import re
import tempfile
import time
import traceback
from mozhttpd import MozHttpd
import mozinfo
from mozprofile import Profile
import mozversion
from .firefoxrunner import TPSFirefoxRunner
from .phase import TPSTestPhase
class TempFile(object):
"""Class for temporary files that delete themselves when garbage-collected.
"""
def __init__(self, prefix=None):
self.fd, self.filename = self.tmpfile = tempfile.mkstemp(prefix=prefix)
def write(self, data):
if self.fd:
os.write(self.fd, data)
def close(self):
if self.fd:
os.close(self.fd)
self.fd = None
def cleanup(self):
if self.fd:
self.close()
if os.access(self.filename, os.F_OK):
os.remove(self.filename)
__del__ = cleanup
class TPSTestRunner(object):
extra_env = {
'MOZ_CRASHREPORTER_DISABLE': '1',
'GNOME_DISABLE_CRASH_DIALOG': '1',
'XRE_NO_WINDOWS_CRASH_DIALOG': '1',
'MOZ_NO_REMOTE': '1',
'XPCOM_DEBUG_BREAK': 'warn',
}
default_preferences = {
'app.update.enabled': False,
'browser.dom.window.dump.enabled': True,
'browser.sessionstore.resume_from_crash': False,
'browser.shell.checkDefaultBrowser': False,
'browser.tabs.warnOnClose': False,
'browser.warnOnQuit': False,
# Allow installing extensions dropped into the profile folder
'extensions.autoDisableScopes': 10,
'extensions.getAddons.get.url': 'http://127.0.0.1:4567/addons/api/%IDS%.xml',
# Our pretend addons server doesn't support metadata...
'extensions.getAddons.cache.enabled': False,
'extensions.install.requireSecureOrigin': False,
'extensions.update.enabled': False,
# Don't open a dialog to show available add-on updates
'extensions.update.notifyUser': False,
'services.sync.firstSync': 'notReady',
'services.sync.lastversion': '1.0',
'toolkit.startup.max_resumed_crashes': -1,
# hrm - not sure what the release/beta channels will do?
'xpinstall.signatures.required': False,
}
debug_preferences = {
'services.sync.log.appender.console': 'Trace',
'services.sync.log.appender.dump': 'Trace',
'services.sync.log.appender.file.level': 'Trace',
'services.sync.log.appender.file.logOnSuccess': True,
'services.sync.log.rootLogger': 'Trace',
'services.sync.log.logger.addonutils': 'Trace',
'services.sync.log.logger.declined': 'Trace',
'services.sync.log.logger.service.main': 'Trace',
'services.sync.log.logger.status': 'Trace',
'services.sync.log.logger.authenticator': 'Trace',
'services.sync.log.logger.network.resources': 'Trace',
'services.sync.log.logger.service.jpakeclient': 'Trace',
'services.sync.log.logger.engine.bookmarks': 'Trace',
'services.sync.log.logger.engine.clients': 'Trace',
'services.sync.log.logger.engine.forms': 'Trace',
'services.sync.log.logger.engine.history': 'Trace',
'services.sync.log.logger.engine.passwords': 'Trace',
'services.sync.log.logger.engine.prefs': 'Trace',
'services.sync.log.logger.engine.tabs': 'Trace',
'services.sync.log.logger.engine.addons': 'Trace',
'services.sync.log.logger.engine.apps': 'Trace',
'services.sync.log.logger.identity': 'Trace',
'services.sync.log.logger.userapi': 'Trace',
}
syncVerRe = re.compile(
r'Sync version: (?P<syncversion>.*)\n')
ffVerRe = re.compile(
r'Firefox version: (?P<ffver>.*)\n')
ffBuildIDRe = re.compile(
r'Firefox buildid: (?P<ffbuildid>.*)\n')
def __init__(self, extensionDir,
binary=None,
config=None,
debug=False,
ignore_unused_engines=False,
logfile='tps.log',
mobile=False,
rlock=None,
resultfile='tps_result.json',
testfile=None,
stop_on_error=False):
self.binary = binary
self.config = config if config else {}
self.debug = debug
self.extensions = []
self.ignore_unused_engines = ignore_unused_engines
self.logfile = os.path.abspath(logfile)
self.mobile = mobile
self.rlock = rlock
self.resultfile = resultfile
self.testfile = testfile
self.stop_on_error = stop_on_error
self.addonversion = None
self.branch = None
self.changeset = None
self.errorlogs = {}
self.extensionDir = extensionDir
self.firefoxRunner = None
self.nightly = False
self.numfailed = 0
self.numpassed = 0
self.postdata = {}
self.productversion = None
self.repo = None
self.tpsxpi = None
@property
def mobile(self):
return self._mobile
@mobile.setter
def mobile(self, value):
self._mobile = value
self.synctype = 'desktop' if not self._mobile else 'mobile'
def log(self, msg, printToConsole=False):
"""Appends a string to the logfile"""
f = open(self.logfile, 'a')
f.write(msg)
f.close()
if printToConsole:
print msg
def writeToResultFile(self, postdata, body=None,
sendTo=['crossweave@mozilla.com']):
"""Writes results to test file"""
results = {'results': []}
if os.access(self.resultfile, os.F_OK):
f = open(self.resultfile, 'r')
results = json.loads(f.read())
f.close()
f = open(self.resultfile, 'w')
if body is not None:
postdata['body'] = body
if self.numpassed is not None:
postdata['numpassed'] = self.numpassed
if self.numfailed is not None:
postdata['numfailed'] = self.numfailed
if self.firefoxRunner and self.firefoxRunner.url:
postdata['firefoxrunnerurl'] = self.firefoxRunner.url
postdata['sendTo'] = sendTo
results['results'].append(postdata)
f.write(json.dumps(results, indent=2))
f.close()
def _zip_add_file(self, zip, file, rootDir):
zip.write(os.path.join(rootDir, file), file)
def _zip_add_dir(self, zip, dir, rootDir):
try:
zip.write(os.path.join(rootDir, dir), dir)
except:
# on some OS's, adding directory entries doesn't seem to work
pass
for root, dirs, files in os.walk(os.path.join(rootDir, dir)):
for f in files:
zip.write(os.path.join(root, f), os.path.join(dir, f))
def handle_phase_failure(self, profiles):
for profile in profiles:
self.log('\nDumping sync log for profile %s\n' % profiles[profile].profile)
for root, dirs, files in os.walk(os.path.join(profiles[profile].profile, 'weave', 'logs')):
for f in files:
weavelog = os.path.join(profiles[profile].profile, 'weave', 'logs', f)
if os.access(weavelog, os.F_OK):
with open(weavelog, 'r') as fh:
for line in fh:
possible_time = line[0:13]
if len(possible_time) == 13 and possible_time.isdigit():
time_ms = int(possible_time)
formatted = time.strftime('%Y-%m-%d %H:%M:%S',
time.localtime(time_ms / 1000))
self.log('%s.%03d %s' % (
formatted, time_ms % 1000, line[14:] ))
else:
self.log(line)
def run_single_test(self, testdir, testname):
testpath = os.path.join(testdir, testname)
self.log("Running test %s\n" % testname, True)
# Read and parse the test file, merge it with the contents of the config
# file, and write the combined output to a temporary file.
f = open(testpath, 'r')
testcontent = f.read()
f.close()
try:
test = json.loads(testcontent)
except:
test = json.loads(testcontent[testcontent.find('{'):testcontent.find('}') + 1])
self.preferences['tps.seconds_since_epoch'] = int(time.time())
# generate the profiles defined in the test, and a list of test phases
profiles = {}
phaselist = []
for phase in test:
profilename = test[phase]
# create the profile if necessary
if not profilename in profiles:
profiles[profilename] = Profile(preferences = self.preferences,
addons = self.extensions)
# create the test phase
phaselist.append(TPSTestPhase(
phase,
profiles[profilename],
testname,
testpath,
self.logfile,
self.env,
self.firefoxRunner,
self.log,
ignore_unused_engines=self.ignore_unused_engines))
# sort the phase list by name
phaselist = sorted(phaselist, key=lambda phase: phase.phase)
# run each phase in sequence, aborting at the first failure
failed = False
for phase in phaselist:
phase.run()
if phase.status != 'PASS':
failed = True
break;
for profilename in profiles:
cleanup_phase = TPSTestPhase(
'cleanup-' + profilename,
profiles[profilename], testname,
testpath,
self.logfile,
self.env,
self.firefoxRunner,
self.log)
cleanup_phase.run()
if cleanup_phase.status != 'PASS':
failed = True
# Keep going to run the remaining cleanup phases.
if failed:
self.handle_phase_failure(profiles)
# grep the log for FF and sync versions
f = open(self.logfile)
logdata = f.read()
match = self.syncVerRe.search(logdata)
sync_version = match.group('syncversion') if match else 'unknown'
match = self.ffVerRe.search(logdata)
firefox_version = match.group('ffver') if match else 'unknown'
match = self.ffBuildIDRe.search(logdata)
firefox_buildid = match.group('ffbuildid') if match else 'unknown'
f.close()
if phase.status == 'PASS':
logdata = ''
else:
# we only care about the log data for this specific test
logdata = logdata[logdata.find('Running test %s' % (str(testname))):]
result = {
'PASS': lambda x: ('TEST-PASS', ''),
'FAIL': lambda x: ('TEST-UNEXPECTED-FAIL', x.rstrip()),
'unknown': lambda x: ('TEST-UNEXPECTED-FAIL', 'test did not complete')
} [phase.status](phase.errline)
logstr = "\n%s | %s%s\n" % (result[0], testname, (' | %s' % result[1] if result[1] else ''))
try:
repoinfo = mozversion.get_version(self.binary)
except:
repoinfo = {}
apprepo = repoinfo.get('application_repository', '')
appchangeset = repoinfo.get('application_changeset', '')
# save logdata to a temporary file for posting to the db
tmplogfile = None
if logdata:
tmplogfile = TempFile(prefix='tps_log_')
tmplogfile.write(logdata)
tmplogfile.close()
self.errorlogs[testname] = tmplogfile
resultdata = ({ 'productversion': { 'version': firefox_version,
'buildid': firefox_buildid,
'builddate': firefox_buildid[0:8],
'product': 'Firefox',
'repository': apprepo,
'changeset': appchangeset,
},
'addonversion': { 'version': sync_version,
'product': 'Firefox Sync' },
'name': testname,
'message': result[1],
'state': result[0],
'logdata': logdata
})
self.log(logstr, True)
for phase in phaselist:
print "\t%s: %s" % (phase.phase, phase.status)
return resultdata
def update_preferences(self):
self.preferences = self.default_preferences.copy()
if self.mobile:
self.preferences.update({'services.sync.client.type' : 'mobile'})
# If we are using legacy Sync, then set a dummy username to force the
# correct authentication type. Without this pref set to a value
# without an '@' character, Sync will initialize for FxA.
if self.config.get('auth_type', 'fx_account') != "fx_account":
self.preferences.update({'services.sync.username': "dummy"})
if self.debug:
self.preferences.update(self.debug_preferences)
if 'preferences' in self.config:
self.preferences.update(self.config['preferences'])
self.preferences['tps.config'] = json.dumps(self.config)
def run_tests(self):
# delete the logfile if it already exists
if os.access(self.logfile, os.F_OK):
os.remove(self.logfile)
# Copy the system env variables, and update them for custom settings
self.env = os.environ.copy()
self.env.update(self.extra_env)
# Update preferences for custom settings
self.update_preferences()
# Acquire a lock to make sure no other threads are running tests
# at the same time.
if self.rlock:
self.rlock.acquire()
try:
# Create the Firefox runner, which will download and install the
# build, as needed.
if not self.firefoxRunner:
self.firefoxRunner = TPSFirefoxRunner(self.binary)
# now, run the test group
self.run_test_group()
except:
traceback.print_exc()
self.numpassed = 0
self.numfailed = 1
try:
self.writeToResultFile(self.postdata,
'<pre>%s</pre>' % traceback.format_exc())
except:
traceback.print_exc()
else:
try:
if self.numfailed > 0 or self.numpassed == 0:
To = self.config['email'].get('notificationlist')
else:
To = self.config['email'].get('passednotificationlist')
self.writeToResultFile(self.postdata,
sendTo=To)
except:
traceback.print_exc()
try:
self.writeToResultFile(self.postdata,
'<pre>%s</pre>' % traceback.format_exc())
except:
traceback.print_exc()
# release our lock
if self.rlock:
self.rlock.release()
# dump out a summary of test results
print 'Test Summary\n'
for test in self.postdata.get('tests', {}):
print '%s | %s | %s' % (test['state'], test['name'], test['message'])
def run_test_group(self):
self.results = []
# reset number of passed/failed tests
self.numpassed = 0
self.numfailed = 0
# build our tps.xpi extension
self.extensions = []
self.extensions.append(os.path.join(self.extensionDir, 'tps'))
self.extensions.append(os.path.join(self.extensionDir, "mozmill"))
# build the test list
try:
f = open(self.testfile)
jsondata = f.read()
f.close()
testfiles = json.loads(jsondata)
testlist = testfiles['tests']
except ValueError:
testlist = [os.path.basename(self.testfile)]
testdir = os.path.dirname(self.testfile)
self.mozhttpd = MozHttpd(port=4567, docroot=testdir)
self.mozhttpd.start()
# run each test, and save the results
for test in testlist:
result = self.run_single_test(testdir, test)
if not self.productversion:
self.productversion = result['productversion']
if not self.addonversion:
self.addonversion = result['addonversion']
self.results.append({'state': result['state'],
'name': result['name'],
'message': result['message'],
'logdata': result['logdata']})
if result['state'] == 'TEST-PASS':
self.numpassed += 1
else:
self.numfailed += 1
if self.stop_on_error:
print '\nTest failed with --stop-on-error specified; not running any more tests.\n'
break
self.mozhttpd.stop()
# generate the postdata we'll use to post the results to the db
self.postdata = { 'tests': self.results,
'os': '%s %sbit' % (mozinfo.version, mozinfo.bits),
'testtype': 'crossweave',
'productversion': self.productversion,
'addonversion': self.addonversion,
'synctype': self.synctype,
}