This is a quick post about the end_test cocotb function. It terminates the test when called from any coroutine. The main observation is that it respects xfail, as the documentation mentions.

end_test Link to heading

From src/cocotb/_test_manager.py, the doc pretty much sums it up as follows:

def end_test(msg: str | None = None) -> NoReturn:
    """End a test.

    The test will end after this function is called as if it passed.
    However, any :deco:`cocotb.xfail` decorators
    or ``expect_error`` and ``expect_fail`` arguments to :func:`cocotb.test` will be respected.
    Meaning, if they are used, the test will fail, as a failure was expected.

    Args:
        msg: The message to display when the test ends.
    """
    raise EndTest(msg)
@cocotb.test()
async def test_end_test(dut):
    """ Finished with end_test() """
    cocotb.log.info("Starting test")

    cocotb.start_soon(coroutine_assert())

    await Timer(40, unit="ns")
    cocotb.log.info("Finished waiting for 40 ns")
    end_test()
    cocotb.log.info("Test completed")

@cocotb.test()
@cocotb.xfail(reason="unsupport feature")
async def test_end_test_xfail(dut):
    """ Finished with end_test() """
    cocotb.log.info("Starting test")

    cocotb.start_soon(coroutine_assert())

    await Timer(40, unit="ns")
    cocotb.log.info("Finished waiting for 100 ns")
    end_test()
    cocotb.log.info("Test completed")

For these tests, the first one passes as expected, but the second one fails due to xfail.

 620.00ns INFO     cocotb.regression                  running tests.test_end_test (5/17)
                                                            Finished with end_test()
   620.00ns INFO     test                               Starting test
   630.00ns INFO     test                               Monitoring... 0
   640.00ns INFO     test                               Monitoring... 1
   650.00ns INFO     test                               Monitoring... 2
   660.00ns INFO     test                               Finished waiting for 40 ns
   660.00ns INFO     cocotb.task.Test test_end_test     Test stopped early by this task
   660.00ns INFO     cocotb.regression                  tests.test_end_test passed
   660.00ns INFO     cocotb.regression                  running tests.test_end_test_xfail (6/17)
                                                            Finished with end_test()
   660.00ns INFO     test                               Starting test
   670.00ns INFO     test                               Monitoring... 0
   680.00ns INFO     test                               Monitoring... 1
   690.00ns INFO     test                               Monitoring... 2
   700.00ns INFO     test                               Finished waiting for 100 ns
   700.00ns INFO     ..tb.task.Test test_end_test_xfail Test stopped early by this task
   700.00ns WARNING  cocotb.regression                  tests.test_end_test_xfail failed: passed but we expected a failure

pass_test Link to heading

pass_test was deprecated as it’s not a good idea to force a test to pass. It is mentioned here for completeness.

def pass_test(msg: str | None = None) -> NoReturn:
    """Force a test to pass.

    The test will end after this function is called.
    The outcome of the test will be forced to pass,
    even if a :deco:`cocotb.xfail` decorator is used,
    or ``expect_error`` and ``expect_fail`` arguments to :deco:`cocotb.test` are passed.

    Args:
        msg: The message to display when the test passes.

    .. versionadded:: 2.0

    .. deprecated:: 2.1
        Forcing a test to pass is considered bad practice.
        Use :func:`cocotb.skip` if you want to end the test immediately and force the outcome to 'skipped'.
        Or use :func:`cocotb.end_test` if you want to end the test immediately while respecting expected failures.