1d201506cSStephen Warren# Copyright (c) 2015 Stephen Warren 2d201506cSStephen Warren# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. 3d201506cSStephen Warren# 4d201506cSStephen Warren# SPDX-License-Identifier: GPL-2.0 5d201506cSStephen Warren 6d201506cSStephen Warren# Implementation of pytest run-time hook functions. These are invoked by 7d201506cSStephen Warren# pytest at certain points during operation, e.g. startup, for each executed 8d201506cSStephen Warren# test, at shutdown etc. These hooks perform functions such as: 9d201506cSStephen Warren# - Parsing custom command-line options. 10d201506cSStephen Warren# - Pullilng in user-specified board configuration. 11d201506cSStephen Warren# - Creating the U-Boot console test fixture. 12d201506cSStephen Warren# - Creating the HTML log file. 13d201506cSStephen Warren# - Monitoring each test's results. 14d201506cSStephen Warren# - Implementing custom pytest markers. 15d201506cSStephen Warren 16d201506cSStephen Warrenimport atexit 17d201506cSStephen Warrenimport errno 18d201506cSStephen Warrenimport os 19d201506cSStephen Warrenimport os.path 20d201506cSStephen Warrenimport pexpect 21d201506cSStephen Warrenimport pytest 22d201506cSStephen Warrenfrom _pytest.runner import runtestprotocol 23d201506cSStephen Warrenimport ConfigParser 24d201506cSStephen Warrenimport StringIO 25d201506cSStephen Warrenimport sys 26d201506cSStephen Warren 27d201506cSStephen Warren# Globals: The HTML log file, and the connection to the U-Boot console. 28d201506cSStephen Warrenlog = None 29d201506cSStephen Warrenconsole = None 30d201506cSStephen Warren 31d201506cSStephen Warrendef mkdir_p(path): 32e8debf39SStephen Warren """Create a directory path. 33d201506cSStephen Warren 34d201506cSStephen Warren This includes creating any intermediate/parent directories. Any errors 35d201506cSStephen Warren caused due to already extant directories are ignored. 36d201506cSStephen Warren 37d201506cSStephen Warren Args: 38d201506cSStephen Warren path: The directory path to create. 39d201506cSStephen Warren 40d201506cSStephen Warren Returns: 41d201506cSStephen Warren Nothing. 42e8debf39SStephen Warren """ 43d201506cSStephen Warren 44d201506cSStephen Warren try: 45d201506cSStephen Warren os.makedirs(path) 46d201506cSStephen Warren except OSError as exc: 47d201506cSStephen Warren if exc.errno == errno.EEXIST and os.path.isdir(path): 48d201506cSStephen Warren pass 49d201506cSStephen Warren else: 50d201506cSStephen Warren raise 51d201506cSStephen Warren 52d201506cSStephen Warrendef pytest_addoption(parser): 53e8debf39SStephen Warren """pytest hook: Add custom command-line options to the cmdline parser. 54d201506cSStephen Warren 55d201506cSStephen Warren Args: 56d201506cSStephen Warren parser: The pytest command-line parser. 57d201506cSStephen Warren 58d201506cSStephen Warren Returns: 59d201506cSStephen Warren Nothing. 60e8debf39SStephen Warren """ 61d201506cSStephen Warren 62d201506cSStephen Warren parser.addoption('--build-dir', default=None, 63d201506cSStephen Warren help='U-Boot build directory (O=)') 64d201506cSStephen Warren parser.addoption('--result-dir', default=None, 65d201506cSStephen Warren help='U-Boot test result/tmp directory') 66d201506cSStephen Warren parser.addoption('--persistent-data-dir', default=None, 67d201506cSStephen Warren help='U-Boot test persistent generated data directory') 68d201506cSStephen Warren parser.addoption('--board-type', '--bd', '-B', default='sandbox', 69d201506cSStephen Warren help='U-Boot board type') 70d201506cSStephen Warren parser.addoption('--board-identity', '--id', default='na', 71d201506cSStephen Warren help='U-Boot board identity/instance') 72d201506cSStephen Warren parser.addoption('--build', default=False, action='store_true', 73d201506cSStephen Warren help='Compile U-Boot before running tests') 74*89ab8410SStephen Warren parser.addoption('--gdbserver', default=None, 75*89ab8410SStephen Warren help='Run sandbox under gdbserver. The argument is the channel '+ 76*89ab8410SStephen Warren 'over which gdbserver should communicate, e.g. localhost:1234') 77d201506cSStephen Warren 78d201506cSStephen Warrendef pytest_configure(config): 79e8debf39SStephen Warren """pytest hook: Perform custom initialization at startup time. 80d201506cSStephen Warren 81d201506cSStephen Warren Args: 82d201506cSStephen Warren config: The pytest configuration. 83d201506cSStephen Warren 84d201506cSStephen Warren Returns: 85d201506cSStephen Warren Nothing. 86e8debf39SStephen Warren """ 87d201506cSStephen Warren 88d201506cSStephen Warren global log 89d201506cSStephen Warren global console 90d201506cSStephen Warren global ubconfig 91d201506cSStephen Warren 92d201506cSStephen Warren test_py_dir = os.path.dirname(os.path.abspath(__file__)) 93d201506cSStephen Warren source_dir = os.path.dirname(os.path.dirname(test_py_dir)) 94d201506cSStephen Warren 95d201506cSStephen Warren board_type = config.getoption('board_type') 96d201506cSStephen Warren board_type_filename = board_type.replace('-', '_') 97d201506cSStephen Warren 98d201506cSStephen Warren board_identity = config.getoption('board_identity') 99d201506cSStephen Warren board_identity_filename = board_identity.replace('-', '_') 100d201506cSStephen Warren 101d201506cSStephen Warren build_dir = config.getoption('build_dir') 102d201506cSStephen Warren if not build_dir: 103d201506cSStephen Warren build_dir = source_dir + '/build-' + board_type 104d201506cSStephen Warren mkdir_p(build_dir) 105d201506cSStephen Warren 106d201506cSStephen Warren result_dir = config.getoption('result_dir') 107d201506cSStephen Warren if not result_dir: 108d201506cSStephen Warren result_dir = build_dir 109d201506cSStephen Warren mkdir_p(result_dir) 110d201506cSStephen Warren 111d201506cSStephen Warren persistent_data_dir = config.getoption('persistent_data_dir') 112d201506cSStephen Warren if not persistent_data_dir: 113d201506cSStephen Warren persistent_data_dir = build_dir + '/persistent-data' 114d201506cSStephen Warren mkdir_p(persistent_data_dir) 115d201506cSStephen Warren 116*89ab8410SStephen Warren gdbserver = config.getoption('gdbserver') 117*89ab8410SStephen Warren if gdbserver and board_type != 'sandbox': 118*89ab8410SStephen Warren raise Exception('--gdbserver only supported with sandbox') 119*89ab8410SStephen Warren 120d201506cSStephen Warren import multiplexed_log 121d201506cSStephen Warren log = multiplexed_log.Logfile(result_dir + '/test-log.html') 122d201506cSStephen Warren 123d201506cSStephen Warren if config.getoption('build'): 124d201506cSStephen Warren if build_dir != source_dir: 125d201506cSStephen Warren o_opt = 'O=%s' % build_dir 126d201506cSStephen Warren else: 127d201506cSStephen Warren o_opt = '' 128d201506cSStephen Warren cmds = ( 129d201506cSStephen Warren ['make', o_opt, '-s', board_type + '_defconfig'], 130d201506cSStephen Warren ['make', o_opt, '-s', '-j8'], 131d201506cSStephen Warren ) 132d201506cSStephen Warren runner = log.get_runner('make', sys.stdout) 133d201506cSStephen Warren for cmd in cmds: 134d201506cSStephen Warren runner.run(cmd, cwd=source_dir) 135d201506cSStephen Warren runner.close() 136d201506cSStephen Warren 137d201506cSStephen Warren class ArbitraryAttributeContainer(object): 138d201506cSStephen Warren pass 139d201506cSStephen Warren 140d201506cSStephen Warren ubconfig = ArbitraryAttributeContainer() 141d201506cSStephen Warren ubconfig.brd = dict() 142d201506cSStephen Warren ubconfig.env = dict() 143d201506cSStephen Warren 144d201506cSStephen Warren modules = [ 145d201506cSStephen Warren (ubconfig.brd, 'u_boot_board_' + board_type_filename), 146d201506cSStephen Warren (ubconfig.env, 'u_boot_boardenv_' + board_type_filename), 147d201506cSStephen Warren (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' + 148d201506cSStephen Warren board_identity_filename), 149d201506cSStephen Warren ] 150d201506cSStephen Warren for (dict_to_fill, module_name) in modules: 151d201506cSStephen Warren try: 152d201506cSStephen Warren module = __import__(module_name) 153d201506cSStephen Warren except ImportError: 154d201506cSStephen Warren continue 155d201506cSStephen Warren dict_to_fill.update(module.__dict__) 156d201506cSStephen Warren 157d201506cSStephen Warren ubconfig.buildconfig = dict() 158d201506cSStephen Warren 159d201506cSStephen Warren for conf_file in ('.config', 'include/autoconf.mk'): 160d201506cSStephen Warren dot_config = build_dir + '/' + conf_file 161d201506cSStephen Warren if not os.path.exists(dot_config): 162d201506cSStephen Warren raise Exception(conf_file + ' does not exist; ' + 163d201506cSStephen Warren 'try passing --build option?') 164d201506cSStephen Warren 165d201506cSStephen Warren with open(dot_config, 'rt') as f: 166d201506cSStephen Warren ini_str = '[root]\n' + f.read() 167d201506cSStephen Warren ini_sio = StringIO.StringIO(ini_str) 168d201506cSStephen Warren parser = ConfigParser.RawConfigParser() 169d201506cSStephen Warren parser.readfp(ini_sio) 170d201506cSStephen Warren ubconfig.buildconfig.update(parser.items('root')) 171d201506cSStephen Warren 172d201506cSStephen Warren ubconfig.test_py_dir = test_py_dir 173d201506cSStephen Warren ubconfig.source_dir = source_dir 174d201506cSStephen Warren ubconfig.build_dir = build_dir 175d201506cSStephen Warren ubconfig.result_dir = result_dir 176d201506cSStephen Warren ubconfig.persistent_data_dir = persistent_data_dir 177d201506cSStephen Warren ubconfig.board_type = board_type 178d201506cSStephen Warren ubconfig.board_identity = board_identity 179*89ab8410SStephen Warren ubconfig.gdbserver = gdbserver 180d201506cSStephen Warren 181d201506cSStephen Warren env_vars = ( 182d201506cSStephen Warren 'board_type', 183d201506cSStephen Warren 'board_identity', 184d201506cSStephen Warren 'source_dir', 185d201506cSStephen Warren 'test_py_dir', 186d201506cSStephen Warren 'build_dir', 187d201506cSStephen Warren 'result_dir', 188d201506cSStephen Warren 'persistent_data_dir', 189d201506cSStephen Warren ) 190d201506cSStephen Warren for v in env_vars: 191d201506cSStephen Warren os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v) 192d201506cSStephen Warren 193d201506cSStephen Warren if board_type == 'sandbox': 194d201506cSStephen Warren import u_boot_console_sandbox 195d201506cSStephen Warren console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig) 196d201506cSStephen Warren else: 197d201506cSStephen Warren import u_boot_console_exec_attach 198d201506cSStephen Warren console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) 199d201506cSStephen Warren 200d201506cSStephen Warrendef pytest_generate_tests(metafunc): 201e8debf39SStephen Warren """pytest hook: parameterize test functions based on custom rules. 202d201506cSStephen Warren 203d201506cSStephen Warren If a test function takes parameter(s) (fixture names) of the form brd__xxx 204d201506cSStephen Warren or env__xxx, the brd and env configuration dictionaries are consulted to 205d201506cSStephen Warren find the list of values to use for those parameters, and the test is 206d201506cSStephen Warren parametrized so that it runs once for each combination of values. 207d201506cSStephen Warren 208d201506cSStephen Warren Args: 209d201506cSStephen Warren metafunc: The pytest test function. 210d201506cSStephen Warren 211d201506cSStephen Warren Returns: 212d201506cSStephen Warren Nothing. 213e8debf39SStephen Warren """ 214d201506cSStephen Warren 215d201506cSStephen Warren subconfigs = { 216d201506cSStephen Warren 'brd': console.config.brd, 217d201506cSStephen Warren 'env': console.config.env, 218d201506cSStephen Warren } 219d201506cSStephen Warren for fn in metafunc.fixturenames: 220d201506cSStephen Warren parts = fn.split('__') 221d201506cSStephen Warren if len(parts) < 2: 222d201506cSStephen Warren continue 223d201506cSStephen Warren if parts[0] not in subconfigs: 224d201506cSStephen Warren continue 225d201506cSStephen Warren subconfig = subconfigs[parts[0]] 226d201506cSStephen Warren vals = [] 227d201506cSStephen Warren val = subconfig.get(fn, []) 228d201506cSStephen Warren # If that exact name is a key in the data source: 229d201506cSStephen Warren if val: 230d201506cSStephen Warren # ... use the dict value as a single parameter value. 231d201506cSStephen Warren vals = (val, ) 232d201506cSStephen Warren else: 233d201506cSStephen Warren # ... otherwise, see if there's a key that contains a list of 234d201506cSStephen Warren # values to use instead. 235d201506cSStephen Warren vals = subconfig.get(fn + 's', []) 236d20e5e97SStephen Warren def fixture_id(index, val): 237d20e5e97SStephen Warren try: 238d20e5e97SStephen Warren return val["fixture_id"] 239d20e5e97SStephen Warren except: 240d20e5e97SStephen Warren return fn + str(index) 241d20e5e97SStephen Warren ids = [fixture_id(index, val) for (index, val) in enumerate(vals)] 242d20e5e97SStephen Warren metafunc.parametrize(fn, vals, ids=ids) 243d201506cSStephen Warren 244636f38d8SStephen Warren@pytest.fixture(scope='function') 245d201506cSStephen Warrendef u_boot_console(request): 246e8debf39SStephen Warren """Generate the value of a test's u_boot_console fixture. 247d201506cSStephen Warren 248d201506cSStephen Warren Args: 249d201506cSStephen Warren request: The pytest request. 250d201506cSStephen Warren 251d201506cSStephen Warren Returns: 252d201506cSStephen Warren The fixture value. 253e8debf39SStephen Warren """ 254d201506cSStephen Warren 255636f38d8SStephen Warren console.ensure_spawned() 256d201506cSStephen Warren return console 257d201506cSStephen Warren 258d201506cSStephen Warrentests_not_run = set() 259d201506cSStephen Warrentests_failed = set() 26078b39cc3SStephen Warrentests_xpassed = set() 26178b39cc3SStephen Warrentests_xfailed = set() 262d201506cSStephen Warrentests_skipped = set() 263d201506cSStephen Warrentests_passed = set() 264d201506cSStephen Warren 265d201506cSStephen Warrendef pytest_itemcollected(item): 266e8debf39SStephen Warren """pytest hook: Called once for each test found during collection. 267d201506cSStephen Warren 268d201506cSStephen Warren This enables our custom result analysis code to see the list of all tests 269d201506cSStephen Warren that should eventually be run. 270d201506cSStephen Warren 271d201506cSStephen Warren Args: 272d201506cSStephen Warren item: The item that was collected. 273d201506cSStephen Warren 274d201506cSStephen Warren Returns: 275d201506cSStephen Warren Nothing. 276e8debf39SStephen Warren """ 277d201506cSStephen Warren 278d201506cSStephen Warren tests_not_run.add(item.name) 279d201506cSStephen Warren 280d201506cSStephen Warrendef cleanup(): 281e8debf39SStephen Warren """Clean up all global state. 282d201506cSStephen Warren 283d201506cSStephen Warren Executed (via atexit) once the entire test process is complete. This 284d201506cSStephen Warren includes logging the status of all tests, and the identity of any failed 285d201506cSStephen Warren or skipped tests. 286d201506cSStephen Warren 287d201506cSStephen Warren Args: 288d201506cSStephen Warren None. 289d201506cSStephen Warren 290d201506cSStephen Warren Returns: 291d201506cSStephen Warren Nothing. 292e8debf39SStephen Warren """ 293d201506cSStephen Warren 294d201506cSStephen Warren if console: 295d201506cSStephen Warren console.close() 296d201506cSStephen Warren if log: 297d201506cSStephen Warren log.status_pass('%d passed' % len(tests_passed)) 298d201506cSStephen Warren if tests_skipped: 299d201506cSStephen Warren log.status_skipped('%d skipped' % len(tests_skipped)) 300d201506cSStephen Warren for test in tests_skipped: 301d201506cSStephen Warren log.status_skipped('... ' + test) 30278b39cc3SStephen Warren if tests_xpassed: 30378b39cc3SStephen Warren log.status_xpass('%d xpass' % len(tests_xpassed)) 30478b39cc3SStephen Warren for test in tests_xpassed: 30578b39cc3SStephen Warren log.status_xpass('... ' + test) 30678b39cc3SStephen Warren if tests_xfailed: 30778b39cc3SStephen Warren log.status_xfail('%d xfail' % len(tests_xfailed)) 30878b39cc3SStephen Warren for test in tests_xfailed: 30978b39cc3SStephen Warren log.status_xfail('... ' + test) 310d201506cSStephen Warren if tests_failed: 311d201506cSStephen Warren log.status_fail('%d failed' % len(tests_failed)) 312d201506cSStephen Warren for test in tests_failed: 313d201506cSStephen Warren log.status_fail('... ' + test) 314d201506cSStephen Warren if tests_not_run: 315d201506cSStephen Warren log.status_fail('%d not run' % len(tests_not_run)) 316d201506cSStephen Warren for test in tests_not_run: 317d201506cSStephen Warren log.status_fail('... ' + test) 318d201506cSStephen Warren log.close() 319d201506cSStephen Warrenatexit.register(cleanup) 320d201506cSStephen Warren 321d201506cSStephen Warrendef setup_boardspec(item): 322e8debf39SStephen Warren """Process any 'boardspec' marker for a test. 323d201506cSStephen Warren 324d201506cSStephen Warren Such a marker lists the set of board types that a test does/doesn't 325d201506cSStephen Warren support. If tests are being executed on an unsupported board, the test is 326d201506cSStephen Warren marked to be skipped. 327d201506cSStephen Warren 328d201506cSStephen Warren Args: 329d201506cSStephen Warren item: The pytest test item. 330d201506cSStephen Warren 331d201506cSStephen Warren Returns: 332d201506cSStephen Warren Nothing. 333e8debf39SStephen Warren """ 334d201506cSStephen Warren 335d201506cSStephen Warren mark = item.get_marker('boardspec') 336d201506cSStephen Warren if not mark: 337d201506cSStephen Warren return 338d201506cSStephen Warren required_boards = [] 339d201506cSStephen Warren for board in mark.args: 340d201506cSStephen Warren if board.startswith('!'): 341d201506cSStephen Warren if ubconfig.board_type == board[1:]: 342d201506cSStephen Warren pytest.skip('board not supported') 343d201506cSStephen Warren return 344d201506cSStephen Warren else: 345d201506cSStephen Warren required_boards.append(board) 346d201506cSStephen Warren if required_boards and ubconfig.board_type not in required_boards: 347d201506cSStephen Warren pytest.skip('board not supported') 348d201506cSStephen Warren 349d201506cSStephen Warrendef setup_buildconfigspec(item): 350e8debf39SStephen Warren """Process any 'buildconfigspec' marker for a test. 351d201506cSStephen Warren 352d201506cSStephen Warren Such a marker lists some U-Boot configuration feature that the test 353d201506cSStephen Warren requires. If tests are being executed on an U-Boot build that doesn't 354d201506cSStephen Warren have the required feature, the test is marked to be skipped. 355d201506cSStephen Warren 356d201506cSStephen Warren Args: 357d201506cSStephen Warren item: The pytest test item. 358d201506cSStephen Warren 359d201506cSStephen Warren Returns: 360d201506cSStephen Warren Nothing. 361e8debf39SStephen Warren """ 362d201506cSStephen Warren 363d201506cSStephen Warren mark = item.get_marker('buildconfigspec') 364d201506cSStephen Warren if not mark: 365d201506cSStephen Warren return 366d201506cSStephen Warren for option in mark.args: 367d201506cSStephen Warren if not ubconfig.buildconfig.get('config_' + option.lower(), None): 368d201506cSStephen Warren pytest.skip('.config feature not enabled') 369d201506cSStephen Warren 370d201506cSStephen Warrendef pytest_runtest_setup(item): 371e8debf39SStephen Warren """pytest hook: Configure (set up) a test item. 372d201506cSStephen Warren 373d201506cSStephen Warren Called once for each test to perform any custom configuration. This hook 374d201506cSStephen Warren is used to skip the test if certain conditions apply. 375d201506cSStephen Warren 376d201506cSStephen Warren Args: 377d201506cSStephen Warren item: The pytest test item. 378d201506cSStephen Warren 379d201506cSStephen Warren Returns: 380d201506cSStephen Warren Nothing. 381e8debf39SStephen Warren """ 382d201506cSStephen Warren 383d201506cSStephen Warren log.start_section(item.name) 384d201506cSStephen Warren setup_boardspec(item) 385d201506cSStephen Warren setup_buildconfigspec(item) 386d201506cSStephen Warren 387d201506cSStephen Warrendef pytest_runtest_protocol(item, nextitem): 388e8debf39SStephen Warren """pytest hook: Called to execute a test. 389d201506cSStephen Warren 390d201506cSStephen Warren This hook wraps the standard pytest runtestprotocol() function in order 391d201506cSStephen Warren to acquire visibility into, and record, each test function's result. 392d201506cSStephen Warren 393d201506cSStephen Warren Args: 394d201506cSStephen Warren item: The pytest test item to execute. 395d201506cSStephen Warren nextitem: The pytest test item that will be executed after this one. 396d201506cSStephen Warren 397d201506cSStephen Warren Returns: 398d201506cSStephen Warren A list of pytest reports (test result data). 399e8debf39SStephen Warren """ 400d201506cSStephen Warren 401d201506cSStephen Warren reports = runtestprotocol(item, nextitem=nextitem) 40278b39cc3SStephen Warren 40378b39cc3SStephen Warren failure_cleanup = False 40478b39cc3SStephen Warren test_list = tests_passed 40578b39cc3SStephen Warren msg = 'OK' 40678b39cc3SStephen Warren msg_log = log.status_pass 407d201506cSStephen Warren for report in reports: 408d201506cSStephen Warren if report.outcome == 'failed': 40978b39cc3SStephen Warren if hasattr(report, 'wasxfail'): 41078b39cc3SStephen Warren test_list = tests_xpassed 41178b39cc3SStephen Warren msg = 'XPASSED' 41278b39cc3SStephen Warren msg_log = log.status_xpass 41378b39cc3SStephen Warren else: 41478b39cc3SStephen Warren failure_cleanup = True 41578b39cc3SStephen Warren test_list = tests_failed 41678b39cc3SStephen Warren msg = 'FAILED:\n' + str(report.longrepr) 41778b39cc3SStephen Warren msg_log = log.status_fail 418d201506cSStephen Warren break 419d201506cSStephen Warren if report.outcome == 'skipped': 42078b39cc3SStephen Warren if hasattr(report, 'wasxfail'): 42178b39cc3SStephen Warren failure_cleanup = True 42278b39cc3SStephen Warren test_list = tests_xfailed 42378b39cc3SStephen Warren msg = 'XFAILED:\n' + str(report.longrepr) 42478b39cc3SStephen Warren msg_log = log.status_xfail 42578b39cc3SStephen Warren break 42678b39cc3SStephen Warren test_list = tests_skipped 42778b39cc3SStephen Warren msg = 'SKIPPED:\n' + str(report.longrepr) 42878b39cc3SStephen Warren msg_log = log.status_skipped 429d201506cSStephen Warren 43078b39cc3SStephen Warren if failure_cleanup: 431c10eb9d3SStephen Warren console.drain_console() 43278b39cc3SStephen Warren 43378b39cc3SStephen Warren test_list.add(item.name) 434d201506cSStephen Warren tests_not_run.remove(item.name) 435d201506cSStephen Warren 436d201506cSStephen Warren try: 43778b39cc3SStephen Warren msg_log(msg) 438d201506cSStephen Warren except: 439d201506cSStephen Warren # If something went wrong with logging, it's better to let the test 440d201506cSStephen Warren # process continue, which may report other exceptions that triggered 441d201506cSStephen Warren # the logging issue (e.g. console.log wasn't created). Hence, just 442d201506cSStephen Warren # squash the exception. If the test setup failed due to e.g. syntax 443d201506cSStephen Warren # error somewhere else, this won't be seen. However, once that issue 444d201506cSStephen Warren # is fixed, if this exception still exists, it will then be logged as 445d201506cSStephen Warren # part of the test's stdout. 446d201506cSStephen Warren import traceback 447d201506cSStephen Warren print 'Exception occurred while logging runtest status:' 448d201506cSStephen Warren traceback.print_exc() 449d201506cSStephen Warren # FIXME: Can we force a test failure here? 450d201506cSStephen Warren 451d201506cSStephen Warren log.end_section(item.name) 452d201506cSStephen Warren 45378b39cc3SStephen Warren if failure_cleanup: 454d201506cSStephen Warren console.cleanup_spawn() 455d201506cSStephen Warren 456d201506cSStephen Warren return reports 457