using AcDream.App.UI; using AcDream.App.UI.Layout; namespace AcDream.App.Tests.UI.Layout; public class ElementReaderTests { // ── ToAnchors (decomp-backed: UIElement::UpdateForParentSizeChange @0x00462640) ───────────── /// /// Top edge (L=1,T=1,R=1,B=2): LeftEdge==1 → Left; RightEdge==1 → Right (stretch); /// TopEdge==1 → Top; BottomEdge==2 (not 1/4, top≠2) → no Bottom. /// This is the top chrome edge — it pins left, stretches width, pins top, fixed height. /// Real vitals values from format doc §11 (0x10000634). /// [Fact] public void ToAnchors_TopEdge_StretchesWidth() { var a = ElementReader.ToAnchors(left: 1, top: 1, right: 1, bottom: 2); Assert.True(a.HasFlag(AnchorEdges.Left)); Assert.True(a.HasFlag(AnchorEdges.Top)); Assert.True(a.HasFlag(AnchorEdges.Right)); Assert.False(a.HasFlag(AnchorEdges.Bottom)); } /// /// TL corner (L=1,T=1,R=2,B=2): LeftEdge==1 → Left; RightEdge==2 (not 1/4), left≠2 → no Right; /// TopEdge==1 → Top; BottomEdge==2, top≠2 → no Bottom. Fixed size, pinned top-left. /// Real vitals values from format doc §11 (0x10000633). /// [Fact] public void ToAnchors_TlCorner_PinsTopLeftFixed() { var a = ElementReader.ToAnchors(left: 1, top: 1, right: 2, bottom: 2); Assert.True(a.HasFlag(AnchorEdges.Left)); Assert.True(a.HasFlag(AnchorEdges.Top)); Assert.False(a.HasFlag(AnchorEdges.Right)); Assert.False(a.HasFlag(AnchorEdges.Bottom)); } /// /// TR corner (L=2,T=1,R=1,B=2): LeftEdge==2 → triggers Right (track-right); RightEdge==1 → Right; /// left≠1 → no Left; TopEdge==1 → Top; BottomEdge==2, top≠2 → no Bottom. /// Fixed-width element whose left and right both track the parent's right edge. /// Real vitals values from format doc §11 (0x10000635). /// [Fact] public void ToAnchors_TrCorner_TracksRight() { var a = ElementReader.ToAnchors(left: 2, top: 1, right: 1, bottom: 2); Assert.False(a.HasFlag(AnchorEdges.Left)); Assert.True(a.HasFlag(AnchorEdges.Top)); Assert.True(a.HasFlag(AnchorEdges.Right)); Assert.False(a.HasFlag(AnchorEdges.Bottom)); } /// /// Left edge (L=1,T=1,R=2,B=1): LeftEdge==1 → Left; RightEdge==2, left≠2 → no Right; /// TopEdge==1 → Top; BottomEdge==1 → Bottom. Pins left+top+bottom, fixed width, stretches height. /// Real vitals values from format doc §11 (0x10000636). /// [Fact] public void ToAnchors_LeftEdge_StretchesHeight() { var a = ElementReader.ToAnchors(left: 1, top: 1, right: 2, bottom: 1); Assert.True(a.HasFlag(AnchorEdges.Left)); Assert.True(a.HasFlag(AnchorEdges.Top)); Assert.False(a.HasFlag(AnchorEdges.Right)); Assert.True(a.HasFlag(AnchorEdges.Bottom)); } /// /// All-ones (L=1,T=1,R=1,B=1): all four flags fire — Left, Right, Top, Bottom. /// A piece pinned to all four sides stretches both horizontally and vertically. /// [Fact] public void ToAnchors_Meter_StretchesBoth() { 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.True(a.HasFlag(AnchorEdges.Right)); Assert.True(a.HasFlag(AnchorEdges.Bottom)); } /// /// All-zero edge flags (prototype-only elements) fall back to Left|Top default. /// [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); } /// /// Value 3 on left and right axes contributes no Left/Right anchor; /// TopEdge==1 → Top; BottomEdge==1 → Bottom. /// left=3 (not 1/4) → no Left; right=3 (not 1/4), left≠2 → no Right; /// top=1 → Top; bottom=1 → Bottom. Result: Top|Bottom. /// [Fact] public void EdgeFlagsToAnchors_ValueThree_HorizAxes_YieldsTopBottom() { var a = ElementReader.ToAnchors(left: 3, top: 1, right: 3, bottom: 1); Assert.False(a.HasFlag(AnchorEdges.Left)); Assert.True(a.HasFlag(AnchorEdges.Top)); Assert.False(a.HasFlag(AnchorEdges.Right)); Assert.True(a.HasFlag(AnchorEdges.Bottom)); } // ── 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); } }