Skip to content

Add comprehensive unit tests and improve input validation#13

Closed
imakris wants to merge 4 commits intomasterfrom
claude/review-refactor-library-QSnid
Closed

Add comprehensive unit tests and improve input validation#13
imakris wants to merge 4 commits intomasterfrom
claude/review-refactor-library-QSnid

Conversation

@imakris
Copy link
Copy Markdown
Owner

@imakris imakris commented Apr 18, 2026

Summary

This PR adds three new comprehensive unit test suites covering core algorithm functionality, asset loading, and concurrent data sources. It also hardens input validation across the codebase by rejecting non-finite values in range setters and improving error logging in shader compilation.

Key Changes

New Test Suites

  • test_core_algo.cpp: Tests LOD level selection, binary-search on timestamps (including segmented snapshots), and axis formatting
  • test_asset_loader.cpp: Verifies asset loading with embedded assets, override directories, fallback behavior, and error logging
  • test_concurrent_series.cpp: Exercises Vector_data_source and ring-buffer Data_source under concurrent snapshot and mutation, verifying consistency and buffer lifetime management

Input Validation Hardening

  • Added std::isfinite() checks in Plot_widget and Plot_time_axis range setters to reject NaN and infinity values
  • Range setters now validate that max > min before applying updates
  • Invalid ranges are silently ignored (no partial updates)
  • Improved error messages in Series_renderer shader loading with more specific diagnostics

Documentation & Clarity

  • Enhanced lifetime contract documentation in data_snapshot_t explaining the hold-based buffer pinning mechanism
  • Added threading model documentation to Plot_widget header
  • Clarified Data_source implementation requirements for snapshot validity

Build System

  • Refactored CMakeLists.txt to use a loop for test configuration, reducing duplication
  • Added Threads::Threads dependency for test_concurrent_series
  • Registered all new tests with CTest

Version & Config

  • Bumped version to 1.0.4
  • Updated Plot_config::field_count to 22 to reflect new fields

Implementation Details

  • The concurrent test uses shared_ptr-based buffer pinning to safely extend snapshot lifetime beyond the Data_source lock
  • Range validation is consistent across all entry points (individual setters and set_view)
  • Asset loader tests use scoped temporary directories for filesystem operations
  • All tests use a simple macro-based assertion framework for portability

https://claude.ai/code/session_012kn22AT9HdT6aoJoi8rz4Y

claude added 4 commits April 18, 2026 15:45
- Plot_widget and Plot_time_axis setters for t-range, available t-range
  and v-range now silently ignore non-finite or mis-ordered inputs
  instead of letting NaN/inf propagate into layout math and rendered
  time<->pixel conversions.
- Plot_widget::set_view validates each optional range independently and
  no longer partially applies an available-range when the primary range
  is missing.
- Narrow the torn-read window in set_rendered_v/t_range by invalidating
  before publishing new endpoints; document that a seqlock would be
  required for a full fix.

Also:
- Plot_config::field_count was out of sync with the struct (20 vs. the
  22 actual fields); bumping it to match keeps the plot_config_equivalent
  static_assert honest so future fields still force comparator updates.
- Document Series_renderer::initialize as infallible (shader compilation
  is lazy) and clarify the data_snapshot_t lifetime contract so
  consumers know hold must be retained for the view to remain valid.
- Add a threading-model summary to Plot_widget so call sites know which
  methods are GUI-thread vs. render-thread, and that invalid range
  inputs are ignored rather than partially applied.

https://claude.ai/code/session_012kn22AT9HdT6aoJoi8rz4Y
Address the remaining items from the earlier review:

- Segregate Plot_widget's render-thread callbacks. The two _from_renderer
  methods still need to be addressable because Plot_renderer posts to
  them through QMetaObject::invokeMethod, but they no longer carry
  Q_INVOKABLE, so QML cannot call them, and a header comment marks them
  as internal-only.
- Route shader and asset-loading failures through Plot_config::log_error.
  Plot_core::render now forwards the callback to the Asset_loader and
  Primitive_renderer (matching what plot_renderer.cpp does for the Qt
  path), and Series_renderer::get_or_load_shader logs each failure mode
  (no asset loader, empty vert stage, asset lookup miss, compile/link
  failure) so silent fallbacks stop swallowing bugs.
- Document the float-precision tradeoff of Data_access_policy::get_value
  and get_range so users know to subtract large biases before narrowing
  from double to float.
- Align the CMake project VERSION with the 1.0.4 tag referenced in the
  README FetchContent example so install metadata and docs no longer
  disagree.
- Add three new core-only tests:
  * test_core_algo covers LOD level selection, contiguous and segmented
    timestamp binary searches, and the axis-label formatter.
  * test_asset_loader covers missing-asset logging, embedded lookup,
    override-directory precedence and fallback, and shader helper.
  * test_concurrent_series runs a ring-buffer Data_source under two
    readers plus a writer and verifies the shared_ptr-based hold
    semantics keep snapshots safe after further writes.

All seven tests pass on both core-only and Qt builds.

https://claude.ai/code/session_012kn22AT9HdT6aoJoi8rz4Y
Previously set_vbar_width_from_renderer and set_auto_v_range_from_renderer
were public (without Q_INVOKABLE) because Plot_renderer's pimpl nested
class (impl_t) could not take pointer-to-member expressions to private
methods of Plot_widget — the friend class Plot_renderer declaration does
not extend to nested types.

Move the hooks into Plot_widget's private section and expose two static
forwarders on Plot_renderer (post_vbar_width_from_renderer and
post_auto_v_range_from_renderer). The forwarders form the pointer-to-
member inside Plot_renderer's access context (where friendship applies)
and pass it to QMetaObject::invokeMethod; impl_t calls those forwarders
instead, which it can do because nested classes share their enclosing
class's access rights.

Net result: the methods no longer appear in the header's public section
and can never be called by application code or QML, while the queued-
invocation mechanism is preserved.

https://claude.ai/code/session_012kn22AT9HdT6aoJoi8rz4Y
Public-header changes:
- Move plot_renderer.h out of include/vnm_plot/qt/ into src/qt/. The
  class is only constructed by Plot_widget::createRenderer() and
  returned as the QQuickFramebufferObject::Renderer base type, so users
  never had a reason to see it.
- Drop the four sub-renderer accessors from Plot_core (primitives,
  series_renderer, chrome_renderer, text_renderer). Nothing under
  examples/, benchmark/, benchmark_headless/, or tests/ used them; they
  were leaky pass-throughs that exposed renderer internals. Only
  asset_loader() — needed for asset overrides — stays.
- Stop core.h umbrella from pulling in default_shaders.h and
  vertex_layout.h. vertex_layout.h still reaches users transitively
  through access_policy.h; default_shaders.h is renderer-internal.
- Sync vnm_plot.h k_version_* with the 1.0.4 CMake project version that
  the README also references.
- Rename Q_PROPERTY timeAxis → time_axis to match every other property
  in Plot_widget; update PlotIndicator.qml, PlotView.qml, README.

Dedup / simplification:
- Extract update_config_field<>() so set_dark_mode / set_grid_visibility
  / set_preview_visibility / set_line_width_px share one lock+revision
  +signal+update body instead of repeating it four times.
- Extract clamp_t_range_to_available() and use it from both
  set_available_t_range and set_view, removing one verbatim copy of
  the pan-clamping logic.
- Use hash_mix_u64 consistently across hash_data_sources and
  hash_series_snapshot (the helper existed but only the third hasher
  was using it).
- Add data_snapshot_t::is_valid() and replace the `!snap || count == 0
  || stride == 0` check at every site (six call sites across core, qt,
  and benchmark).

Test cleanup:
- Move TEST_ASSERT and RUN_TEST into tests/test_macros.h and include it
  from all seven test binaries instead of duplicating the macros in
  each.

All 7 tests still pass on the full Qt build.

https://claude.ai/code/session_012kn22AT9HdT6aoJoi8rz4Y
@imakris imakris closed this Apr 18, 2026
@imakris imakris deleted the claude/review-refactor-library-QSnid branch May 3, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants