# Unit Testing

## Purpose

<span style="white-space: pre-wrap;">Unit tests in this repository are designed to validate </span>**component behavior**, not entrypoint wiring.

- <span style="white-space: pre-wrap;">Tests target logic in </span>`<span class="editor-theme-code">components/common/*</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">components/<board>/*</span>`.
- `<span class="editor-theme-code">src/<board>/main.c</span>`<span style="white-space: pre-wrap;"> remains focused on initialization and task orchestration.</span>
- <span style="white-space: pre-wrap;">Test ownership mirrors component ownership: each component should have corresponding tests under </span>`<span class="editor-theme-code">test/<owner>/</span>`.

## Test stack

The project uses PlatformIO’s unit testing framework with Unity.

- Each test binary follows the Unity lifecycle:
    - `<span class="editor-theme-code">UNITY_BEGIN()</span>`
    - `<span class="editor-theme-code">RUN_TEST(...)</span>`
    - `<span class="editor-theme-code">UNITY_END()</span>`
- Global Unity output configuration is provided via:
    - `<span class="editor-theme-code">test/unity_config.h</span>`
    - `<span class="editor-theme-code">test/unity_config.c</span>`
- The current output implementation initializes UART and writes test output character-by-character over a COM interface.

---

## Test layout

Tests are organized by ownership and module:

```
test/
├─ common/
│  ├─ test_bucketed_pqueue/
│  ├─ test_kv_pool/
├─ sensor_board/
│  ├─ test_gps_sensor/
│  ├─ test_imu_sensor/
│  ├─ test_ph_sensor/
│  ├─ test_sensor_basics/
├─ driving_board/
│  ├─ test_calculator/
│  ├─ test_motor/
├─ debugging_board/
│  ├─ test_input_handler/
├─ unity_config.c
└─ unity_config.h
```

Naming conventions:

- <span style="white-space: pre-wrap;">Directory: </span>`<span class="editor-theme-code">test/<owner>/test_<module>/</span>`
- <span style="white-space: pre-wrap;">File: </span>`<span class="editor-theme-code">test_<behavior>.c</span>`<span style="white-space: pre-wrap;"> or </span>`<span class="editor-theme-code">test_<module>.c</span>`
- <span style="white-space: pre-wrap;">Function: </span>`<span class="editor-theme-code">test_<expected_behavior>()</span>`

## Running tests

Run all tests for a specific environment:

```
pio test -e sensor_board
```

Run all tests across all environments:

```
pio test
```

Run a specific test directory:

```
pio test -e sensor_board -f test_imu_sensor
```

## Environment test selection (`<span class="editor-theme-code">platformio.ini</span>`)

<span style="white-space: pre-wrap;">Test execution is controlled per environment using the </span>`<span class="editor-theme-code">test_filter</span>`<span style="white-space: pre-wrap;"> setting.</span>

Current configuration:

- `<span class="editor-theme-code">env:sensor_board</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">test_filter = sensor_board/*</span>`
- `<span class="editor-theme-code">env:driving_board</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">test_filter = driving_board/*</span>`
- `<span class="editor-theme-code">env:debugging_board</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">test_filter = common/*</span>`

<span style="white-space: pre-wrap;">When adding new tests, ensure the corresponding environment includes the test path in its </span>`<span class="editor-theme-code">test_filter</span>`. Otherwise, the tests will not be executed.

## Writing a new unit test

### 1) Place it by ownership

Tests must follow the same ownership structure as the components.

Example:

- <span style="white-space: pre-wrap;">Component: </span>`<span class="editor-theme-code">components/common/kv_pool</span>`
- <span style="white-space: pre-wrap;">Test location: </span>`<span class="editor-theme-code">test/common/test_kv_pool/</span>`

### 2) Use Unity structure

```c
#include "unity.h"

void setUp(void) {}
void tearDown(void) {}

void test_example_behavior(void) {
    TEST_ASSERT_TRUE(1);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_example_behavior);
    return UNITY_END();
}
```

---

### 3) Assert behavior, not implementation

- Use public APIs instead of accessing internal state directly.
- Cover both success and failure cases.
- Use precise assertions:
    - `<span class="editor-theme-code">TEST_ASSERT_EQUAL</span>`
    - `<span class="editor-theme-code">TEST_ASSERT_FLOAT_WITHIN</span>`
    - etc.

### 4) Keep tests deterministic

- Avoid reliance on shared or previous test state.
- <span style="white-space: pre-wrap;">Reset all required state in </span>`<span class="editor-theme-code">setUp</span>`.
- Use time-based operations only when necessary, and keep them bounded.

## What to test

- Public functions in component modules
- Input validation and error handling
- Boundary conditions and invalid arguments
- State transitions and invariants

## What not to test as unit tests

The following are outside the scope of unit testing:

- <span style="white-space: pre-wrap;">Full board startup flows in </span>`<span class="editor-theme-code">main.c</span>`
- End-to-end hardware integration
- Multi-component system orchestration

These belong to integration or system-level testing.

## Troubleshooting

- <span style="white-space: pre-wrap;">No tests executed: verify </span>`<span class="editor-theme-code">test_filter</span>`<span style="white-space: pre-wrap;"> for the selected environment (</span>`<span class="editor-theme-code">-e</span>`)
- Test not detected: ensure correct directory naming (`<span class="editor-theme-code">test_<module></span>`<span style="white-space: pre-wrap;">) under </span>`<span class="editor-theme-code">test/</span>`
- <span style="white-space: pre-wrap;">No Unity output: check UART/COM configuration in </span>`<span class="editor-theme-code">test/unity_config.c</span>`<span style="white-space: pre-wrap;"> and board connection settings</span>