Golden differential testing for Pd/Max: broaden coverage and fix bindings#169
Merged
Conversation
Record and sweep the cases a control/message backend actually exercises: false/min/max/enum-mode control sweeps, string overrides, impulse engagements, declared message-port invocations, and callback firings. Container controls (vectors, arrays, scalar aggregates) are set and recorded as JSON arrays. Tick-only objects (audio-free, `using tick`) run one synthesized processing pass instead of the audio path, which would static_assert. The recorded c_name is the CMake C_NAME (what the external registers under), so an object may keep a differing halp_meta(c_name). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
Refine the "unknown" value_type into list/array/aggregate (with a component count) so patch generators and harnesses know a control takes a value list. Audio ports carry their statically-known channel count, and objects with an initialize(...) get its argument signature. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
Several defects surfaced by output differential testing: - Audio-in/control-out analyzers (Peak, etc.) were routed to the message processor and never ran their DSP; dispatch on avnd::audio_processor. - The audio processor never emitted non-audio outputs. Create a message outlet per non-audio output and commit their values on bang; wire output callbacks. - from_atoms distributed only the first atom to every field of a scalar aggregate (xy/rgb/rgba/...), so all components collapsed to the first value. Advance the field index. - Tick-driven objects could not run in the message processor (no tick overload) and prepare() got no sample rate; synthesize a one-frame tick and pass the engine rate. - Impulse ports could not be engaged: from_atoms rejected an empty optional value type. Engage on any message, reset one-shot after use. - Unnamed ports all collided on the "unnamed" selector; address them positionally as p<index>. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
The Max counterparts of the pd binding fixes: - Dispatch audio-in/control-out analyzers to the DSP processor. - Give the audio processor a message outlet per non-audio output, to the right of the signal outlet, and commit their values on bang. - from_atoms now advances the field index so aggregate controls receive every component instead of only the first. - Synthesize a one-frame tick and pass the engine rate so tick-driven message objects run. - Wire a no-op request_channels for audio busses so a variable_audio_bus no longer calls an empty std::function and crashes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
The object box now uses the CMake C_NAME the external registers under, not the class c_name, so a renamed external's help patch instantiates. Emit a valid symbolatom record for string controls, correct the hradio cell count, drive impulse ports with a bang, feed multi-component and container controls a value list, size signal connections to the audio channel count, and pass initialize(...) default arguments. Non-parameter inputs, messages and outlets get a reference section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
5ac2d36 to
50114bc
Compare
…rnesses Extend the golden differential harnesses to the cases the golden now records. The pd harness drives control/message/callback objects and analyzers (bang-commit after one DSP block), replays container controls as list messages, and captures outlet output from a headless pd. The Max driver replays strings, impulses, messages and list-valued controls, and its report loader rejoins lines split at Max's 32 KB File-write boundary; a --recompute mode re-diffs an existing report. Shared control/callback comparison handles sequences; the python/gst/td harnesses skip case kinds they cannot drive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SvvcvSnStMbRqdC2EYbcg
50114bc to
04c6f10
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extends the golden output-differential harnesses to verify that Pd and Max externals produce the same output as the C++ object given the same inputs — across controls, messages, callbacks, impulses, strings, containers and textures, not just audio. Running them surfaced and fixed several real binding bugs.
Binding bugs fixed (found by output differential, invisible to load tests)
Peak) were routed to the message processor and never ran their DSP. Both backends now dispatch onavnd::audio_processor.from_atomshandler assigned the first atom to every field, soxy/xyz/rgba/xyzw/range controls silently received only their first value in both backends.halp::tickcould not run in the message processors andprepare()got no sample rate.variable_audio_buscrash —request_channelswas an emptystd::function, crashing on the first process pass.c_name()instead of the registered CMakeC_NAME, so a renamed external's help patch failed to instantiate.Coverage
Results (real Pd/Max instances vs the C++ oracle)
🤖 Generated with Claude Code