Skip to content

Add cross-language symmetry test framework for NDI-python ↔ NDI-matlab#37

Merged
stevevanhooser merged 13 commits into
mainfrom
claude/add-matlab-symmetry-tests-nZan2
Mar 15, 2026
Merged

Add cross-language symmetry test framework for NDI-python ↔ NDI-matlab#37
stevevanhooser merged 13 commits into
mainfrom
claude/add-matlab-symmetry-tests-nZan2

Conversation

@stevevanhooser

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a comprehensive cross-language symmetry test framework that verifies interoperability between NDI-python and NDI-matlab implementations. The framework enables both language stacks to generate and validate artifacts, ensuring that sessions, documents, and probes created by one language can be correctly read and interpreted by the other.

Key Changes

  • Framework Architecture: Implemented a two-phase artifact system:

    • makeArtifacts: Generates NDI sessions, documents, and probe metadata to a shared temporary directory
    • readArtifacts: Loads and validates artifacts from either language implementation
  • Artifact Storage: Centralized artifact location at <tempdir>/NDI/symmetryTest/{pythonArtifacts,matlabArtifacts}/<namespace>/<className>/<testName>/ with standardized directory structure including:

    • NDI session database (.ndi/ folder)
    • JSON document exports (jsonDocuments/ directory)
    • Probe manifest (probes.json)
  • Initial Test Suite: Added first symmetry test for basic session building:

    • tests/symmetry/make_artifacts/session/test_build_session.py: Creates a session with subject and measurement documents
    • tests/symmetry/read_artifacts/session/test_build_session.py: Validates artifacts from both language sources with parameterized testing
  • Configuration & Documentation:

    • Added tests/symmetry/conftest.py with shared fixtures and artifact path constants
    • Updated pyproject.toml to exclude symmetry tests from default pytest runs (via --ignore=tests/symmetry)
    • Created comprehensive framework documentation in docs/developer_notes/symmetry_tests.md
    • Added INSTRUCTIONS.md files for both make_artifacts/ and read_artifacts/ phases
    • Updated README.md with symmetry test execution instructions

Notable Implementation Details

  • Graceful Skipping: readArtifacts tests skip (rather than fail) when artifacts from the other language are unavailable, allowing the suite to run on single-language environments
  • Parameterized Testing: Python readArtifacts tests use pytest fixtures to validate both matlabArtifacts and pythonArtifacts in a single test class
  • Naming Conventions: Artifact directory components use camelCase to ensure identical paths across both language implementations
  • Separation from CI: Symmetry tests are excluded from default pytest runs because they have side effects (writing to temp directory) and require prior artifact generation

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47

claude added 13 commits March 14, 2026 17:01
Introduce tests/symmetry/ with make_artifacts and read_artifacts suites
that mirror the MATLAB tests/+ndi/+symmetry/ structure. The makeArtifacts
tests build NDI sessions and export documents, probes, and the session
database to a shared temp directory; the readArtifacts tests verify those
artifacts from either language. Symmetry tests are excluded from the
default pytest run (--ignore=tests/symmetry) since they require prior
MATLAB execution to be fully exercised.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
DirSession.__init__ had two bugs preventing it from reading existing
session data (e.g. MATLAB artifacts):

1. Used self.database_search() which appends a session_id filter,
   creating a circular dependency — can't find the session_id without
   searching, can't search without the session_id. Fixed by using
   self._database.search() directly to bypass the filter.

2. Used hasattr/getattr on document_properties (a dict) as if it were
   an object with attributes. Fixed to use dict access (in/get).

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
DID-matlab and NDI-matlab have always used branch "a" as the default
DID database branch. NDI-python was using "main", which meant it could
never see documents stored by MATLAB — the SQL query filters by
branch_id, so documents under branch "a" were invisible.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
Separates test_build_session_artifacts into two independent tests:
- test_build_session_documents: verifies JSON document round-trip (runs first)
- test_build_session_probes: verifies probe reconstruction (depends on more)

This makes it easier to isolate failures since getprobes depends on
many other things working correctly.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
…ction

Three fixes for cross-language parity:

1. Ido: Generate 16_16 hex UIDs matching MATLAB DID format.
   Python was generating 13_12 hex UIDs which MATLAB's DID database
   rejects with DID:Database:ValidationFieldUID.

2. Element._load_from_document: Use dict access instead of attribute
   access on document_properties. The old code used props.element.name
   which silently failed (dicts don't have attribute access), leaving
   all loaded elements with blank name/reference/type.

3. Session._document_to_object: Check element.ndi_element_class for
   "probe" to identify probe documents. Probes use document_class
   "element", so doc_isa("probe") never matched. MATLAB sets
   ndi_element_class to values like "ndi.probe.timeseries.mfdaq".

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
MATLAB's element.newdocument() sets element.ndi_element_class to
class(ndi_element_obj), which returns the MATLAB class name (e.g.
"ndi.element", "ndi.probe.timeseries.mfdaq"). Python was leaving this
as the schema default "ndi_element".

Changes:
- Element: Add ndi_element_class() returning "ndi.element", set it
  in newdocument()
- Probe: Override ndi_element_class() to use probetype2object.json
  mapping (e.g. "n-trode" -> "ndi.probe.timeseries.mfdaq"), remove
  redundant newdocument() override (inherit from Element like MATLAB)
- Element.searchquery(): Match MATLAB's query which includes name,
  type, ndi_element_class, and reference (was only base.id)

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
…bject

MATLAB's class(self) returns the actual subclass name, and
ndi_document2ndi_object uses eval() to dispatch to the right
constructor. Python now mirrors this exactly:

- Each probe subclass overrides ndi_element_class() to return its
  MATLAB class name (ndi.probe, ndi.probe.timeseries,
  ndi.probe.timeseries.mfdaq, ndi.probe.timeseries.stimulator)
- _document_to_object uses a class registry mapping MATLAB class
  names to Python classes, matching MATLAB's eval([class '(S,D)'])
- Removed probetype2object.json lookup from Probe.ndi_element_class
  since the class hierarchy handles it directly, same as MATLAB

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
Port MATLAB ndi.util.sessionSummary and ndi.util.compareSessionSummary to
Python. Update make_artifacts and read_artifacts symmetry tests to write
and compare sessionSummary.json instead of probes.json, matching the
updated MATLAB branch add-symmetry-make-artifacts-test-15791077857957030388.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
…tem loading

Two fixes for MATLAB/Python symmetry testing:

1. Populate doc_data/fields tables in SQLite when adding documents.
   DID-python's _do_add_doc skips field indexing, but DID-matlab's
   search relies on the doc_data table. Without these entries, MATLAB
   cannot find Python-generated documents via database_search.

2. Fix DAQSystem._load_from_document to use dict access instead of
   broken attribute access (doc_props.base.name → doc_props["base"]["name"]),
   add MATLAB class names to the reader lookup map, and gracefully
   handle missing reader/navigator dependencies instead of raising.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
…ehavior

Documents the architectural gaps between DID-python and DID-matlab's
sqlitedb implementation: doc_data population, SQL-based search, query
resolution, and field naming conventions.

https://claude.ai/code/session_01RBSaNF8SSrGqnpnP5vUT47
@stevevanhooser stevevanhooser merged commit d50b26c into main Mar 15, 2026
4 checks passed
@stevevanhooser stevevanhooser deleted the claude/add-matlab-symmetry-tests-nZan2 branch March 15, 2026 23:13
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