premiere-libtorrent/tools/run_tests.py

380 lines
10 KiB
Python
Raw Normal View History

#!/bin/python
# Copyright (c) 2013, Arvid Norberg
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the distribution.
# * Neither the name of the author nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# this is meant to be run from the root of the repository
# the arguments are the boost-build toolsets to use.
# these will vary between testers and operating systems
# common ones are: clang, darwin, gcc, msvc, icc
import random
import os
import platform
import subprocess
import xml.etree.ElementTree as et
from datetime import datetime
import json
import sys
import yaml
2013-06-21 07:47:30 +02:00
import glob
import shutil
import traceback
2014-01-26 18:31:06 +01:00
import clean
# the .regression.yml configuration file format looks like this (it's yaml):
# test_dirs:
# - <path-to-test-folder>
# - ...
#
# features:
# - <list of boost-built features>
# - ...
#
def svn_info():
# figure out which revision this is
p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE)
revision = -1
author = ''
for l in p.stdout:
if 'Last Changed Rev' in l:
revision = int(l.split(':')[1].strip())
if 'Last Changed Author' in l:
author = l.split(':')[1].strip()
if revision == -1:
print 'Failed to extract subversion revision'
sys.exit(1)
if author == '':
print 'Failed to extract subversion author'
sys.exit(1)
return (revision, author)
2014-01-26 18:31:06 +01:00
def run_tests(toolset, tests, features, options, test_dir, time_limit):
2013-06-23 03:34:19 +02:00
assert(type(features) == str)
xml_file = 'bjam_build.%d.xml' % random.randint(0, 100000)
try:
results = {}
feature_list = features.split(' ')
os.chdir(test_dir)
2014-03-30 11:11:38 +02:00
c = 0
for t in tests:
2014-03-30 11:11:38 +02:00
c = c + 1
2013-10-03 17:47:58 +02:00
options_copy = options[:]
if t != '': options_copy.append(t)
if t == '':
t = os.path.split(os.getcwd())[1]
# we can't pass in a launcher when just building, that only
# works for actual unit tests
2013-10-03 17:47:58 +02:00
if 'launcher=valgrind' in options_copy:
options_copy.remove('launcher=valgrind')
2014-03-17 00:55:18 +01:00
cmdline = ['bjam', '--out-xml=%s' % xml_file, '-l%d' % time_limit, \
'-q', '--abbreviate-paths', toolset] + options_copy + feature_list
2014-07-06 21:18:00 +02:00
# print 'calling ', cmdline
p = subprocess.Popen(cmdline, stdout=subprocess.PIPE, cwd=test_dir)
output = ''
for l in p.stdout:
if 'launcher=valgrind' in options_copy and l.startswith('chase_cuOff'):
continue
output += l.decode('latin-1')
2014-03-30 11:11:38 +02:00
sys.stdout.write('.')
sys.stdout.flush()
p.wait()
# parse out the toolset version from the xml file
compiler = ''
compiler_version = ''
command = ''
# make this parse the actual test to pick up the time
# spent runnin the test
try:
dom = et.parse(xml_file)
command = dom.find('./command').text
prop = dom.findall('./action/properties/property')
for a in prop:
name = a.attrib['name']
if name == 'toolset':
compiler = a.text
if compiler_version != '': break
if name.startswith('toolset-') and name.endswith(':version'):
compiler_version = a.text
if compiler != '': break
if compiler != '' and compiler_version != '':
toolset = compiler + '-' + compiler_version
except: pass
r = { 'status': p.returncode, 'output': output, 'command': command }
results[t + '|' + features] = r
2014-03-30 11:11:38 +02:00
if p.returncode != 0:
# if the build or test failed, print out the
# important parts
2014-03-30 11:11:38 +02:00
sys.stdout.write('\n')
print command
for l in output:
if 'error: ' in l or \
': fatal error: ' in l or \
'failed to write output file' in l or \
': error C' in l or \
'undefined reference to ' in l or \
' error LNK' in l or \
'TEST_CHECK' in l or \
'TEST_EQUAL_ERROR' in l or \
'"ERROR: "' in l or \
l.startswith('EXIT STATUS: ') or \
' second time limit exceeded' in l or \
l.startswith('signal: SIG') or \
'jump or move depends on uninitialised value(s)' in l or \
'Invalid read of size' in l or \
'Invalid write of size' in l or \
'Use of uninitialised value of size' in l or \
'Uninitialised byte(s) found during' in l or \
'points to uninitialised byte(s)' in l:
print l
2014-03-30 11:11:38 +02:00
print '\n%s - %d / %d' % (toolset, c, len(tests))
except Exception, e:
# need this to make child processes exit
print 'exiting test process: ', traceback.format_exc()
sys.exit(1)
finally:
try: os.unlink(xml_file)
except: pass
return (toolset, results)
def print_usage():
print '''usage: run_tests.py [options] bjam-toolset [bjam-toolset...] [bjam-option...]
options:
-j<n> use n parallel processes
-h prints this message and exits
-i build incrementally (i.e. don't clean between checkouts)
-valgrind run tests with valgrind (requires valgrind to be installed)
'''
def main(argv):
toolsets = []
incremental = False
test_dirs = []
2014-03-17 00:55:18 +01:00
build_dirs = []
configs = []
options = ['boost=source', 'preserve-test-targets=on']
time_limit = 1200
for arg in argv:
if arg[0] == '-':
if arg[1] == 'j':
num_processes = int(arg[2:])
2014-03-30 11:11:38 +02:00
options.append('-j%d' % num_processes)
elif arg[1] == 'h':
print_usage()
sys.exit(1)
elif arg[1] == 'i':
incremental = True
elif arg[1:] == 'valgrind':
options.append('launcher=valgrind')
else:
print 'unknown option: %s' % arg
print_usage()
sys.exit(1)
elif '=' in arg:
options.append(arg)
else:
toolsets.append(arg)
if toolsets == []:
print_usage()
sys.exit(1)
2014-01-26 18:31:06 +01:00
if not incremental:
print 'cleaning repo'
clean.clean()
try:
cfg = open('.regression.yml', 'r')
except:
print '.regression.yml not found in current directory'
sys.exit(1)
cfg = yaml.load(cfg.read())
if 'test_dirs' in cfg:
for d in cfg['test_dirs']:
test_dirs.append(os.path.abspath(d))
2014-03-17 00:55:18 +01:00
if 'build_dirs' in cfg:
for d in cfg['build_dirs']:
build_dirs.append(os.path.abspath(d))
test_dirs.append(os.path.abspath(d))
if len(build_dirs) == 0 and len(test_dirs) == 0:
print 'no test or build directory specified by .regression.yml'
sys.exit(1)
configs = []
if 'features' in cfg:
for d in cfg['features']:
configs.append(d)
else:
2013-06-23 03:34:19 +02:00
configs = ['']
2014-03-17 00:55:18 +01:00
build_configs = []
if 'build_features' in cfg:
for d in cfg['build_features']:
build_configs.append(d)
clean_files = []
if 'clean' in cfg:
clean_files = cfg['clean']
branch_name = 'trunk'
if 'branch' in cfg:
branch_name = cfg['branch']
2013-06-17 08:54:00 +02:00
if 'time_limit' in cfg:
time_limit = int(cfg['time_limit'])
# it takes a bit longer to run in valgrind
if 'launcher=valgrind' in options:
2014-03-17 00:55:18 +01:00
time_limit *= 7
architecture = platform.machine()
build_platform = platform.system() + '-' + platform.release()
revision, author = svn_info()
timestamp = datetime.now()
print '%s-%d - %s - %s' % (branch_name, revision, author, timestamp)
print 'toolsets: %s' % ' '.join(toolsets)
# print 'configs: %s' % '|'.join(configs)
current_dir = os.getcwd()
try:
rev_dir = os.path.join(current_dir, 'regression_tests')
try: os.mkdir(rev_dir)
except: pass
rev_dir = os.path.join(rev_dir, '%s-%d' % (branch_name, revision))
try: os.mkdir(rev_dir)
except: pass
2014-03-30 11:11:38 +02:00
for toolset in toolsets:
results = {}
for test_dir in test_dirs:
print 'running tests from "%s" in %s' % (test_dir, branch_name)
os.chdir(test_dir)
test_dir = os.getcwd()
# figure out which tests are exported by this Jamfile
p = subprocess.Popen(['bjam', '--dump-tests', 'non-existing-target'], stdout=subprocess.PIPE, cwd=test_dir)
tests = []
output = ''
for l in p.stdout:
output += l
if not 'boost-test(RUN)' in l: continue
test_name = os.path.split(l.split(' ')[1][1:-1])[1]
tests.append(test_name)
print 'found %d tests' % len(tests)
if len(tests) == 0:
tests = ['']
2014-03-17 00:55:18 +01:00
additional_configs = []
if test_dir in build_dirs:
additional_configs = build_configs
2014-03-17 00:55:18 +01:00
futures = []
for features in configs + additional_configs:
2014-03-30 11:11:38 +02:00
(compiler, r) = run_tests(toolset, tests, features, options, test_dir, time_limit)
results.update(r)
2014-03-17 00:55:18 +01:00
2013-06-18 04:30:41 +02:00
print ''
if len(clean_files) > 0:
2013-06-23 03:34:19 +02:00
print 'deleting ',
for filt in clean_files:
2014-03-30 11:11:38 +02:00
for f in glob.glob(os.path.join(test_dir, filt)):
2014-01-26 18:31:06 +01:00
# a precaution to make sure a malicious repo
# won't clean things outside of the test directory
if not os.path.abspath(f).startswith(test_dir): continue
2013-06-23 03:34:19 +02:00
print '%s ' % f,
try: shutil.rmtree(f)
except: pass
print ''
# each file contains a full set of tests for one speific toolset and platform
try:
f = open(os.path.join(rev_dir, build_platform + '#' + toolset + '.json'), 'w+')
except IOError, e:
print e
rev_dir = os.path.join(current_dir, 'regression_tests')
try: os.mkdir(rev_dir)
except: pass
rev_dir = os.path.join(rev_dir, '%s-%d' % (branch_name, revision))
try: os.mkdir(rev_dir)
except: pass
f = open(os.path.join(rev_dir, build_platform + '#' + toolset + '.json'), 'w+')
2014-03-30 11:11:38 +02:00
print >>f, json.dumps(results)
f.close()
finally:
# always restore current directory
2013-06-22 22:32:17 +02:00
try:
os.chdir(current_dir)
except: pass
if __name__ == "__main__":
main(sys.argv[1:])