Files
core-build/tests/freenas/main.py
2016-09-30 23:20:54 +02:00

180 lines
6.7 KiB
Python
Executable File

#+
# Copyright 2016 iXsystems, Inc.
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
#
import os
import json
import time
import argparse
import shutil
import subprocess
from utils import e, sh, objdir, info
from xml.etree.ElementTree import Element, SubElement, tostring, parse
from distutils.core import run_setup
from xml.dom import minidom
EXCLUDES = ['os', 'objs', 'ports', 'release', 'release.build.log', 'repo-manifest']
venvdir = objdir('tests/venv')
output_root = objdir('test-output')
class Main(object):
def __init__(self):
self.test_suites = []
self.output_path = None
self.excluded = ['os', 'objs', 'ports', 'release']
def find_tests(self):
info('Looking for test manifests in ${{BE_ROOT}}')
for dir in os.listdir(e('${BE_ROOT}')):
if dir not in self.excluded:
for root, _, files in os.walk(os.path.join(e('${BE_ROOT}'), dir)):
if os.path.split(root)[1] == 'tests' and 'MANIFEST.json' in files:
info('Found test manifest at {0}', root)
self.test_suites.append(root)
def load_manifest(self, path):
with open(os.path.join(path, 'MANIFEST.json'), 'r') as manifest_file:
return json.load(manifest_file)
def run(self):
for s in self.test_suites:
script = os.path.join(s, 'run.py')
start_time = time.time()
manifest = self.load_manifest(s)
os.chdir(s)
info("Running tests from {0}".format(s))
args = [e('${venvdir}/bin/python'), script]
test = None
try:
test = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=True
)
test.wait(timeout=manifest['timeout'])
except subprocess.TimeoutExpired as err:
self.generate_suite_error(
os.path.join(s, 'results.xml'),
manifest['name'],
time.time() - start_time,
'Test timeout reached',
err
)
except subprocess.SubprocessError as err:
self.generate_suite_error(
os.path.join(s, 'results.xml'),
manifest['name'],
time.time() - start_time,
'Test could not be started',
err
)
out, err = test.communicate()
if test and test.returncode:
self.generate_suite_error(
os.path.join(s, 'results.xml'),
manifest['name'],
time.time() - start_time,
'Test process has returned an error',
out
)
info("{0} error:".format(script))
print(out.decode('utf-8'))
def aggregate_results(self):
sh('mkdir -p ${output_root}')
for s in self.test_suites:
manifest = self.load_manifest(s)
try:
shutil.move(
os.path.join(s, 'results.xml'),
os.path.join(output_root, '{}-results.xml'.format(manifest['name']))
)
except FileNotFoundError as e:
self.generate_suite_error(
os.path.join(output_root, '{}-results.xml'.format(manifest['name'])),
manifest['name'],
0,
'Results file not found',
e
)
results = Element('testsuites')
for r in os.listdir(output_root):
if r.endswith('results.xml'):
single_result = parse(os.path.join(output_root, r))
results.append(single_result.getroot())
with open(os.path.join(output_root, 'aggregated_results.xml'), 'w') as output_file:
output_file.write(self.print_xml(results))
def generate_suite_error(self, out_path, name, test_time, text, err):
top = Element('testsuite', errors="1", failures="0", name=name, skipped="0", tests='0', time=str(test_time))
case = SubElement(top, 'testcase', classname="UNDEFINED", name="UNDEFINED", time=str(test_time))
error = SubElement(case, 'error', message=text)
error.text = str(err)
SubElement(case, 'system-err')
with open(out_path, 'w') as output_file:
output_file.write(self.print_xml(top))
def print_xml(self, elem):
rough_string = tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
def main(self):
parser = argparse.ArgumentParser()
parser.add_argument('-a', metavar='ADDRESS', required=True, help='FreeNAS box address')
parser.add_argument('-u', metavar='USERNAME', required=True, help='Username')
parser.add_argument('-p', metavar='PASSWORD', required=True, help='Password')
args = parser.parse_args()
os.environ['TEST_HOST'] = args.a
os.environ['TEST_USERNAME'] = args.u
os.environ['TEST_PASSWORD'] = args.p
os.environ['TEST_XML'] = 'yes'
print('Test VM address: {0}'.format(args.a))
print('Test VM username: {0}'.format(args.u))
print('Test VM password: {0}'.format(args.p))
self.find_tests()
self.run()
self.aggregate_results()
if __name__ == '__main__':
m = Main()
m.main()