- Merge: defensive copy `new List<ElementInfo>(derived.Children)` so a later mutation of the merged result or the input can't corrupt the other - Merge: add comment on Width/Height 0-sentinel (Plan-1 safe; Plan-2 limitation and float?-upgrade path documented inline) - Test: replace mid-sentence "Wait —" authoring trace in EdgeFlagsToAnchors_ValueThree_FallsBackToTopLeft with a clean conclusion-first summary of the value-3 mapping rule 9/9 ElementReaderTests pass; 0 build errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
137 lines
6 KiB
C#
137 lines
6 KiB
C#
using AcDream.App.UI;
|
|
using AcDream.App.UI.Layout;
|
|
namespace AcDream.App.Tests.UI.Layout;
|
|
|
|
public class ElementReaderTests
|
|
{
|
|
// ── ToAnchors ────────────────────────────────────────────────────────────
|
|
|
|
/// <summary>
|
|
/// Edge value 4 = stretch (pinned to BOTH near AND far sides simultaneously).
|
|
/// LeftEdge=4 → Left anchor; RightEdge=4 → Right anchor.
|
|
/// TopEdge=1 → Top only (near-pin); BottomEdge=1 → near-pin (left/top axis), NOT Bottom.
|
|
/// </summary>
|
|
[Fact]
|
|
public void EdgeFlagsToAnchors_LeftRight_Stretches()
|
|
{
|
|
// left=4 (stretch ⇒ Left), top=1 (near-pin ⇒ Top), right=4 (stretch ⇒ Right), bottom=1 (near-pin of bottom axis ⇒ not Bottom)
|
|
var a = ElementReader.ToAnchors(left: 4, top: 1, right: 4, bottom: 1);
|
|
Assert.True(a.HasFlag(AnchorEdges.Left));
|
|
Assert.True(a.HasFlag(AnchorEdges.Right));
|
|
Assert.False(a.HasFlag(AnchorEdges.Bottom));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Edge value 1 = pinned to the NEAR edge of that axis.
|
|
/// For LeftEdge: near = Left. For TopEdge: near = Top.
|
|
/// For RightEdge: value 1 means near-pin of the right axis → does NOT map to Right anchor.
|
|
/// For BottomEdge: value 1 means near-pin of the bottom axis → does NOT map to Bottom anchor.
|
|
/// </summary>
|
|
[Fact]
|
|
public void EdgeFlagsToAnchors_AllOnes_PinsTopLeftOnly()
|
|
{
|
|
// 1 everywhere: only Left and Top anchors set (near-pins). Right/Bottom are far edges and value 1 is near-pin.
|
|
var a = ElementReader.ToAnchors(left: 1, top: 1, right: 1, bottom: 1);
|
|
Assert.True(a.HasFlag(AnchorEdges.Left));
|
|
Assert.True(a.HasFlag(AnchorEdges.Top));
|
|
Assert.False(a.HasFlag(AnchorEdges.Right));
|
|
Assert.False(a.HasFlag(AnchorEdges.Bottom));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Edge value 2 = pinned to the FAR edge of that axis.
|
|
/// For RightEdge: far = Right anchor. For BottomEdge: far = Bottom anchor.
|
|
/// For LeftEdge: value 2 means far-pin of the left axis → does NOT map to Left anchor.
|
|
/// For TopEdge: value 2 means far-pin of the top axis → does NOT map to Top anchor.
|
|
/// </summary>
|
|
[Fact]
|
|
public void EdgeFlagsToAnchors_AllTwos_PinsRightBottomOnly()
|
|
{
|
|
// 2 everywhere: only Right and Bottom anchors set (far-pins).
|
|
var a = ElementReader.ToAnchors(left: 2, top: 2, right: 2, bottom: 2);
|
|
Assert.False(a.HasFlag(AnchorEdges.Left));
|
|
Assert.False(a.HasFlag(AnchorEdges.Top));
|
|
Assert.True(a.HasFlag(AnchorEdges.Right));
|
|
Assert.True(a.HasFlag(AnchorEdges.Bottom));
|
|
}
|
|
|
|
/// <summary>
|
|
/// All-zero edge flags (prototype-only elements) fall back to Left|Top default.
|
|
/// </summary>
|
|
[Fact]
|
|
public void EdgeFlagsToAnchors_AllZero_DefaultsToTopLeft()
|
|
{
|
|
var a = ElementReader.ToAnchors(left: 0, top: 0, right: 0, bottom: 0);
|
|
Assert.Equal(AnchorEdges.Left | AnchorEdges.Top, a);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Value 3 = floating/centered between both far edges on that axis (format doc §4).
|
|
/// The anchor mapping fires on near-pin (1) and stretch (4) for Left/Top, and on
|
|
/// far-pin (2) and stretch (4) for Right/Bottom — value 3 matches none of these rules.
|
|
/// Therefore all-3 edge flags contribute no anchor bits and fall through to the
|
|
/// Left|Top default (pin top-left, fixed size).
|
|
/// This test covers that corner case (element 0x100004A9 — expand-detail overlay).
|
|
/// </summary>
|
|
[Fact]
|
|
public void EdgeFlagsToAnchors_ValueThree_FallsBackToTopLeft()
|
|
{
|
|
// value 3 doesn't match any anchor rule; falls back to Left|Top default.
|
|
var a = ElementReader.ToAnchors(left: 3, top: 3, right: 3, bottom: 3);
|
|
Assert.Equal(AnchorEdges.Left | AnchorEdges.Top, a);
|
|
}
|
|
|
|
// ── Merge ────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Merge_BaseThenOverride_DerivedWins()
|
|
{
|
|
var base_ = new ElementInfo { Type = 0, FontDid = 0x40000000, Width = 150, Height = 16 };
|
|
var derived = new ElementInfo { Type = 0, Width = 200 }; // overrides width, inherits font + height
|
|
var merged = ElementReader.Merge(base_, derived);
|
|
Assert.Equal(200, merged.Width); // override
|
|
Assert.Equal(16, merged.Height); // inherited
|
|
Assert.Equal(0x40000000u, merged.FontDid);// inherited
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_DerivedHasFontDid_OverridesBase()
|
|
{
|
|
var base_ = new ElementInfo { FontDid = 0x40000000, Width = 100, Height = 10 };
|
|
var derived = new ElementInfo { FontDid = 0x40000001, Width = 100 };
|
|
var merged = ElementReader.Merge(base_, derived);
|
|
Assert.Equal(0x40000001u, merged.FontDid);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_DerivedStateMediaOverridesBase()
|
|
{
|
|
var base_ = new ElementInfo();
|
|
base_.StateMedia[""] = (0x06001000u, 1);
|
|
base_.StateMedia["HideDetail"] = (0x06001001u, 1);
|
|
|
|
var derived = new ElementInfo();
|
|
derived.StateMedia[""] = (0x06002000u, 3); // overrides base default state
|
|
|
|
var merged = ElementReader.Merge(base_, derived);
|
|
// derived's "" overrides base's ""
|
|
Assert.Equal((0x06002000u, 3), merged.StateMedia[""]);
|
|
// base's "HideDetail" is kept (derived didn't provide it)
|
|
Assert.Equal((0x06001001u, 1), merged.StateMedia["HideDetail"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_ChildrenComeFromDerived()
|
|
{
|
|
var base_ = new ElementInfo();
|
|
base_.Children.Add(new ElementInfo { Id = 0x1u });
|
|
|
|
var derived = new ElementInfo();
|
|
derived.Children.Add(new ElementInfo { Id = 0x2u });
|
|
|
|
var merged = ElementReader.Merge(base_, derived);
|
|
// children must come from derived only
|
|
Assert.Single(merged.Children);
|
|
Assert.Equal(0x2u, merged.Children[0].Id);
|
|
}
|
|
}
|