namespace SceneTests.Components;
///
/// Tests for the dirty-marking machinery in Component.Dirty.cs: OnPropertyDirty
/// schedules a single OnDirty callback through the CallbackBatch, deduplicating
/// repeat marks until the dirty state has been flushed.
///
[TestClass]
[DoNotParallelize]
public class ComponentDirtyTest : SceneTest
{
///
/// Marking a component dirty outside of any callback batch invokes OnDirty
/// synchronously, exactly once.
///
[TestMethod]
public void MarkDirtyInvokesOnDirty()
{
var scene = new Scene();
using var sceneScope = scene.Push();
var go = scene.CreateObject();
var comp = go.Components.Create();
Assert.AreEqual( 0, comp.DirtyCalls );
comp.MarkDirty();
Assert.AreEqual( 1, comp.DirtyCalls );
}
///
/// Inside a callback batch the dirty callback is deferred to the end of the
/// batch, and marking the component dirty multiple times still results in a
/// single OnDirty call.
///
[TestMethod]
public void DirtyIsDeduplicatedWithinBatch()
{
var scene = new Scene();
using var sceneScope = scene.Push();
var go = scene.CreateObject();
var comp = go.Components.Create();
using ( CallbackBatch.Batch() )
{
comp.MarkDirty();
comp.MarkDirty();
comp.MarkDirty();
Assert.AreEqual( 0, comp.DirtyCalls );
}
Assert.AreEqual( 1, comp.DirtyCalls );
}
///
/// Once the dirty state has been flushed the component can become dirty
/// again - a later mark fires OnDirty a second time.
///
[TestMethod]
public void DirtyFiresAgainAfterFlush()
{
var scene = new Scene();
using var sceneScope = scene.Push();
var go = scene.CreateObject();
var comp = go.Components.Create();
comp.MarkDirty();
comp.MarkDirty();
Assert.AreEqual( 2, comp.DirtyCalls );
}
///
/// A destroyed (invalid) component ignores dirty marks completely - OnDirty
/// is never invoked for it.
///
[TestMethod]
public void DirtyIgnoredOnDestroyedComponent()
{
var scene = new Scene();
using var sceneScope = scene.Push();
var go = scene.CreateObject();
var comp = go.Components.Create();
comp.Destroy();
comp.MarkDirty();
Assert.AreEqual( 0, comp.DirtyCalls );
}
}
///
/// Component exposing the protected, obsolete OnPropertyDirty mechanism so the
/// tests can drive it, counting OnDirty invocations.
///
public class DirtyProbeComponent : Component
{
public int DirtyCalls;
///
/// Marks this component dirty via the protected OnPropertyDirty helper.
///
public void MarkDirty() => OnPropertyDirty();
#pragma warning disable CS0672 // overrides an obsolete member - that's the point of the test
///
/// Counts how many times the dirty callback has run.
///
protected override void OnDirty()
{
DirtyCalls++;
}
#pragma warning restore CS0672
}