mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2026-10-08 00:00:23 -04:00
6.9 KiB
6.9 KiB
Plan: Add Unit Tests
See also: RECOMMENDATIONS.md | PLAN.md | Architecture Overview
Current State
- Zero test coverage. No test framework, no test files, no test targets in CMakeLists.txt.
- CI exists (
.circleci/) but only builds — no test execution.
Implementation Plan
Phase 1: Set up test infrastructure
- Add a
tests/directory at the project root. - Add a test framework. Recommendation: Catch2 v3 (header-only, single header, modern C++14, good assertion macros, CMake integration via
FetchContentor bundled header). - Add a
tests/CMakeLists.txtwith acubicsdr_teststarget. - Add
add_subdirectory(tests)to the rootCMakeLists.txt(guarded by an option likeBUILD_TESTING). - Update CI config to run
ctestafter build.
Phase 2: Core utility tests (highest value, lowest effort)
Test file: tests/test_SpinMutex.cpp
src/util/SpinMutex.h— header-only, zero external dependencies (<atomic>only).- Tests:
- Single-thread:
lock()→try_lock()returns false →unlock()→try_lock()returns true. - Multi-thread: two threads incrementing a shared counter under the lock; verify final count equals sum of increments.
- Verify
lock_guard<SpinMutex>works (satisfies C++ Lockable concept). - Verify copy construction and assignment are deleted (compile-time).
- Single-thread:
Test file: tests/test_Gradient.cpp
src/util/Gradient.h+src/util/Gradient.cpp— self-contained, no project dependencies.- Tests:
- Two-color gradient (black to white) produces linear interpolation [0.0 → 1.0].
- Output array length matches requested
len. - Values clamped to [0.0, 1.0] for edge cases.
clear()resets state;generate()afterclear()produces empty arrays.- Multi-color gradient (3+ stops) produces correct interpolation.
- Single-color gradient returns an array of the single color (no interpolation needed).
Test file: tests/test_ThreadBlockingQueue.cpp
src/util/ThreadBlockingQueue.h— depends only onSpinMutex.h(also header-only).- Tests:
- Single-threaded:
push/popordering,size()tracking,empty()/full()states,flush(). - Capacity:
set_max_num_items(),try_push()returns false when full. - Multi-threaded producer-consumer: one thread pushes N items, another pops N items; verify all items received.
- Timeout:
pushwith short timeout returns false when queue is full. NON_BLOCKING_TIMEOUTpath: verify immediate fail when queue is full/empty.
- Single-threaded:
Test file: tests/test_DataTree.cpp
src/util/DataTree.h+src/util/DataTree.cpp— depends on bundledexternal/tinyxml/.- Tests:
DataElement::set()/get()for all scalar types (char, int, float, double, etc.).DataElement::getDataType()returns correct enum for each type.DataNodetree building:newChild(),child()by name/index,numChildren().DataNodeiteration:hasAnother()/getNext()/rewind().DataTree::SaveToFileXML()/LoadFromFileXML()round-trip.DataElementvector types:set()/get()forvector<int>,vector<float>, etc.
Test file: tests/test_Timer.cpp
src/util/Timer.h+src/util/Timer.cpp— platform-specific (<windows.h>on Windows).- Tests (using
lockFramerate()for determinism):start()resets timer;getMilliseconds()returns 0 after start.lockFramerate(30.0)→update()advances by ~33.33ms each call.paused(true)freezesgetMilliseconds()whiletotalMilliseconds()continues.getNumUpdates()countsupdate()calls correctly.setMilliseconds()/setSeconds()force specific values.
Phase 3: Additional module tests (after Phase 2 is stable)
Test file: tests/test_FreqConversion.cpp
- Frequency conversion utilities used throughout the SDR pipeline.
- Note: Identify actual frequency utility functions during implementation (e.g., frequency formatting, Hz/MHz conversion helpers used in UI display code). The original draft referenced
freqToHz()/hzToFreq()which do not exist in the codebase. - Tests:
- Round-trip conversion for common frequencies (1 MHz, 144 MHz, 440 MHz)
- Edge cases: DC (0 Hz), Nyquist boundary, negative frequencies
- Frequency formatting for display strings
Test file: tests/test_DataNode.cpp
DataNodetree structure operations (beyond basic DataTree round-trip in Phase 2).- Tests:
newChild()with various types (string, int, float, vector)child()by name and by indexnumChildren()accuracy after insertions- Iterator pattern:
hasAnother()/getNext()/rewind() - Deep tree traversal (3+ levels nesting)
DataNodecopy semantics
Test file: tests/test_IOThread.cpp
IOThreadbase class queue binding and lifecycle.- Tests:
setInputQueue()/getInputQueue()by namesetOutputQueue()/getOutputQueue()by namestoppingflag transitionsterminatedflag set afterrun()completesisTerminated()timeout behavior- Named queue rebinding (replace a bound queue)
Test file: tests/test_ReBuffer.cpp
ReBuffer<T>buffer pool allocation and garbage collection.- Tests:
getBuffer()returns new buffer when pool is emptygetBuffer()reuses buffer when use_count drops to 1- Multiple buffers in flight simultaneously
GC_LIMITage-out behavior (mock or observe pool size)- Thread-safe concurrent
getBuffer()calls
Phase 4: Update CI
- Add
BUILD_TESTING=ONto CI build steps. - Add
ctest --test-dir build --output-on-failureafter build. - Consider adding a separate "test" job that depends on the "build" job.
Verification Criteria
cmake -B build -DBUILD_TESTING=ONconfigures without errors.cmake --build buildcompiles all test files and thecubicsdr_teststarget.ctest --test-dir build --output-on-failureruns all Phase 2 tests and they pass.- Phase 3 tests compile and pass when enabled.
- CI pipeline runs tests after build and reports results.
Rollback Strategy
This plan only adds new files and a CMake option. If tests fail or cause build issues:
- Disable
BUILD_TESTING(set toOFF) to skip test compilation entirely. - Or remove the
add_subdirectory(tests)line fromCMakeLists.txt. - No existing source code is modified.
Files to Create/Modify
| File | Action |
|---|---|
tests/CMakeLists.txt |
Create |
tests/test_SpinMutex.cpp |
Create |
tests/test_Gradient.cpp |
Create |
tests/test_ThreadBlockingQueue.cpp |
Create |
tests/test_DataTree.cpp |
Create |
tests/test_Timer.cpp |
Create |
tests/test_FreqConversion.cpp |
Create (Phase 3) |
tests/test_DataNode.cpp |
Create (Phase 3) |
tests/test_IOThread.cpp |
Create (Phase 3) |
tests/test_ReBuffer.cpp |
Create (Phase 3) |
CMakeLists.txt |
Add add_subdirectory(tests) and BUILD_TESTING option |
CI config (.github/workflows/ or similar) |
Add test execution step |