Files
KSP_project/tests/test_guanghan_topology_demo.py
T
2026-07-09 17:33:57 +08:00

116 lines
4.4 KiB
Python

from __future__ import annotations
import re
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
HTML_PATH = ROOT / "data" / "wiki" / "guanghan_topology_demo.html"
TIMELINE_PATH = ROOT / "user_input" / "广寒计划" / "Timeline.md"
CLICKABLE_MODULES = {
"core",
"habitat-1",
"habitat-2",
"greenhouse",
"laboratory",
"power-node",
"power-north",
"power-east",
"power-south",
"power-west",
"wugang-2",
"binglun-1",
"binglun-2",
"binglun-3",
}
class GuanghanTopologySourceTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.html = HTML_PATH.read_text(encoding="utf-8")
timeline = TIMELINE_PATH.read_text(encoding="utf-8")
cls.ghc05 = timeline.split("GHC-05任务", 1)[1].split("JC-02任务", 1)[0]
def test_clickable_module_inventory_is_exact(self) -> None:
found = set(re.findall(r'data-module="([^"]+)"', self.html))
self.assertEqual(found, CLICKABLE_MODULES)
def test_connection_inventory_matches_approved_topology(self) -> None:
self.assertEqual(self.html.count('data-connection="pressurized"'), 2)
self.assertIn('data-link="wugang-2--greenhouse"', self.html)
self.assertIn('data-link="habitat-1--power-north"', self.html)
self.assertNotIn("perimeter-corridor", self.html)
def test_walkways_and_radiators_are_not_clickable(self) -> None:
self.assertNotRegex(
self.html,
r'<(?:line|path|rect)[^>]+data-connection="pressurized"[^>]+data-module=',
)
self.assertNotRegex(
self.html,
r'<(?:g|rect)[^>]+class="[^"]*radiator[^"]*"[^>]+data-module=',
)
def test_each_radiator_has_a_visible_text_label(self) -> None:
radiator_groups = re.findall(
r'<g class="radiator"[^>]*>(.*?)</g>',
self.html,
flags=re.S,
)
self.assertEqual(len(radiator_groups), 4)
for group in radiator_groups:
self.assertIn(">散热器</text>", group)
rect = re.search(r'<rect x="(\d+)" y="(\d+)" width="(\d+)"', group)
label = re.search(r'<text[^>]+x="(\d+)" y="(\d+)"[^>]*>散热器</text>', group)
self.assertIsNotNone(rect)
self.assertIsNotNone(label)
rect_x, rect_y, rect_width = map(int, rect.groups())
label_x, label_y = map(int, label.groups())
self.assertGreaterEqual(label_x, rect_x)
self.assertLessEqual(label_x, rect_x + rect_width)
self.assertLess(label_y, rect_y)
def test_future_extensions_exist_only_as_source_comments(self) -> None:
comments = "\n".join(re.findall(r"<!--(.*?)-->", self.html, flags=re.S))
visible_source = re.sub(r"<!--.*?-->", "", self.html, flags=re.S)
for direction in ("north", "east", "west"):
self.assertIn(f"FUTURE_EXPANSION_ANCHOR:{direction}", comments)
self.assertNotIn("未来北部十字结构", visible_source)
self.assertNotIn("未来东部十字结构", visible_source)
self.assertNotIn("未来西部十字结构", visible_source)
def test_every_clickable_module_has_dimensions_field(self) -> None:
for module_id in CLICKABLE_MODULES:
match = re.search(
rf'"{re.escape(module_id)}"\s*:\s*\{{(.*?)\n \}}(?:,|\n)',
self.html,
flags=re.S,
)
self.assertIsNotNone(match, msg=f"{module_id} detail object is missing")
self.assertIn("dimensions:", match.group(1), msg=f"{module_id} is missing dimensions")
def test_timeline_contains_only_the_two_real_corridors(self) -> None:
for incorrect in (
"4段加压走道",
"居住舱#1↔居住舱#2",
"居住舱#2↔实验舱",
"实验舱↔温室",
"温室↔居住舱#1",
"环形加压通路",
"8个对接面",
"4段加压走道48小时",
):
self.assertNotIn(incorrect, self.ghc05)
self.assertIn("居住舱#1远端对接口", self.ghc05)
self.assertIn("电力模块北部", self.ghc05)
self.assertIn("温室远端对接口", self.ghc05)
self.assertIn("吴刚#2", self.ghc05)
self.assertIn("从温室模块远端接口断开", self.ghc05)
if __name__ == "__main__":
unittest.main()