The VST3 ABI integration harness.
src/tests.zig covers the pure-Zig core with 35 unit tests. This binary covers
the other half: it links the built DanzigGain plugin, calls its exported
GetPluginFactory, and drives the returned object through the raw VST3 C ABI
the way a host does.
The point is that it does not import the plugin's Zig types. It declares its own
copy of the IPluginFactory vtable layout, casts the returned pointer to a
struct whose only field is a vtable pointer, and calls through C function
pointers:
const FactoryObject = extern struct {
vtbl: *const IPluginFactoryVTable,
};
extern fn GetPluginFactory() ?*anyopaque;
const factory: *FactoryObject = @ptrCast(@alignCast(raw.?));
check(factory.vtbl.countClasses(raw.?) == 1, "countClasses reports one exported class");If the plugin ever stops putting the vtable pointer in the first machine word, that dereference fails here rather than inside a DAW.
The checks run the whole plugin lifecycle through the C ABI:
- The module entry point (
bundleEntry) accepts the load. - The factory:
GetPluginFactory,countClasses,getFactoryInfo,getClassInfo, andqueryInterfacerefusing an unknown IID without handing back a garbage pointer. createInstancerefuses an unknown class id and builds a real object for the advertised one, and that object counts references and hands backIAudioProcessorandIEditControlleras distinct interface pointers.- The component reports one input and one output bus with two channels and named buses; the controller exposes the gain and bypass parameters and round trips a parameter display string.
- The processing path:
setupProcessingat 48 kHz,setActive,setProcessing, and aprocesscall whose output is checked against the DSP, 0 dB passing DC at unity, +6 dB scaling by ~1.995, and bypass passing the input through untouched. terminate, then releasing the last reference drops the count to zero.- The linked static library still converts dB correctly and the
ParamStorereaches full scale.
From the repository root:
zig build test-integrationdanzig integration harness
VST3 module entry
ok bundleEntry accepts the load and reports success
VST3 factory ABI
ok GetPluginFactory returns a non-null object
ok countClasses reports one exported class
...
ok createInstance accepts the advertised class id
ok createInstance writes a non-null object
ok the object hands back IAudioProcessor
ok the object hands back IEditController
...
ok setupProcessing accepts 48 kHz
ok process returns kResultOk
ok the default 0 dB setting passes DC through at unity
ok a +6 dB gain setting scales DC by ~1.995
ok bypass passes the input through untouched
ok releasing the last reference drops the count to zero
danzig static library
ok AudioBuffer reports its geometry
ok dBToLinear(0 dB) is unity
ok dBToLinear(+6 dB) is ~1.995
ok ParamStore reaches +48 dB at full scale
all integration checks passed
The run has about fifty checks; the full list prints when you run it.
It exits non-zero if any check fails.
zig build test --summary allBuild Summary: 9/9 steps succeeded; 35/35 tests passed
The 35 counts the unit tests in src/tests.zig. This harness is a separate run
step, so its failures surface as a failed build step rather than a failed test
count.
zig build
./zig-out/bin/danzig_test
echo "exit=$?"exit=0
It exercises 32-bit processing on mono and stereo, which is what the plugin
supports. It does not drive 64-bit processing or an editor view, both of which
the plugin declines on purpose (canProcessSampleSize refuses 64-bit and
createView returns null for the host's generic editor). The full host-side
validation, including automation and state round-trips under a real host, is
what pluginval covers; see docs/WIKI.md.
See docs/WIKI.md for the testing section.