summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/P61GlyphCoverageTests.fs
blob: f5f1a1001c78c66db1d362c0cce4d39bf511d207 (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
namespace LivingVillage.Desktop.Tests

open System.IO
open System.Text.RegularExpressions
open Microsoft.VisualStudio.TestTools.UnitTesting
open LivingVillage.Desktop

/// P61(G1 整改):中文界面字形覆盖。
/// 断言 Desktop 全部用户可见中文文案所需字形 100% 在 CjkGlyphAtlas 内,
/// 并对 G1 盘点缺的 16 字逐字断言。缺字会被 PixelText 置空(Game.fs:73-92),
/// 因此该测试是「中文界面完整」的红→绿门。
[<TestClass>]
type P61GlyphCoverageTests () =

    let isCjk (c: char) = c >= '\u4e00' && c <= '\u9fff'
    let isInAtlas (c: char) = CjkGlyphAtlas.contains c

    /// G1 盘点确认缺失、P61 修复的 16 个字。
    let g1MissingChars = "冬包卷品夏料春玩秋第背车轴页香鱼".ToCharArray()

    let repoRoot () : string =
        let mutable dir = System.AppContext.BaseDirectory
        let mutable found = None
        while found.IsNone && not (System.String.IsNullOrEmpty dir) do
            if File.Exists(Path.Combine(dir, "LivingVillage.sln")) then found <- Some dir
            else
                let parent = Path.GetDirectoryName dir
                if parent = dir then dir <- "" else dir <- parent
        match found with
        | Some d -> d
        | None -> failwith "找不到仓库根(LivingVillage.sln)"

    /// 扫描 Desktop 源内字符串字面量里的 CJK 字符(跳过注释行)。
    let sourceCjkChars () : Set<char> =
        let root = repoRoot ()
        let dir = Path.Combine(root, "src", "LivingVillage.Desktop")
        let literal = Regex("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"")
        let mutable chars = Set.empty
        for file in Directory.GetFiles(dir, "*.fs") do
            for line in File.ReadAllLines file do
                let trimmed = line.TrimStart()
                if not (trimmed.StartsWith "//") then
                    for m in literal.Matches line do
                        for c in m.Groups.[1].Value do
                            if isCjk c then chars <- Set.add c chars
        chars

    [<TestMethod>]
    member _.AllSourceChineseLiteralsHaveAtlasGlyphs () =
        let chars = sourceCjkChars ()
        let missing = chars |> Set.filter (fun c -> not (isInAtlas c)) |> Set.toList
        CollectionAssert.AreEqual(
            [||],
            missing |> List.map string |> List.toArray,
            sprintf "Desktop 用户可见中文文案缺字形 %d 个: %s(需补入 CjkGlyphAtlas.characterTable 并重生成图集)"
                missing.Length (missing |> List.map string |> String.concat ""))

    [<TestMethod>]
    member _.G1MissingCharsAreNowInAtlas () =
        let missing = g1MissingChars |> Array.filter (fun c -> not (isInAtlas c)) |> Array.toList
        CollectionAssert.AreEqual(
            [||],
            missing |> List.map string |> List.toArray,
            sprintf "G1 缺字仍未入图集: %s" (missing |> List.map string |> String.concat ""))

    [<TestMethod>]
    member _.RequiredUiLabelsAreAtlasBacked () =
        let missing =
            ChineseText.requiredUiLabels
            |> List.collect (fun s -> s.ToCharArray() |> Array.toList)
            |> List.filter isCjk
            |> List.filter (fun c -> not (isInAtlas c))
            |> List.distinct
        CollectionAssert.AreEqual(
            [||],
            missing |> List.map string |> List.toArray,
            sprintf "requiredUiLabels 缺字形: %s" (missing |> List.map string |> String.concat ""))

    [<TestMethod>]
    member _.CalendarAndFooterAndItemCopyAreAtlasBacked () =
        // 派发词点名要可见完整的三类运行期文案,逐字钉住,防止再次缺字。
        let pinned =
            [ HudLayout.seasonNames |> String.concat ""
              HudLayout.calendarLine1 { Season = "春"; DayNumber = 1L; Period = "清晨"; Clock = "06:00" }
              "第 1/3 页  空格 翻页  Esc 关闭"
              "背包物品×1"
              "鱼"; "卷轴"; "香料"; "玩家"; "回车:接受任务" ]
        let missing =
            pinned
            |> List.collect (fun s -> s.ToCharArray() |> Array.toList)
            |> List.filter isCjk
            |> List.distinct
            |> List.filter (fun c -> not (isInAtlas c))
        CollectionAssert.AreEqual(
            [||],
            missing |> List.map string |> List.toArray,
            sprintf "运行期文案缺字形: %s" (missing |> List.map string |> String.concat ""))