Lua: Support inline scripts with optional gzip compression

This commit is contained in:
Meo597
2026-10-12 03:58:48 +08:00
parent 0fe8514f93
commit 13396ff9c6
17 changed files with 344 additions and 165 deletions
+122 -3
View File
@@ -1,10 +1,14 @@
package lua
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"errors"
"os"
"path/filepath"
"strings"
"testing"
glua "github.com/yuin/gopher-lua"
@@ -15,10 +19,13 @@ func TestProgramStatesAreIndependent(t *testing.T) {
if err := os.WriteFile(path, []byte("value = (value or 0) + 1"), 0o600); err != nil {
t.Fatal(err)
}
program, err := CompileFile(path)
program, err := Compile(path)
if err != nil {
t.Fatal(err)
}
if program.proto.SourceName != path {
t.Fatalf("program source = %q, want %q", program.proto.SourceName, path)
}
first, err := program.NewState(context.Background(), nil, nil)
if err != nil {
t.Fatal(err)
@@ -40,7 +47,7 @@ func TestProgramInitializationObservesCancellation(t *testing.T) {
if err := os.WriteFile(path, []byte("while true do end"), 0o600); err != nil {
t.Fatal(err)
}
program, err := CompileFile(path)
program, err := Compile(path)
if err != nil {
t.Fatal(err)
}
@@ -60,7 +67,7 @@ func TestNewStateClosesFailedValidation(t *testing.T) {
if err := os.WriteFile(path, []byte("value = 1"), 0o600); err != nil {
t.Fatal(err)
}
program, err := CompileFile(path)
program, err := Compile(path)
if err != nil {
t.Fatal(err)
}
@@ -74,3 +81,115 @@ func TestNewStateClosesFailedValidation(t *testing.T) {
t.Fatalf("state = %v, error = %v, checked state closed = %t", L, err, checked != nil && checked.IsClosed())
}
}
func compressSource(t *testing.T, code string) []byte {
t.Helper()
var data bytes.Buffer
writer := gzip.NewWriter(&data)
if _, err := writer.Write([]byte(code)); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
return data.Bytes()
}
func TestCompileInline(t *testing.T) {
const code = `value = "你好"`
for _, tc := range []struct {
name, script string
value glua.LValue
}{
{"base64", "base64:" + base64.StdEncoding.EncodeToString([]byte(code)), glua.LString("你好")},
{"base64+gzip", "base64+gzip:" + base64.StdEncoding.EncodeToString(compressSource(t, code)), glua.LString("你好")},
{"empty base64", "base64:", glua.LNil},
} {
t.Run(tc.name, func(t *testing.T) {
program, err := Compile(tc.script)
if err != nil {
t.Fatal(err)
}
if program.proto.SourceName != "[inline]" {
t.Fatalf("program source = %q, want [inline]", program.proto.SourceName)
}
state, err := program.NewState(context.Background(), nil, nil)
if err != nil {
t.Fatal(err)
}
defer state.Close()
if got := state.GetGlobal("value"); got != tc.value {
t.Fatalf("value = %v, want %v", got, tc.value)
}
})
}
}
func TestCompileRejectsInvalidInline(t *testing.T) {
corrupt := compressSource(t, "value = 1")
corrupt[len(corrupt)-8] ^= 0xff
for _, tc := range []struct {
name, script string
wantErr error
wantMessage string
}{
{"base64", "base64:%", base64.CorruptInputError(0), "failed to decode base64"},
{"gzip base64", "base64+gzip:%", base64.CorruptInputError(0), "failed to decode base64"},
{"gzip header", "base64+gzip:" + base64.StdEncoding.EncodeToString([]byte("value = 'uncompressed'")), gzip.ErrHeader, "failed to decompress gzip"},
{"gzip checksum", "base64+gzip:" + base64.StdEncoding.EncodeToString(corrupt), gzip.ErrChecksum, "failed to decompress gzip"},
} {
t.Run(tc.name, func(t *testing.T) {
program, err := Compile(tc.script)
if program != nil || !errors.Is(err, tc.wantErr) || !strings.Contains(err.Error(), tc.wantMessage) {
t.Fatalf("Compile = %v, %v; want nil program and %q error wrapping %v", program, err, tc.wantMessage, tc.wantErr)
}
})
}
}
func TestCompileInlineDiagnostics(t *testing.T) {
for _, tc := range []struct {
code string
compileError bool
}{
{"function broken(", true},
{`error("initialization failed")`, false},
} {
program, err := Compile("base64:" + base64.StdEncoding.EncodeToString([]byte(tc.code)))
if tc.compileError {
if program != nil || err == nil {
t.Fatalf("Compile = %v, %v; want nil program and syntax error", program, err)
}
} else {
if err != nil {
t.Fatal(err)
}
state, initErr := program.NewState(context.Background(), nil, nil)
if state != nil {
state.Close()
t.Fatal("NewState returned a state after failed initialization")
}
err = initErr
}
if err == nil || !strings.Contains(err.Error(), "[inline]") {
t.Fatalf("Lua error = %v, want [inline] diagnostic", err)
}
}
}
func TestCompileRejectsReadFailure(t *testing.T) {
path := filepath.Join(t.TempDir(), "unreadable.lua")
if err := os.WriteFile(path, []byte("value = 42"), 0o600); err != nil {
t.Fatal(err)
}
if err := os.Chmod(path, 0); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.Chmod(path, 0o600) })
if _, err := os.ReadFile(path); !errors.Is(err, os.ErrPermission) {
t.Skip("file read permissions are not enforced")
}
if program, err := Compile(path); program != nil || !errors.Is(err, os.ErrPermission) {
t.Fatalf("Compile = %v, %v; want nil program and permission error", program, err)
}
}