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
+225
View File
@@ -0,0 +1,225 @@
package jscriptx
// 这是**内部测试**package jscriptx,不是 jscriptx_test):它要直接断言
// vmHandle.scoped 这个未导出字段。行为测试盖不住那条——borrowStatic 那条路
// 靠 finish 永远丢弃兜底,漏标 scoped 从外面看不出来,标志位本身才是真相。
import (
"context"
"strings"
"sync"
"testing"
)
// recorder 是个带状态的扩展,用来验证作用域之间有没有串。
type recorder struct {
mu sync.Mutex
tag string
seen []string
}
func (*recorder) Name() string { return "rec" }
func (*recorder) Module() (string, string) { return "", "" }
func (r *recorder) Bindings() map[string]any {
return map[string]any{
"tag": func() string { return r.tag },
"note": func(m string) {
r.mu.Lock()
defer r.mu.Unlock()
r.seen = append(r.seen, m)
},
}
}
func scoped(t *testing.T, r *recorder) context.Context {
t.Helper()
return WithScope(context.Background(), ScopeExtensions(r))
}
// Script.Call 走的是共享池,以前不注入作用域扩展,脚本顶层 import 直接
// ReferenceError。现在带作用域就单造一个 VM。
func TestScopePool_池化调用也能拿到扩展(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, err := e.Compile("s.ts", `
rec.note("顶层:" + rec.tag())
export default function () { return rec.tag() }`)
if err != nil {
t.Fatal(err)
}
r := &recorder{tag: "A"}
got, err := s.Call(scoped(t, r), "")
if err != nil {
t.Fatalf("带作用域的池化调用该能用扩展: %v", err)
}
if got != "A" {
t.Errorf("拿到 %#v", got)
}
if len(r.seen) != 1 || r.seen[0] != "顶层:A" {
t.Errorf("模块顶层没跑或没拿到扩展: %v", r.seen)
}
}
// 关键:带作用域的 VM 用完必须丢,不能回池——回池就把上一个作用域的扩展
// 漏给下一个调用了。
func TestScopePool_作用域之间不串(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, _ := e.Compile("s.ts", `export default function () { return rec.tag() }`)
for _, want := range []string{"A", "B", "C"} {
r := &recorder{tag: want}
got, err := s.Call(scoped(t, r), "")
if err != nil {
t.Fatal(err)
}
if got != want {
t.Fatalf("拿到的是别的作用域的扩展: 想要 %q,拿到 %#v", want, got)
}
}
}
// 不带作用域的调用照旧走池,行为不变。
func TestScopePool_无作用域仍然走池(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, _ := e.Compile("s.ts", `export default function () { return 1 }`)
ctx := context.Background()
for range 5 {
if _, err := s.Call(ctx, ""); err != nil {
t.Fatal(err)
}
}
// 第一次建一个,后面四次都该命中池
if st := s.Stats(); st.Dropped != 0 {
t.Errorf("不带作用域不该丢 VMDropped = %d", st.Dropped)
}
}
// 带作用域的 VM 每次都丢,不占池子。
func TestScopePool_带作用域的VM不回池(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, _ := e.Compile("s.ts", `export default function () { return rec.tag() }`)
before := s.Stats().Dropped
for range 3 {
if _, err := s.Call(scoped(t, &recorder{tag: "X"}), ""); err != nil {
t.Fatal(err)
}
}
if got := s.Stats().Dropped - before; got != 3 {
t.Errorf("三次带作用域的调用该丢三个 VM,实际丢了 %d", got)
}
}
// 没有扩展、只有 key 的作用域也不能走池:脚本能读到 scope.key,
// 池化 VM 会把上一个作用域的 key 漏给下一个调用。
func TestScopePool_只有key的作用域也不走池(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, _ := e.Compile("s.ts", `export default function () { return scope.key }`)
for _, want := range []string{"k1", "k2", "k3"} {
ctx := WithScope(context.Background(), ScopeKey(want))
got, err := s.Call(ctx, "")
if err != nil {
t.Fatal(err)
}
if got != want {
t.Fatalf("拿到的是别的作用域的 key: 想要 %q,拿到 %#v", want, got)
}
}
}
// 并发下各作用域看到的必须是自己那份。
func TestScopePool_并发不串(t *testing.T) {
e, _ := New(WithLogger(nil))
defer e.Close()
s, _ := e.Compile("s.ts", `export default function () { return rec.tag() }`)
var wg sync.WaitGroup
errs := make(chan string, 64)
for i := range 32 {
wg.Add(1)
go func() {
defer wg.Done()
want := string(rune('a' + i%26))
got, err := s.Call(scoped(t, &recorder{tag: want}), "")
if err != nil {
errs <- err.Error()
return
}
if got != want {
errs <- "串了: 想要 " + want + ",拿到 " + got.(string)
}
}()
}
wg.Wait()
close(errs)
var bad []string
for e := range errs {
bad = append(bad, e)
}
if len(bad) > 0 {
t.Errorf("%d 次出错:\n%s", len(bad), strings.Join(bad, "\n"))
}
}
// 取 VM 的每一条路径,带作用域时都必须把 scoped 标上。
//
// scoped 决定 release 会不会把 VM 放回池子。漏标的后果是**跨调用泄漏作用域扩展**:
// 下一个不带作用域的调用会看见上一个作用域的 store。
//
// 这条用内部测试直接断言标志位,而不是测「有没有漏」——borrowStatic 那条路今天
// 靠 staticTarget.finish 永远丢弃兜底,行为上看不出漏标,等于同一个约束靠两套
// 机制守。标志位本身才是唯一的真相。
func TestVM_每条取VM的路径都标了scoped(t *testing.T) {
e, err := New(WithLogger(nil))
if err != nil {
t.Fatal(err)
}
defer e.Close()
s, err := e.Compile("c.ts", `export default class C { static S() { return 1 } Run() { return 1 } }`)
if err != nil {
t.Fatal(err)
}
scoped := WithScope(context.Background(), ScopeKey("k"))
plain := context.Background()
for _, c := range []struct {
name string
get func(ctx context.Context) (*vmHandle, error)
}{
{"borrow(池化路径)", s.borrow},
{"borrowStatic(静态路径)", s.borrowStatic},
} {
vm, err := c.get(scoped)
if err != nil {
t.Fatalf("%s: %v", c.name, err)
}
if !vm.scoped {
t.Errorf("%s 在有作用域时没标 scoped——这个 VM 会被放回池子,"+
"把作用域的扩展漏给下一个调用", c.name)
}
vm, err = c.get(plain)
if err != nil {
t.Fatalf("%s(无作用域): %v", c.name, err)
}
if vm.scoped {
t.Errorf("%s 没有作用域却标了 scoped——白白丢弃可复用的 VM", c.name)
}
}
}