Skip to content

Commit 3f55538

Browse files
committed
docs: fix test counts, acquire-shape comment, and stale API examples
- README: from_task acquire shape (n_samples, n_channels); mocked tier 663 -> 916; replace the broken 2-task software-trigger snippet with the valid single-task form - TESTING: 905 -> 916 mocked, 33 -> 36 hardware; remove the removed device_ind from the mocked/simulated/hardware examples and fix the (n_samples, n_channels) shapes; document both hardware test files
1 parent 01f3345 commit 3f55538

2 files changed

Lines changed: 33 additions & 19 deletions

File tree

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,16 +186,24 @@ sensitivity_units = "mV/g"
186186
units = "g"
187187
```
188188

189-
### Multi-task synchronized acquisition
189+
### Software-triggered acquisition (level trigger)
190+
191+
`set_trigger()` arms a software level trigger on a **single** input task
192+
(software triggering reads one task sequentially; use a hardware trigger for
193+
synchronized multi-task acquisition — see the next section):
190194

191195
```python
192-
from nidaqwrapper import MultiHandler
196+
from nidaqwrapper import AITask, MultiHandler
197+
198+
task = AITask('vibration', sample_rate=25600)
199+
task.add_channel('ch0', device='cDAQ1Mod1', channel_ind=0,
200+
sensitivity=100, sensitivity_units='mV/g', units='g')
193201

194202
adv = MultiHandler()
195-
adv.configure(input_tasks=[task1, task2])
203+
adv.configure(input_tasks=[task])
196204
adv.connect()
197205
adv.set_trigger(n_samples=25600, trigger_channel=0, trigger_level=0.5)
198-
data = adv.acquire()
206+
data = adv.acquire() # {channel_name: ndarray, 'time': ndarray}
199207
adv.disconnect()
200208
```
201209

@@ -270,7 +278,7 @@ raw_task.timing.cfg_samp_clk_timing(rate=25600)
270278

271279
wrapped = AITask.from_task(raw_task)
272280
# Use wrapped task with DAQHandler or read directly
273-
data = wrapped.acquire() # shape: (n_channels, n_samples)
281+
data = wrapped.acquire() # shape: (n_samples, n_channels)
274282
raw_task.close() # Caller retains ownership
275283
```
276284

@@ -333,7 +341,7 @@ nidaqwrapper uses a three-tier test strategy:
333341
| Simulated | `uv run pytest -m simulated -v` | NI-DAQmx driver + simulated device |
334342
| Hardware | `uv run pytest -m hardware -v` | Physical NI hardware |
335343

336-
The mocked tier (663 tests) runs by default and requires no NI-DAQmx driver. The simulated tier uses the real driver with simulated devices to catch API contract violations. The hardware tier validates real-world timing and physical signals.
344+
The mocked tier (916 tests) runs by default and requires no NI-DAQmx driver. The simulated tier uses the real driver with simulated devices to catch API contract violations. The hardware tier validates real-world timing and physical signals.
337345

338346
See [TESTING.md](TESTING.md) for detailed setup instructions, troubleshooting, and how to configure simulated devices.
339347

TESTING.md

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ nidaqwrapper uses a three-tier test strategy to balance speed, coverage, and har
66

77
| Tier | Tests | Requirements | Purpose |
88
|------|-------|-------------|---------|
9-
| **Mocked** | 905 | None | Fast unit tests with mocked nidaqmx for CI/CD |
9+
| **Mocked** | 916 | None | Fast unit tests with mocked nidaqmx for CI/CD |
1010
| **Simulated** | 81 + 1 xfail (+1 env-dependent skip) | NI-DAQmx driver + simulated devices | Real driver API validation without physical hardware |
11-
| **Hardware** | 33 | Physical NI-DAQmx devices | Real-world timing, triggers, and signal validation |
11+
| **Hardware** | 36 | Physical NI-DAQmx devices | Real-world timing, triggers, and signal validation |
1212

1313
The simulated tier bridges the gap between mocked tests and hardware tests. All 4 bugs found during hardware testing were invisible to mocked tests because `MagicMock` auto-generates any attribute on access, masking real API mismatches. Simulated devices use the real NI-DAQmx driver with simulated hardware, catching API contract violations while remaining fast and deterministic.
1414

@@ -35,7 +35,7 @@ The default `uv run pytest` excludes both simulated and hardware tests to ensure
3535

3636
## Test Tiers
3737

38-
### Mocked Tests (905 tests)
38+
### Mocked Tests (916 tests)
3939

4040
**What they test:**
4141
- Public API contracts (function signatures, return types)
@@ -95,7 +95,12 @@ Simulated tests detect these issues without requiring physical hardware.
9595
(DaqError -89125) — full cross-device bursts are hardware-test only
9696
- cDAQ chassis/modules cannot be simulated on Linux
9797

98-
### Hardware Tests (`tests/test_hardware.py`)
98+
### Hardware Tests (`tests/test_hardware.py`, `tests/test_hardware_task_input.py`)
99+
100+
The hardware tier spans two files: `tests/test_hardware.py` (the consolidated,
101+
product-type-resolved suite — IEPE/SAR/AO rig) and the older
102+
`tests/test_hardware_task_input.py` (task-input lifecycle on real hardware).
103+
Both are `@pytest.mark.hardware` and run together under `-m hardware`.
99104

100105
**What they test:**
101106
- Real-world timing accuracy and buffer fill rates
@@ -115,8 +120,8 @@ Simulated tests detect these issues without requiring physical hardware.
115120

116121
**Running hardware tests:**
117122
```bash
118-
# All hardware tests (serial, stop on first failure — recommended)
119-
uv run pytest tests/test_hardware.py -m hardware -x -p no:randomly -v
123+
# All hardware tests across both files (serial, stop on first failure — recommended)
124+
uv run pytest -m hardware -x -p no:randomly -v
120125
```
121126

122127
## Setting Up Simulated Devices
@@ -294,7 +299,7 @@ Example:
294299
```python
295300
def test_ai_task_configuration(mock_task):
296301
task = AITask('test_task', sample_rate=25600)
297-
task.add_channel('ch0', device_ind=0, channel_ind=0, units='V')
302+
task.add_channel('ch0', device='Dev1', channel_ind=0, units='V')
298303
task.configure()
299304
assert task.task_name == 'test_task'
300305
assert task.sample_rate == 25600
@@ -312,24 +317,25 @@ Use the `@pytest.mark.simulated` decorator and fixtures from `tests/conftest.py`
312317

313318
Example:
314319
```python
320+
import numpy as np
315321
import pytest
316322
from nidaqwrapper import AITask
317323

318324
@pytest.mark.simulated
319-
def test_ai_task_read(sim_device_index):
325+
def test_ai_task_read(simulated_device_name):
320326
task = AITask('sim_test', sample_rate=10000)
321-
task.add_channel('ch0', device_ind=sim_device_index, channel_ind=0, units='V')
327+
task.add_channel('ch0', device=simulated_device_name, channel_ind=0, units='V')
322328
task.configure()
323329
task.start()
324330
data = task.acquire(n_samples=100)
325-
assert data.shape == (1, 100) # 1 channel, 100 samples
331+
assert data.shape == (100, 1) # 100 samples, 1 channel
326332
assert data.dtype == np.float64
327333
task.clear_task()
328334
```
329335

330336
### Hardware Tests
331337

332-
Location: `tests/test_hardware_<topic>.py`
338+
Location: `tests/test_hardware.py`
333339

334340
Use the `@pytest.mark.hardware` decorator. Clearly document required hardware in the test docstring.
335341

@@ -347,12 +353,12 @@ def test_accelerometer_acquisition():
347353
- Accelerometer connected to channel 0
348354
"""
349355
task = AITask('accel_test', sample_rate=25600)
350-
task.add_channel('accel', device_ind=1, channel_ind=0,
356+
task.add_channel('accel', device='cDAQ1Mod1', channel_ind=0,
351357
sensitivity=100, sensitivity_units='mV/g', units='g')
352358
task.configure()
353359
task.start()
354360
data = task.acquire(n_samples=1000)
355-
assert data.shape == (1, 1000)
361+
assert data.shape == (1000, 1)
356362
task.clear_task()
357363
```
358364

0 commit comments

Comments
 (0)