139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
|
|
package mods
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"os"
|
|||
|
|
"path/filepath"
|
|||
|
|
"testing"
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// TestLuaRegister 脚本注册方块/物品/配方与事件(Mod开发指南.md §4)。
|
|||
|
|
func TestLuaRegister(t *testing.T) {
|
|||
|
|
reg := NewRegistry()
|
|||
|
|
m := NewLuaMod("test_mod", reg)
|
|||
|
|
script := `
|
|||
|
|
api.register_block{ id = "ruby_ore", hardness = 3.0, light = 0 }
|
|||
|
|
api.register_item{ id = "ruby", stack = 64 }
|
|||
|
|
api.register_recipe{ type = "crafting_shaped", pattern = {"RRR","R R","RRR"}, result = {item = "ruby_block"} }
|
|||
|
|
`
|
|||
|
|
if err := m.Load(script); err != nil {
|
|||
|
|
t.Fatalf("加载失败: %v", err)
|
|||
|
|
}
|
|||
|
|
entries := reg.Entries()
|
|||
|
|
if len(entries) != 3 {
|
|||
|
|
t.Fatalf("注册条目期望 3,实际 %d", len(entries))
|
|||
|
|
}
|
|||
|
|
if reg.Gen("test_mod") != 1 {
|
|||
|
|
t.Fatalf("代数期望 1,实际 %d", reg.Gen("test_mod"))
|
|||
|
|
}
|
|||
|
|
// 方块数据完整性
|
|||
|
|
found := false
|
|||
|
|
for _, e := range entries {
|
|||
|
|
if e.Key == "block:ruby_ore" {
|
|||
|
|
found = true
|
|||
|
|
if d, ok := e.Data.(map[string]any); !ok || d["hardness"] != 3.0 {
|
|||
|
|
t.Fatalf("方块数据异常: %+v", e.Data)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if !found {
|
|||
|
|
t.Fatal("缺少 block:ruby_ore 注册")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestHotReload 热重载:新脚本替换旧注册(generation 机制,热更新.md §4.1)。
|
|||
|
|
func TestHotReload(t *testing.T) {
|
|||
|
|
reg := NewRegistry()
|
|||
|
|
m := NewLuaMod("hot_mod", reg)
|
|||
|
|
if err := m.Load(`api.register_block{ id = "old_block" }`); err != nil {
|
|||
|
|
t.Fatalf("首次加载失败: %v", err)
|
|||
|
|
}
|
|||
|
|
// 热重载:注册新方块
|
|||
|
|
if err := m.Load(`api.register_block{ id = "new_block" }`); err != nil {
|
|||
|
|
t.Fatalf("重载失败: %v", err)
|
|||
|
|
}
|
|||
|
|
if reg.Gen("hot_mod") != 2 {
|
|||
|
|
t.Fatalf("重载后代数期望 2,实际 %d", reg.Gen("hot_mod"))
|
|||
|
|
}
|
|||
|
|
for _, e := range reg.Entries() {
|
|||
|
|
if e.Key == "block:old_block" {
|
|||
|
|
t.Fatal("旧代注册未回收")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestReloadRollback 坏脚本回滚:保留旧版本(热更新.md §4.2)。
|
|||
|
|
func TestReloadRollback(t *testing.T) {
|
|||
|
|
reg := NewRegistry()
|
|||
|
|
m := NewLuaMod("rollback_mod", reg)
|
|||
|
|
if err := m.Load(`api.register_block{ id = "good_block" }`); err != nil {
|
|||
|
|
t.Fatalf("首次加载失败: %v", err)
|
|||
|
|
}
|
|||
|
|
// 语法错误脚本:重载失败,旧版保留
|
|||
|
|
if err := m.Load(`api.register_block{ id = `); err == nil {
|
|||
|
|
t.Fatal("坏脚本应报错")
|
|||
|
|
}
|
|||
|
|
if reg.Gen("rollback_mod") != 1 {
|
|||
|
|
t.Fatalf("回滚后代数应保持 1,实际 %d", reg.Gen("rollback_mod"))
|
|||
|
|
}
|
|||
|
|
found := false
|
|||
|
|
for _, e := range reg.Entries() {
|
|||
|
|
if e.Key == "block:good_block" {
|
|||
|
|
found = true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if !found {
|
|||
|
|
t.Fatal("回滚后旧注册丢失")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestSandbox 沙箱:os/io 库不可用(Mod开发指南.md §6)。
|
|||
|
|
func TestSandbox(t *testing.T) {
|
|||
|
|
reg := NewRegistry()
|
|||
|
|
m := NewLuaMod("sandbox_mod", reg)
|
|||
|
|
if err := m.Load(`os.exit(0)`); err == nil {
|
|||
|
|
t.Fatal("沙箱应禁止 os 库")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TestWatcher 文件监听热重载(热更新.md §4.5 防抖)。
|
|||
|
|
func TestWatcher(t *testing.T) {
|
|||
|
|
dir := t.TempDir()
|
|||
|
|
path := filepath.Join(dir, "init.lua")
|
|||
|
|
if err := os.WriteFile(path, []byte(`api.register_block{ id = "v1" }`), 0o644); err != nil {
|
|||
|
|
t.Fatalf("写脚本失败: %v", err)
|
|||
|
|
}
|
|||
|
|
reg := NewRegistry()
|
|||
|
|
m := NewLuaMod("watched_mod", reg)
|
|||
|
|
reload := func() error {
|
|||
|
|
b, err := os.ReadFile(path)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return m.Load(string(b))
|
|||
|
|
}
|
|||
|
|
w, err := NewWatcher()
|
|||
|
|
if err != nil {
|
|||
|
|
t.Fatalf("创建监听器失败: %v", err)
|
|||
|
|
}
|
|||
|
|
defer w.Close()
|
|||
|
|
if err := w.WatchFile(path, reload); err != nil {
|
|||
|
|
t.Fatalf("监听失败: %v", err)
|
|||
|
|
}
|
|||
|
|
go w.Run()
|
|||
|
|
if err := reload(); err != nil {
|
|||
|
|
t.Fatalf("初始加载失败: %v", err)
|
|||
|
|
}
|
|||
|
|
// 修改文件 → 防抖后自动重载
|
|||
|
|
if err := os.WriteFile(path, []byte(`api.register_block{ id = "v2" }`), 0o644); err != nil {
|
|||
|
|
t.Fatalf("写脚本失败: %v", err)
|
|||
|
|
}
|
|||
|
|
deadline := time.Now().Add(5 * time.Second)
|
|||
|
|
for reg.Gen("watched_mod") != 2 && time.Now().Before(deadline) {
|
|||
|
|
time.Sleep(50 * time.Millisecond)
|
|||
|
|
}
|
|||
|
|
if reg.Gen("watched_mod") != 2 {
|
|||
|
|
t.Fatalf("文件监听未触发重载(代数 %d)", reg.Gen("watched_mod"))
|
|||
|
|
}
|
|||
|
|
}
|