Source code for cocotb_tools._pytest.mark
# Copyright cocotb contributors
# Licensed under the Revised BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-3-Clause
"""Plugin markers."""
from __future__ import annotations
from inspect import Parameter, signature
from typing import Callable
from pytest import Config, MarkDecorator, mark
from cocotb.simtime import TimeUnit
from cocotb_tools.runner import (
VHDL,
PathLike,
VerilatorControlFile,
Verilog,
)
[docs]
def cocotb_runner(test_module: str = "", *extra_test_module: str) -> MarkDecorator:
"""Mark test function as cocotb runner.
Example usage:
.. code:: python
import pytest
from cocotb_tools._pytest.hdl import HDL
@pytest.fixture(name="sample_module")
def sample_module_fixture(hdl: HDL) -> HDL:
# Define HDL design and build it
hdl.toplevel = "sample_module"
hdl.sources = (DESIGNS / "sample_module.sv",)
hdl.build()
return hdl
@pytest.mark.cocotb_runner
def test_dut(sample_module: HDL) -> None:
# Run HDL simulator with cocotb tests
sample_module.test()
Args:
test_module:
Name of Python module with cocotb tests to be loaded by cocotb :py:attr:`~cocotb_tools._pytest.hdl.HDL.runner`.
Returns:
Decorated test function as cocotb runner.
"""
return mark.cocotb_runner(test_module=test_module, *extra_test_module)
[docs]
def cocotb_test() -> MarkDecorator:
"""Mark coroutine function as cocotb test.
Example usage:
.. code:: python
# NOTE: decorator is not needed if coroutine function starts with the test_ prefix and it uses dut fixture
async def test_dut_feature(dut) -> None:
# Test DUT feature
...
@pytest.mark.cocotb_test
async def non_canonical_test_name(dut) -> None:
# Test DUT feature but from a test function that doesn't follow with the pytest naming convention
...
Returns:
Decorated coroutine function as cocotb test.
"""
return mark.cocotb_test()
[docs]
def cocotb_timeout(duration: float, unit: TimeUnit) -> MarkDecorator:
"""Mark coroutine function with simulation time duration before the test is forced to fail.
Example usage:
.. code:: python
@pytest.mark.cocotb_timeout(duration=200, unit="ns")
async def test_dut_feature_with_timeout(dut) -> None:
# Test DUT feature with timeout configured from cocotb marker
...
Args:
duration: Simulation time duration before the test is forced to fail.
unit: Simulation time unit that accepts any unit that :class:`~cocotb.triggers.Timer` does.
Raises:
:exc:`~cocotb.triggers.SimTimeoutError`: Test function timeouted.
Returns:
Decorated coroutine function with simulation time duration before the test is forced to fail.
"""
return mark.cocotb_timeout(duration=duration, unit=unit)
[docs]
def cocotb_sources(
*source: PathLike | Verilog | VHDL | VerilatorControlFile,
) -> MarkDecorator:
"""Add language-agnostic list of source files to build."""
return mark.cocotb_sources(*source)
[docs]
def cocotb_defines(**define: object) -> MarkDecorator:
"""Set defines."""
return mark.cocotb_defines(**define)
[docs]
def cocotb_parameters(**parameter: object) -> MarkDecorator:
"""Set Verilog/SystemVerilog parameters and VHDL generics."""
return mark.cocotb_parameters(**parameter)
[docs]
def cocotb_env(**env: str) -> MarkDecorator:
"""Set environment variables."""
return mark.cocotb_env(**env)
[docs]
def cocotb_includes(*include: PathLike) -> MarkDecorator:
"""Add Verilog includes."""
return mark.cocotb_includes(*include)
[docs]
def cocotb_plusargs(*plusarg: str) -> MarkDecorator:
"""Add plus arguments for the simulator."""
return mark.cocotb_plusargs(*plusarg)
[docs]
def cocotb_timescale(unit: str, precision: str | None = None) -> MarkDecorator:
"""Set time unit and time precision for simulation."""
return mark.cocotb_timescale(unit=unit, precision=precision)
[docs]
def cocotb_seed(value: str | int) -> MarkDecorator:
"""A specific random seed to use."""
return mark.cocotb_seed(value=value)
[docs]
def cocotb_build_args(*arg: str | VHDL | Verilog) -> MarkDecorator:
"""Add extra build arguments for the simulator."""
return mark.cocotb_build_args(*arg)
[docs]
def cocotb_elab_args(*arg: str) -> MarkDecorator:
"""Add extra elaboration arguments to the simulator."""
return mark.cocotb_elab_args(*arg)
[docs]
def cocotb_test_args(*arg: str) -> MarkDecorator:
"""Add extra runtime arguments to the simulator."""
return mark.cocotb_test_args(*arg)
[docs]
def cocotb_pre_cmd(*arg: str) -> MarkDecorator:
"""Add extra commands to run before simulation begins. Typically Tcl commands for simulators that support them.."""
return mark.cocotb_pre_cmd(*arg)
[docs]
def cocotb_library(name: str) -> MarkDecorator:
"""Set the library name to compile into."""
return mark.cocotb_library(name=name)
[docs]
def cocotb_waves(condition: bool = True) -> MarkDecorator:
"""Record signal traces."""
return mark.cocotb_waves(condition=condition)
def cocotb_verbose(condition: bool = True) -> MarkDecorator:
"""Enable verbose messages."""
return mark.cocotb_verbose(condition=condition)
[docs]
def cocotb_always(condition: bool = True) -> MarkDecorator:
"""Always run the build step."""
return mark.cocotb_always(condition=condition)
[docs]
def cocotb_clean(condition: bool = True) -> MarkDecorator:
"""Delete *build_dir* before building."""
return mark.cocotb_clean(condition=condition)
def _marker_description(marker: Callable[..., MarkDecorator]) -> str:
"""Get pretty formatted description of marker.
Args:
marker: Definition of marker for pytest.
Returns:
Pretty formatted description of provided marker.
"""
args: list[str] = []
for name, parameter in signature(marker).parameters.items():
arg: str = ""
if parameter.kind == Parameter.VAR_KEYWORD:
arg = f"{name}=..."
else:
arg = name
if parameter.default != Parameter.empty:
arg += f"={parameter.default}"
if parameter.kind == Parameter.KEYWORD_ONLY and "*" not in args:
args.append("*")
args.append(arg)
if parameter.kind == Parameter.VAR_POSITIONAL:
args.append("...")
description: str = str(marker.__doc__).lstrip().splitlines()[0]
return f"{marker.__name__}({', '.join(args)}): {description}".rstrip()
def _register_markers(config: Config) -> None:
"""Register plugin markers.
Args:
config: Pytest configuration object.
"""
for marker in (
cocotb_runner,
cocotb_test,
cocotb_timeout,
cocotb_library,
cocotb_sources,
cocotb_defines,
cocotb_includes,
cocotb_parameters,
cocotb_plusargs,
cocotb_env,
cocotb_seed,
cocotb_timescale,
cocotb_always,
cocotb_clean,
cocotb_waves,
cocotb_build_args,
cocotb_elab_args,
cocotb_test_args,
cocotb_pre_cmd,
):
config.addinivalue_line("markers", _marker_description(marker))