Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.roboticks.io/llms.txt

Use this file to discover all available pages before exploring further.

Decorators

The four decorators in roboticks are the SDK’s smallest surface and the one users touch most. All four work on any callable that pytest collects — functions, methods, parametrized variants, async tests. They stack in any order.
from roboticks import confirms, tags, deadline, requires_sim

@confirms(*req_ids)

Records which requirement IDs the decorated test confirms. The pytest plugin reads this and emits a <property name="roboticks.confirms" value="REQ-001,REQ-014"/> into the JUnit XML at session-end.

Signature

def confirms(*req_ids: str) -> Callable: ...
ParameterTypeConstraint
req_ids*strAt least one. Must match a known requirement ID after platform ingestion, otherwise the platform reports the test as “confirms an unknown requirement” in the Check Run.

Behaviour

  • Stacking@confirms("A") on a class plus @confirms("B") on a method unions to {"A", "B"}. Useful when an entire test class addresses one umbrella requirement and individual methods address sub-requirements.
  • Parametrize — A @pytest.mark.parametrize variant inherits the function’s @confirms set; all branches confirm the same requirement IDs. If you need different requirements per branch, split into multiple test functions.
  • Skipped tests — A skipped test still records its @confirms (so the matrix shows which requirements are intended to be covered, even when the test didn’t run on this commit). The matrix surfaces them with a “skipped” badge.

Example

from roboticks import confirms

@confirms("REQ-014")
class TestEStop:

    @confirms("REQ-015")  # union: {REQ-014, REQ-015}
    def test_engages_on_button(self):
        ...

    @confirms("REQ-016")  # union: {REQ-014, REQ-016}
    def test_engages_on_heartbeat_loss(self):
        ...

@tags(*tags)

Free-form labels for filtering. The platform doesn’t interpret them — they are pass-through metadata for your filters, dashboards, and policies.

Signature

def tags(*tags: str) -> Callable: ...

Conventions

By community convention (not enforced):
TagMeaning
smokeRun on every PR
nightlyRun only on the nightly suite
slowWall-clock > 10 s
safetyTouches a safety requirement
flakyKnown intermittent; quarantine candidate

Example

from roboticks import tags, confirms

@confirms("REQ-101")
@tags("nightly", "perception", "slow")
def test_full_warehouse_traversal(...):
    ...
The platform exposes tag-based test filters in Project → Tests → Filter by tag.

@deadline(milliseconds=int)

Fails the test if its wall-clock duration exceeds the budget. Useful for soft real-time guarantees that don’t otherwise show up as an assertion.

Signature

def deadline(*, milliseconds: int) -> Callable: ...
ParameterTypeConstraint
millisecondsint (keyword-only)> 0

Behaviour

  • The decorator wraps the test body in a time.monotonic() measurement and raises DeadlineExceeded (an AssertionError subclass) if the test runs over.
  • Reported in JUnit XML as <property name="roboticks.deadline_ms" value="100"/>.
  • The platform surfaces the actual duration vs the deadline in the run detail. A test that passes its assertions but blows the deadline is failed.

Example

from roboticks import confirms, deadline

@confirms("REQ-001")
@deadline(milliseconds=100)
def test_estop_halts_within_100ms(robot):
    robot.trigger_estop()
    robot.wait_until_stopped()
    # No explicit time assertion — @deadline handles it.

Interaction with pytest timeouts

@deadline and pytest-timeout are independent. pytest-timeout kills the process; @deadline only fails the test. Use both for hard-stop budgets:
@deadline(milliseconds=100)
@pytest.mark.timeout(1)   # 1 s hard kill
def test_estop(...): ...

@requires_sim(engine, *, gpu=False)

Tells the job router the test needs a simulation runtime.

Signature

def requires_sim(engine: Literal["gazebo", "webots"], *, gpu: bool = False) -> Callable: ...
ParameterTypeConstraint
engineLiteral["gazebo", "webots"]Required.
gpubool (keyword-only)Defaults to False.

Behaviour

  • Reported in JUnit XML as <property name="roboticks.requires_sim" value="gazebo:gpu"/> or "gazebo" if gpu=False.
  • The platform’s scheduler refuses to dispatch the test to a runner that doesn’t satisfy the engine + GPU label.
  • Local pytest runs do not skip the test — they run it. If sim isn’t available locally the test will fail at the rclpy step, which is the right behaviour: a developer needs to know they’re trying to run a sim test without a sim.

Example

from roboticks import confirms, requires_sim

@confirms("REQ-031")
@requires_sim("gazebo", gpu=True)
def test_navigation_through_warehouse(...):
    ...

Stacking order

Decorators are evaluated bottom-up (closest-to-function first), but for these four the order doesn’t change behaviour — they are independent metadata. Pick a convention and stick with it. The convention in our examples:
@confirms(...)      # outermost: what
@tags(...)          #            why
@requires_sim(...)  #            where
@deadline(...)      # innermost: how fast
def test_x(...): ...

What about @pytest.mark.X?

Roboticks decorators are not pytest markers. They mutate the function’s attributes; the pytest plugin reads those attributes at collection time. This avoids pytest’s marker-registration warnings and keeps @confirms working on classes (where @pytest.mark.X semantics are subtle). You can still use pytest markers freely. They land in the JUnit XML as standard pytest properties; they don’t conflict with the roboticks.* namespace.

Next

Pytest plugin

How the plugin reads these decorators and writes JUnit properties.

Assertions

The rclpy-aware companion APIs.

Sim runners

What @requires_sim triggers on the runner side.

Wire contract

The JUnit-with-confirms schema these decorators feed into.