PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
章节目录
源码索引 / CURRENT IMPLEMENTATION

prepared_test.go

147 行 · 构建时读取的实际文件,不是讲解用伪代码。

package spine

import (
	"context"
	"encoding/json"
	"os"
	"strings"
	"testing"
	"time"
)

func TestPreparedGuest(t *testing.T) {
	wasm, err := os.ReadFile("dist/spine.wasm")
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	calls := make(chan string, 32)
	var second, prefixStarted chan struct{}
	read := func(ctx context.Context, raw json.RawMessage) (any, error) {
		var a struct {
			Key string `json:"key"`
		}
		if err := json.Unmarshal(raw, &a); err != nil {
			return nil, err
		}
		calls <- a.Key
		switch a.Key {
		case "first":
			select {
			case <-second:
				return 21, nil
			case <-ctx.Done():
				return nil, ctx.Err()
			}
		case "second":
			close(second)
			return 5, nil
		case "prefix":
			close(prefixStarted)
			return 21, nil
		default:
			return 21, nil
		}
	}
	r, err := NewPrepared(ctx, wasm, map[string]Tool{"lookup": read}, "lookup")
	if err != nil {
		t.Fatal(err)
	}
	defer r.Close(context.Background())
	if len(r.image) == 0 {
		t.Fatal("missing actual initialized image")
	}
	if len(calls) != 0 {
		t.Fatal("capture called a Host tool")
	}
	expect := func(t *testing.T, out Output, err error, want string) {
		t.Helper()
		if err != nil || string(out.Value) != want {
			t.Fatalf("%+v %v; want %s", out, err, want)
		}
	}

	t.Run("ordinary tool", func(t *testing.T) {
		out, err := r.Run(ctx, `result=tool("lookup",key="book")`, nil)
		expect(t, out, err, "21")
	})
	t.Run("private Python state", func(t *testing.T) {
		out, err := r.Run(ctx, `import builtins; builtins.secret=123; result=builtins.secret`, nil)
		expect(t, out, err, "123")
		out, err = r.Run(ctx, `import builtins; result=hasattr(builtins,"secret")`, nil)
		expect(t, out, err, "false")
	})
	t.Run("two live memories and image are independent", func(t *testing.T) {
		a, err := r.newGuest(ctx, &boundedText{}, &boundedText{})
		if err != nil {
			t.Fatal(err)
		}
		defer a.Close(context.Background())
		b, err := r.newGuest(ctx, &boundedText{}, &boundedText{})
		if err != nil {
			t.Fatal(err)
		}
		defer b.Close(context.Background())
		// Last byte is only a memory-isolation probe. These two instances execute no code afterward.
		offset := uint32(len(r.image) - 1)
		before := r.image[offset]
		if !a.Memory().WriteByte(offset, before^255) {
			t.Fatal("write")
		}
		value, ok := b.Memory().ReadByte(offset)
		if !ok || value != before || r.image[offset] != before {
			t.Fatal("shared writable storage")
		}
	})
	t.Run("PLM still overlaps", func(t *testing.T) {
		second = make(chan struct{})
		out, err := r.RunPLM(ctx, "a=tool('lookup',key='first')\nb=tool('lookup',key='second')\nresult=a+b", nil)
		expect(t, out, err, "26")
		if !strings.Contains(out.Transformed, "_spine_prepare") {
			t.Fatal("PLM not used")
		}
	})
	t.Run("prefix before EOF", func(t *testing.T) {
		runCtx, stop := context.WithCancel(ctx)
		defer stop()
		prefixStarted = make(chan struct{})
		chunks := make(chan string)
		go func() {
			defer close(chunks)
			select {
			case chunks <- "a=tool('lookup',key='prefix')\n":
			case <-runCtx.Done():
				return
			}
			select {
			case <-prefixStarted:
			case <-runCtx.Done():
				return
			}
			select {
			case chunks <- "result=a\n":
			case <-runCtx.Done():
				return
			}
		}()
		out, err := r.RunPrefix(runCtx, chunks, nil)
		expect(t, out, err, "21")
	})
	t.Run("error does not change image", func(t *testing.T) {
		if _, err := r.Run(ctx, `raise ValueError("bad")`, nil); err == nil {
			t.Fatal("missing error")
		}
		out, err := r.Run(ctx, `result=7`, nil)
		expect(t, out, err, "7")
	})
	t.Run("cancel does not change image", func(t *testing.T) {
		deadline, stop := context.WithTimeout(ctx, 2*time.Second)
		defer stop()
		if _, err := r.Run(deadline, `while True: pass`, nil); err == nil || deadline.Err() == nil {
			t.Fatalf("expected deadline: %v", err)
		}
		out, err := r.Run(ctx, `result=7`, nil)
		expect(t, out, err, "7")
	})
}