mirror of https://github.com/ARMmbed/mbed-os.git
351 lines
13 KiB
Python
351 lines
13 KiB
Python
#! /usr/bin/env python2
|
|
"""
|
|
Copyright 2018 ARM Limited
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
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.
|
|
"""
|
|
|
|
from __future__ import print_function, division, absolute_import
|
|
import sys
|
|
import os
|
|
import re
|
|
from os.path import abspath, join, dirname, relpath, sep
|
|
import json, operator
|
|
import traceback
|
|
from fnmatch import translate
|
|
from argparse import ArgumentParser
|
|
|
|
ROOT = abspath(join(dirname(__file__), '..'))
|
|
sys.path.insert(0, ROOT)
|
|
|
|
from tools.config import ConfigException
|
|
from tools.utils import cmd, run_cmd
|
|
|
|
plugins_path = abspath(join(ROOT, 'TEST_APPS', 'icetea_plugins', 'plugins_to_load.py'))
|
|
|
|
|
|
def find_build_from_build_data(build_data, id, target, toolchain):
|
|
if 'builds' not in build_data:
|
|
raise Exception("build data is in wrong format, does not include builds object")
|
|
|
|
for build in build_data['builds']:
|
|
if 'id' in build.keys() \
|
|
and id.upper() in build['id'].upper() \
|
|
and 'target_name' in build.keys() \
|
|
and target.upper() == build['target_name'].upper() \
|
|
and 'toolchain_name' in build.keys() \
|
|
and toolchain.upper() == build['toolchain_name'].upper() \
|
|
and 'result' in build.keys() \
|
|
and "OK" == build['result']:
|
|
return build
|
|
return None
|
|
|
|
|
|
def create_test_suite(target, tool, icetea_json_output, build_data, tests_by_name):
|
|
"""
|
|
Create test suite content
|
|
:param target:
|
|
:param tool:
|
|
:param icetea_json_output:
|
|
:param build_data:
|
|
:return:
|
|
"""
|
|
test_suite = dict()
|
|
test_suite['testcases'] = list()
|
|
|
|
for test in icetea_json_output:
|
|
skip = False
|
|
|
|
for dut in test['requirements']['duts'].values():
|
|
# Set binary path based on application name
|
|
if 'application' in dut.keys() and 'name' in dut['application'].keys():
|
|
build = find_build_from_build_data(
|
|
build_data=build_data,
|
|
id=dut['application']['name'],
|
|
target=target,
|
|
toolchain=tool)
|
|
if build:
|
|
try:
|
|
dut['application']['bin'] = build['bin_fullpath']
|
|
except KeyError:
|
|
raise Exception('Full path is missing from build: {}'.format(build))
|
|
else:
|
|
skip = True
|
|
|
|
if not tests_by_name or is_test_in_test_by_name(test['name'], tests_by_name):
|
|
test_case = {
|
|
'name': test['name'],
|
|
'config': {
|
|
'requirements': set_allowed_platform(test['requirements'], target)
|
|
}
|
|
}
|
|
|
|
# Skip test if not binary path
|
|
if skip:
|
|
test_case['config']['execution'] = {
|
|
'skip': {
|
|
'value': True,
|
|
'reason': "Test requiring application binary not build"
|
|
}
|
|
}
|
|
|
|
test_suite['testcases'].append(test_case)
|
|
|
|
return test_suite
|
|
|
|
|
|
def set_allowed_platform(requirements, target):
|
|
"""
|
|
Allowed platform restrict icetea to run tests on specific board
|
|
This targets tests to the right board in case that user has multiple ones connected same time
|
|
"""
|
|
if '*' not in requirements['duts'].keys():
|
|
requirements['duts']['*'] = dict()
|
|
requirements['duts']['*']['allowed_platforms'] = [target]
|
|
return requirements
|
|
|
|
|
|
def get_applications(test):
|
|
ret = list()
|
|
for dut in test['requirements']['duts'].values():
|
|
if 'application' in dut.keys() and 'name' in dut['application'].keys():
|
|
ret.append(dut['application']['name'])
|
|
return ret
|
|
|
|
|
|
def filter_test_by_build_data(icetea_json_output, build_data, target, toolchain):
|
|
if not build_data:
|
|
return icetea_json_output
|
|
|
|
ret = list()
|
|
for test in icetea_json_output:
|
|
for dut in test['requirements']['duts'].values():
|
|
if 'application' in dut.keys() and 'name' in dut['application'].keys():
|
|
id = dut['application']['name']
|
|
if find_build_from_build_data(build_data, id, target, toolchain):
|
|
# Test requiring build found
|
|
ret.append(test)
|
|
return ret
|
|
|
|
|
|
def filter_test_by_name(icetea_json_output, test_by_name):
|
|
if not test_by_name:
|
|
return icetea_json_output
|
|
ret = list()
|
|
for test_temp in icetea_json_output:
|
|
if is_test_in_test_by_name(test_temp['name'], test_by_name) and test_temp not in ret:
|
|
ret.append(test_temp)
|
|
return ret
|
|
|
|
|
|
def get_applications_from_test(test):
|
|
ret = list()
|
|
if u'requirements' in test.keys() and u'duts' in test[u'requirements']:
|
|
for name, dut in test[u'requirements'][u'duts'].items():
|
|
if u'application' in dut.keys() and u'name' in dut[u'application']:
|
|
ret.append(dut[u'application'][u'name'])
|
|
return ret
|
|
|
|
|
|
def get_application_list(icetea_json_output, tests_by_name):
|
|
""" Return comma separated list of application which are used in tests """
|
|
ret = list()
|
|
for test in filter_test_by_name(icetea_json_output, tests_by_name):
|
|
ret.extend(get_applications_from_test(test))
|
|
# Remove duplicates
|
|
return list(set(ret))
|
|
|
|
|
|
def icetea_tests(target, tcdir, verbose):
|
|
if not os.path.exists(tcdir):
|
|
raise Exception("Icetea run error: No TEST_APPS folder in {}".format(os.path.curdir))
|
|
|
|
command = ['icetea', '--tcdir', tcdir, '--list', '--json', '--platform_filter', target] \
|
|
+ (['-v'] if verbose else [])
|
|
|
|
stdout, stderr, returncode = run_cmd(command)
|
|
|
|
list_json = json.loads(stdout)
|
|
list_json.sort(key=operator.itemgetter('name'))
|
|
|
|
if returncode != 0:
|
|
additional_information = "\ncwd:{} \nCommand:'{}' \noutput:{}".format(os.getcwd(), ' '.join(command),
|
|
stderr.decode())
|
|
raise Exception("Error when running icetea. {}".format(additional_information))
|
|
|
|
return list_json
|
|
|
|
|
|
def is_test_in_test_by_name(test_name, test_by_name):
|
|
for tbn_temp in test_by_name:
|
|
if re.search(translate(tbn_temp), test_name):
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_tests(icetea_json_output):
|
|
"""
|
|
Check that all tests have all necessary information
|
|
:return:
|
|
"""
|
|
for test in icetea_json_output:
|
|
if not get_applications_from_test(test):
|
|
raise Exception('Test {} does not have application with correct name'.format(test['name']))
|
|
|
|
|
|
def load_build_data(build_data_path):
|
|
"""
|
|
:return: build_data.json content as dict and None if build data is not available
|
|
"""
|
|
if not os.path.isfile(build_data_path):
|
|
return None
|
|
return json.load(open(build_data_path))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
# Parse Options
|
|
parser = ArgumentParser()
|
|
|
|
parser.add_argument('-m', '--mcu',
|
|
dest='target',
|
|
default=None,
|
|
help='Test target MCU',
|
|
required=True)
|
|
|
|
parser.add_argument('-t', '--toolchain',
|
|
dest='toolchain',
|
|
default=None,
|
|
help='Toolchain',
|
|
required=True)
|
|
|
|
parser.add_argument('--build-data',
|
|
dest='build_data',
|
|
default=None,
|
|
help='Detail data from build')
|
|
|
|
parser.add_argument('--test-suite',
|
|
dest='test_suite',
|
|
default=None,
|
|
help='Path used for test suite file')
|
|
|
|
parser.add_argument('-n', '--tests-by-name',
|
|
dest='tests_by_name',
|
|
default=None,
|
|
help='Limit the tests to a list (ex. test1,test2,test3)')
|
|
|
|
parser.add_argument('--tcdir',
|
|
dest='tcdir',
|
|
default='TEST_APPS',
|
|
help='Test case directory',
|
|
required=False)
|
|
|
|
parser.add_argument('--compile-list',
|
|
action='store_true',
|
|
dest='compile_list',
|
|
default=False,
|
|
help='List tests, which applications can be compiled')
|
|
|
|
parser.add_argument('--run-list',
|
|
action='store_true',
|
|
dest='run_list',
|
|
default=False,
|
|
help='List tests, which applications are compiled and ready for run')
|
|
|
|
parser.add_argument('--application-list',
|
|
action='store_true',
|
|
dest='application_list',
|
|
default=False,
|
|
help='List applications that need to be build')
|
|
|
|
parser.add_argument('--ignore-checks',
|
|
action='store_true',
|
|
dest='ignore_checks',
|
|
default=False,
|
|
help='Ignore data validation checks')
|
|
|
|
parser.add_argument('-v', '--verbose',
|
|
action='store_true',
|
|
dest='verbose',
|
|
default=False,
|
|
help='Verbose diagnostic output')
|
|
|
|
options = parser.parse_args()
|
|
|
|
icetea_json_output = icetea_tests(options.target, options.tcdir, options.verbose)
|
|
tests_by_name = options.tests_by_name.split(',') if options.tests_by_name else None
|
|
build_data = load_build_data(options.build_data) if options.build_data else None
|
|
|
|
if not options.ignore_checks:
|
|
check_tests(icetea_json_output)
|
|
|
|
if options.compile_list:
|
|
print('Available icetea tests for build \'{}-{}\', location \'{}\''.format(
|
|
options.target, options.toolchain, options.tcdir))
|
|
for test in icetea_json_output:
|
|
print(
|
|
'Test Case:\n Name: {name}\n Path: .{sep}{filepath}\n Test applications: .{sep}{apps}'.format(
|
|
name=test['name'],
|
|
sep=sep,
|
|
filepath=relpath(test['filepath'], ROOT),
|
|
apps=''.join(get_applications(test)).replace('-', os.path.sep)))
|
|
|
|
elif options.run_list:
|
|
print('Available icetea tests for build \'{}-{}\', location \'{}\''.format(
|
|
options.target, options.toolchain, options.tcdir))
|
|
|
|
# Filters
|
|
tests = filter_test_by_name(icetea_json_output, tests_by_name)
|
|
if build_data:
|
|
tests = filter_test_by_build_data(tests, build_data, options.target, options.toolchain)
|
|
|
|
for test in tests:
|
|
print(' test \'{name}\''.format(name=test['name']))
|
|
|
|
elif options.application_list:
|
|
print(','.join(get_application_list(icetea_json_output, tests_by_name)))
|
|
|
|
else:
|
|
if not build_data:
|
|
raise Exception("Build data file does not exist: {}".format(options.build_data))
|
|
|
|
test_suite = create_test_suite(options.target, options.toolchain, icetea_json_output, build_data,
|
|
tests_by_name)
|
|
|
|
if not test_suite['testcases']:
|
|
raise Exception("Test suite is empty. Check that --tcdir and --tests-by-name have correct values")
|
|
|
|
if not options.test_suite:
|
|
raise Exception('--test-suite is required when running tests')
|
|
|
|
with open(options.test_suite, 'w') as f:
|
|
json.dump(test_suite, f, indent=2)
|
|
|
|
# List just for debug
|
|
if options.verbose:
|
|
cmd(['icetea', '--tcdir', options.tcdir, '--list'] + (['-v'] if options.verbose else []))
|
|
|
|
cmd(['icetea', '--tcdir', options.tcdir, '--suite', options.test_suite, '--clean', '--plugin_path',
|
|
plugins_path] + (['-v'] if options.verbose else []))
|
|
|
|
except KeyboardInterrupt as e:
|
|
print('\n[CTRL+c] exit')
|
|
except ConfigException as e:
|
|
# Catching ConfigException here to prevent a traceback
|
|
print('[ERROR] {}'.format(e))
|
|
except Exception as e:
|
|
traceback.print_exc(file=sys.stdout)
|
|
print('[ERROR] {}'.format(e))
|
|
sys.exit(1)
|