namespace LivingVillage.Desktop.Tests open Microsoft.VisualStudio.TestTools.UnitTesting open LivingVillage.Kernel open LivingVillage.Kernel.Sim open LivingVillage.Desktop /// P38 界面升级切片:顶部常驻 HUD 图标化(时段/时间/倍速/体力/钱币)的布局与确定性回归。 [] type P38HudIconTests () = let world = Sim.initialWorldN 42UL 4 let control = SimulationControl.initial let segments = HudLayout.segments world control [] member _.EveryHudSegmentCarriesAWellFormedDistinctIcon () = let expected = [ "时段", HudLayout.PeriodIcon "时间", HudLayout.ClockIcon "倍速", HudLayout.SpeedIcon "体力", HudLayout.EnergyIcon "钱币", HudLayout.MoneyIcon ] Assert.AreEqual(expected |> List.map fst, segments |> List.map (fun segment -> segment.Label)) Assert.AreEqual(expected |> List.map snd, segments |> List.map (fun segment -> segment.Icon)) for segment in segments do let rows = HudLayout.iconRows segment.Icon Assert.IsTrue(rows.Length = HudLayout.iconGrid, sprintf "%s 图标行数" segment.Label) for row in rows do Assert.IsTrue(row.Length = HudLayout.iconGrid, sprintf "%s 图标行宽" segment.Label) Assert.IsTrue(rows |> List.exists (fun row -> row.Contains "1"), sprintf "%s 图标必须有实心像素" segment.Label) let distinct = segments |> List.map (fun segment -> segment.Icon) |> List.distinct |> List.length Assert.IsTrue(segments.Length = distinct, "每个常驻段图标应互不相同") [] member _.HudLayoutReservesIconThenTextAndFitsTheStrip () = let placed = HudLayout.layout segments Assert.AreEqual(segments.Length, placed.Length) let (firstX, _, _) = List.head placed Assert.AreEqual(HudLayout.paddingX, firstX) for (x, width, segment) in placed do let textWidth = FloatingPrompt.measureWidth (HudLayout.segmentText segment) HudLayout.textScale Assert.AreEqual(HudLayout.iconWidth + HudLayout.iconGap + textWidth, width) // 图标与文本都必须落在段宽内。 Assert.IsTrue(x + HudLayout.iconWidth <= x + width, "图标越出段宽") Assert.IsTrue(HudLayout.textOrigin x + textWidth <= x + width, "文本越出段宽") Assert.IsTrue(HudLayout.iconHeight <= HudLayout.barHeight, "图标必须放进窄条") placed |> List.pairwise |> List.iter (fun ((x1, w1, _), (x2, _, _)) -> Assert.IsTrue(x2 >= x1 + w1, sprintf "段重叠:%d+%d vs %d" x1 w1 x2)) let (lastX, lastWidth, _) = List.last placed Assert.IsTrue(lastX + lastWidth <= 1280, "最后一段必须留在 1280 视口内") [] member _.HudSegmentsAndLayoutAreDeterministic () = Assert.AreEqual(segments, HudLayout.segments world control) Assert.AreEqual<(int * int * HudLayout.Segment) list>(HudLayout.layout segments, HudLayout.layout segments) for segment in segments do Assert.AreEqual(HudLayout.iconRows segment.Icon, HudLayout.iconRows segment.Icon) [] member _.HudBackdropCoversIconAndTextInBothPeriods () = for mode in [ Day; Night ] do let palette = HudLayout.palette mode Assert.IsTrue(palette.SegmentBackdrop.A > 0uy && palette.SegmentBackdrop.A < 255uy, "底色必须半透明") for (x, width, _) in HudLayout.layout segments do let rect = HudLayout.segmentBackdrop x width Assert.IsTrue(rect.X <= x, "底色未覆盖图标左沿") Assert.IsTrue(rect.X + rect.Width >= HudLayout.textOrigin x, "底色未覆盖文本区") Assert.IsTrue(rect.Y >= 0 && rect.Y + rect.Height <= HudLayout.barHeight, "底色必须留在窄条内")