Made-with: Cursor
9.0 KiB
Test Specification — OutputManager
Acceptance Criteria Traceability
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|---|---|---|---|
| AC-26 | Total RAM ≤6GB (OutputManager must not contribute significant memory) | PT-02 | Covered |
| AC-27 | Coexist with YOLO pipeline — recording must not block inference | IT-01, PT-01 | Covered |
Note: OutputManager has no direct performance ACs from the acceptance criteria. Its tests ensure it supports the system's recording, logging, and operator delivery requirements defined in the architecture.
Integration Tests
IT-01: log_detection Writes Valid JSON-Line
Summary: Verify log_detection appends a correctly formatted JSON line to the detection log file.
Traces to: AC-27
Input data:
- DetectionLogEntry: {frame_id: 1000, label: "footpath_winter", confidence: 0.72, tier: 2, centerX: 0.5, centerY: 0.3}
- Output dir: temporary test directory
Expected result:
- detections.jsonl file exists in output dir
- Last line is valid JSON parseable to a dict with all input fields
- Trailing newline present
Max execution time: 50ms
Dependencies: Writable filesystem
IT-02: record_frame Saves JPEG to Sequential Filename
Summary: Verify record_frame encodes and saves frame as JPEG with correct naming.
Traces to: AC-27
Input data:
- Frame: numpy array (1080, 1920, 3), random pixel data
- frame_id: 42, level: 1
Expected result:
- File
42.jpgexists in output dirframes/subdirectory - File is a valid JPEG (OpenCV can re-read it)
- File size > 0 and < 500KB (reasonable JPEG of 1080p noise)
Max execution time: 100ms
Dependencies: Writable filesystem
IT-03: log_health Writes Health Entry
Summary: Verify log_health appends a JSON line with health data.
Traces to: AC-27
Input data:
- HealthLogEntry: {timestamp: epoch, t_junction_c: 65.0, vlm_available: true, gimbal_available: true, semantic_available: true}
Expected result:
- health.jsonl file exists
- Last line contains all input fields as valid JSON
Max execution time: 50ms
Dependencies: Writable filesystem
IT-04: log_gimbal_command Appends to Gimbal Log
Summary: Verify gimbal command strings are appended to the gimbal log file.
Traces to: AC-27
Input data:
- cmd_str: "SET_ANGLES pan=10.0 tilt=-20.0 zoom=5.0"
Expected result:
- gimbal.log file exists
- Last line matches the input command string
Max execution time: 50ms
Dependencies: Writable filesystem
IT-05: report_to_operator Formats Detection in YOLO Schema
Summary: Verify operator delivery formats detections with centerX, centerY, width, height, classNum, label, confidence.
Traces to: AC-27
Input data:
- list of 3 Detection objects
Expected result:
- Output matches existing YOLO output format (same field names, same coordinate normalization)
- All 3 detections present in output
Max execution time: 50ms
Dependencies: None
IT-06: get_storage_status Returns Correct NVMe Stats
Summary: Verify storage status reports accurate free space percentage.
Traces to: AC-26
Input data:
- Output dir on test filesystem
Expected result:
- StorageStatus: nvme_free_pct in [0, 100], frames_recorded ≥ 0, detections_logged ≥ 0
- should_reduce_recording matches threshold logic (true if free < 20%)
Max execution time: 50ms
Dependencies: Writable filesystem
IT-07: Circular Buffer Triggers on Low Storage
Summary: Verify should_reduce_recording becomes true when free space drops below 20%.
Traces to: AC-26
Input data:
- Mock statvfs to report 15% free space
Expected result:
- get_storage_status().should_reduce_recording == true
- At 25% free → should_reduce_recording == false
Max execution time: 50ms
Dependencies: Mock filesystem stats
IT-08: Init Creates Output Directory Structure
Summary: Verify init() creates the expected directory structure.
Traces to: AC-27
Input data:
- output_dir: temporary path that does not exist yet
Expected result:
- Directory created with subdirectories for frames
- No errors
Max execution time: 100ms
Dependencies: Writable filesystem
IT-09: WriteError Does Not Block Caller
Summary: Verify that a disk write failure (e.g., permission denied) is caught and does not propagate as an unhandled exception.
Traces to: AC-27
Input data:
- Output dir set to a read-only path
Expected result:
- log_detection raises no unhandled exception (catches WriteError internally)
- Error counter incremented
- Function returns normally
Max execution time: 50ms
Dependencies: Read-only filesystem path
Performance Tests
PT-01: Frame Recording Throughput at L2 Rate
Summary: Verify OutputManager can sustain 30 FPS frame recording without becoming a bottleneck.
Traces to: AC-27
Load scenario:
- 30 frames/second, 1080p JPEG encoding + write
- Duration: 10 seconds (300 frames)
- Ramp-up: immediate
Expected results:
| Metric | Target | Failure Threshold |
|---|---|---|
| Sustained write rate | ≥30 FPS | <25 FPS |
| Encoding latency (p95) | ≤20ms | >33ms |
| Dropped frames | 0 | >5 |
| Write throughput | ≥3 MB/s | <2 MB/s |
Resource limits:
- CPU: ≤20% (JPEG encoding)
- Memory: ≤100MB (buffer)
PT-02: Memory Usage Under Sustained Load
Summary: Verify no memory leak during continuous logging and recording.
Traces to: AC-26
Load scenario:
- 1000 log_detection calls + 300 record_frame calls
- Duration: 60 seconds
- Measure RSS before and after
Expected results:
| Metric | Target | Failure Threshold |
|---|---|---|
| Memory growth | ≤10MB | >50MB |
| Memory leak rate | 0 MB/min | >5 MB/min |
Resource limits:
- Memory: ≤100MB total for OutputManager
Security Tests
ST-01: Detection Log Does Not Contain Raw Image Data
Summary: Verify detection JSON lines contain metadata only, not embedded image data.
Traces to: AC-27
Attack vector: Information leakage through oversized log entries
Test procedure:
- Log 10 detections
- Read detections.jsonl
- Verify no field contains base64, raw bytes, or binary data
Expected behavior: Each JSON line is < 1KB; only text/numeric fields.
Pass criteria: All lines < 1KB, no binary data patterns.
Fail criteria: Any line contains embedded image data or exceeds 10KB.
ST-02: Path Traversal Prevention in Output Directory
Summary: Verify frame_id or other inputs cannot cause writes outside the output directory.
Traces to: AC-27
Attack vector: Path traversal via crafted frame_id
Test procedure:
- Call record_frame with frame_id containing "../" characters (e.g., as uint64 this shouldn't be possible, but verify string conversion)
- Verify file is written inside output_dir only
Expected behavior: File written within output_dir; no file created outside.
Pass criteria: All files within output_dir subtree.
Fail criteria: File created outside output_dir.
Acceptance Tests
AT-01: Full Flight Recording Session
Summary: Verify OutputManager correctly handles a simulated 5-minute flight with mixed L1 and L2 recording.
Traces to: AC-27
Preconditions:
- Temporary output directory with sufficient space
- Config: recording_l1_fps=2, recording_l2_fps=30
Steps:
| Step | Action | Expected Result |
|---|---|---|
| 1 | init(output_dir) | Directory structure created |
| 2 | Simulate 3 min L1: record_frame at 2 FPS | 360 frames written |
| 3 | Simulate 1 min L2: record_frame at 30 FPS | 1800 frames written |
| 4 | Log 50 detections during L2 | detections.jsonl has 50 lines |
| 5 | get_storage_status() | frames_recorded=2160, detections_logged=50 |
AT-02: Storage Reduction Under Pressure
Summary: Verify the storage management signals reduce recording at the right thresholds.
Traces to: AC-26
Preconditions:
- Mock filesystem with configurable free space
Steps:
| Step | Action | Expected Result |
|---|---|---|
| 1 | Set free space to 25% | should_reduce_recording = false |
| 2 | Set free space to 15% | should_reduce_recording = true |
| 3 | Set free space to 8% | should_reduce_recording = true (critical) |
Test Data Management
Required test data:
| Data Set | Description | Source | Size |
|---|---|---|---|
| sample_frames | 10 sample 1080p frames for recording tests | Generated (random or real) | ~10 MB |
| sample_detections | 50 DetectionLogEntry dicts | Generated fixtures | ~5 KB |
| sample_health | 10 HealthLogEntry dicts | Generated fixtures | ~2 KB |
Setup procedure:
- Create temporary output directory
- Call init(output_dir)
Teardown procedure:
- Delete temporary output directory and all contents
Data isolation strategy: Each test uses its own temporary directory. No shared output paths between tests.