diff options
Diffstat (limited to 'src/LivingVillage.Desktop.Tests')
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj | 1 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/P57MenuHighlightTests.fs | 105 |
2 files changed, 106 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj index a3c1e45..951e59b 100644 --- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj +++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj @@ -33,6 +33,7 @@ <Compile Include="P53InteractionPromptTests.fs" /> <Compile Include="P54ProfessionDailyTests.fs" /> <Compile Include="P55ProfessionRewardTests.fs" /> + <Compile Include="P57MenuHighlightTests.fs" /> <Compile Include="SampleTests.fs" /> </ItemGroup> diff --git a/src/LivingVillage.Desktop.Tests/P57MenuHighlightTests.fs b/src/LivingVillage.Desktop.Tests/P57MenuHighlightTests.fs new file mode 100644 index 0000000..74f8eba --- /dev/null +++ b/src/LivingVillage.Desktop.Tests/P57MenuHighlightTests.fs @@ -0,0 +1,105 @@ +namespace LivingVillage.Desktop.Tests + +open Microsoft.VisualStudio.TestTools.UnitTesting +open LivingVillage.Desktop + +/// P57 美术 A3:菜单/启动水面第二层高光波光(P46 遗留短板)。 +/// +/// 高光层必须与主水带**可分辨**:更亮更窄、列相位错开、推进速度略不同; +/// 全为帧计数纯函数(无随机),且**不得改变**既有 waterPhase / lantern / 云影输出。 +[<TestClass>] +type P57MenuHighlightTests () = + + let twoPi = 2.0f * float32 System.Math.PI + let assertClose (expected: float32) (actual: float32) (message: string) = + Assert.IsTrue((abs (expected - actual) < 1e-4f), sprintf "%s: expected %f actual %f" message expected actual) + let assertInt (expected: int) (actual: int) (message: string) = + Assert.IsTrue((expected = actual), sprintf "%s: expected %d actual %d" message expected actual) + + let visibleColumn (frame: int64) (rowIndex: int) : int = + [ 0 .. 63 ] + |> List.tryFind (fun column -> MenuAmbience.highlightVisible frame column rowIndex) + |> Option.defaultValue -1 + + [<TestMethod>] + member _.HighlightPhaseOffsetsAdjacentColumnsByANonZeroStep () = + for frame in [ 0L; 16L; 150L; 4097L ] do + for rowIndex in [ 0; 1 ] do + for column in [ 0; 5; 21; 38 ] do + let step = + MenuAmbience.highlightPhase frame (column + 1) rowIndex + - MenuAmbience.highlightPhase frame column rowIndex + assertClose MenuAmbience.highlightColumnStep step (sprintf "frame %d col %d row %d" frame column rowIndex) + Assert.IsTrue(abs step > 1e-3f, "adjacent highlight columns must be phase-shifted") + + [<TestMethod>] + member _.HighlightMovesAtADifferentSpeedThanTheMainWaterBand () = + // 主水带连续相位速率 = (2π/3)/32;高光层 = 2π/period,二者比值应略偏离 1(本实现 1.2)。 + let mainDelta = + MenuAmbience.waterPhase 151L 5 0 - MenuAmbience.waterPhase 150L 5 0 + let highlightDelta = + MenuAmbience.highlightPhase 151L 5 0 - MenuAmbience.highlightPhase 150L 5 0 + Assert.IsTrue(highlightDelta > 0.0f && mainDelta > 0.0f, "both layers must advance with the frame") + let ratio = highlightDelta / mainDelta + assertClose 1.2f ratio "highlight speed must be slightly different (1.2x)" + Assert.IsTrue(abs (highlightDelta - mainDelta) > 1e-3f, "the two layers must not move at the same speed") + + [<TestMethod>] + member _.HighlightLayerIsBrighterAndNarrowerThanTheMainBand () = + // 更窄:条更薄、更短。 + Assert.IsTrue(MenuAmbience.highlightThickness < 3, "highlight bar must be thinner than the 3px main crest") + Assert.IsTrue(MenuAmbience.highlightBarWidth < 32, "highlight bar must be narrower than a tile") + // 更亮:纯白高光 > 主水带波峰色 (225,240,245) 的 luma。 + let r, g, b = MenuAmbience.highlightRgb + let highlightLuma = 0.299f * float32 r + 0.587f * float32 g + 0.114f * float32 b + let crestLuma = 0.299f * 225.0f + 0.587f * 240.0f + 0.114f * 245.0f + Assert.IsTrue(highlightLuma > crestLuma, sprintf "highlight luma %.1f must exceed crest luma %.1f" highlightLuma crestLuma) + // 更窄的可见条件(阈值更高 → 亮列更稀疏),但仍有可见列。 + let frame = 150L + let highlightCount = [ 0 .. 39 ] |> List.filter (fun c -> MenuAmbience.highlightVisible frame c 0) |> List.length + let mainCount = [ 0 .. 39 ] |> List.filter (fun c -> MenuAmbience.waterColumnCrest frame c 0) |> List.length + Assert.IsTrue(highlightCount > 0, "some highlight columns must be lit") + Assert.IsTrue(highlightCount <= mainCount, sprintf "highlight %d must be no denser than main crest %d" highlightCount mainCount) + + [<TestMethod>] + member _.HighlightIsDeterministicAndBounded () = + for frame in [ 0L; 1L; 33L; 173L; 99999L; 100000L; 500000L ] do + for column in [ 0; 13; 39 ] do + for rowIndex in [ 0; 1 ] do + assertClose + (MenuAmbience.highlightPhase frame column rowIndex) + (MenuAmbience.highlightPhase frame column rowIndex) + "highlight phase must be deterministic" + let alpha = MenuAmbience.highlightAlpha MenuAmbience.highlightDayLight frame column rowIndex + Assert.IsTrue(alpha >= 0 && alpha <= 255, sprintf "alpha %d out of [0,255]" alpha) + + [<TestMethod>] + member _.NightLightDimmsTheHighlightButKeepsItReadable () = + let frame = 150L + let column = visibleColumn frame 0 + Assert.IsTrue(column >= 0, "need a lit column to compare day/night") + let day = MenuAmbience.highlightAlpha MenuAmbience.highlightDayLight frame column 0 + let night = MenuAmbience.highlightAlpha MenuAmbience.highlightNightLight frame column 0 + Assert.IsTrue(night < day, sprintf "night alpha %d must be below day %d" night day) + Assert.IsTrue(night >= int (0.35f * float32 day), sprintf "night alpha %d must stay readable (>= 35%% of day)" night) + Assert.IsTrue(night >= 20, sprintf "night alpha %d must not vanish" night) + + [<TestMethod>] + member _.ExistingWaterLanternCloudOutputsArePinned () = + // 回归:高光层不得改变既有 waterPhase / lantern / 云影输出帧。 + assertInt 0 (VillageArt.waterFrameTickAt 150L 5 0) "waterFrameTickAt pin" + assertClose 1.317237f (MenuAmbience.waterColumnBrightness 150L 5 0) "main water brightness pin" + assertInt 89 (MenuAmbience.lanternBreathAlpha 50L 0) "lantern breath pin" + assertInt 70 (MenuAmbience.cloudOffsetPx 0 140L 1280) "cloud offset pin" + Assert.AreEqual<int list>([ 624; 656 ], (TitleScreen.layout 1280 720).WaterRows) + + [<TestMethod>] + member _.P57ShotScriptWaterFramesAreOrderedAndDistinct () = + Assert.IsTrue(P57ShotScript.splashFrame > 0L && P57ShotScript.splashFrame < LaunchScreen.durationFrames) + Assert.IsTrue(P57ShotScript.menuFrame >= 120L, "menu frame must wait for the entrance to settle") + let names = + [ P57ShotScript.splashBaseName + P57ShotScript.splashHighlightName + P57ShotScript.menuBaseName + P57ShotScript.menuHighlightName ] + assertInt names.Length (names |> List.distinct |> List.length) "shot names distinct" |
