acdream/tests/AcDream.App.Tests/Rendering/EntityClipTests.cs
Erik a8b831c23b test(render): Phase W Stage 4/5 — assembler OutsideView AABB + PView BFS + entity-clip tests
ClipFrameAssemblerTests (3 new):
- Assemble_OutsideViewWithExitPortal_HasOutsideViewTrue_AabbMatchesBounds
- Assemble_OutsideViewMultiPolygon_ScissorMode_HasOutsideViewTrue_AabbValid
- Assemble_EmptyOutsideView_HasOutsideViewFalse_AabbZero

PortalVisibilityBuilderTests (3 new):
- Build_ExitPortalVisible_OutsideViewNonEmpty
- Build_NoExitPortal_OutsideViewEmpty
- Build_RootCellAlwaysFirstInOrderedVisibleCells

EntityClipTests (new file, 5 tests):
- EntityClip_ParentInVisibleSet_Included
- EntityClip_ParentNotInVisibleSet_Excluded
- EntityClip_NullVisibleSet_IncludesAll
- EntityClip_NullParentCell_NullVisibleSet_Included
- EntityClip_NullParentCell_NonNullVisibleSet_Included

WbDrawDispatcher.EntityPassesVisibleCellGate changed private → internal static
(AcDream.App already has InternalsVisibleTo AcDream.App.Tests; no new seam needed).

160 → 171 tests, all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 16:21:08 +02:00

101 lines
3.7 KiB
C#

// EntityClipTests.cs
//
// Phase W Stage 4/5: unit-test WbDrawDispatcher.EntityPassesVisibleCellGate.
// The gate is internal static (AcDream.App is InternalsVisibleTo AcDream.App.Tests)
// and pure — tests it without a GL context. Covers: ParentCellId in the visible
// set → included; ParentCellId NOT in the set → excluded; null visibleCellIds →
// everything included (outdoor / unconstrained root).
using System.Collections.Generic;
using System.Numerics;
using AcDream.App.Rendering.Wb;
using AcDream.Core.World;
using Xunit;
namespace AcDream.App.Tests.Rendering;
public sealed class EntityClipTests
{
// Minimal WorldEntity factory. EntityPassesVisibleCellGate only reads
// ParentCellId and IsBuildingShell/BuildingShellAnchorCellId from the entity;
// the other required fields are set to safe sentinel values.
private static WorldEntity Entity(uint? parentCellId, bool isShell = false, uint? shellAnchor = null) =>
new WorldEntity
{
Id = 1u,
SourceGfxObjOrSetupId = 0u,
Position = Vector3.Zero,
Rotation = Quaternion.Identity,
MeshRefs = System.Array.Empty<MeshRef>(),
ParentCellId = parentCellId,
IsBuildingShell = isShell,
BuildingShellAnchorCellId = shellAnchor,
};
[Fact]
public void EntityClip_ParentInVisibleSet_Included()
{
// Entity whose ParentCellId is in the visible set must pass the gate.
const uint cellId = 0xA9B40170u;
var visibleCellIds = new HashSet<uint> { cellId };
var entity = Entity(parentCellId: cellId);
bool result = WbDrawDispatcher.EntityPassesVisibleCellGate(
entity, visibleCellIds, WbDrawDispatcher.EntitySet.All);
Assert.True(result);
}
[Fact]
public void EntityClip_ParentNotInVisibleSet_Excluded()
{
// Entity whose ParentCellId is NOT in the visible set must fail the gate.
const uint visibleCell = 0xA9B40170u;
const uint entityCell = 0xA9B40172u;
var visibleCellIds = new HashSet<uint> { visibleCell };
var entity = Entity(parentCellId: entityCell);
bool result = WbDrawDispatcher.EntityPassesVisibleCellGate(
entity, visibleCellIds, WbDrawDispatcher.EntitySet.All);
Assert.False(result);
}
[Fact]
public void EntityClip_NullVisibleSet_IncludesAll()
{
// Null visibleCellIds means the outdoor root — no culling, all entities pass.
var entity = Entity(parentCellId: 0xA9B40172u);
bool result = WbDrawDispatcher.EntityPassesVisibleCellGate(
entity, visibleCellIds: null, WbDrawDispatcher.EntitySet.All);
Assert.True(result);
}
[Fact]
public void EntityClip_NullParentCell_NullVisibleSet_Included()
{
// An outdoor entity (ParentCellId == null) with null visibleCellIds passes.
var entity = Entity(parentCellId: null);
bool result = WbDrawDispatcher.EntityPassesVisibleCellGate(
entity, visibleCellIds: null, WbDrawDispatcher.EntitySet.All);
Assert.True(result);
}
[Fact]
public void EntityClip_NullParentCell_NonNullVisibleSet_Included()
{
// An outdoor entity (ParentCellId == null) with a non-null visibleCellIds
// falls through to the final return-true (not a shell, not shell-scoped);
// outdoor scenery is not gated by the indoor cell filter.
var visibleCellIds = new HashSet<uint> { 0xA9B40170u };
var entity = Entity(parentCellId: null);
bool result = WbDrawDispatcher.EntityPassesVisibleCellGate(
entity, visibleCellIds, WbDrawDispatcher.EntitySet.All);
Assert.True(result);
}
}