From 6c68927c7466dfa988a52933a29d487222cf3211 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 10:05:58 +0800 Subject: p71: 中文 API/模块说明(docs/api-reference) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 docs/api-reference/:README + 6 模块(Sim/地图生成/桌面渲染/交互交易/职业/存读档) 每节含 职责·对外 API 概览·单位与量纲·不变量·失败分支·算法与性能取舍 - 口径以当前源码为准;性能引用实测 p70 基准(不含未测宣称) - P71ApiDocsTests(3 例,红先→绿):文件齐备/章节齐备/关键接口与单位锚点 - Desktop 341/341、Kernel 120/120;未改生产 .fs/.fsproj、未 push --- src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs | 93 ++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs (limited to 'src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs') diff --git a/src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs b/src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs new file mode 100644 index 0000000..8aa3c71 --- /dev/null +++ b/src/LivingVillage.Desktop.Tests/P71ApiDocsTests.fs @@ -0,0 +1,93 @@ +namespace LivingVillage.Desktop.Tests + +open System.IO +open Microsoft.VisualStudio.TestTools.UnitTesting + +/// P71(中文 API/模块说明,docs-only):锁定 docs/api-reference/ 的存在、分节与关键口径, +/// 防止说明文档被误删或章节漂移。 +[] +type P71ApiDocsTests () = + + 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)" + + let apiDir () = Path.Combine(repoRoot (), "docs", "api-reference") + + let expectedFiles = + [ "README.md" + "sim-kernel.md" + "map-and-mapgen.md" + "rendering-desktop.md" + "interaction-and-trade.md" + "occupation.md" + "world-save.md" ] + + let requiredSections = + [ "## 职责" + "## 对外 API 概览" + "## 单位与量纲" + "## 不变量" + "## 失败分支" + "## 算法与性能取舍" ] + + let allDocs () : string = + expectedFiles + |> List.filter (fun name -> File.Exists(Path.Combine(apiDir (), name))) + |> List.map (fun name -> File.ReadAllText(Path.Combine(apiDir (), name))) + |> String.concat "\n" + + [] + member _.ApiReferenceDirectoryHasAllModuleFiles () = + Assert.IsTrue(Directory.Exists(apiDir ()), sprintf "缺少目录 %s" (apiDir ())) + let missing = expectedFiles |> List.filter (fun name -> not (File.Exists(Path.Combine(apiDir (), name)))) + CollectionAssert.AreEqual( + [||], + missing |> List.toArray, + sprintf "docs/api-reference 缺少文件: %s" (String.concat ", " missing)) + + [] + member _.EveryModuleSectionHasTheRequiredHeadings () = + let docs = allDocs () + let missing = requiredSections |> List.filter (fun section -> not (docs.Contains section)) + CollectionAssert.AreEqual( + [||], + missing |> List.toArray, + sprintf "api-reference 缺少规定章节: %s" (String.concat ", " missing)) + // 六个模块文件各一个「## 职责」,确保每个模块都有齐六个小节。 + let responsibilities = requiredSections.[0] + let count = + docs.Split('\n') + |> Array.filter (fun line -> line.Trim() = responsibilities) + |> Array.length + Assert.IsTrue(count >= 6, sprintf "「## 职责」出现 %d 次,应 >= 6(每模块一节)" count) + + [] + member _.ApiReferencePinsKeyInterfacesAndUnits () = + let docs = allDocs () + let anchors = + [ "ticksPerDay" + "configureBounds" + "953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C" + "WorldSave.save" + "LV_WORLD_SAVE_V2" + "LV_WORLD_SAVE_V3" + "MapGen.paramsForSize" + "MapSnapshot.renderPng" + "CjkGlyphAtlas" + "M5Command" + "PlayerTrade.sell" + "Occupation.saveToken" ] + let missing = anchors |> List.filter (fun anchor -> not (docs.Contains anchor)) + CollectionAssert.AreEqual( + [||], + missing |> List.toArray, + sprintf "api-reference 未钉住关键接口/单位: %s" (String.concat ", " missing)) -- cgit v1.2.3