Files
opendance-windows/tests/test_about.gd
T
zijunchiang 5581cbe05d feat(scene): 场景选择与物理落地 + 整包发布(T-WIN-012/013/014)
T-WIN-013:API 升级与场景选择
- 契约快照同步到 rest-api 0.5.0 / data-models 0.7.0 / database 0.4.0
- catalog_types.gd 新增 SceneConfig(物理/环境/出生点/相机)与 Scene;
  CatalogBundle 增加 scenes[];from_dict 按契约 7.1 再收敛一次
  (缺项默认、越界夹紧、非法枚举回退),与服务端 normalizeSceneConfig 同规则
- 新增 src/scene/scene_runtime.gd:GLB 载入 -> 碰撞体(mesh 三角网 /
  auto 命名约定 *_col、*_colonly / none 不兜底)-> 场景级重力(Area3D
  SPACE_OVERRIDE_REPLACE,不改全局 ProjectSettings)-> 风(每物理帧
  apply_central_force,阵风正弦叠加,支持 local 坐标系)-> 环境光与雾
  -> 出生点与相机(follow/fixed/orbit);无碰撞体时兜底地面
- CatalogStore 增加 scenes/default_scene/find_scene/ensure_scene_cached/
  fetch_thumbnail(场景缓存带 sha256 指纹,避免换包后仍用旧 GLB)
- 主界面改为 2x2 四个选择器(曲目/动作/模型/场景)+ 场景参数面板
  (缩略图 + 重力/摩擦/弹性/碰撞体/风摘要)
- 游戏界面载入场景并落地物理,角色落在出生点;场景缺失/下载失败/
  解析失败一律回退内置空场景并提示,不阻塞开始跟练

T-WIN-012:「关于」清单展示当前版本号
- 版本唯一来源为 project.godot 的 application/config/version(v0.2.0)
- about_manifest() 输出版本/引擎/契约/协议/地址/隐私清单;
  无头测试无 autoload,读取 AppConfig 走 get_node_or_null 容错
- Credits 的单个 CONTRACT_VERSION 常量改为 CONTRACT_VERSIONS 字典
  (六项契约各自独立递增),并双向核对 contracts/VERSION

T-WIN-014:发布必须是单个完整包
- build/package-release.sh 重写:只产出单个 zip + SHA256SUMS,
  清理历史 .partN,超限时告警而非回退分卷;新增源产物与解压后完整性
  自检(PE 头 MZ / pck 魔数 GDPC / exe+pck 同目录同名)与体积-上限对比
- deploy/proxy 相关的 Gitea IIS 256MB 上限说明见 opendance-web

测试:tests/test_scene_config.gd(12 例)+ tests/test_about.gd(4 例);
无头测试 687 通过 / 0 失败(10 个套件),verify.sh 全部通过
2026-09-17 00:45:23 +08:00

65 lines
2.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends TestCase
## T-WIN-012:版本号展示(文档 §11)
##
## 硬性要求是「单一来源 + 可核对」:
## 版本号只允许有一处权威定义(project.godot 的 application/config/version),
## 界面从该来源读取,且必须与 project.yaml / 产物文件名 / Git tag 一致。
## 因此这里同时核对 project.godot、project.yaml、README 三处。
const AboutScreen = preload("res://src/ui/about_screen.gd")
const Credits = preload("res://src/ui/credits.gd")
func _read_text(path: String) -> String:
if not FileAccess.file_exists(path):
return ""
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
return ""
var text := file.get_as_text()
file.close()
return text
func test_version_is_semver_and_matches_project_yaml() -> void:
var version := AboutScreen.app_version()
var parts := version.split(".")
eq(parts.size(), 3, "版本号形如 X.Y.Z(实际 %s)" % version)
for part in parts:
ok(part.is_valid_int(), "版本号各段为整数(%s)" % part)
var yaml := _read_text("res://project.yaml")
ne(yaml, "", "可读取 project.yaml")
contains(yaml, "version: %s" % version, "project.yaml 的 version 与 project.godot 一致")
func test_about_manifest_lists_version() -> void:
var manifest := AboutScreen.about_manifest()
var found := {}
for item in manifest:
found[str(item["label"])] = str(item["value"])
ok(found.has("客户端版本"), "关于清单含「客户端版本」一项")
eq(found.get("客户端版本", ""), "v%s" % AboutScreen.app_version(), "清单版本号与唯一来源一致")
ok(found.has("契约版本"), "关于清单含「契约版本」")
for key in Credits.CONTRACT_VERSIONS:
contains(str(found.get("契约版本", "")), "%s v%s" % [key, Credits.CONTRACT_VERSIONS[key]], "清单含 %s 契约版本" % key)
ok(str(found.get("隐私说明", "")).contains("不存储"), "关于清单声明不存储视频画面")
func test_version_not_hardcoded_in_ui_sources() -> void:
# 反例防护:界面代码里不允许再手写一份版本号(文档 §11.2 第 1 条)
var version := AboutScreen.app_version()
for path in ["res://src/ui/about_screen.gd", "res://src/ui/main_menu.gd", "res://src/ui/game_screen.gd"]:
var text := _read_text(path)
ne(text, "", "可读取 %s" % path)
is_false(text.contains("\"%s\"" % version), "%s 未硬编码版本号字面量" % path.get_file())
func test_entry_scene_version_is_exported_into_build() -> void:
# 导出产物名必须能由版本号推导(Documentation §11.2 第 2 条)
var version := AboutScreen.app_version()
contains("opendance-windows-v%s.zip" % version, version, "产物文件名包含版本号")
ok(Credits.entry_count() >= 10, "开源声明条目仍齐备")