namespace LivingVillage.Desktop.Tests open Microsoft.VisualStudio.TestTools.UnitTesting open LivingVillage.Desktop open LivingVillage.Kernel open LivingVillage.Kernel.Sim /// P74 回归:启动开场过渡(水墨渐显)纯函数性质 + 开始界面布局入口。 [] type P74LaunchTransitionTests () = let width = 1280 let height = 720 [] member _.InkRevealDissolvesMonotonicallyAndStaysSoft () = // 起手满墨、96 帧后散尽,且整体强度随帧非增(确定性 ease-out)。 Assert.AreEqual(1.0f, InkReveal.inkStrength 0L) Assert.AreEqual(0.0f, InkReveal.inkStrength InkReveal.dissolveFrames) Assert.AreEqual(0.0f, InkReveal.inkStrength 140L) let strengths = [ 0L; 8L; 30L; 60L; 95L ] |> List.map InkReveal.inkStrength Assert.AreEqual(strengths, strengths |> List.sortDescending) // 墨滴始终存在、均在画布内、alpha < 255(柔和墨晕,非实心黑块)、半径不覆盖半屏。 for frame in [ 0L; 8L; 30L; 60L; 95L ] do let dabs = InkReveal.dabs frame width height Assert.IsTrue(dabs.Length > 0, "墨滴集合不得为空") for dab in dabs do Assert.IsTrue(dab.Alpha >= 0 && dab.Alpha < 255, sprintf "alpha %d 越界" dab.Alpha) Assert.IsTrue(dab.Rx > 0 && dab.Rx < width / 2, "Rx 覆盖过半屏") Assert.IsTrue(dab.Ry > 0 && dab.Ry < height / 2, "Ry 覆盖过半屏") [] member _.InkRevealDabsChangeOverTheTransition () = // 同一帧两次调用一致(确定性);不同帧墨滴布局不同(动画在变)。 Assert.AreEqual(InkReveal.dabs 20L width height, InkReveal.dabs 20L width height) Assert.AreNotEqual(InkReveal.dabs 8L width height, InkReveal.dabs 60L width height) [] member _.LaunchScreenTransitionIsDeterministic () = Assert.AreEqual(0.0f, LaunchScreen.revealProgress 0L) Assert.AreEqual(1.0f, LaunchScreen.revealProgress LaunchScreen.fadeInFrames) Assert.AreEqual(1.0f, LaunchScreen.veilAlpha 0L) Assert.AreEqual(0.0f, LaunchScreen.veilAlpha LaunchScreen.durationFrames) Assert.IsTrue(LaunchScreen.isComplete LaunchScreen.durationFrames) Assert.IsFalse(LaunchScreen.isComplete (LaunchScreen.durationFrames - 1L)) Assert.AreEqual("江南水乡", LaunchScreen.title) [] member _.StartMenuLayoutExposesStartLoadExit () = // 开场结束后的可交互开始界面:无存档 → 开始/读档/退出。 let fresh = MenuState.create { Paused = false; Speed = OneX } false Assert.AreEqual(MainMenu, fresh.Page) Assert.AreEqual([ NewGameItem; LoadItem; ExitItem ], MenuState.items fresh) let labels = MenuState.items fresh |> List.map MenuState.itemLabel Assert.AreEqual([ "开始"; "读档"; "退出" ], labels) // 有进行中的世界 → 继续/读档/退出。 let resumed = MenuState.create { Paused = false; Speed = OneX } true Assert.AreEqual([ ContinueItem; LoadItem; ExitItem ], MenuState.items resumed) Assert.AreEqual("继续", MenuState.itemLabel (MenuState.selectedItem resumed))