go-ph7/ph7_test.go

253 lines
5.6 KiB
Go

package ph7_test
import (
"bytes"
"errors"
"fmt"
"strings"
"testing"
ph7 "git.codemonkeysoftware.net/b/go-ph7"
)
func mustSucceed(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatal(err.Error())
}
}
func mustFail(t *testing.T, err error) {
t.Helper()
if err == nil {
t.Fatal("expected error, got nil")
}
}
func mustSucceedF(t *testing.T, f func() error) {
t.Helper()
err := f()
if err != nil {
t.Fatal(err.Error())
}
}
func setupVM(t *testing.T, script string) (vm *ph7.VM, close func()) {
t.Helper()
engine, err := ph7.NewEngine()
mustSucceed(t, err)
vm, err = engine.Compile([]byte(script), true)
if err != nil {
mustSucceed(t, engine.Close())
mustSucceed(t, err)
}
return vm, func() {
mustSucceed(t, vm.Close())
mustSucceed(t, engine.Close())
}
}
func expectOutput(t *testing.T, vm *ph7.VM, expected string) {
t.Helper()
mustSucceed(t, vm.Exec())
output, err := vm.ExtractOutput()
mustSucceed(t, err)
if output != expected {
t.Fatalf("expected %q, got %q", expected, output)
}
}
func TestCore(t *testing.T) {
engine, err := ph7.NewEngine()
mustSucceed(t, err)
defer mustSucceedF(t, engine.Close)
vm, err := engine.Compile([]byte("<?php echo 'Hello world!'; ?>"), false)
mustSucceed(t, err)
defer mustSucceedF(t, vm.Close)
mustSucceed(t, vm.Exec())
output, err := vm.ExtractOutput()
mustSucceed(t, err)
if output != "Hello world!" {
t.Fatalf("unexpected output: %s", output)
}
mustSucceed(t, vm.Reset())
mustSucceed(t, vm.Exec())
output, err = vm.ExtractOutput()
mustSucceed(t, err)
if output != "Hello world!" {
t.Fatalf("unexpected output: %s", output)
}
}
func TestCompileFile(t *testing.T) {
engine, err := ph7.NewEngine()
mustSucceed(t, err)
defer mustSucceedF(t, engine.Close)
vm, err := engine.CompileFile("testdata/test_compile_file.php", false)
mustSucceed(t, err)
defer mustSucceedF(t, vm.Close)
expectOutput(t, vm, "Hello world!")
}
func TestCompileError(t *testing.T) {
engine, err := ph7.NewEngine()
mustSucceed(t, err)
defer mustSucceedF(t, engine.Close)
_, err = engine.Compile([]byte("<?php 1234 echo 'Hello world!'; ?>"), false)
mustFail(t, err)
var target ph7.CompileError
isCompileError := errors.As(err, &target)
if !isCompileError || target.Msg == "" {
t.Fatalf("expected compile error, got %#v", err)
}
}
func TestOutputWriter(t *testing.T) {
const source = `
for ($x = 0; $x <= 1000; $x++) {
echo $x;
}`
var expected bytes.Buffer
for i := 0; i <= 1000; i++ {
_, err := fmt.Fprint(&expected, i)
mustSucceed(t, err)
}
vm, close := setupVM(t, source)
defer close()
var actual bytes.Buffer
err := vm.SetOutputWriter(&actual)
mustSucceed(t, err)
mustSucceed(t, vm.Exec())
if !bytes.Equal(expected.Bytes(), actual.Bytes()) {
t.Fatalf("expected %s, got %s", expected.String(), actual.String())
}
}
// TODO Test failing writer, including vm.OutputWriteError
// TODO Test setting writer twice
func CtxFunc(f func(*ph7.Context) error) ph7.Function {
return func(ctx *ph7.Context, _ []*ph7.Value) ph7.ResultCode {
err := f(ctx)
if err != nil {
return f(ctx).(ph7.Error).Code
}
return ph7.ResultCodeOK
}
}
func testResultCode(t *testing.T, expectedOutput string, f func(*ph7.Context) error) {
t.Helper()
const script = "echo doStuff();"
vm, close := setupVM(t, script)
defer close()
mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))
expectOutput(t, vm, expectedOutput)
}
func TestContextWrite(t *testing.T) {
const script = "echo doStuff();"
vm, close := setupVM(t, script)
defer close()
const s = "aoeu htns"
var written int
f := func(ctx *ph7.Context) error {
var err error
written, err = ctx.OutputWriter().Write([]byte(s))
return err
}
mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))
expectOutput(t, vm, s)
if written != len(s) {
t.Fatalf("wrote wrong number of bytes: expected %d, got %d", len(s), written)
}
}
func TestContextThrowError(t *testing.T) {
const script = "echo doStuff();"
vm, close := setupVM(t, script)
defer close()
const msg = "aock.,rabkaeop"
f := func(ctx *ph7.Context) error {
return ctx.ThrowError(ph7.SeverityErr, msg)
}
mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))
mustSucceed(t, vm.ReportErrors())
mustSucceed(t, vm.Exec())
output, err := vm.ExtractOutput()
mustSucceed(t, err)
if !strings.Contains(output, msg) {
t.Fatalf("expected output to contain %q, got %q", msg, output)
}
}
func TestFunctionName(t *testing.T) {
const script = "echo doStuff();"
vm, close := setupVM(t, script)
defer close()
f := func(ctx *ph7.Context) error {
_, err := fmt.Fprint(ctx.OutputWriter(), ctx.FunctionName())
return err
}
mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))
expectOutput(t, vm, "doStuff")
}
func TestContextResultInt(t *testing.T) {
testResultCode(t, "42", func(ctx *ph7.Context) error {
return ctx.ResultInt(42)
})
}
func TestContextResultInt64(t *testing.T) {
testResultCode(t, "1234", func(ctx *ph7.Context) error {
return ctx.ResultInt64(1234)
})
}
func TestContextResultBool(t *testing.T) {
testResultCode(t, "TRUE", func(ctx *ph7.Context) error {
return ctx.ResultBool(true)
})
testResultCode(t, "", func(ctx *ph7.Context) error {
return ctx.ResultBool(false)
})
}
func TestContextResultDouble(t *testing.T) {
testResultCode(t, "1.23", func(ctx *ph7.Context) error {
return ctx.ResultDouble(1.23)
})
}
func TestContextResultNull(t *testing.T) {
testResultCode(t, "", (*ph7.Context).ResultNull)
}
func TestContextResultString(t *testing.T) {
testResultCode(t, "hello", func(ctx *ph7.Context) error {
return ctx.ResultString("hello")
})
}
// TODO TestContextResultValue
// TODO TestContextResultResource