using AcDream.App.Rendering;
using AcDream.Core.Meshing;
namespace AcDream.App.Tests.Rendering;
///
/// S4-c2 fix round 1 (M1, blocking):
/// is the pure router-input derivation the blocking fix extracted from
/// DeferToRetailAlphaQueue so it is directly testable without a GPU/
/// mesh-manager harness. Before this round, a mesh-particle batch classified
/// (mask 0x00) reaching DrawMesh
/// row 5 (Immediate) hit an unconditional throw in the render loop — these
/// tests pin the two outcomes the fix made reachable.
///
public sealed class ParticleRendererRouteTests
{
/// M1(d) pin 1: an Opaque-classified mesh-particle batch (mask
/// 0x00) with a non-fully-opaque current alpha (top ColorArgb byte !=
/// 0xFF, i.e. materialHasAlpha true) routes to ALPHA (Append) — retail's
/// row 4 material-alpha fallback — not Immediate. Mutation check:
/// reverting to the pre-fix code (which threw whenever the decision
/// wasn't Append) would not distinguish this from pin 2 below at all
/// (both would just throw); reverting ONLY the materialHasAlpha
/// derivation to a hardcoded `false` makes this assertion fail because
/// the decision becomes Immediate instead of Append.
[Fact]
public void OpaqueClassifiedMeshBatch_WithMaterialAlpha_RoutesToAlphaAppend()
{
// Top byte (alpha) = 0x80 -> not 0xFF -> materialHasAlpha = true.
const uint colorArgbWithAlpha = 0x80FFFFFFu;
RetailAlphaMeshDecision decision = ParticleRenderer.RouteParticleSubmission(
ParticleSubmissionKind.Mesh, TranslucencyKind.Opaque, colorArgbWithAlpha);
Assert.Equal(RetailAlphaMeshAction.Append, decision.Action);
Assert.Equal(RetailAlphaList.Alpha, decision.List);
}
/// M1(d) pin 2: the SAME Opaque-classified mesh-particle batch
/// with a fully-opaque current alpha (top ColorArgb byte == 0xFF, i.e.
/// materialHasAlpha false) draws immediately and never enters a list —
/// retail's row 5 fallthrough. Mutation check: this is the EXACT case
/// that threw before the fix (mask 0x00, materialHasAlpha false ->
/// row 5 Immediate -> the old "rows 1/2/4/5 unreachable" guard fired);
/// reverting the throw's removal reproduces
/// InvalidOperationException here, which this test would report
/// as a failure (an unhandled exception) rather than an assertion
/// mismatch.
[Fact]
public void OpaqueClassifiedMeshBatch_WithNoMaterialAlpha_RoutesImmediate()
{
const uint fullyOpaqueColorArgb = 0xFFFFFFFFu;
RetailAlphaMeshDecision decision = ParticleRenderer.RouteParticleSubmission(
ParticleSubmissionKind.Mesh, TranslucencyKind.Opaque, fullyOpaqueColorArgb);
Assert.Equal(RetailAlphaMeshAction.Immediate, decision.Action);
}
/// A billboard submission always carries the alpha-family mask
/// (0x02) regardless of the (unused) mesh parameters, and therefore
/// always satisfies row 3 (Append, ALPHA) under the fixed default delay
/// mask — never Immediate, never CLIP. Mutation check: swapping
/// for
/// in the billboard
/// branch would route this to the CLIP list instead of ALPHA.
[Fact]
public void Billboard_AlwaysRoutesToAlphaAppend()
{
RetailAlphaMeshDecision decision = ParticleRenderer.RouteParticleSubmission(
ParticleSubmissionKind.Billboard, default, default);
Assert.Equal(RetailAlphaMeshAction.Append, decision.Action);
Assert.Equal(RetailAlphaList.Alpha, decision.List);
}
}