Anders Blomdell / mbed-sdk-tools
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers test.py Source File

test.py

00001 #! /usr/bin/env python2
00002 """
00003 mbed SDK
00004 Copyright (c) 2011-2013 ARM Limited
00005 
00006 Licensed under the Apache License, Version 2.0 (the "License");
00007 you may not use this file except in compliance with the License.
00008 You may obtain a copy of the License at
00009 
00010     http://www.apache.org/licenses/LICENSE-2.0
00011 
00012 Unless required by applicable law or agreed to in writing, software
00013 distributed under the License is distributed on an "AS IS" BASIS,
00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 See the License for the specific language governing permissions and
00016 limitations under the License.
00017 
00018 
00019 TEST BUILD
00020 """
00021 from __future__ import print_function, division, absolute_import
00022 import sys
00023 import os
00024 import fnmatch
00025 
00026 ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
00027 sys.path.insert(0, ROOT)
00028 
00029 from tools.config import ConfigException, Config
00030 from tools.test_configs import get_default_config
00031 from tools.config import ConfigException
00032 from tools.test_api import find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds
00033 import tools.test_configs as TestConfig
00034 from tools.options import get_default_options_parser, extract_profile, extract_mcus
00035 from tools.build_api import build_project, build_library
00036 from tools.build_api import print_build_memory_usage
00037 from tools.build_api import merge_build_data
00038 from tools.targets import TARGET_MAP
00039 from tools.notifier.term import TerminalNotifier
00040 from tools.utils import mkdir, ToolException, NotSupportedException, args_error, write_json_to_file
00041 from tools.test_exporters import ReportExporter, ResultExporterType
00042 from tools.utils import argparse_filestring_type, argparse_lowercase_type, argparse_many
00043 from tools.utils import argparse_dir_not_parent
00044 from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS, TOOLCHAIN_CLASSES
00045 from tools.settings import CLI_COLOR_MAP
00046 
00047 if __name__ == '__main__':
00048     try:
00049         # Parse Options
00050         parser = get_default_options_parser(add_app_config=True)
00051 
00052         parser.add_argument("-D",
00053                           action="append",
00054                           dest="macros",
00055                           help="Add a macro definition")
00056 
00057         parser.add_argument("-j", "--jobs",
00058                           type=int,
00059                           dest="jobs",
00060                           default=0,
00061                           help="Number of concurrent jobs. Default: 0/auto (based on host machine's number of CPUs)")
00062 
00063         parser.add_argument("--source", dest="source_dir",
00064                           type=argparse_filestring_type,
00065                             default=None, help="The source (input) directory (for sources other than tests). Defaults to current directory.", action="append")
00066 
00067         parser.add_argument("--build", dest="build_dir", type=argparse_dir_not_parent(ROOT),
00068                           default=None, help="The build (output) directory")
00069 
00070         parser.add_argument("-l", "--list", action="store_true", dest="list",
00071                           default=False, help="List (recursively) available tests in order and exit")
00072 
00073         parser.add_argument("-p", "--paths", dest="paths",
00074                           type=argparse_many(argparse_filestring_type),
00075                           default=None, help="Limit the tests to those within the specified comma separated list of paths")
00076 
00077         format_choices = ["list", "json"]
00078         format_default_choice = "list"
00079         format_help = "Change the format in which tests are listed. Choices include: %s. Default: %s" % (", ".join(format_choices), format_default_choice)
00080         parser.add_argument("-f", "--format", dest="format",
00081                             type=argparse_lowercase_type(format_choices, "format"),
00082                             default=format_default_choice, help=format_help)
00083 
00084         parser.add_argument("--continue-on-build-fail", action="store_true", dest="continue_on_build_fail",
00085                           default=None, help="Continue trying to build all tests if a build failure occurs")
00086 
00087         #TODO validate the names instead of just passing through str
00088         parser.add_argument("-n", "--names", dest="names", type=argparse_many(str),
00089                           default=None, help="Limit the tests to a comma separated list of names")
00090 
00091         parser.add_argument("--test-config", dest="test_config", type=str,
00092                           default=None, help="Test config for a module")
00093 
00094         parser.add_argument("--test-spec", dest="test_spec",
00095                           default=None, help="Destination path for a test spec file that can be used by the Greentea automated test tool")
00096 
00097         parser.add_argument("--build-report-junit", dest="build_report_junit",
00098                           default=None, help="Destination path for a build report in the JUnit xml format")
00099         parser.add_argument("--build-data",
00100                             dest="build_data",
00101                             default=None,
00102                             help="Dump build_data to this file")
00103 
00104         parser.add_argument("-v", "--verbose",
00105                           action="store_true",
00106                           dest="verbose",
00107                           default=False,
00108                           help="Verbose diagnostic output")
00109 
00110         parser.add_argument("--stats-depth",
00111                             type=int,
00112                             dest="stats_depth",
00113                             default=2,
00114                             help="Depth level for static memory report")
00115         parser.add_argument("--ignore", dest="ignore", type=argparse_many(str),
00116                           default=None, help="Comma separated list of patterns to add to mbedignore (eg. ./main.cpp)")
00117         parser.add_argument("--icetea",
00118                             action="store_true",
00119                             dest="icetea",
00120                             default=False,
00121                             help="Only icetea tests")
00122 
00123         parser.add_argument("--greentea",
00124                             action="store_true",
00125                             dest="greentea",
00126                             default=False,
00127                             help="Only greentea tests")
00128 
00129         options = parser.parse_args()
00130 
00131         # Filter tests by path if specified
00132         if options.paths:
00133             all_paths = options.paths
00134         else:
00135             all_paths = ["."]
00136 
00137         all_tests = {}
00138         tests = {}
00139 
00140         # As default both test tools are enabled
00141         if not (options.greentea or options.icetea):
00142             options.greentea = True
00143             options.icetea = True
00144 
00145         # Target
00146         if options.mcu is None:
00147             args_error(parser, "argument -m/--mcu is required")
00148         mcu = extract_mcus(parser, options)[0]
00149 
00150         # Toolchain
00151         if options.tool is None:
00152             args_error(parser, "argument -t/--tool is required")
00153         toolchain = options.tool[0]
00154 
00155         if not TOOLCHAIN_CLASSES[toolchain].check_executable():
00156             search_path = TOOLCHAIN_PATHS[toolchain] or "No path set"
00157             args_error(parser, "Could not find executable for %s.\n"
00158                                "Currently set search path: %s"
00159                        % (toolchain, search_path))
00160 
00161         # Assign config file. Precedence: test_config>app_config
00162         # TODO: merge configs if both given
00163         if options.test_config:
00164             config = get_test_config(options.test_config, mcu)
00165             if not config:
00166                 args_error(parser, "argument --test-config contains invalid path or identifier")
00167         elif options.app_config:
00168             config = options.app_config
00169         else:
00170             config = Config.find_app_config(options.source_dir)
00171 
00172         if not config:
00173             config = get_default_config(options.source_dir or ['.'], mcu)
00174 
00175 
00176         # Find all tests in the relevant paths
00177         for path in all_paths:
00178             all_tests.update(find_tests(
00179                 base_dir=path,
00180                 target_name=mcu,
00181                 toolchain_name=toolchain,
00182                 icetea=options.icetea,
00183                 greentea=options.greentea,
00184                 app_config=config))
00185 
00186         # Filter tests by name if specified
00187         if options.names:
00188             all_names = options.names
00189             all_names = [x.lower() for x in all_names]
00190 
00191             for name in all_names:
00192                 if any(fnmatch.fnmatch(testname, name) for testname in all_tests):
00193                     for testname, test in all_tests.items():
00194                         if fnmatch.fnmatch(testname, name):
00195                             tests[testname] = test
00196                 else:
00197                     print("[Warning] Test with name '%s' was not found in the "
00198                           "available tests" % (name))
00199         else:
00200             tests = all_tests
00201 
00202 
00203         if options.list:
00204             # Print available tests in order and exit
00205             print_tests(tests, options.format)
00206             sys.exit(0)
00207         else:
00208             # Build all tests
00209             if not options.build_dir:
00210                 args_error(parser, "argument --build is required")
00211 
00212             base_source_paths = options.source_dir
00213 
00214             # Default base source path is the current directory
00215             if not base_source_paths:
00216                 base_source_paths = ['.']
00217 
00218             build_report = {}
00219             build_properties = {}
00220 
00221             library_build_success = False
00222             profile = extract_profile(parser, options, toolchain)
00223             try:
00224                 # Build sources
00225                 notify = TerminalNotifier(options.verbose)
00226                 build_library(base_source_paths, options.build_dir, mcu,
00227                               toolchain, jobs=options.jobs,
00228                               clean=options.clean, report=build_report,
00229                               properties=build_properties, name="mbed-build",
00230                               macros=options.macros,
00231                               notify=notify, archive=False,
00232                               app_config=config,
00233                               build_profile=profile,
00234                               ignore=options.ignore)
00235 
00236                 library_build_success = True
00237             except ToolException as e:
00238                 # ToolException output is handled by the build log
00239                 pass
00240             except NotSupportedException as e:
00241                 # NotSupportedException is handled by the build log
00242                 pass
00243             except Exception as e:
00244                 if options.verbose:
00245                     import traceback
00246                     traceback.print_exc()
00247                 # Some other exception occurred, print the error message
00248                 print(e)
00249 
00250             if not library_build_success:
00251                 print("Failed to build library")
00252             else:
00253                 # Build all the tests
00254                 notify = TerminalNotifier(options.verbose)
00255                 test_build_success, test_build = build_tests(
00256                     tests,
00257                     [os.path.relpath(options.build_dir)],
00258                     options.build_dir,
00259                     mcu,
00260                     toolchain,
00261                     clean=options.clean,
00262                     report=build_report,
00263                     properties=build_properties,
00264                     macros=options.macros,
00265                     notify=notify,
00266                     jobs=options.jobs,
00267                     continue_on_build_fail=options.continue_on_build_fail,
00268                     app_config=config,
00269                     build_profile=profile,
00270                     stats_depth=options.stats_depth,
00271                     ignore=options.ignore)
00272 
00273                 # If a path to a test spec is provided, write it to a file
00274                 if options.test_spec:
00275                     write_json_to_file(test_spec_from_test_builds(test_build), options.test_spec)
00276 
00277             # If a path to a JUnit build report spec is provided, write it to a file
00278             if options.build_report_junit:
00279                 report_exporter = ReportExporter(ResultExporterType.JUNIT, package="build")
00280                 report_exporter.report_to_file(build_report, options.build_report_junit, test_suite_properties=build_properties)
00281 
00282             # Print memory map summary on screen
00283             if build_report:
00284                 print
00285                 print(print_build_memory_usage(build_report))
00286 
00287             print_report_exporter = ReportExporter(ResultExporterType.PRINT, package="build")
00288             status = print_report_exporter.report(build_report)
00289             if options.build_data:
00290                 merge_build_data(options.build_data, build_report, "test")
00291 
00292             if status:
00293                 sys.exit(0)
00294             else:
00295                 sys.exit(1)
00296 
00297     except KeyboardInterrupt as e:
00298         print("\n[CTRL+c] exit")
00299     except ConfigException as e:
00300         # Catching ConfigException here to prevent a traceback
00301         print("[ERROR] %s" % str(e))
00302     except Exception as e:
00303         import traceback
00304         traceback.print_exc(file=sys.stdout)
00305         print("[ERROR] %s" % str(e))
00306         sys.exit(1)
00307