80 lines
3.4 KiB
GDScript
80 lines
3.4 KiB
GDScript
# tests/unit/data_config/rng_service_test.gd — 统一随机流单元测试:单盐派生确定性/计数器账本/标签词表封闭
|
||
# 运行:godot --headless --path game --script res://tests/unit/data_config/rng_service_test.gd
|
||
# 红线:零自建 RNG——本测试只经 RngService 统一流取数(确定性契约:同种子逐位复现)。
|
||
# 测试种子为夹具基建值(非玩法数值),与冒烟黄金向量锚(smoke_all m=42 scope 0-4)分工:本文件测
|
||
# 「同种子两实例逐位一致」的相对确定性,绝对向量锚归冒烟单点,避免双处维护同一常量。
|
||
extends SceneTree
|
||
|
||
const RngServiceScript := preload("res://core/rng_service.gd")
|
||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点
|
||
|
||
const TEST_SEED := 20260928 # 夹具种子(任意固定值;语义 = 可复现性,非玩法数值)
|
||
|
||
|
||
func _init() -> void:
|
||
_test_derive_determinism()
|
||
_test_counter_ledger()
|
||
_test_stream_draw_determinism()
|
||
_test_negative_seed_normalization()
|
||
quit(Kit.finish("rng_service_test"))
|
||
|
||
|
||
func _fresh_service():
|
||
var rng = RngServiceScript.new()
|
||
rng.set_master_seed(TEST_SEED)
|
||
return rng
|
||
|
||
|
||
# 单盐派生纯函数:两全新实例同种子逐位一致(derive_at 不推计数器——纯函数契约)
|
||
func _test_derive_determinism() -> void:
|
||
var a = _fresh_service()
|
||
var b = _fresh_service()
|
||
var same := true
|
||
for scope in range(8):
|
||
if a.derive_at(scope) != b.derive_at(scope):
|
||
same = false
|
||
Kit.check("同种子两实例 derive_at(0..7) 逐位一致", same)
|
||
var a2 = _fresh_service()
|
||
var before: int = a2.counter()
|
||
a2.derive_at(0)
|
||
Kit.check("derive_at 为纯函数不推计数器", a2.counter() == before)
|
||
|
||
|
||
# 全局取数计数器:唯一账本——open_stream 逐次 +1(scope 单调)
|
||
func _test_counter_ledger() -> void:
|
||
var rng = _fresh_service()
|
||
var c0: int = rng.counter()
|
||
rng.open_stream("armor_roll")
|
||
var c1: int = rng.counter()
|
||
rng.open_stream("armor_roll")
|
||
var c2: int = rng.counter()
|
||
Kit.check("open_stream 逐次推进全局计数器(+1/次)", c1 == c0 + 1 and c2 == c0 + 2)
|
||
# 用途标签只进事件日志不参与派生——同 scope 同派生种子(统一流口径)
|
||
var rng_x = _fresh_service()
|
||
var rng_y = _fresh_service()
|
||
var s1: int = int(rng_x.open_stream("board_gen").seed)
|
||
var s2: int = int(rng_y.open_stream("reward_roll").seed)
|
||
Kit.check("同 scope 异用途标签派生种子一致(标签不参与派生)", s1 == s2)
|
||
|
||
|
||
# 同种子两实例同标签流的实际取数序列逐位一致(回放确定性契约——存档快照=主种子+计数器的前提)
|
||
func _test_stream_draw_determinism() -> void:
|
||
var faces := 20 # d20 探针面数(夹具值,非玩法表值——只用于产生可比较序列)
|
||
var a = _fresh_service()
|
||
var b = _fresh_service()
|
||
var same := true
|
||
for i in range(12):
|
||
var ra: int = a.open_stream("armor_roll").randi_range(1, faces)
|
||
var rb: int = b.open_stream("armor_roll").randi_range(1, faces)
|
||
if ra != rb:
|
||
same = false
|
||
Kit.check("同种子两实例同标签流取数 12 次逐位一致", same)
|
||
|
||
|
||
# 负输入 abs 归一化(警告通道)——|−s| 与 s 同派生
|
||
func _test_negative_seed_normalization() -> void:
|
||
var pos = _fresh_service()
|
||
var neg = RngServiceScript.new()
|
||
neg.set_master_seed(-TEST_SEED)
|
||
Kit.check("负种子 abs 归一化后派生与正种子一致", pos.derive_at(0) == neg.derive_at(0))
|