xref: /rk3399_rockchip-uboot/test/py/conftest.py (revision 1cd85f571dd888a20ba23308231da0022ae1ea9e)
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
24*1cd85f57SStephen Warrenimport re
25d201506cSStephen Warrenimport StringIO
26d201506cSStephen Warrenimport sys
27d201506cSStephen Warren
28d201506cSStephen Warren# Globals: The HTML log file, and the connection to the U-Boot console.
29d201506cSStephen Warrenlog = None
30d201506cSStephen Warrenconsole = None
31d201506cSStephen Warren
32d201506cSStephen Warrendef mkdir_p(path):
33e8debf39SStephen Warren    """Create a directory path.
34d201506cSStephen Warren
35d201506cSStephen Warren    This includes creating any intermediate/parent directories. Any errors
36d201506cSStephen Warren    caused due to already extant directories are ignored.
37d201506cSStephen Warren
38d201506cSStephen Warren    Args:
39d201506cSStephen Warren        path: The directory path to create.
40d201506cSStephen Warren
41d201506cSStephen Warren    Returns:
42d201506cSStephen Warren        Nothing.
43e8debf39SStephen Warren    """
44d201506cSStephen Warren
45d201506cSStephen Warren    try:
46d201506cSStephen Warren        os.makedirs(path)
47d201506cSStephen Warren    except OSError as exc:
48d201506cSStephen Warren        if exc.errno == errno.EEXIST and os.path.isdir(path):
49d201506cSStephen Warren            pass
50d201506cSStephen Warren        else:
51d201506cSStephen Warren            raise
52d201506cSStephen Warren
53d201506cSStephen Warrendef pytest_addoption(parser):
54e8debf39SStephen Warren    """pytest hook: Add custom command-line options to the cmdline parser.
55d201506cSStephen Warren
56d201506cSStephen Warren    Args:
57d201506cSStephen Warren        parser: The pytest command-line parser.
58d201506cSStephen Warren
59d201506cSStephen Warren    Returns:
60d201506cSStephen Warren        Nothing.
61e8debf39SStephen Warren    """
62d201506cSStephen Warren
63d201506cSStephen Warren    parser.addoption('--build-dir', default=None,
64d201506cSStephen Warren        help='U-Boot build directory (O=)')
65d201506cSStephen Warren    parser.addoption('--result-dir', default=None,
66d201506cSStephen Warren        help='U-Boot test result/tmp directory')
67d201506cSStephen Warren    parser.addoption('--persistent-data-dir', default=None,
68d201506cSStephen Warren        help='U-Boot test persistent generated data directory')
69d201506cSStephen Warren    parser.addoption('--board-type', '--bd', '-B', default='sandbox',
70d201506cSStephen Warren        help='U-Boot board type')
71d201506cSStephen Warren    parser.addoption('--board-identity', '--id', default='na',
72d201506cSStephen Warren        help='U-Boot board identity/instance')
73d201506cSStephen Warren    parser.addoption('--build', default=False, action='store_true',
74d201506cSStephen Warren        help='Compile U-Boot before running tests')
7589ab8410SStephen Warren    parser.addoption('--gdbserver', default=None,
7689ab8410SStephen Warren        help='Run sandbox under gdbserver. The argument is the channel '+
7789ab8410SStephen Warren        'over which gdbserver should communicate, e.g. localhost:1234')
78d201506cSStephen Warren
79d201506cSStephen Warrendef pytest_configure(config):
80e8debf39SStephen Warren    """pytest hook: Perform custom initialization at startup time.
81d201506cSStephen Warren
82d201506cSStephen Warren    Args:
83d201506cSStephen Warren        config: The pytest configuration.
84d201506cSStephen Warren
85d201506cSStephen Warren    Returns:
86d201506cSStephen Warren        Nothing.
87e8debf39SStephen Warren    """
88d201506cSStephen Warren
89d201506cSStephen Warren    global log
90d201506cSStephen Warren    global console
91d201506cSStephen Warren    global ubconfig
92d201506cSStephen Warren
93d201506cSStephen Warren    test_py_dir = os.path.dirname(os.path.abspath(__file__))
94d201506cSStephen Warren    source_dir = os.path.dirname(os.path.dirname(test_py_dir))
95d201506cSStephen Warren
96d201506cSStephen Warren    board_type = config.getoption('board_type')
97d201506cSStephen Warren    board_type_filename = board_type.replace('-', '_')
98d201506cSStephen Warren
99d201506cSStephen Warren    board_identity = config.getoption('board_identity')
100d201506cSStephen Warren    board_identity_filename = board_identity.replace('-', '_')
101d201506cSStephen Warren
102d201506cSStephen Warren    build_dir = config.getoption('build_dir')
103d201506cSStephen Warren    if not build_dir:
104d201506cSStephen Warren        build_dir = source_dir + '/build-' + board_type
105d201506cSStephen Warren    mkdir_p(build_dir)
106d201506cSStephen Warren
107d201506cSStephen Warren    result_dir = config.getoption('result_dir')
108d201506cSStephen Warren    if not result_dir:
109d201506cSStephen Warren        result_dir = build_dir
110d201506cSStephen Warren    mkdir_p(result_dir)
111d201506cSStephen Warren
112d201506cSStephen Warren    persistent_data_dir = config.getoption('persistent_data_dir')
113d201506cSStephen Warren    if not persistent_data_dir:
114d201506cSStephen Warren        persistent_data_dir = build_dir + '/persistent-data'
115d201506cSStephen Warren    mkdir_p(persistent_data_dir)
116d201506cSStephen Warren
11789ab8410SStephen Warren    gdbserver = config.getoption('gdbserver')
11889ab8410SStephen Warren    if gdbserver and board_type != 'sandbox':
11989ab8410SStephen Warren        raise Exception('--gdbserver only supported with sandbox')
12089ab8410SStephen Warren
121d201506cSStephen Warren    import multiplexed_log
122d201506cSStephen Warren    log = multiplexed_log.Logfile(result_dir + '/test-log.html')
123d201506cSStephen Warren
124d201506cSStephen Warren    if config.getoption('build'):
125d201506cSStephen Warren        if build_dir != source_dir:
126d201506cSStephen Warren            o_opt = 'O=%s' % build_dir
127d201506cSStephen Warren        else:
128d201506cSStephen Warren            o_opt = ''
129d201506cSStephen Warren        cmds = (
130d201506cSStephen Warren            ['make', o_opt, '-s', board_type + '_defconfig'],
131d201506cSStephen Warren            ['make', o_opt, '-s', '-j8'],
132d201506cSStephen Warren        )
13383357fd5SStephen Warren        with log.section('make'):
134d201506cSStephen Warren            runner = log.get_runner('make', sys.stdout)
135d201506cSStephen Warren            for cmd in cmds:
136d201506cSStephen Warren                runner.run(cmd, cwd=source_dir)
137d201506cSStephen Warren            runner.close()
13883357fd5SStephen Warren            log.status_pass('OK')
139d201506cSStephen Warren
140d201506cSStephen Warren    class ArbitraryAttributeContainer(object):
141d201506cSStephen Warren        pass
142d201506cSStephen Warren
143d201506cSStephen Warren    ubconfig = ArbitraryAttributeContainer()
144d201506cSStephen Warren    ubconfig.brd = dict()
145d201506cSStephen Warren    ubconfig.env = dict()
146d201506cSStephen Warren
147d201506cSStephen Warren    modules = [
148d201506cSStephen Warren        (ubconfig.brd, 'u_boot_board_' + board_type_filename),
149d201506cSStephen Warren        (ubconfig.env, 'u_boot_boardenv_' + board_type_filename),
150d201506cSStephen Warren        (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' +
151d201506cSStephen Warren            board_identity_filename),
152d201506cSStephen Warren    ]
153d201506cSStephen Warren    for (dict_to_fill, module_name) in modules:
154d201506cSStephen Warren        try:
155d201506cSStephen Warren            module = __import__(module_name)
156d201506cSStephen Warren        except ImportError:
157d201506cSStephen Warren            continue
158d201506cSStephen Warren        dict_to_fill.update(module.__dict__)
159d201506cSStephen Warren
160d201506cSStephen Warren    ubconfig.buildconfig = dict()
161d201506cSStephen Warren
162d201506cSStephen Warren    for conf_file in ('.config', 'include/autoconf.mk'):
163d201506cSStephen Warren        dot_config = build_dir + '/' + conf_file
164d201506cSStephen Warren        if not os.path.exists(dot_config):
165d201506cSStephen Warren            raise Exception(conf_file + ' does not exist; ' +
166d201506cSStephen Warren                'try passing --build option?')
167d201506cSStephen Warren
168d201506cSStephen Warren        with open(dot_config, 'rt') as f:
169d201506cSStephen Warren            ini_str = '[root]\n' + f.read()
170d201506cSStephen Warren            ini_sio = StringIO.StringIO(ini_str)
171d201506cSStephen Warren            parser = ConfigParser.RawConfigParser()
172d201506cSStephen Warren            parser.readfp(ini_sio)
173d201506cSStephen Warren            ubconfig.buildconfig.update(parser.items('root'))
174d201506cSStephen Warren
175d201506cSStephen Warren    ubconfig.test_py_dir = test_py_dir
176d201506cSStephen Warren    ubconfig.source_dir = source_dir
177d201506cSStephen Warren    ubconfig.build_dir = build_dir
178d201506cSStephen Warren    ubconfig.result_dir = result_dir
179d201506cSStephen Warren    ubconfig.persistent_data_dir = persistent_data_dir
180d201506cSStephen Warren    ubconfig.board_type = board_type
181d201506cSStephen Warren    ubconfig.board_identity = board_identity
18289ab8410SStephen Warren    ubconfig.gdbserver = gdbserver
183d201506cSStephen Warren
184d201506cSStephen Warren    env_vars = (
185d201506cSStephen Warren        'board_type',
186d201506cSStephen Warren        'board_identity',
187d201506cSStephen Warren        'source_dir',
188d201506cSStephen Warren        'test_py_dir',
189d201506cSStephen Warren        'build_dir',
190d201506cSStephen Warren        'result_dir',
191d201506cSStephen Warren        'persistent_data_dir',
192d201506cSStephen Warren    )
193d201506cSStephen Warren    for v in env_vars:
194d201506cSStephen Warren        os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v)
195d201506cSStephen Warren
196d201506cSStephen Warren    if board_type == 'sandbox':
197d201506cSStephen Warren        import u_boot_console_sandbox
198d201506cSStephen Warren        console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig)
199d201506cSStephen Warren    else:
200d201506cSStephen Warren        import u_boot_console_exec_attach
201d201506cSStephen Warren        console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
202d201506cSStephen Warren
203*1cd85f57SStephen Warrenre_ut_test_list = re.compile(r'_u_boot_list_2_(dm|env)_test_2_\1_test_(.*)\s*$')
204*1cd85f57SStephen Warrendef generate_ut_subtest(metafunc, fixture_name):
205*1cd85f57SStephen Warren    """Provide parametrization for a ut_subtest fixture.
206*1cd85f57SStephen Warren
207*1cd85f57SStephen Warren    Determines the set of unit tests built into a U-Boot binary by parsing the
208*1cd85f57SStephen Warren    list of symbols generated by the build process. Provides this information
209*1cd85f57SStephen Warren    to test functions by parameterizing their ut_subtest fixture parameter.
210*1cd85f57SStephen Warren
211*1cd85f57SStephen Warren    Args:
212*1cd85f57SStephen Warren        metafunc: The pytest test function.
213*1cd85f57SStephen Warren        fixture_name: The fixture name to test.
214*1cd85f57SStephen Warren
215*1cd85f57SStephen Warren    Returns:
216*1cd85f57SStephen Warren        Nothing.
217*1cd85f57SStephen Warren    """
218*1cd85f57SStephen Warren
219*1cd85f57SStephen Warren    fn = console.config.build_dir + '/u-boot.sym'
220*1cd85f57SStephen Warren    try:
221*1cd85f57SStephen Warren        with open(fn, 'rt') as f:
222*1cd85f57SStephen Warren            lines = f.readlines()
223*1cd85f57SStephen Warren    except:
224*1cd85f57SStephen Warren        lines = []
225*1cd85f57SStephen Warren    lines.sort()
226*1cd85f57SStephen Warren
227*1cd85f57SStephen Warren    vals = []
228*1cd85f57SStephen Warren    for l in lines:
229*1cd85f57SStephen Warren        m = re_ut_test_list.search(l)
230*1cd85f57SStephen Warren        if not m:
231*1cd85f57SStephen Warren            continue
232*1cd85f57SStephen Warren        vals.append(m.group(1) + ' ' + m.group(2))
233*1cd85f57SStephen Warren
234*1cd85f57SStephen Warren    ids = ['ut_' + s.replace(' ', '_') for s in vals]
235*1cd85f57SStephen Warren    metafunc.parametrize(fixture_name, vals, ids=ids)
236*1cd85f57SStephen Warren
237*1cd85f57SStephen Warrendef generate_config(metafunc, fixture_name):
238*1cd85f57SStephen Warren    """Provide parametrization for {env,brd}__ fixtures.
239d201506cSStephen Warren
240d201506cSStephen Warren    If a test function takes parameter(s) (fixture names) of the form brd__xxx
241d201506cSStephen Warren    or env__xxx, the brd and env configuration dictionaries are consulted to
242d201506cSStephen Warren    find the list of values to use for those parameters, and the test is
243d201506cSStephen Warren    parametrized so that it runs once for each combination of values.
244d201506cSStephen Warren
245d201506cSStephen Warren    Args:
246d201506cSStephen Warren        metafunc: The pytest test function.
247*1cd85f57SStephen Warren        fixture_name: The fixture name to test.
248d201506cSStephen Warren
249d201506cSStephen Warren    Returns:
250d201506cSStephen Warren        Nothing.
251e8debf39SStephen Warren    """
252d201506cSStephen Warren
253d201506cSStephen Warren    subconfigs = {
254d201506cSStephen Warren        'brd': console.config.brd,
255d201506cSStephen Warren        'env': console.config.env,
256d201506cSStephen Warren    }
257*1cd85f57SStephen Warren    parts = fixture_name.split('__')
258d201506cSStephen Warren    if len(parts) < 2:
259*1cd85f57SStephen Warren        return
260d201506cSStephen Warren    if parts[0] not in subconfigs:
261*1cd85f57SStephen Warren        return
262d201506cSStephen Warren    subconfig = subconfigs[parts[0]]
263d201506cSStephen Warren    vals = []
264*1cd85f57SStephen Warren    val = subconfig.get(fixture_name, [])
265d201506cSStephen Warren    # If that exact name is a key in the data source:
266d201506cSStephen Warren    if val:
267d201506cSStephen Warren        # ... use the dict value as a single parameter value.
268d201506cSStephen Warren        vals = (val, )
269d201506cSStephen Warren    else:
270d201506cSStephen Warren        # ... otherwise, see if there's a key that contains a list of
271d201506cSStephen Warren        # values to use instead.
272*1cd85f57SStephen Warren        vals = subconfig.get(fixture_name+ 's', [])
273d20e5e97SStephen Warren    def fixture_id(index, val):
274d20e5e97SStephen Warren        try:
275*1cd85f57SStephen Warren            return val['fixture_id']
276d20e5e97SStephen Warren        except:
277*1cd85f57SStephen Warren            return fixture_name + str(index)
278d20e5e97SStephen Warren    ids = [fixture_id(index, val) for (index, val) in enumerate(vals)]
279*1cd85f57SStephen Warren    metafunc.parametrize(fixture_name, vals, ids=ids)
280*1cd85f57SStephen Warren
281*1cd85f57SStephen Warrendef pytest_generate_tests(metafunc):
282*1cd85f57SStephen Warren    """pytest hook: parameterize test functions based on custom rules.
283*1cd85f57SStephen Warren
284*1cd85f57SStephen Warren    Check each test function parameter (fixture name) to see if it is one of
285*1cd85f57SStephen Warren    our custom names, and if so, provide the correct parametrization for that
286*1cd85f57SStephen Warren    parameter.
287*1cd85f57SStephen Warren
288*1cd85f57SStephen Warren    Args:
289*1cd85f57SStephen Warren        metafunc: The pytest test function.
290*1cd85f57SStephen Warren
291*1cd85f57SStephen Warren    Returns:
292*1cd85f57SStephen Warren        Nothing.
293*1cd85f57SStephen Warren    """
294*1cd85f57SStephen Warren
295*1cd85f57SStephen Warren    for fn in metafunc.fixturenames:
296*1cd85f57SStephen Warren        if fn == 'ut_subtest':
297*1cd85f57SStephen Warren            generate_ut_subtest(metafunc, fn)
298*1cd85f57SStephen Warren            continue
299*1cd85f57SStephen Warren        generate_config(metafunc, fn)
300d201506cSStephen Warren
301636f38d8SStephen Warren@pytest.fixture(scope='function')
302d201506cSStephen Warrendef u_boot_console(request):
303e8debf39SStephen Warren    """Generate the value of a test's u_boot_console fixture.
304d201506cSStephen Warren
305d201506cSStephen Warren    Args:
306d201506cSStephen Warren        request: The pytest request.
307d201506cSStephen Warren
308d201506cSStephen Warren    Returns:
309d201506cSStephen Warren        The fixture value.
310e8debf39SStephen Warren    """
311d201506cSStephen Warren
312636f38d8SStephen Warren    console.ensure_spawned()
313d201506cSStephen Warren    return console
314d201506cSStephen Warren
31583357fd5SStephen Warrenanchors = {}
316d201506cSStephen Warrentests_not_run = set()
317d201506cSStephen Warrentests_failed = set()
31878b39cc3SStephen Warrentests_xpassed = set()
31978b39cc3SStephen Warrentests_xfailed = set()
320d201506cSStephen Warrentests_skipped = set()
321d201506cSStephen Warrentests_passed = set()
322d201506cSStephen Warren
323d201506cSStephen Warrendef pytest_itemcollected(item):
324e8debf39SStephen Warren    """pytest hook: Called once for each test found during collection.
325d201506cSStephen Warren
326d201506cSStephen Warren    This enables our custom result analysis code to see the list of all tests
327d201506cSStephen Warren    that should eventually be run.
328d201506cSStephen Warren
329d201506cSStephen Warren    Args:
330d201506cSStephen Warren        item: The item that was collected.
331d201506cSStephen Warren
332d201506cSStephen Warren    Returns:
333d201506cSStephen Warren        Nothing.
334e8debf39SStephen Warren    """
335d201506cSStephen Warren
336d201506cSStephen Warren    tests_not_run.add(item.name)
337d201506cSStephen Warren
338d201506cSStephen Warrendef cleanup():
339e8debf39SStephen Warren    """Clean up all global state.
340d201506cSStephen Warren
341d201506cSStephen Warren    Executed (via atexit) once the entire test process is complete. This
342d201506cSStephen Warren    includes logging the status of all tests, and the identity of any failed
343d201506cSStephen Warren    or skipped tests.
344d201506cSStephen Warren
345d201506cSStephen Warren    Args:
346d201506cSStephen Warren        None.
347d201506cSStephen Warren
348d201506cSStephen Warren    Returns:
349d201506cSStephen Warren        Nothing.
350e8debf39SStephen Warren    """
351d201506cSStephen Warren
352d201506cSStephen Warren    if console:
353d201506cSStephen Warren        console.close()
354d201506cSStephen Warren    if log:
35583357fd5SStephen Warren        with log.section('Status Report', 'status_report'):
356d201506cSStephen Warren            log.status_pass('%d passed' % len(tests_passed))
357d201506cSStephen Warren            if tests_skipped:
358d201506cSStephen Warren                log.status_skipped('%d skipped' % len(tests_skipped))
359d201506cSStephen Warren                for test in tests_skipped:
36083357fd5SStephen Warren                    anchor = anchors.get(test, None)
36183357fd5SStephen Warren                    log.status_skipped('... ' + test, anchor)
36278b39cc3SStephen Warren            if tests_xpassed:
36378b39cc3SStephen Warren                log.status_xpass('%d xpass' % len(tests_xpassed))
36478b39cc3SStephen Warren                for test in tests_xpassed:
36583357fd5SStephen Warren                    anchor = anchors.get(test, None)
36683357fd5SStephen Warren                    log.status_xpass('... ' + test, anchor)
36778b39cc3SStephen Warren            if tests_xfailed:
36878b39cc3SStephen Warren                log.status_xfail('%d xfail' % len(tests_xfailed))
36978b39cc3SStephen Warren                for test in tests_xfailed:
37083357fd5SStephen Warren                    anchor = anchors.get(test, None)
37183357fd5SStephen Warren                    log.status_xfail('... ' + test, anchor)
372d201506cSStephen Warren            if tests_failed:
373d201506cSStephen Warren                log.status_fail('%d failed' % len(tests_failed))
374d201506cSStephen Warren                for test in tests_failed:
37583357fd5SStephen Warren                    anchor = anchors.get(test, None)
37683357fd5SStephen Warren                    log.status_fail('... ' + test, anchor)
377d201506cSStephen Warren            if tests_not_run:
378d201506cSStephen Warren                log.status_fail('%d not run' % len(tests_not_run))
379d201506cSStephen Warren                for test in tests_not_run:
38083357fd5SStephen Warren                    anchor = anchors.get(test, None)
38183357fd5SStephen Warren                    log.status_fail('... ' + test, anchor)
382d201506cSStephen Warren        log.close()
383d201506cSStephen Warrenatexit.register(cleanup)
384d201506cSStephen Warren
385d201506cSStephen Warrendef setup_boardspec(item):
386e8debf39SStephen Warren    """Process any 'boardspec' marker for a test.
387d201506cSStephen Warren
388d201506cSStephen Warren    Such a marker lists the set of board types that a test does/doesn't
389d201506cSStephen Warren    support. If tests are being executed on an unsupported board, the test is
390d201506cSStephen Warren    marked to be skipped.
391d201506cSStephen Warren
392d201506cSStephen Warren    Args:
393d201506cSStephen Warren        item: The pytest test item.
394d201506cSStephen Warren
395d201506cSStephen Warren    Returns:
396d201506cSStephen Warren        Nothing.
397e8debf39SStephen Warren    """
398d201506cSStephen Warren
399d201506cSStephen Warren    mark = item.get_marker('boardspec')
400d201506cSStephen Warren    if not mark:
401d201506cSStephen Warren        return
402d201506cSStephen Warren    required_boards = []
403d201506cSStephen Warren    for board in mark.args:
404d201506cSStephen Warren        if board.startswith('!'):
405d201506cSStephen Warren            if ubconfig.board_type == board[1:]:
406d201506cSStephen Warren                pytest.skip('board not supported')
407d201506cSStephen Warren                return
408d201506cSStephen Warren        else:
409d201506cSStephen Warren            required_boards.append(board)
410d201506cSStephen Warren    if required_boards and ubconfig.board_type not in required_boards:
411d201506cSStephen Warren        pytest.skip('board not supported')
412d201506cSStephen Warren
413d201506cSStephen Warrendef setup_buildconfigspec(item):
414e8debf39SStephen Warren    """Process any 'buildconfigspec' marker for a test.
415d201506cSStephen Warren
416d201506cSStephen Warren    Such a marker lists some U-Boot configuration feature that the test
417d201506cSStephen Warren    requires. If tests are being executed on an U-Boot build that doesn't
418d201506cSStephen Warren    have the required feature, the test is marked to be skipped.
419d201506cSStephen Warren
420d201506cSStephen Warren    Args:
421d201506cSStephen Warren        item: The pytest test item.
422d201506cSStephen Warren
423d201506cSStephen Warren    Returns:
424d201506cSStephen Warren        Nothing.
425e8debf39SStephen Warren    """
426d201506cSStephen Warren
427d201506cSStephen Warren    mark = item.get_marker('buildconfigspec')
428d201506cSStephen Warren    if not mark:
429d201506cSStephen Warren        return
430d201506cSStephen Warren    for option in mark.args:
431d201506cSStephen Warren        if not ubconfig.buildconfig.get('config_' + option.lower(), None):
432d201506cSStephen Warren            pytest.skip('.config feature not enabled')
433d201506cSStephen Warren
434d201506cSStephen Warrendef pytest_runtest_setup(item):
435e8debf39SStephen Warren    """pytest hook: Configure (set up) a test item.
436d201506cSStephen Warren
437d201506cSStephen Warren    Called once for each test to perform any custom configuration. This hook
438d201506cSStephen Warren    is used to skip the test if certain conditions apply.
439d201506cSStephen Warren
440d201506cSStephen Warren    Args:
441d201506cSStephen Warren        item: The pytest test item.
442d201506cSStephen Warren
443d201506cSStephen Warren    Returns:
444d201506cSStephen Warren        Nothing.
445e8debf39SStephen Warren    """
446d201506cSStephen Warren
44783357fd5SStephen Warren    anchors[item.name] = log.start_section(item.name)
448d201506cSStephen Warren    setup_boardspec(item)
449d201506cSStephen Warren    setup_buildconfigspec(item)
450d201506cSStephen Warren
451d201506cSStephen Warrendef pytest_runtest_protocol(item, nextitem):
452e8debf39SStephen Warren    """pytest hook: Called to execute a test.
453d201506cSStephen Warren
454d201506cSStephen Warren    This hook wraps the standard pytest runtestprotocol() function in order
455d201506cSStephen Warren    to acquire visibility into, and record, each test function's result.
456d201506cSStephen Warren
457d201506cSStephen Warren    Args:
458d201506cSStephen Warren        item: The pytest test item to execute.
459d201506cSStephen Warren        nextitem: The pytest test item that will be executed after this one.
460d201506cSStephen Warren
461d201506cSStephen Warren    Returns:
462d201506cSStephen Warren        A list of pytest reports (test result data).
463e8debf39SStephen Warren    """
464d201506cSStephen Warren
465d201506cSStephen Warren    reports = runtestprotocol(item, nextitem=nextitem)
46678b39cc3SStephen Warren
46778b39cc3SStephen Warren    failure_cleanup = False
46878b39cc3SStephen Warren    test_list = tests_passed
46978b39cc3SStephen Warren    msg = 'OK'
47078b39cc3SStephen Warren    msg_log = log.status_pass
471d201506cSStephen Warren    for report in reports:
472d201506cSStephen Warren        if report.outcome == 'failed':
47378b39cc3SStephen Warren            if hasattr(report, 'wasxfail'):
47478b39cc3SStephen Warren                test_list = tests_xpassed
47578b39cc3SStephen Warren                msg = 'XPASSED'
47678b39cc3SStephen Warren                msg_log = log.status_xpass
47778b39cc3SStephen Warren            else:
47878b39cc3SStephen Warren                failure_cleanup = True
47978b39cc3SStephen Warren                test_list = tests_failed
48078b39cc3SStephen Warren                msg = 'FAILED:\n' + str(report.longrepr)
48178b39cc3SStephen Warren                msg_log = log.status_fail
482d201506cSStephen Warren            break
483d201506cSStephen Warren        if report.outcome == 'skipped':
48478b39cc3SStephen Warren            if hasattr(report, 'wasxfail'):
48578b39cc3SStephen Warren                failure_cleanup = True
48678b39cc3SStephen Warren                test_list = tests_xfailed
48778b39cc3SStephen Warren                msg = 'XFAILED:\n' + str(report.longrepr)
48878b39cc3SStephen Warren                msg_log = log.status_xfail
48978b39cc3SStephen Warren                break
49078b39cc3SStephen Warren            test_list = tests_skipped
49178b39cc3SStephen Warren            msg = 'SKIPPED:\n' + str(report.longrepr)
49278b39cc3SStephen Warren            msg_log = log.status_skipped
493d201506cSStephen Warren
49478b39cc3SStephen Warren    if failure_cleanup:
495c10eb9d3SStephen Warren        console.drain_console()
49678b39cc3SStephen Warren
49778b39cc3SStephen Warren    test_list.add(item.name)
498d201506cSStephen Warren    tests_not_run.remove(item.name)
499d201506cSStephen Warren
500d201506cSStephen Warren    try:
50178b39cc3SStephen Warren        msg_log(msg)
502d201506cSStephen Warren    except:
503d201506cSStephen Warren        # If something went wrong with logging, it's better to let the test
504d201506cSStephen Warren        # process continue, which may report other exceptions that triggered
505d201506cSStephen Warren        # the logging issue (e.g. console.log wasn't created). Hence, just
506d201506cSStephen Warren        # squash the exception. If the test setup failed due to e.g. syntax
507d201506cSStephen Warren        # error somewhere else, this won't be seen. However, once that issue
508d201506cSStephen Warren        # is fixed, if this exception still exists, it will then be logged as
509d201506cSStephen Warren        # part of the test's stdout.
510d201506cSStephen Warren        import traceback
511d201506cSStephen Warren        print 'Exception occurred while logging runtest status:'
512d201506cSStephen Warren        traceback.print_exc()
513d201506cSStephen Warren        # FIXME: Can we force a test failure here?
514d201506cSStephen Warren
515d201506cSStephen Warren    log.end_section(item.name)
516d201506cSStephen Warren
51778b39cc3SStephen Warren    if failure_cleanup:
518d201506cSStephen Warren        console.cleanup_spawn()
519d201506cSStephen Warren
520d201506cSStephen Warren    return reports
521