1#!/usr/bin/env python3 2# SPDX-License-Identifier: GPL-2.0+ 3 4# Copyright (c) 2016 Google, Inc 5# Written by Simon Glass <sjg@chromium.org> 6# 7# Creates binary images from input files controlled by a description 8# 9 10"""See README for more information""" 11 12from __future__ import print_function 13 14from distutils.sysconfig import get_python_lib 15import glob 16import multiprocessing 17import os 18import site 19import sys 20import traceback 21import unittest 22 23# Bring in the patman and dtoc libraries (but don't override the first path 24# in PYTHONPATH) 25our_path = os.path.dirname(os.path.realpath(__file__)) 26for dirname in ['../patman', '../dtoc', '..', '../concurrencytest']: 27 sys.path.insert(2, os.path.join(our_path, dirname)) 28 29# Bring in the libfdt module 30sys.path.insert(2, 'scripts/dtc/pylibfdt') 31sys.path.insert(2, os.path.join(our_path, 32 '../../build-sandbox_spl/scripts/dtc/pylibfdt')) 33 34# When running under python-coverage on Ubuntu 16.04, the dist-packages 35# directories are dropped from the python path. Add them in so that we can find 36# the elffile module. We could use site.getsitepackages() here but unfortunately 37# that is not available in a virtualenv. 38sys.path.append(get_python_lib()) 39 40import cmdline 41import command 42use_concurrent = True 43try: 44 from concurrencytest import ConcurrentTestSuite, fork_for_tests 45except: 46 use_concurrent = False 47import control 48import test_util 49 50def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): 51 """Run the functional tests and any embedded doctests 52 53 Args: 54 debug: True to enable debugging, which shows a full stack trace on error 55 verbosity: Verbosity level to use 56 test_preserve_dirs: True to preserve the input directory used by tests 57 so that it can be examined afterwards (only useful for debugging 58 tests). If a single test is selected (in args[0]) it also preserves 59 the output directory for this test. Both directories are displayed 60 on the command line. 61 processes: Number of processes to use to run tests (None=same as #CPUs) 62 args: List of positional args provided to binman. This can hold a test 63 name to execute (as in 'binman test testSections', for example) 64 toolpath: List of paths to use for tools 65 """ 66 import cbfs_util_test 67 import elf_test 68 import entry_test 69 import fdt_test 70 import ftest 71 import image_test 72 import test 73 import doctest 74 75 result = unittest.TestResult() 76 for module in []: 77 suite = doctest.DocTestSuite(module) 78 suite.run(result) 79 80 sys.argv = [sys.argv[0]] 81 if debug: 82 sys.argv.append('-D') 83 if verbosity: 84 sys.argv.append('-v%d' % verbosity) 85 if toolpath: 86 for path in toolpath: 87 sys.argv += ['--toolpath', path] 88 89 # Run the entry tests first ,since these need to be the first to import the 90 # 'entry' module. 91 test_name = args and args[0] or None 92 suite = unittest.TestSuite() 93 loader = unittest.TestLoader() 94 for module in (entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, 95 elf_test.TestElf, image_test.TestImage, 96 cbfs_util_test.TestCbfs): 97 # Test the test module about our arguments, if it is interested 98 if hasattr(module, 'setup_test_args'): 99 setup_test_args = getattr(module, 'setup_test_args') 100 setup_test_args(preserve_indir=test_preserve_dirs, 101 preserve_outdirs=test_preserve_dirs and test_name is not None, 102 toolpath=toolpath, verbosity=verbosity) 103 if test_name: 104 try: 105 suite.addTests(loader.loadTestsFromName(test_name, module)) 106 except AttributeError: 107 continue 108 else: 109 suite.addTests(loader.loadTestsFromTestCase(module)) 110 if use_concurrent and processes != 1: 111 concurrent_suite = ConcurrentTestSuite(suite, 112 fork_for_tests(processes or multiprocessing.cpu_count())) 113 concurrent_suite.run(result) 114 else: 115 suite.run(result) 116 117 # Remove errors which just indicate a missing test. Since Python v3.5 If an 118 # ImportError or AttributeError occurs while traversing name then a 119 # synthetic test that raises that error when run will be returned. These 120 # errors are included in the errors accumulated by result.errors. 121 if test_name: 122 errors = [] 123 for test, err in result.errors: 124 if ("has no attribute '%s'" % test_name) not in err: 125 errors.append((test, err)) 126 result.testsRun -= 1 127 result.errors = errors 128 129 print(result) 130 for test, err in result.errors: 131 print(test.id(), err) 132 for test, err in result.failures: 133 print(err, result.failures) 134 if result.skipped: 135 print('%d binman test%s SKIPPED:' % 136 (len(result.skipped), 's' if len(result.skipped) > 1 else '')) 137 for skip_info in result.skipped: 138 print('%s: %s' % (skip_info[0], skip_info[1])) 139 if result.errors or result.failures: 140 print('binman tests FAILED') 141 return 1 142 return 0 143 144def GetEntryModules(include_testing=True): 145 """Get a set of entry class implementations 146 147 Returns: 148 Set of paths to entry class filenames 149 """ 150 glob_list = glob.glob(os.path.join(our_path, 'etype/*.py')) 151 return set([os.path.splitext(os.path.basename(item))[0] 152 for item in glob_list 153 if include_testing or '_testing' not in item]) 154 155def RunTestCoverage(): 156 """Run the tests and check that we get 100% coverage""" 157 glob_list = GetEntryModules(False) 158 all_set = set([os.path.splitext(os.path.basename(item))[0] 159 for item in glob_list if '_testing' not in item]) 160 test_util.RunTestCoverage('tools/binman/binman.py', None, 161 ['*test*', '*binman.py', 'tools/patman/*', 'tools/dtoc/*'], 162 args.build_dir, all_set) 163 164def RunBinman(args): 165 """Main entry point to binman once arguments are parsed 166 167 Args: 168 args: Command line arguments Namespace object 169 """ 170 ret_code = 0 171 172 if not args.debug: 173 sys.tracebacklimit = 0 174 175 if args.cmd == 'test': 176 if args.test_coverage: 177 RunTestCoverage() 178 else: 179 ret_code = RunTests(args.debug, args.verbosity, args.processes, 180 args.test_preserve_dirs, args.tests, 181 args.toolpath) 182 183 elif args.cmd == 'entry-docs': 184 control.WriteEntryDocs(GetEntryModules()) 185 186 else: 187 try: 188 ret_code = control.Binman(args) 189 except Exception as e: 190 print('binman: %s' % e) 191 if args.debug: 192 print() 193 traceback.print_exc() 194 ret_code = 1 195 return ret_code 196 197 198if __name__ == "__main__": 199 args = cmdline.ParseArgs(sys.argv[1:]) 200 201 ret_code = RunBinman(args) 202 sys.exit(ret_code) 203