Tests
This commit is contained in:
@@ -50,8 +50,10 @@ void TestRuntimeEventPayloadTypes()
|
||||
acceptedMutation.action = "SetLayerShader";
|
||||
acceptedMutation.accepted = true;
|
||||
acceptedMutation.shaderBuildRequested = true;
|
||||
acceptedMutation.persistenceRequested = true;
|
||||
Expect(RuntimeEventPayloadType(acceptedMutation) == RuntimeEventType::RuntimeMutationAccepted, "accepted mutation payload maps to accepted event type");
|
||||
Expect(acceptedMutation.shaderBuildRequested, "mutation payload carries shader build follow-up");
|
||||
Expect(acceptedMutation.persistenceRequested, "mutation payload carries persistence follow-up");
|
||||
|
||||
RuntimeMutationEvent rejectedMutation;
|
||||
rejectedMutation.accepted = false;
|
||||
@@ -59,6 +61,12 @@ void TestRuntimeEventPayloadTypes()
|
||||
Expect(RuntimeEventPayloadType(rejectedMutation) == RuntimeEventType::RuntimeMutationRejected, "rejected mutation payload maps to rejected event type");
|
||||
Expect(rejectedMutation.errorMessage == "Unknown layer.", "mutation payload carries rejection error");
|
||||
|
||||
RuntimePersistenceRequestedEvent persistence;
|
||||
persistence.reason = "UpdateLayerParameter";
|
||||
persistence.debounceAllowed = true;
|
||||
Expect(RuntimeEventPayloadType(persistence) == RuntimeEventType::RuntimePersistenceRequested, "runtime persistence payload maps to persistence event type");
|
||||
Expect(persistence.debounceAllowed, "runtime persistence payload carries debounce policy");
|
||||
|
||||
ShaderBuildEvent preparedBuild;
|
||||
preparedBuild.phase = RuntimeEventShaderBuildPhase::Prepared;
|
||||
preparedBuild.inputWidth = 1920;
|
||||
@@ -72,6 +80,40 @@ void TestRuntimeEventPayloadTypes()
|
||||
Expect(RuntimeEventPayloadType(appliedReset) == RuntimeEventType::RenderResetApplied, "render reset payload maps applied state");
|
||||
Expect(appliedReset.scope == RuntimeEventRenderResetScope::TemporalHistoryAndFeedback, "render reset payload carries reset scope");
|
||||
|
||||
RenderSnapshotPublishRequestedEvent snapshotRequest;
|
||||
snapshotRequest.outputWidth = 1920;
|
||||
snapshotRequest.outputHeight = 1080;
|
||||
snapshotRequest.reason = "test";
|
||||
Expect(RuntimeEventPayloadType(snapshotRequest) == RuntimeEventType::RenderSnapshotPublishRequested, "render snapshot request payload maps to request event");
|
||||
|
||||
RenderSnapshotPublishedEvent snapshotPublished;
|
||||
snapshotPublished.snapshotVersion = 3;
|
||||
snapshotPublished.parameterVersion = 4;
|
||||
snapshotPublished.layerCount = 2;
|
||||
Expect(RuntimeEventPayloadType(snapshotPublished) == RuntimeEventType::RenderSnapshotPublished, "render snapshot published payload maps to published event");
|
||||
Expect(snapshotPublished.layerCount == 2, "render snapshot published payload carries layer count");
|
||||
|
||||
OutputFrameCompletedEvent completedFrame;
|
||||
completedFrame.result = "Completed";
|
||||
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputFrameCompleted, "completed output frame payload maps to completed event");
|
||||
completedFrame.result = "DisplayedLate";
|
||||
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputLateFrameDetected, "late output frame payload maps to late event");
|
||||
completedFrame.result = "Dropped";
|
||||
Expect(RuntimeEventPayloadType(completedFrame) == RuntimeEventType::OutputDroppedFrameDetected, "dropped output frame payload maps to dropped event");
|
||||
|
||||
TimingSampleRecordedEvent timingSample;
|
||||
timingSample.subsystem = "RuntimeEventDispatcher";
|
||||
timingSample.metric = "dispatchDuration";
|
||||
timingSample.value = 0.5;
|
||||
timingSample.unit = "ms";
|
||||
Expect(RuntimeEventPayloadType(timingSample) == RuntimeEventType::TimingSampleRecorded, "timing sample payload maps to timing event");
|
||||
|
||||
QueueDepthChangedEvent queueDepth;
|
||||
queueDepth.queueName = "runtime-events";
|
||||
queueDepth.depth = 1;
|
||||
queueDepth.capacity = 16;
|
||||
Expect(RuntimeEventPayloadType(queueDepth) == RuntimeEventType::QueueDepthChanged, "queue depth payload maps to queue depth event");
|
||||
|
||||
SubsystemWarningEvent warning;
|
||||
warning.subsystem = "VideoBackend";
|
||||
warning.warningKey = "late-frame";
|
||||
@@ -310,6 +352,132 @@ void TestRuntimeEventTestHarness()
|
||||
const auto* seenPayload = seenOsc ? std::get_if<OscValueReceivedEvent>(&seenOsc->payload) : nullptr;
|
||||
Expect(seenPayload && seenPayload->valueJson == "0.8", "test harness keeps latest coalesced payload");
|
||||
}
|
||||
|
||||
void TestAcceptedMutationFollowUps()
|
||||
{
|
||||
RuntimeEventTestHarness harness;
|
||||
|
||||
RuntimeMutationEvent mutation;
|
||||
mutation.action = "SetLayerShader";
|
||||
mutation.accepted = true;
|
||||
mutation.runtimeStateChanged = true;
|
||||
mutation.runtimeStateBroadcastRequired = true;
|
||||
mutation.shaderBuildRequested = true;
|
||||
mutation.persistenceRequested = true;
|
||||
|
||||
RuntimeStateChangedEvent stateChanged;
|
||||
stateChanged.reason = mutation.action;
|
||||
stateChanged.persistenceRequested = true;
|
||||
|
||||
RuntimePersistenceRequestedEvent persistence;
|
||||
persistence.reason = mutation.action;
|
||||
persistence.debounceAllowed = true;
|
||||
|
||||
RuntimeReloadRequestedEvent reload;
|
||||
reload.reason = mutation.action;
|
||||
reload.preserveFeedbackState = false;
|
||||
|
||||
ShaderBuildEvent build;
|
||||
build.phase = RuntimeEventShaderBuildPhase::Requested;
|
||||
build.succeeded = true;
|
||||
build.message = "Shader rebuild queued.";
|
||||
|
||||
Expect(harness.Publish(mutation, "RuntimeCoordinator"), "accepted mutation event publishes");
|
||||
Expect(harness.Publish(stateChanged, "RuntimeCoordinator"), "state changed follow-up publishes");
|
||||
Expect(harness.Publish(persistence, "RuntimeCoordinator"), "persistence follow-up publishes");
|
||||
Expect(harness.Publish(reload, "RuntimeCoordinator"), "reload follow-up publishes");
|
||||
Expect(harness.Publish(build, "RuntimeCoordinator"), "shader build follow-up publishes");
|
||||
|
||||
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||
Expect(result.dispatchedEvents == 5, "accepted mutation dispatches every expected follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeMutationAccepted) == 1, "accepted mutation fact is observed");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeStateChanged) == 1, "accepted mutation publishes state changed follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimePersistenceRequested) == 1, "accepted mutation publishes persistence follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeReloadRequested) == 1, "accepted mutation publishes reload follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildRequested) == 1, "accepted mutation publishes shader build follow-up");
|
||||
|
||||
const RuntimeEvent* persistenceEvent = harness.LastSeen(RuntimeEventType::RuntimePersistenceRequested);
|
||||
const auto* persistencePayload = persistenceEvent ? std::get_if<RuntimePersistenceRequestedEvent>(&persistenceEvent->payload) : nullptr;
|
||||
Expect(persistencePayload && persistencePayload->reason == "SetLayerShader", "persistence follow-up preserves mutation action reason");
|
||||
}
|
||||
|
||||
void TestRejectedMutationHasNoDownstreamFollowUps()
|
||||
{
|
||||
RuntimeEventTestHarness harness;
|
||||
|
||||
RuntimeMutationEvent mutation;
|
||||
mutation.action = "SetLayerShader";
|
||||
mutation.accepted = false;
|
||||
mutation.errorMessage = "Unknown shader id: missing";
|
||||
|
||||
Expect(harness.Publish(mutation, "RuntimeCoordinator"), "rejected mutation event publishes");
|
||||
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||
Expect(result.dispatchedEvents == 1, "rejected mutation dispatches only the rejection fact");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeMutationRejected) == 1, "rejected mutation fact is observed");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeStateChanged) == 0, "rejected mutation has no state follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimePersistenceRequested) == 0, "rejected mutation has no persistence follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::RuntimeReloadRequested) == 0, "rejected mutation has no reload follow-up");
|
||||
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildRequested) == 0, "rejected mutation has no shader build follow-up");
|
||||
|
||||
const RuntimeEvent* rejectedEvent = harness.LastSeen(RuntimeEventType::RuntimeMutationRejected);
|
||||
const auto* rejectedPayload = rejectedEvent ? std::get_if<RuntimeMutationEvent>(&rejectedEvent->payload) : nullptr;
|
||||
Expect(rejectedPayload && rejectedPayload->errorMessage == "Unknown shader id: missing", "rejected mutation preserves error message");
|
||||
}
|
||||
|
||||
void TestShaderBuildGenerationEventMatching()
|
||||
{
|
||||
RuntimeEventTestHarness harness;
|
||||
std::size_t handledBuilds = 0;
|
||||
uint64_t handledGeneration = 0;
|
||||
|
||||
harness.Dispatcher().Subscribe(RuntimeEventType::ShaderBuildPrepared, [&](const RuntimeEvent& event) {
|
||||
const auto* payload = std::get_if<ShaderBuildEvent>(&event.payload);
|
||||
if (!payload || payload->generation != 7)
|
||||
return;
|
||||
|
||||
++handledBuilds;
|
||||
handledGeneration = payload->generation;
|
||||
});
|
||||
|
||||
ShaderBuildEvent stale;
|
||||
stale.phase = RuntimeEventShaderBuildPhase::Prepared;
|
||||
stale.generation = 6;
|
||||
stale.succeeded = true;
|
||||
|
||||
ShaderBuildEvent current = stale;
|
||||
current.generation = 7;
|
||||
|
||||
Expect(harness.Publish(stale, "ShaderBuildQueue"), "stale shader build event publishes");
|
||||
Expect(harness.Publish(current, "ShaderBuildQueue"), "current shader build event publishes");
|
||||
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||
Expect(result.dispatchedEvents == 2, "shader build readiness events dispatch in order");
|
||||
Expect(harness.SeenCount(RuntimeEventType::ShaderBuildPrepared) == 2, "both shader build readiness facts are observable");
|
||||
Expect(handledBuilds == 1, "generation-aware handler applies only the expected build once");
|
||||
Expect(handledGeneration == 7, "generation-aware handler records the expected generation");
|
||||
}
|
||||
|
||||
void TestHandlerFailureCanBecomeTelemetryEvent()
|
||||
{
|
||||
RuntimeEventTestHarness harness;
|
||||
harness.Dispatcher().Subscribe(RuntimeEventType::RuntimeStateBroadcastRequested, [](const RuntimeEvent&) {
|
||||
throw std::runtime_error("handler failed");
|
||||
});
|
||||
|
||||
RuntimeStateBroadcastRequestedEvent broadcast;
|
||||
broadcast.reason = "test";
|
||||
Expect(harness.Publish(broadcast, "test"), "broadcast event publishes before failing handler");
|
||||
RuntimeEventDispatchResult result = harness.DispatchPending();
|
||||
Expect(result.handlerFailures == 1, "dispatcher reports handler failure for telemetry");
|
||||
|
||||
TimingSampleRecordedEvent timing;
|
||||
timing.subsystem = "RuntimeEventDispatcher";
|
||||
timing.metric = "handlerFailures";
|
||||
timing.value = static_cast<double>(result.handlerFailures);
|
||||
timing.unit = "count";
|
||||
Expect(harness.Publish(timing, "HealthTelemetry"), "handler failure timing sample publishes");
|
||||
harness.DispatchPending();
|
||||
Expect(harness.SeenCount(RuntimeEventType::TimingSampleRecorded) == 1, "handler failure can be observed as telemetry event");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -322,6 +490,10 @@ int main()
|
||||
TestRuntimeEventCoalescingQueue();
|
||||
TestRuntimeEventCoalescingCustomKey();
|
||||
TestRuntimeEventTestHarness();
|
||||
TestAcceptedMutationFollowUps();
|
||||
TestRejectedMutationHasNoDownstreamFollowUps();
|
||||
TestShaderBuildGenerationEventMatching();
|
||||
TestHandlerFailureCanBecomeTelemetryEvent();
|
||||
|
||||
if (gFailures != 0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user