File size: 9,882 Bytes
c8c8f12
 
c89c89c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8c8f12
 
 
 
 
 
 
 
 
 
 
 
 
580d9f5
 
c8c8f12
c89c89c
c8c8f12
c89c89c
c8c8f12
 
 
 
 
 
 
 
c89c89c
c8c8f12
 
 
 
 
 
c89c89c
c8c8f12
 
c89c89c
 
c8c8f12
 
1f09aa2
c89c89c
1f09aa2
 
 
c8c8f12
 
 
c89c89c
c8c8f12
 
c89c89c
c8c8f12
 
 
c89c89c
c8c8f12
 
1f09aa2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c89c89c
c8c8f12
c89c89c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7659b58
c89c89c
 
 
 
 
 
 
7659b58
 
 
 
 
 
 
c89c89c
 
 
 
 
 
 
 
 
 
 
7659b58
 
 
 
 
 
 
 
c89c89c
 
 
 
 
 
e22d8a3
 
 
1f09aa2
 
 
7659b58
 
 
 
e22d8a3
c89c89c
 
c8c8f12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c89c89c
 
 
 
 
e22d8a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c8c8f12
 
 
 
 
 
 
 
 
 
7659b58
c89c89c
 
1f09aa2
c8c8f12
 
c89c89c
 
1f09aa2
7659b58
 
c8c8f12
7659b58
 
c8c8f12
c89c89c
 
 
c8c8f12
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# Testing β€” Marionette

## Run the tests

The quickest way to run everything and log results:

```bash
cd marionette
python tests/run_tests.py
```

This will:
1. Auto-detect your OS (Linux / macOS / Windows)
2. Ask which robot model you have connected (None / Lite / Wireless)
3. Run unit tests with verbose output
4. Run E2E tests for each available Playwright browser
5. Print a per-class test catalog and coverage matrix
6. Save results to `tests/test_results.json`
7. Export `marionette/static/test_coverage.json` for the web UI

After running, **commit `marionette/static/test_coverage.json`** and push so
other contributors can see the latest coverage on the web page.

## Install dependencies

```bash
cd marionette
pip install -e ".[dev]"
```

For E2E browser tests you also need Playwright browsers:

```bash
pip install pytest-playwright
playwright install --with-deps chromium firefox
```

WebKit (Safari's browser engine) is available on some platforms
(`playwright install webkit`) but is not required.

## Running individual suites

### Unit tests only (~4 s)

```bash
cd marionette
pytest tests/test_api.py
```

No hardware or running server needed β€” tests use FastAPI's `TestClient`.

### E2E single browser (~20 s)

```bash
cd marionette
pytest tests/e2e --browser chromium
```

### E2E multi-browser

```bash
cd marionette
pytest tests/e2e --browser chromium --browser firefox --browser webkit
```

### Hardware integration tests (local)

Requires a physical Reachy Mini robot connected and powered on.
This runs tests on the local machine (useful when developing directly
on the robot or when the robot is accessible locally):

```bash
cd marionette
pytest tests/test_hardware.py -m hardware
```

Or via the test runner:

```bash
cd marionette
python tests/run_tests.py --hardware
```

### Hardware tests on the robot (recommended)

Hardware tests should run **on the robot itself** because:
- Audio playback (`play_sound()`) requires local speakers β€” WAV files are
  silently dropped over WebRTC
- The robot has direct hardware access (local mic, speakers, motors)
- Running locally eliminates network latency from test measurements

**One-time SSH setup:**

```bash
# Copy your SSH key to the robot (password: pollen)
ssh-copy-id pollen@reachy-mini.local

# Verify passwordless access
ssh pollen@reachy-mini.local echo OK
```

**Run tests remotely:**

```bash
cd marionette
python tests/run_on_robot.py
```

This will:
1. rsync the marionette package + tests to `/tmp/marionette_test/` on the robot
2. Install dev deps if missing (pytest, httpx, pytest-json-report, scipy)
3. Run `pytest -m hardware` on the robot, streaming output in real-time
4. Fetch the JSON report back and print a summary

**Options:**

```bash
python tests/run_on_robot.py --dry-run             # show what would be synced
python tests/run_on_robot.py --host 192.168.1.42   # custom host/IP
python tests/run_on_robot.py --user pollen          # custom SSH user
python tests/run_on_robot.py -k test_playback       # extra pytest args
```

Or via the main test runner:

```bash
python tests/run_tests.py --on-robot
python tests/run_tests.py --on-robot --host 192.168.1.42
```

### All tests at once (no hardware)

```bash
cd marionette
pytest tests/ --browser chromium
```

## What's tested

### Unit tests (`tests/test_api.py`)

| Class | Tests | Description |
|-------|------:|-------------|
| TestSlugify | 7 | Move label slug generation |
| TestStateEndpoint | 9 | GET /api/state shape and initial values |
| TestStartingUpMode | 3 | Starting-up mode rejects commands |
| TestRecordEndpoint | 9 | POST /api/record validation and state transitions |
| TestPlayEndpoint | 3 | POST /api/play validation |
| TestStopEndpoints | 3 | POST /api/play/stop and /api/record/stop |
| TestMoveDelete | 4 | DELETE /api/moves/:id and file cleanup |
| TestDatasets | 11 | Dataset create, select, root change, origin |
| TestRegistryPersistence | 3 | Registry file survival across restarts |
| TestMovesRefresh | 4 | Move listing and metadata |
| TestExperiments | 4 | Feature toggles and experimental settings |
| TestHfAutoLogin | 5 | HF auto-login detection and caching |
| TestSensorData | 1 | Sensor data dummy endpoint |
| TestUploadAudio | 7 | POST /api/upload-audio validation and integration |
| TestMotionModelEndpoint | 4 | POST /api/motion-model enable, set, persist |
| TestCommunityDatasets | 3 | Community dataset listing and download validation |
| TestCorruptData | 4 | Malformed JSON and corrupt registry recovery |
| TestConcurrentStateChanges | 4 | Concurrent operations rejected when busy |
| TestDurationEdgeCases | 3 | Duration boundary validation (gt=0.5, le=300) |
| TestSyncDatasetExtended | 4 | Sync endpoint edge cases and validation |

### E2E browser tests (`tests/e2e/test_ui.py`)

| Class | Tests | Description |
|-------|------:|-------------|
| TestPageLoad | 3 | Page loads and main sections visible |
| TestIdleState | 4 | Idle state display and controls |
| TestSimplifiedForm | 3 | Simplified form layout |
| TestFormValidation | 5 | Form fields and HTML5 validation |
| TestRecordingSubmission | 1 | Recording submission changes mode |
| TestDatasetUI | 2 | Dataset UI elements visible |
| TestRecordingLifecycle | 4 | Recording submit, stop, and injected move visibility |
| TestPlaybackLifecycle | 3 | Play button, queued playback, and move metadata |
| TestDeleteMove | 3 | Delete button, confirm/cancel dialog handling |
| TestCreateDataset | 4 | New dataset form show/hide/create/validate |
| TestSwitchDataset | 2 | Dataset switching and dropdown population |
| TestAudioSourceRadios | 3 | Audio source radio buttons and upload area toggle |
| TestSettingsPanel | 3 | Settings expand, experimental toggle, root display |
| TestFormEdgeCases | 4 | Empty/long/special labels and localStorage persistence |

### Hardware tests (`tests/test_hardware.py`)

| Class | Tests | Description |
|-------|------:|-------------|
| TestHardwareStartup | 1 | Robot reaches idle after startup |
| TestHardwareRecording | 1 | Record silent and verify motion capture |
| TestHardwarePlayback | 2 | Playback and delete (silent) |
| TestFullPipeline | 4 | Full record β†’ verify files β†’ replay β†’ delete lifecycle |
| TestMotionAccuracy | 3 | Synthetic playback accuracy β€” reference vs observed poses |
| TestMultiDuration | 7 | Recording and playback across 1s/3s/5s/10s durations |
| TestPerformance | 3 | Startup, recording, and playback latency benchmarks |
| TestExistingRecordingPlayback | 2 | Play back existing audio and silent recordings |
| TestRecordingRoundTrip | 2 | Record β†’ playback fidelity and timing verification |
| TestAntennaAndBodyYaw | 2 | Verify antenna and body_yaw data in recordings |
| TestPlaybackAntennas | 1 | Synthetic antenna oscillation playback |
| TestHardwareAudio | 3 | Audio recording and playback (may skip on mic issues) |

## View the matrix without running tests

```bash
cd marionette
python tests/show_matrix.py
```

### Matrix format

```
Coverage Matrix (latest result per combination)

             β”‚ Unit Tests β”‚ Chromium  β”‚ Firefox   β”‚ WebKit
─────────────┼────────────┼───────────┼───────────┼──────────
Linux/None   β”‚ βœ… Feb 20  β”‚ βœ… Feb 20 β”‚ β€”         β”‚ β€”
Linux/Lite   β”‚ β€”          β”‚ β€”         β”‚ β€”         β”‚ β€”
macOS/None   β”‚ β€”          β”‚ β€”         β”‚ β€”         β”‚ β€”
Windows/None β”‚ β€”          β”‚ β€”         β”‚ β€”         β”‚ β€”
...
```

Each cell shows the latest result for that OS + robot + test-suite
combination. `β€”` means never tested. A "Hardware" column appears when
hardware test results are available.

## Web coverage

The Space's `index.html` displays a **Test Coverage** section that loads
`marionette/static/test_coverage.json` (exported by `run_tests.py`).
This is visible on the
[HuggingFace Space page](https://huggingface.co/spaces/RemiFabre/marionette),
not the app's web GUI at `localhost:8042`.

### Preview locally

To see the coverage table as it appears on the Space, open `index.html`
in a browser. Because it fetches a JSON file, you need a local server:

```bash
cd marionette
python -m http.server 8080
```

Then open <http://localhost:8080> β€” you should see the Space landing page
with the Test Coverage matrix near the bottom.

### Commit and push results

After running the tests, commit the updated coverage file so the Space
page reflects the latest results:

```bash
cd marionette
git add marionette/static/test_coverage.json
git commit -m "Update test coverage matrix"
git push
```

## Markers

- `@pytest.mark.hardware` β€” tests that require a physical robot (skipped by
  default unless you run with `-m hardware`)

## Project layout

```
tests/
β”œβ”€β”€ conftest.py          # Shared fixtures (TestClient, temp paths, make_wav_bytes)
β”œβ”€β”€ test_api.py          # Unit tests β€” backend API
β”œβ”€β”€ test_hardware.py     # Hardware integration tests (real robot)
β”œβ”€β”€ pose_utils.py        # Trajectory comparison utilities
β”œβ”€β”€ e2e/
β”‚   β”œβ”€β”€ conftest.py      # Playwright server fixture (port 18042)
β”‚   └── test_ui.py       # E2E browser tests
β”œβ”€β”€ run_tests.py         # Test runner + matrix logger + web export
β”œβ”€β”€ run_on_robot.py      # Remote test runner (SSH + rsync to robot)
β”œβ”€β”€ run_smoke_on_robot.py # Smoke test runner (SSH + rsync)
β”œβ”€β”€ smoke_test_on_robot.py # Smoke test script (runs on robot)
β”œβ”€β”€ show_matrix.py       # Matrix viewer
β”œβ”€β”€ REMOTE_TESTING.md    # SSH/rsync workflow documentation
β”œβ”€β”€ HARDWARE_TEST_PLAN.md # Hardware test plan
└── test_results.json    # Auto-generated results log (git-ignored)

marionette/static/
└── test_coverage.json   # Web matrix export (committed)
```