summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/P16UiTests.fs
blob: 2e474acb1b35ccf6845dac8881ab496babfb3e55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
namespace LivingVillage.Desktop.Tests

open Microsoft.VisualStudio.TestTools.UnitTesting
open LivingVillage.Kernel
open LivingVillage.Desktop

// ---- P16 启动画面 / 开始界面 ----

[<TestClass>]
type P16LaunchTests () =

    [<TestMethod>]
    member _.LaunchRevealAndVeilAreDeterministicAndComplete () =
        Assert.AreEqual<float32>(0.0f, LaunchScreen.revealProgress 0L)
        Assert.AreEqual<float32>(1.0f, LaunchScreen.revealProgress LaunchScreen.fadeInFrames)
        Assert.AreEqual<float32>(1.0f, LaunchScreen.revealProgress 1000L)
        Assert.AreEqual<float32>(1.0f, LaunchScreen.veilAlpha 0L)
        Assert.AreEqual<float32>(0.0f, LaunchScreen.veilAlpha LaunchScreen.durationFrames)
        Assert.IsTrue(LaunchScreen.veilAlpha 30L > LaunchScreen.veilAlpha 90L, "veil must decrease monotonically")
        Assert.IsFalse(LaunchScreen.isComplete (LaunchScreen.durationFrames - 1L))
        Assert.IsTrue(LaunchScreen.isComplete LaunchScreen.durationFrames)

    [<TestMethod>]
    member _.LaunchSkipIsEscOrSpaceAndTextUsesTheAtlasPath () =
        Assert.IsTrue(LaunchScreen.skipRequested true false)
        Assert.IsTrue(LaunchScreen.skipRequested false true)
        Assert.IsFalse(LaunchScreen.skipRequested false false)
        Assert.IsTrue(PixelText.isRenderable LaunchScreen.title)
        Assert.IsTrue(PixelText.isRenderable LaunchScreen.skipHint)
        Assert.IsTrue(PixelText.isRenderable LaunchScreen.subtitle)
        Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains LaunchScreen.skipHint)

[<TestClass>]
type P16StartMenuTests () =

    [<TestMethod>]
    member _.StartMenuIsExactlyThreeItemsWithChineseLabels () =
        let state = MenuState.create SimulationControl.initial false

        Assert.AreEqual<int>(3, MenuState.items state |> List.length)
        Assert.AreEqual<string list>([ "开始"; "读档"; "退出" ], MenuState.items state |> List.map MenuState.itemLabel)

    [<TestMethod>]
    member _.StartMenuNavigationSelectsEachItemAndWraps () =
        let state = MenuState.create SimulationControl.initial false
        let down1 = MenuState.move Down state
        let down2 = MenuState.move Down down1
        let wrap = MenuState.move Down down2

        Assert.AreEqual<MenuItem>(NewGameItem, MenuState.selectedItem state)
        Assert.AreEqual<MenuItem>(LoadItem, MenuState.selectedItem down1)
        Assert.AreEqual<MenuItem>(ExitItem, MenuState.selectedItem down2)
        Assert.AreEqual<MenuItem>(NewGameItem, MenuState.selectedItem wrap)

        Assert.AreEqual<MenuCommand>(StartNewGame, MenuState.update Confirm state |> snd)
        Assert.AreEqual<MenuCommand>(LoadGame, MenuState.update Confirm down1 |> snd)
        Assert.AreEqual<MenuCommand>(ExitGame, MenuState.update Confirm down2 |> snd)

    [<TestMethod>]
    member _.ReturningToMenuOffersContinueAsTheFirstOfThreeItems () =
        let playing = MenuState.create SimulationControl.initial true |> MenuState.enterGame
        let main = MenuState.openMain playing

        Assert.AreEqual<MenuItem list>([ ContinueItem; LoadItem; ExitItem ], MenuState.items main)
        Assert.AreEqual<string list>([ "继续"; "读档"; "退出" ], MenuState.items main |> List.map MenuState.itemLabel)
        Assert.AreEqual<MenuCommand>(ContinueGame, MenuState.update Confirm main |> snd)

// ---- P16 HUD 顶部窄信息条 ----

[<TestClass>]
type P16HudTests () =

    let world = Sim.initialWorldN 42UL 4

    [<TestMethod>]
    member _.HudBarIsANarrowStripNotAFullscreenBlock () =
        Assert.IsTrue(HudLayout.barHeight <= 40, "HUD bar must stay a narrow strip")
        Assert.IsTrue(HudLayout.barHeight * 4 < 720, "HUD bar must not dominate the viewport")

    [<TestMethod>]
    member _.HudSegmentsAreDeterministicAndUseRealState () =
        let control = SimulationControl.initial
        let segments = HudLayout.segments world control
        Assert.AreEqual<string list>([ "时段"; "时间"; "倍速"; "体力"; "钱币" ], segments |> List.map (fun segment -> segment.Label))
        Assert.AreEqual<HudLayout.Segment list>(segments, HudLayout.segments world control)
        let energy = segments |> List.find (fun segment -> segment.Label = "体力")
        Assert.AreEqual<string>(sprintf "%.0f" world.Avatar.Mind.Needs.Energy, energy.Value)
        let money = segments |> List.find (fun segment -> segment.Label = "钱币")
        Assert.AreEqual<string>(sprintf "%.0f" world.Avatar.Mind.Needs.Money, money.Value)

    [<TestMethod>]
    member _.HudLayoutPlacesSegmentsDeterministicallyWithinWidth () =
        let placed = HudLayout.layout (HudLayout.segments world SimulationControl.initial)
        Assert.AreEqual<int>(5, placed.Length)
        let (firstX, _, _) = List.head placed
        Assert.AreEqual<int>(HudLayout.paddingX, firstX)
        placed
        |> List.pairwise
        |> List.iter (fun ((x1, w1, _), (x2, _, _)) ->
            Assert.IsTrue(x2 >= x1 + w1, sprintf "segments overlap: %d+%d vs %d" x1 w1 x2))
        let (lastX, lastWidth, _) = List.last placed
        Assert.IsTrue(lastX + lastWidth <= 1280, "last segment must fit inside a 1280 viewport")

    [<TestMethod>]
    member _.HudClockAndPeriodArePureFunctions () =
        let ticksPerHour = Sim.ticksPerDay / 24L
        Assert.AreEqual<string>("12:00", M6Presentation.clockTimeLabel (12L * ticksPerHour))
        Assert.AreEqual<string>("00:00", M6Presentation.clockTimeLabel 0L)
        Assert.AreEqual<string>("06:30", M6Presentation.clockTimeLabel (6L * ticksPerHour + ticksPerHour / 2L))
        Assert.AreEqual<string>("白天", M6Presentation.timePeriodLabel (12L * ticksPerHour))
        Assert.AreEqual<string>("夜晚", M6Presentation.timePeriodLabel 0L)
        Assert.AreEqual<string>("清晨", M6Presentation.timePeriodLabel (6L * ticksPerHour))
        Assert.AreEqual<string>("黄昏", M6Presentation.timePeriodLabel (19L * ticksPerHour))
        // P18: 黄昏 covers the golden hour through 20:59; 21:00 onwards is 夜晚.
        Assert.AreEqual<string>("黄昏", M6Presentation.timePeriodLabel (20L * ticksPerHour))
        Assert.AreEqual<string>("黄昏", M6Presentation.timePeriodLabel (int64 20 * ticksPerHour + ticksPerHour * 3L / 4L))
        Assert.AreEqual<string>("夜晚", M6Presentation.timePeriodLabel (21L * ticksPerHour))

    [<TestMethod>]
    member _.HudNightPaletteLeansCoolAndLabelsAreAtlasBacked () =
        let day = HudLayout.palette Day
        let night = HudLayout.palette Night
        Assert.IsTrue(night.Accent.B > night.Accent.R, "night accent should lean cool/blue (winter-night tint)")
        Assert.IsTrue(day.Accent.R > day.Accent.B, "day accent should lean warm")
        for label in [ "时段"; "时间"; "倍速"; "体力"; "钱币" ] do
            Assert.IsTrue(PixelText.isRenderable label, sprintf "HUD label %s must render" label)
        Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains "时段")
        Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains "体力")
        Assert.IsTrue(ChineseText.requiredUiLabels |> List.contains "钱币")

// ---- P16-FIX 菜单截图帧表 ----

[<TestClass>]
type P16MenuShotTests () =

    [<TestMethod>]
    member _.MenuShotFrameTableIsOrderedAndNamesAreDistinct () =
        Assert.IsTrue(MenuShotScript.splashMidFrame > 0L)
        Assert.IsTrue(LaunchScreen.revealProgress MenuShotScript.splashMidFrame > 0.0f)
        Assert.IsTrue(LaunchScreen.revealProgress MenuShotScript.splashMidFrame < 1.0f)
        Assert.IsTrue(MenuShotScript.splashMidFrame < MenuShotScript.initialFrame)
        Assert.IsTrue(MenuShotScript.initialFrame < MenuShotScript.downFrame)
        Assert.IsTrue(MenuShotScript.downFrame < MenuShotScript.loadFrame)
        Assert.IsTrue(MenuShotScript.loadFrame < MenuShotScript.exitFrame)
        Assert.IsTrue(MenuShotScript.initialFrame >= 120L, "initial shot must wait for the 2s entrance to settle")
        let names = [ MenuShotScript.splashMidName; MenuShotScript.initialName; MenuShotScript.loadName ]
        Assert.AreEqual<int>(names.Length, names |> List.distinct |> List.length)

    [<TestMethod>]
    member _.MenuShotSelectsStartThenLoadOnFreshState () =
        let state = MenuState.create SimulationControl.initial false
        Assert.AreEqual<MenuItem>(NewGameItem, MenuState.selectedItem state)
        Assert.AreEqual<MenuItem>(LoadItem, MenuState.selectedItem (MenuState.move Down state))