|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | import logging | 
|  | import os | 
|  | import shutil | 
|  | import subprocess | 
|  | from pathlib import Path | 
|  |  | 
|  | import pytest | 
|  | import yaml | 
|  |  | 
|  |  | 
|  | def pytest_addoption(parser): | 
|  | """Test harness configuration options.""" | 
|  | parser.addoption("--test_bin", action="store", default="") | 
|  | parser.addoption("--rom_bin", action="store", default="") | 
|  | parser.addoption("--verilator_model", action="store", default="") | 
|  | parser.addoption("--openocd", action="store", default="openocd") | 
|  | parser.addoption("--uart_timeout", action="store", default="60") | 
|  | parser.addoption("--fpga_uart", action="store", default="") | 
|  | parser.addoption("--spiflash", action="store", default="") | 
|  | parser.addoption("--log", action="store", default="") | 
|  |  | 
|  | @pytest.hookimpl(tryfirst=True) | 
|  | def pytest_exception_interact(node, call, report): | 
|  | """Dump all log files in case of a test failure.""" | 
|  | try: | 
|  | if not report.failed: | 
|  | return | 
|  | if not 'tmp_path' in node.funcargs: | 
|  | return | 
|  | except: | 
|  | return | 
|  |  | 
|  | tmp_path = str(node.funcargs['tmp_path']) | 
|  | print("\n\n") | 
|  | print("================= DUMP OF ALL TEMPORARY FILES =================") | 
|  |  | 
|  | for f in os.listdir(tmp_path): | 
|  | f_abs = os.path.join(tmp_path, f) | 
|  | if not os.path.isfile(f_abs): | 
|  | continue | 
|  | print("vvvvvvvvvvvvvvvvvvvv {} vvvvvvvvvvvvvvvvvvvv".format(f)) | 
|  | with open(f_abs, 'r') as fp: | 
|  | print(fp.read()) | 
|  | print("^^^^^^^^^^^^^^^^^^^^ {} ^^^^^^^^^^^^^^^^^^^^\n\n".format(f)) | 
|  |  | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def localconf(request): | 
|  | """Host-local configuration.""" | 
|  | if os.getenv('OPENTITAN_TEST_LOCALCONF') and os.path.isfile( | 
|  | os.environ['OPENTITAN_TEST_LOCALCONF']): | 
|  | localconf_yaml_file = os.environ['OPENTITAN_TEST_LOCALCONF'] | 
|  | else: | 
|  | XDG_CONFIG_HOME = os.getenv( | 
|  | 'XDG_CONFIG_HOME', os.path.join(os.environ['HOME'], '.config')) | 
|  | localconf_yaml_file = os.path.join(XDG_CONFIG_HOME, 'opentitan', | 
|  | 'test-localconf.yaml') | 
|  | logging.getLogger(__name__).info('Reading configuration from ' + | 
|  | localconf_yaml_file) | 
|  |  | 
|  | with open(str(localconf_yaml_file), 'r') as fp: | 
|  | return yaml.load(fp) | 
|  |  | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def topsrcdir(request): | 
|  | """Return the top-level source directory as Path object.""" | 
|  | # TODO: Consider making this configurable using a pytest arg. | 
|  | path = (Path(os.path.dirname(__file__)) / '..' / '..').resolve() | 
|  | assert path.is_dir() | 
|  | return path | 
|  |  | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def sw_test_bin(pytestconfig): | 
|  | """Return path to software test binary.""" | 
|  | path = Path(pytestconfig.getoption('test_bin')).resolve() | 
|  | assert path.is_file() | 
|  | return path | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def rom_bin(pytestconfig): | 
|  | """Return path to boot_rom binary.""" | 
|  | path = Path(pytestconfig.getoption('rom_bin')).resolve() | 
|  | assert path.is_file() | 
|  | return path | 
|  |  | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def sim_top_build(pytestconfig): | 
|  | """Return path to Verilator sim model.""" | 
|  | path = Path(pytestconfig.getoption('verilator_model')).resolve() | 
|  | assert path.is_file() | 
|  | return path | 
|  |  | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def openocd(pytestconfig): | 
|  | """Return path to OpenOCD executable.""" | 
|  | path = Path(pytestconfig.getoption('openocd')) | 
|  | # TODO: Require that the OpenOCD executable be passed as a command-line | 
|  | # argument in the future, rather than relying on $PATH lookup. | 
|  | # assert path.is_file() | 
|  | return path | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def uart_timeout(pytestconfig): | 
|  | """Return the timeout in seconds for UART to print PASS.""" | 
|  | return int(pytestconfig.getoption('uart_timeout')) | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def fpga_uart(pytestconfig): | 
|  | """Return the path to the UART attached to the FPGA.""" | 
|  | path = Path(pytestconfig.getoption('fpga_uart')).resolve() | 
|  | assert path.exists() and not path.is_dir() | 
|  | return path | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def spiflash(pytestconfig): | 
|  | """Return the path to the spiflash executable.""" | 
|  | path = Path(pytestconfig.getoption('spiflash')).resolve() | 
|  | assert path.is_file() | 
|  | return path | 
|  |  | 
|  | @pytest.fixture(scope="session") | 
|  | def logfile(pytestconfig): | 
|  | """Return path to logfile.""" | 
|  | log = pytestconfig.getoption('log') | 
|  | if not log: | 
|  | return | 
|  | # The strict option is only availabe on python 3.6 | 
|  | # CI currently uses >=3.5.2 | 
|  | # Turns out however even not using resolve doesn't work. | 
|  | # The logging function in 3.5 uses os.path.isabs to check whether | 
|  | # path is absolute and does not accept POSIXPATH objects | 
|  | # path = Path(log).resolve(strict=False) | 
|  | # assert not path.is_dir() | 
|  |  | 
|  | path = os.path.abspath(log) | 
|  | assert not os.path.isdir(path) | 
|  | return path |