Clone of official tools

Revision:
43:2a7da56ebd24
diff -r 2cf3f29fece1 -r 2a7da56ebd24 run_icetea.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/run_icetea.py	Tue Sep 25 13:43:09 2018 -0500
@@ -0,0 +1,342 @@
+#! /usr/bin/env python2
+"""
+Copyright 2018 ARM Limited
+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
+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):
+    command = ['icetea', '--tcdir', tcdir, '--list', '--json', '--platform_filter', target] \
+              + (['-v'] if verbose else [])
+
+    stdout, stderr, returncode = run_cmd(command)
+
+    if returncode != 0:
+        raise Exception(
+            "Error when running icetea. \ncwd:{} \nCommand:'{}' \noutput:{}".format(os.getcwd(), ' '.join(command),
+                                                                                    stderr.decode()))
+
+    return json.loads(stdout)
+
+
+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)