> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crucihil.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bring your pytest & Robot suites

> Run existing pytest and Robot Framework HiL suites through CruciHiL — no rewrites, no migration

You do not have to rewrite your test base to adopt CruciHiL. Existing pytest and Robot Framework suites run through the platform unchanged — the adapters map their results onto CruciHiL's model with the blocked≠fail distinction intact (`BlockedError` → `blocked`, never `fail`). Your test files stay exactly as they are.

## Installation

The framework adoption layer is an optional extra:

```bash theme={null}
pip install 'crucihil[frameworks]'
```

Or from source:

```bash theme={null}
pip install -e ".[frameworks]"
```

## Three adoption tiers

Adopt incrementally — each tier works on its own, and none requires changing your test files.

<Steps>
  <Step title="Tier 1 — Import your existing results">
    Keep your current runner. Import its JUnit XML or Robot `output.xml` so history, trends, and MCP querying work before any rig integration:

    ```bash theme={null}
    crucihil import-results build/junit.xml
    crucihil import-results output.xml --format robot
    ```

    See [`crucihil import-results`](/cli/import-results) for formats, options, and the blocked-status mapping.
  </Step>

  <Step title="Tier 2 — Run your suites through CruciHiL">
    Point `crucihil run` at your unmodified test tree with `--framework`:

    ```bash theme={null}
    # Unmodified pytest suite
    crucihil run --suite legacy/pytest_tests/ --framework pytest --rig rigs/virtual.toml

    # Unmodified Robot suite
    crucihil run --suite legacy/robot_tests/ --framework robot --rig rigs/virtual.toml
    ```

    The pytest plugin provides the async `rig` fixture; the Robot library exposes rig verbs as keywords. Results, reporters (`--output`, `--html`), and cloud sync are identical downstream regardless of who executed the tests. See [`crucihil run`](/cli/run).
  </Step>

  <Step title="Tier 3 — Mutation-verify your foreign suites">
    Prove your existing tests actually catch regressions — a strength report with zero rewrites:

    ```bash theme={null}
    crucihil verify --suite legacy/pytest_tests/ --framework pytest --rig rigs/virtual.toml
    crucihil verify --suite legacy/robot_tests/  --framework robot  --rig rigs/virtual.toml
    ```

    The simulated DUT is broken on purpose (dead DUT, stuck outputs, latency violations, declared faults) and every test is scored `caught`, `missed`, or `wrong_reason`. See [`crucihil verify`](/cli/verify).
  </Step>
</Steps>

## The pytest plugin

When you run a pytest tree with `--framework pytest`, the CruciHiL pytest plugin:

* **Injects the `rig` fixture** — an async fixture built from your rig TOML. Each test gets a fresh, connected `Rig`; it is disconnected after the test, so runs are isolated.
* **Maps outcomes onto CruciHiL statuses** with the load-bearing distinction preserved:

| pytest outcome        | CruciHiL status                                           |
| --------------------- | --------------------------------------------------------- |
| Test passes           | `pass`                                                    |
| Assertion failure     | `fail` — firmware is sick                                 |
| `BlockedError` raised | `blocked` — rig is sick, does not count against pass rate |
| Fixture/setup failure | `blocked` — precondition, not firmware                    |
| Any other exception   | `error`                                                   |
| pytest skip           | `skip`                                                    |

Your test functions just take the fixture:

```python theme={null}
async def test_engine_startup(rig):
    await rig.can.send(message="EngineControl", fields={"throttle": 50})
    result = await rig.can.expect(
        signal="EngineRPM", condition=lambda v: v > 800, timeout=2.0
    )
    assert result.passed, result.fail_msg
```

## The Robot Framework library and listener

Robot suites use two pieces, both wired up automatically by `crucihil run --framework robot` (the rig TOML path is passed as the `${RIG_TOML}` variable):

**`CruciHiLLibrary`** exposes rig verbs as keywords. Robot keywords are synchronous; the library owns a background asyncio loop that hosts the rig, and every keyword bridges into it.

```robotframework theme={null}
*** Settings ***
Library    crucihil.adapters.robot_library.CruciHiLLibrary    ${RIG_TOML}

*** Test Cases ***
Engine Reaches Idle
    Set Signal      EngineData.RPM    1500
    Start Message   EngineData
    Expect Signal   EngineData.RPM    min=800    timeout=2.0
    [Teardown]      Stop Message      EngineData
```

Available keywords:

| Keyword               | Description                                                                              |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `Set Signal`          | Set a simulated signal value                                                             |
| `Start Message`       | Start periodic transmission of a DBC message                                             |
| `Stop Message`        | Stop periodic transmission of a DBC message                                              |
| `Expect Signal`       | Assert a signal enters `[min, max]` within a timeout; returns the value                  |
| `Inject Fault`        | Activate a fault for the rest of the test (e.g. `Inject Fault  can_dropout  arb_id=256`) |
| `Clear Faults`        | Deactivate every fault injected by the library (reverse order)                           |
| `Require Rig Healthy` | Raise `BlockedError` (→ `blocked`, not `fail`) when the rig is down                      |
| `Block Test`          | Explicitly mark the current test blocked: precondition not met                           |
| `Disconnect Rig`      | Tear the rig down (suite teardown) — always safe to call                                 |

**`CruciHiLListener`** (Listener v3) streams Robot results as CruciHiL test results: `PASS` → `pass`, `SKIP` → `skip`, a `FAIL` whose message contains `BlockedError` → `blocked`, any other `FAIL` → `fail`.

<Info>
  The Blocked failure convention runs end-to-end: raise `BlockedError` from a keyword (or use `Block Test`), and the result is `blocked` in the live listener, in the run summary, and even after a round-trip through `output.xml` and [`crucihil import-results`](/cli/import-results).
</Info>

## Drop `--framework` with a wrapper manifest

Wrap the foreign test tree once in a YAML manifest and run it like any native suite. The `framework:` field selects the executor; `path:` points at the foreign suite root:

```yaml theme={null}
# suites/legacy_wrap.yaml
suite:
  name: legacy_wrap

framework: pytest        # 'pytest' | 'robot'  ('crucihil' is the default)
path: legacy/pytest_tests

tests: []
```

```bash theme={null}
crucihil run --suite suites/legacy_wrap.yaml --rig rigs/virtual.toml
```

`framework: pytest` or `framework: robot` requires `path:` — the manifest is validated at load time.

## What stays the same

* **Your test files.** pytest tests and `.robot` files run unchanged — no imports to rewrite, no annotations to add, no migration.
* **Your framework's semantics.** pytest collects and executes your tests; Robot runs your suites. CruciHiL wraps execution and translates results.
* **Downstream behavior.** JUnit XML / HTML reports, cloud sync, and mutation verification are identical whether a suite is native, pytest, or Robot.

What you gain: the injected `rig` fixture / keyword library for hardware access, the blocked≠fail distinction in your results, and CruciHiL's history, dashboard, and verification on top of the test base you already have.

## See also

* [`crucihil import-results`](/cli/import-results) — Tier 1 command reference
* [`crucihil run`](/cli/run) — run native or foreign suites
* [`crucihil verify`](/cli/verify) — mutation verification, including `--framework`
* [YAML Manifest Reference](/test-authoring/yaml-manifest) — the native suite schema
