Files

81 lines
3.1 KiB
C++

// native_bridge/tests/seh_channel_test.cpp
// G3.3: SEH per-channel crash isolation. A plugin whose processAudioBlock
// faults (access violation) must be caught by SafeProcessChannel -> renderAll
// mutes the channel instead of taking down the whole bridge process.
// Assert-based, no framework. Includes the engine .cpp (like shm_selfcheck).
#include "../include/NativeInstrumentEngine.h"
#include "../src/NativeInstrumentEngine.cpp"
#include <cassert>
#include <cstdio>
#include <cstring>
class CrashingStub : public INativeInstrument {
public:
bool init(double, uint32_t) override { return true; }
void selectProgram(uint32_t, uint32_t, uint32_t) override {}
void noteOn(uint32_t, uint32_t, float, uint32_t) override {}
void noteOff(uint32_t, uint32_t, uint32_t) override {}
void controlChange(uint32_t, uint32_t, uint32_t) override {}
void programChange(uint32_t, uint32_t) override {}
void pitchBend(uint32_t, uint32_t) override {}
bool openGUI(void*) override { return false; }
void closeGUI() override {}
void processAudioBlock(float*, float*, uint32_t) override {
volatile int* p = nullptr;
*p = 42; // deliberate access violation
}
};
class OkStub : public INativeInstrument {
public:
bool init(double, uint32_t) override { return true; }
void selectProgram(uint32_t, uint32_t, uint32_t) override {}
void noteOn(uint32_t, uint32_t, float, uint32_t) override {}
void noteOff(uint32_t, uint32_t, uint32_t) override {}
void controlChange(uint32_t, uint32_t, uint32_t) override {}
void programChange(uint32_t, uint32_t) override {}
void pitchBend(uint32_t, uint32_t) override {}
bool openGUI(void*) override { return false; }
void closeGUI() override {}
void processAudioBlock(float* outL, float* outR, uint32_t n) override {
for (uint32_t i = 0; i < n; ++i) { outL[i] = 1.0f; outR[i] = 2.0f; }
}
};
int main() {
// 1. Faulting plugin: SEH catches the AV, process survives, returns false.
{
float L[64] = {}, R[64] = {};
CrashingStub stub;
bool ok = SafeProcessChannel(&stub, L, R, 64);
assert(!ok);
// Block left untouched (renderAll continues without this channel).
bool allZero = true;
for (float f : L) if (f != 0.0f) allZero = false;
assert(allZero);
}
// 2. Healthy plugin passes through unchanged.
{
float L[64] = {}, R[64] = {};
OkStub stub;
bool ok = SafeProcessChannel(&stub, L, R, 64);
assert(ok);
assert(L[0] == 1.0f && R[0] == 2.0f);
}
// 3. Manager crash-mute flags: set, read, and renderAll skips a crashed
// channel (no exception escapes into the audio loop).
{
InstrumentEngineManager mgr;
assert(!mgr.isCrashed(3));
mgr.markCrashed(3);
assert(mgr.isCrashed(3));
assert(!mgr.isCrashed(4)); // other channels unaffected
float L[64] = {}, R[64] = {};
mgr.renderAll(L, R, 64); // crashed + unassigned: no-op, no crash
assert(L[0] == 0.0f);
}
std::printf("SEH_CHANNEL_TEST PASS\n");
return 0;
}