2022-11-25 01:58:10 +00:00
|
|
|
package ph7_test
|
|
|
|
|
|
|
|
import (
|
2022-11-25 07:56:56 +00:00
|
|
|
"bytes"
|
2022-11-27 16:53:49 +00:00
|
|
|
"errors"
|
2022-11-25 07:56:56 +00:00
|
|
|
"fmt"
|
2022-11-28 00:10:28 +00:00
|
|
|
"strings"
|
2022-11-25 01:58:10 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
ph7 "git.codemonkeysoftware.net/b/go-ph7"
|
|
|
|
)
|
|
|
|
|
|
|
|
func mustSucceed(t *testing.T, err error) {
|
2022-11-25 04:51:44 +00:00
|
|
|
t.Helper()
|
2022-11-25 01:58:10 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
func mustFail(t *testing.T, err error) {
|
|
|
|
t.Helper()
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-25 01:58:10 +00:00
|
|
|
func mustSucceedF(t *testing.T, f func() error) {
|
2022-11-25 04:51:44 +00:00
|
|
|
t.Helper()
|
2022-11-25 01:58:10 +00:00
|
|
|
err := f()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-27 17:56:48 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 23:30:16 +00:00
|
|
|
func TestCore(t *testing.T) {
|
2022-11-25 01:58:10 +00:00
|
|
|
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)
|
|
|
|
}
|
2022-11-26 23:30:16 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
2022-11-25 04:51:44 +00:00
|
|
|
|
2022-11-27 18:31:44 +00:00
|
|
|
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!")
|
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
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)
|
2022-11-27 16:53:49 +00:00
|
|
|
var target ph7.CompileError
|
|
|
|
isCompileError := errors.As(err, &target)
|
|
|
|
if !isCompileError || target.Msg == "" {
|
|
|
|
t.Fatalf("expected compile error, got %#v", err)
|
2022-11-25 04:51:44 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-25 07:56:56 +00:00
|
|
|
|
|
|
|
func TestOutputWriter(t *testing.T) {
|
2022-11-27 17:56:48 +00:00
|
|
|
const source = `
|
2022-11-25 07:56:56 +00:00
|
|
|
for ($x = 0; $x <= 1000; $x++) {
|
|
|
|
echo $x;
|
2022-11-27 17:56:48 +00:00
|
|
|
}`
|
2022-11-25 07:56:56 +00:00
|
|
|
var expected bytes.Buffer
|
|
|
|
for i := 0; i <= 1000; i++ {
|
|
|
|
_, err := fmt.Fprint(&expected, i)
|
|
|
|
mustSucceed(t, err)
|
|
|
|
}
|
|
|
|
|
2022-11-27 17:56:48 +00:00
|
|
|
vm, close := setupVM(t, source)
|
|
|
|
defer close()
|
2022-11-25 07:56:56 +00:00
|
|
|
|
|
|
|
var actual bytes.Buffer
|
2022-11-27 17:56:48 +00:00
|
|
|
err := vm.SetOutputWriter(&actual)
|
2022-11-25 07:56:56 +00:00
|
|
|
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
|
2022-11-27 17:56:48 +00:00
|
|
|
|
|
|
|
type CtxFunc func(*ph7.Context) error
|
|
|
|
|
|
|
|
func (r CtxFunc) Call(ctx *ph7.Context, _ []*ph7.Value) ph7.ResultCode {
|
|
|
|
err := r(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return r(ctx).(ph7.Error).Code
|
|
|
|
}
|
|
|
|
return ph7.ResultCodeOK
|
|
|
|
}
|
|
|
|
|
|
|
|
func testResultCode(t *testing.T, expectedOutput string, f CtxFunc) {
|
|
|
|
t.Helper()
|
|
|
|
const script = "echo doStuff();"
|
|
|
|
vm, close := setupVM(t, script)
|
|
|
|
defer close()
|
|
|
|
|
|
|
|
mustSucceed(t, vm.CreateFunction("doStuff", f))
|
|
|
|
expectOutput(t, vm, expectedOutput)
|
|
|
|
}
|
|
|
|
|
2022-11-27 22:45:18 +00:00
|
|
|
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
|
2022-11-28 04:49:36 +00:00
|
|
|
written, err = ctx.OutputWriter().Write([]byte(s))
|
2022-11-27 22:45:18 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-28 00:10:28 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-28 01:35:51 +00:00
|
|
|
func TestFunctionName(t *testing.T) {
|
|
|
|
const script = "echo doStuff();"
|
|
|
|
vm, close := setupVM(t, script)
|
|
|
|
defer close()
|
|
|
|
|
|
|
|
f := func(ctx *ph7.Context) error {
|
2022-11-28 04:49:36 +00:00
|
|
|
_, err := fmt.Fprint(ctx.OutputWriter(), ctx.FunctionName())
|
2022-11-28 01:35:51 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
mustSucceed(t, vm.CreateFunction("doStuff", CtxFunc(f)))
|
|
|
|
expectOutput(t, vm, "doStuff")
|
|
|
|
}
|
|
|
|
|
2022-11-27 17:56:48 +00:00
|
|
|
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
|