| # 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 |
| |
| from . import utils |
| |
| log = logging.getLogger(__name__) |
| |
| |
| @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 'tmp_path_factory' not in node.funcargs: |
| return |
| except Exception: |
| return |
| |
| utils.dump_temp_files(node.funcargs['tmp_path_factory'].getbasetemp()) |
| |
| |
| @pytest.fixture(scope="session") |
| def localconf(): |
| """Host-local configuration.""" |
| if 'OPENTITAN_TEST_LOCALCONF' in os.environ: |
| localconf_yaml_file = Path(os.environ['OPENTITAN_TEST_LOCALCONF']) |
| log.info("Attempting to use configuration file set in " |
| "OPENTITAN_TEST_LOCALCONF.") |
| else: |
| XDG_CONFIG_HOME = Path(os.getenv( |
| 'XDG_CONFIG_HOME', os.path.join(os.environ['HOME'], '.config'))) |
| localconf_yaml_file = XDG_CONFIG_HOME / 'opentitan' / 'test-localconf.yaml' |
| |
| log.info('Reading configuration from ' + str(localconf_yaml_file)) |
| |
| with open(str(localconf_yaml_file), 'r') as fp: |
| return yaml.load(fp, Loader=yaml.SafeLoader) |
| |
| |
| @pytest.fixture(scope="session") |
| def topsrcdir(): |
| """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 bin_dir(topsrcdir): |
| """ Return the BIN_DIR (build output directory) """ |
| if 'BIN_DIR' in os.environ: |
| bin_dir = Path(os.environ['BIN_DIR']) |
| log.info("Using build outputs from environment variable BIN_DIR={}".format(str(bin_dir))) |
| else: |
| bin_dir = topsrcdir / 'build-bin' |
| log.info("Using build outputs from $REPO_TOP/build-bin ({})".format(str(bin_dir))) |
| |
| assert bin_dir.is_dir() |
| return bin_dir |
| |
| |
| @pytest.fixture(scope="session") |
| def openocd(): |
| """ Return the path to the openocd binary. """ |
| openocd_bin = shutil.which('openocd') |
| assert openocd_bin |
| |
| proc = subprocess.run([openocd_bin, '--version'], |
| check=True, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| encoding="utf8") |
| log.info("Using OpenOCD at {}: '{}'".format(openocd_bin, |
| proc.stdout.splitlines()[0])) |
| return Path(openocd_bin) |
| |
| |
| def pytest_configure(config): |
| config.addinivalue_line("markers", "slow: mark test as slow, will be excluded from the main " |
| "verilator job in CI") |
| |
| |
| @pytest.fixture(scope="module") |
| def uart_persistent(localconf_board, tmp_path_factory): |
| """ |
| A UART device connected to the FPGA board, which is kept active for |
| for multiple tests. |
| |
| Use the `uart` fixture for per-test access to the UART device. |
| """ |
| uart_device = localconf_board['uart_device'] |
| uart_speed = localconf_board['uart_speed'] |
| log_dir_path = tmp_path_factory.mktemp('uart') |
| log.debug("Opening UART on device {} ({} baud)".format( |
| uart_device, uart_speed)) |
| with utils.LoggingSerial(uart_device, |
| uart_speed, |
| timeout=1, |
| log_dir_path=log_dir_path, |
| default_filter_func=utils. |
| filter_remove_device_sw_log_prefix) as uart: |
| |
| yield uart |
| |
| |
| @pytest.fixture |
| def uart(uart_persistent, request): |
| """ A UART device connected to the FPGA board. |
| |
| The UART is drained between test runs to give better isolation between the |
| tests. |
| """ |
| |
| uart_persistent.log_add_marker("===== TEST {} START =====\n".format( |
| request.node.name)) |
| |
| yield uart_persistent |
| |
| # Read all remaining data from UART to have it available in the log file. |
| uart_persistent.drain_in() |
| |
| uart_persistent.log_add_marker("===== TEST {} DONE =====\n\n".format( |
| request.node.name)) |