test: 测试文件跟源文件同名,删掉复制粘贴出来的重复

改完之后「某个行为在哪测」就是查同名文件:

  lazyglobal_test.go      → engine_globals_test.go
  static_test.go          → script_static_test.go
  scope_pool_test.go      → script_vm_test.go     它测的是 VM 池,不是作用域
  esmwrap_test.go         → bundle_finalize_test.go
  missing_global_test.go  → errors_hints_test.go
  entry_test.go           → bundle_entry_test.go  并收下 bundle_test.go 里
                                                  那三个 TestEntry_*

删掉 5 组复制粘贴。其中 TestNew_同实例调用串行 和 TestInstance_调用串行 **函数体
完全一致**,只差 mustCompile 的文件名参数 counter.js / counter.ts。Instance 的
测试全部归 instance_test.go 并统一前缀成 TestInstance_,scope_test.go 只留作用域。

vmstate_test.go + govalue_test.go → goja_facts_test.go。这两个测的都是 **goja
本身**,不是这个库:Program 能不能给多个 Runtime 共用、顶层变量跟不跟 Runtime 走、
Go 的切片进到脚本里是不是真数组。它们是「为什么这个库要这么设计」的实验记录,
goja 行为变了会先红。原来那个名字看不出这层定位。

helper 集中到 helper_test.go:newEngine / mustCompile 被十几个文件依赖,原来住在
script_test.go 里,那个名字暗示「测 Script」,找不到。

caller_example_test.go 并进 caller_test.go——同一个类型没必要两个文件。

overlay_test.go 和 script_vm_test.go 保持内部测试,各写了一句为什么:前者要读
Engine.loader 和 isPrepared,后者要直接断言 vmHandle.scoped。我试过把 overlay
挪成外部测试,挪不动。
This commit is contained in:
2026-09-10 15:39:25 +08:00
parent 7e1893b246
commit 7c9a70712e
16 changed files with 427 additions and 572 deletions
+129 -110
View File
@@ -3,12 +3,14 @@ package jscriptx_test
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"testing"
"time"
"git.fsdpf.net/go/jscriptx"
"git.fsdpf.net/go/jscriptx/internal/testext"
)
const objScript = `
@@ -24,79 +26,8 @@ export default class DeviceHandler {
}
`
// 最基本的:new 出对象,当普通 Go 对象用。
func TestNew_当对象用(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.js", objScript)
obj, err := s.New(context.Background(), "dev-1", "温控器")
if err != nil {
t.Fatalf("New 失败: %v", err)
}
defer obj.Close()
ctx := context.Background()
got, err := obj.Call(ctx, "info")
if err != nil {
t.Fatal(err)
}
if got != "dev-1/温控器" {
t.Errorf("info = %#v", got)
}
for i := 1; i <= 3; i++ {
got, err := obj.Call(ctx, "onMessage", "p")
if err != nil {
t.Fatal(err)
}
if got.(int64) != int64(i) {
t.Errorf("第 %d 次 = %#v", i, got)
}
}
if obj.Calls() != 4 {
t.Errorf("Calls = %d, want 4", obj.Calls())
}
if obj.Name() != "device.js" {
t.Errorf("Name = %q", obj.Name())
}
}
// 多个实例互相独立。
func TestNew_实例互相独立(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.js", objScript)
ctx := context.Background()
a, err := s.New(ctx, "dev-A", "型号A")
if err != nil {
t.Fatal(err)
}
defer a.Close()
b, err := s.New(ctx, "dev-B", "型号B")
if err != nil {
t.Fatal(err)
}
defer b.Close()
a.Call(ctx, "onMessage", "x")
a.Call(ctx, "onMessage", "x")
got, err := b.Call(ctx, "onMessage", "x")
if err != nil {
t.Fatal(err)
}
if got.(int64) != 1 {
t.Errorf("B 被 A 污染: %#v", got)
}
if got, _ := a.Call(ctx, "info"); got != "dev-A/型号A" {
t.Errorf("A 的构造参数不对: %#v", got)
}
if got, _ := b.Call(ctx, "info"); got != "dev-B/型号B" {
t.Errorf("B 的构造参数不对: %#v", got)
}
}
// constructor 抛异常要在 New 就报出来,不用等到第一次调用。
func TestNew_构造失败当场报错(t *testing.T) {
func TestInstance_构造失败当场报错(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "bad.js", `
export default class H {
@@ -121,7 +52,7 @@ func TestNew_构造失败当场报错(t *testing.T) {
t.Logf("%v", err)
}
func TestNew_Close后拒绝调用(t *testing.T) {
func TestInstance_Close后拒绝调用(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.js", objScript)
ctx := context.Background()
@@ -137,10 +68,69 @@ func TestNew_Close后拒绝调用(t *testing.T) {
obj.Close() // 重复关闭不该 panic
}
// 同一个实例被并发调用时必须串行。
func TestNew_同实例调用串行(t *testing.T) {
func TestInstance_Has(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "counter.js", `
s := mustCompile(t, e, "device.js", objScript)
obj, _ := s.New(context.Background(), "d", "m")
defer obj.Close()
if !obj.Has("onMessage") || !obj.Has("info") {
t.Error("Has 认不出实例方法")
}
if obj.Has("nope") {
t.Error("Has 不该认出不存在的方法")
}
}
// 实例独占 VM,this.xxx 在它活着期间跨调用保持。
func TestInstance_状态跨调用保持(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.ts", deviceScript)
ctx := context.Background()
obj, err := s.New(ctx, "device-A", "温控器")
if err != nil {
t.Fatal(err)
}
defer obj.Close()
for i, want := range []string{"device-A#1:m1", "device-A#2:m2", "device-A#3:m3"} {
got, err := obj.Call(ctx, "onMessage", fmt.Sprintf("m%d", i+1))
if err != nil {
t.Fatalf("第 %d 条失败: %v", i+1, err)
}
if got != want {
t.Errorf("第 %d 条 = %#v, want %q", i+1, got, want)
}
}
}
// 不同实例互不相干,这是并发的基础。
func TestInstance_互相隔离(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.ts", deviceScript)
ctx := context.Background()
a, _ := s.New(ctx, "device-A", "型号A")
defer a.Close()
b, _ := s.New(ctx, "device-B", "型号B")
defer b.Close()
a.Call(ctx, "onMessage", "x")
a.Call(ctx, "onMessage", "x")
got, err := b.Call(ctx, "onMessage", "x")
if err != nil {
t.Fatal(err)
}
if got != "device-B#1:x" {
t.Errorf("B 被 A 污染: %#v", got)
}
}
// 同一个实例的调用串行,保护 this.xxx 不错乱。
func TestInstance_调用串行(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "counter.ts", `
export default class C {
constructor() { this.n = 0 }
bump() { this.n++; return this.n }
@@ -176,16 +166,62 @@ func TestNew_同实例调用串行(t *testing.T) {
}
}
// 脚本出错会重建实例,状态归零,Resets 能查到
func TestNew_出错后重建(t *testing.T) {
e := newEngine(t, jscriptx.WithTimeout(80*time.Millisecond))
s := mustCompile(t, e, "device.js", objScript)
ctx := context.Background()
// 多个实例并发跑,各自的状态正确
func TestInstance_多实例并发(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.ts", deviceScript)
obj, _ := s.New(ctx, "dev-1", "温控器")
const devices, messages = 16, 30
var wg sync.WaitGroup
for d := 0; d < devices; d++ {
id := fmt.Sprintf("device-%d", d)
wg.Add(1)
go func(id string) {
defer wg.Done()
obj, err := s.New(context.Background(), id, "型号")
if err != nil {
t.Errorf("%v", err)
return
}
defer obj.Close()
for m := 0; m < messages; m++ {
got, err := obj.Call(context.Background(), "onMessage", "m")
if err != nil {
t.Errorf("%s: %v", id, err)
return
}
want := fmt.Sprintf("%s#%d:m", id, m+1)
if got != want {
t.Errorf("%s 第 %d 条 = %#v, want %q", id, m+1, got, want)
return
}
}
}(id)
}
wg.Wait()
}
// 脚本出错会丢 this 上的状态,但扩展里的东西还在。
func TestInstance_出错后状态归零(t *testing.T) {
e := newEngine(t, jscriptx.WithTimeout(80*time.Millisecond))
s := mustCompile(t, e, "mix.ts", `
export default class M {
constructor() { this.n = 0 }
bump() { store.Incr("kept", 1); return ++this.n }
spin() { while (true) {} }
}
`)
st := testext.New()
ctx := jscriptx.WithScope(context.Background(), jscriptx.ScopeExtensions(st))
obj, err := s.New(ctx)
if err != nil {
t.Fatal(err)
}
defer obj.Close()
obj.Call(ctx, "onMessage", "x")
obj.Call(ctx, "onMessage", "x")
obj.Call(ctx, "bump")
obj.Call(ctx, "bump")
if _, err := obj.Call(ctx, "spin"); !errors.Is(err, jscriptx.ErrTimeout) {
t.Fatalf("want ErrTimeout, got %v", err)
@@ -194,44 +230,27 @@ func TestNew_出错后重建(t *testing.T) {
t.Errorf("Resets = %d, want 1", obj.Resets())
}
got, err := obj.Call(ctx, "onMessage", "x")
got, err := obj.Call(ctx, "bump")
if err != nil {
t.Fatal(err)
}
if got.(int64) != 1 {
t.Errorf("重建后状态应该归零, got %#v", got)
t.Errorf("this 上的状态应该归零, got %#v", got)
}
// 构造参数会重新传一遍,所以 info 仍然正确
if got, _ := obj.Call(ctx, "info"); got != "dev-1/温控器" {
t.Errorf("重建后构造参数丢了: %#v", got)
// 扩展在 Go 侧,不受 VM 重建影响
if n := toNumber(t, st.Get("kept")); n != 3 {
t.Errorf("扩展里的状态不该丢, got %v want 3", n)
}
}
func TestNew_Has(t *testing.T) {
// 脚本关闭后实例失效。
func TestInstance_脚本关闭后失效(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.js", objScript)
obj, _ := s.New(context.Background(), "d", "m")
defer obj.Close()
if !obj.Has("onMessage") || !obj.Has("info") {
t.Error("Has 认不出实例方法")
}
if obj.Has("nope") {
t.Error("Has 不该认出不存在的方法")
}
}
// 脚本关闭后,已经 new 出来的实例也不能再用。
func TestNew_脚本关闭后失效(t *testing.T) {
e := newEngine(t)
s := mustCompile(t, e, "device.js", objScript)
s := mustCompile(t, e, "device.ts", deviceScript)
obj, _ := s.New(context.Background(), "d", "m")
s.Close()
if _, err := obj.Call(context.Background(), "info"); !errors.Is(err, jscriptx.ErrClosed) {
if _, err := obj.Call(context.Background(), "onMessage", "x"); !errors.Is(err, jscriptx.ErrClosed) {
t.Errorf("want ErrClosed, got %v", err)
}
if _, err := s.New(context.Background(), "d", "m"); !errors.Is(err, jscriptx.ErrClosed) {
t.Errorf("关闭的脚本不该能 New, got %v", err)
}
}