> ## 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.

# Quickstart

> Scaffold, run, and understand your first CruciHiL test — no hardware required

This guide assumes you have CruciHiL installed. If not, start with [Installation](/installation).

All steps below use only the **virtual backend** — no CAN hardware, no ECU, no special OS configuration needed.

***

## Step 1 — Scaffold the hello\_world example

The `scaffold` command drops a self-contained, runnable example into your current directory:

```bash theme={null}
crucihil scaffold --example hello_world
```

This creates:

```
examples/hello_world/
├── rigs/
│   └── virtual.toml          # virtual rig — no hardware needed
├── suites/
│   └── hello.yaml            # YAML suite manifest
└── tests/
    └── hello.py              # Python test functions
```

## Step 2 — Run the suite

```bash theme={null}
cd examples/hello_world
crucihil run --suite suites/hello.yaml --rig rigs/virtual.toml
```

Expected output:

```
Suite  : hello_world v1.0.0  (2 tests)
Rig    : rigs/virtual.toml

  ✓ [PASS   ] framework_works  (0.002s)
  ✓ [PASS   ] power_cycle  (0.000s)

2 tests  2 passed
```

## Step 3 — Understand the test

Open `tests/hello.py`:

```python theme={null}
async def test_framework_works(rig: Rig) -> None:
    assert rig is not None
    assert rig.config.name == "hello_rig"
```

A few things to notice:

* **`async def`** — all tests are coroutines. CruciHiL uses asyncio throughout.
* **`rig: Rig`** — the framework injects the `Rig` object. Tests never construct it.

For a signal-level test, scaffold the `can_signals` example (`crucihil scaffold --example can_signals`). Its tests use `rig.can.expect()`:

```python theme={null}
async def test_signal_range_check(rig: Rig) -> None:
    result = await rig.can.expect(
        signal="EngineData.RPM",
        condition=lambda v: 0.0 <= v <= 8000.0,
        timeout=3.0,
    )
    assert result.passed, f"RPM out of range [0, 8000]: {result.fail_msg}"
```

No CAN interface name, no backend-specific code — this same function call works against a virtual backend in CI and a PEAK adapter on your bench. The YAML manifest declares the setup steps that run before the function:

```yaml theme={null}
tests:
  - id: signal_range_check
    name: Signal stays within range
    setup:
      - sim.set:   { signal: "EngineData.RPM", value: 1500.0 }
      - sim.start: EngineData
    teardown:
      - sim.stop: EngineData
    module: tests.can_signals
    function: test_signal_range_check
```

The `sim.set` step puts a value into the virtual bus; `sim.start` begins broadcasting the CAN message at its DBC-defined cycle time. The test function then asserts on the decoded signal value.

## Step 4 — Get a report

Add `--html` to generate a self-contained HTML report:

```bash theme={null}
crucihil run \
  --suite suites/hello.yaml \
  --rig rigs/virtual.toml \
  --html report.html
```

Open `report.html` in any browser — per-test status, duration, error messages, and signal trace data.

For JUnit XML (CI integration):

```bash theme={null}
crucihil run \
  --suite suites/hello.yaml \
  --rig rigs/virtual.toml \
  --output results.xml
```

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Write your own tests" icon="pen" href="/test-authoring/python-api">
    Full Rig Python API — send, expect, fault injection, sim control.
  </Card>

  <Card title="Connect real hardware" icon="wrench" href="/rig-config/overview">
    Wire up SocketCAN, PEAK, or a custom backend in the rig TOML.
  </Card>

  <Card title="Analyze a firmware component" icon="magnifying-glass-chart" href="/cli/analyze">
    Use `crucihil analyze` to extract the signal interface of a C/C++ SWC.
  </Card>

  <Card title="Connect an AI assistant" icon="robot" href="/mcp/overview">
    Wire Claude or Copilot to your rig and test history via MCP.
  </Card>
</CardGroup>
