vmcli: add tests
This commit is contained in:
parent
f8728e4f44
commit
33f13ab1c0
4 changed files with 486 additions and 6 deletions
1
go.mod
1
go.mod
|
@ -2,6 +2,7 @@ module github.com/nspcc-dev/neo-go
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Workiva/go-datastructures v1.0.50
|
github.com/Workiva/go-datastructures v1.0.50
|
||||||
|
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||||
github.com/btcsuite/btcd v0.20.1-beta
|
github.com/btcsuite/btcd v0.20.1-beta
|
||||||
github.com/dgraph-io/badger/v2 v2.0.3
|
github.com/dgraph-io/badger/v2 v2.0.3
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -53,6 +53,7 @@ github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReG
|
||||||
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
|
github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
@ -232,6 +233,7 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73 h1:I2drr5K0tykBofr74ZEGliE/Hf6fNkEbcPyFvsy7wZk=
|
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73 h1:I2drr5K0tykBofr74ZEGliE/Hf6fNkEbcPyFvsy7wZk=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"github.com/abiosoft/readline"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
|
@ -28,6 +29,7 @@ const (
|
||||||
boolTrue = "true"
|
boolTrue = "true"
|
||||||
intType = "int"
|
intType = "int"
|
||||||
stringType = "string"
|
stringType = "string"
|
||||||
|
exitFunc = "exitFunc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands = []*ishell.Cmd{
|
var commands = []*ishell.Cmd{
|
||||||
|
@ -193,15 +195,26 @@ var (
|
||||||
type VMCLI struct {
|
type VMCLI struct {
|
||||||
vm *vm.VM
|
vm *vm.VM
|
||||||
shell *ishell.Shell
|
shell *ishell.Shell
|
||||||
|
// printLogo specifies if logo is printed.
|
||||||
|
printLogo bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new VMCLI object.
|
// New returns a new VMCLI object.
|
||||||
func New() *VMCLI {
|
func New() *VMCLI {
|
||||||
|
return NewWithConfig(true, os.Exit, &readline.Config{
|
||||||
|
Prompt: ">>>",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithConfig returns new VMCLI instance using provided config.
|
||||||
|
func NewWithConfig(printLogo bool, onExit func(int), c *readline.Config) *VMCLI {
|
||||||
vmcli := VMCLI{
|
vmcli := VMCLI{
|
||||||
vm: vm.New(),
|
vm: vm.New(),
|
||||||
shell: ishell.New(),
|
shell: ishell.NewWithConfig(c),
|
||||||
|
printLogo: printLogo,
|
||||||
}
|
}
|
||||||
vmcli.shell.Set(vmKey, vmcli.vm)
|
vmcli.shell.Set(vmKey, vmcli.vm)
|
||||||
|
vmcli.shell.Set(exitFunc, onExit)
|
||||||
for _, c := range commands {
|
for _, c := range commands {
|
||||||
vmcli.shell.AddCmd(c)
|
vmcli.shell.AddCmd(c)
|
||||||
}
|
}
|
||||||
|
@ -224,7 +237,7 @@ func checkVMIsReady(c *ishell.Context) bool {
|
||||||
|
|
||||||
func handleExit(c *ishell.Context) {
|
func handleExit(c *ishell.Context) {
|
||||||
c.Println("Bye!")
|
c.Println("Bye!")
|
||||||
os.Exit(0)
|
c.Get(exitFunc).(func(int))(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleIP(c *ishell.Context) {
|
func handleIP(c *ishell.Context) {
|
||||||
|
@ -460,7 +473,9 @@ func changePrompt(c ishell.Actions, v *vm.VM) {
|
||||||
|
|
||||||
// Run waits for user input from Stdin and executes the passed command.
|
// Run waits for user input from Stdin and executes the passed command.
|
||||||
func (c *VMCLI) Run() error {
|
func (c *VMCLI) Run() error {
|
||||||
printLogo(c.shell)
|
if c.printLogo {
|
||||||
|
printLogo(c.shell)
|
||||||
|
}
|
||||||
c.shell.Run()
|
c.shell.Run()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -564,14 +579,15 @@ func parseArgs(args []string) ([]stackitem.Item, error) {
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLogo(c *ishell.Shell) {
|
const logo = `
|
||||||
logo := `
|
|
||||||
_ ____________ __________ _ ____ ___
|
_ ____________ __________ _ ____ ___
|
||||||
/ | / / ____/ __ \ / ____/ __ \ | | / / |/ /
|
/ | / / ____/ __ \ / ____/ __ \ | | / / |/ /
|
||||||
/ |/ / __/ / / / /_____/ / __/ / / /____| | / / /|_/ /
|
/ |/ / __/ / / / /_____/ / __/ / / /____| | / / /|_/ /
|
||||||
/ /| / /___/ /_/ /_____/ /_/ / /_/ /_____/ |/ / / / /
|
/ /| / /___/ /_/ /_____/ /_/ / /_/ /_____/ |/ / / / /
|
||||||
/_/ |_/_____/\____/ \____/\____/ |___/_/ /_/
|
/_/ |_/_____/\____/ \____/\____/ |___/_/ /_/
|
||||||
`
|
`
|
||||||
|
|
||||||
|
func printLogo(c *ishell.Shell) {
|
||||||
c.Print(logo)
|
c.Print(logo)
|
||||||
c.Println()
|
c.Println()
|
||||||
c.Println()
|
c.Println()
|
||||||
|
|
461
pkg/vm/cli/cli_test.go
Normal file
461
pkg/vm/cli/cli_test.go
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
gio "io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/abiosoft/readline"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type readCloser struct {
|
||||||
|
sync.Mutex
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloser) Read(p []byte) (int, error) {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
return r.Buffer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *readCloser) WriteString(s string) {
|
||||||
|
r.Lock()
|
||||||
|
defer r.Unlock()
|
||||||
|
r.Buffer.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type executor struct {
|
||||||
|
in *readCloser
|
||||||
|
out *bytes.Buffer
|
||||||
|
cli *VMCLI
|
||||||
|
ch chan struct{}
|
||||||
|
exit atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestVMCLI(t *testing.T) *executor {
|
||||||
|
return newTestVMCLIWithLogo(t, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestVMCLIWithLogo(t *testing.T, printLogo bool) *executor {
|
||||||
|
e := &executor{
|
||||||
|
in: &readCloser{Buffer: *bytes.NewBuffer(nil)},
|
||||||
|
out: bytes.NewBuffer(nil),
|
||||||
|
ch: make(chan struct{}),
|
||||||
|
}
|
||||||
|
e.cli = NewWithConfig(printLogo,
|
||||||
|
func(int) { e.exit.Store(true) },
|
||||||
|
&readline.Config{
|
||||||
|
Prompt: "",
|
||||||
|
Stdin: e.in,
|
||||||
|
Stdout: e.out,
|
||||||
|
})
|
||||||
|
go func() {
|
||||||
|
require.NoError(t, e.cli.Run())
|
||||||
|
close(e.ch)
|
||||||
|
}()
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *executor) runProg(t *testing.T, commands ...string) {
|
||||||
|
cmd := strings.Join(commands, "\n") + "\n"
|
||||||
|
e.in.WriteString(cmd + "\n")
|
||||||
|
select {
|
||||||
|
case <-e.ch:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
require.Fail(t, "command took too long time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *executor) checkNextLine(t *testing.T, expected string) {
|
||||||
|
line, err := e.out.ReadString('\n')
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Regexp(t, expected, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *executor) checkError(t *testing.T, expectedErr error) {
|
||||||
|
line, err := e.out.ReadString('\n')
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, strings.HasPrefix(line, "Error: "+expectedErr.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *executor) checkStack(t *testing.T, items ...interface{}) {
|
||||||
|
d := json.NewDecoder(e.out)
|
||||||
|
var actual interface{}
|
||||||
|
require.NoError(t, d.Decode(&actual))
|
||||||
|
rawActual, err := json.Marshal(actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := vm.NewStack("")
|
||||||
|
for i := range items {
|
||||||
|
expected.PushVal(items[i])
|
||||||
|
}
|
||||||
|
rawExpected, err := json.Marshal(expected)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, string(rawExpected), string(rawActual))
|
||||||
|
|
||||||
|
// Decoder has it's own buffer, we need to return unread part to the output.
|
||||||
|
outRemain := e.out.String()
|
||||||
|
e.out.Reset()
|
||||||
|
_, err = gio.Copy(e.out, d.Buffered())
|
||||||
|
require.NoError(t, err)
|
||||||
|
e.out.WriteString(outRemain)
|
||||||
|
_, err = e.out.ReadString('\n')
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoad(t *testing.T) {
|
||||||
|
script := []byte{byte(opcode.PUSH3), byte(opcode.PUSH4), byte(opcode.ADD)}
|
||||||
|
t.Run("loadhex", func(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadhex",
|
||||||
|
"loadhex notahex",
|
||||||
|
"loadhex "+hex.EncodeToString(script))
|
||||||
|
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
e.checkNextLine(t, "READY: loaded 3 instructions")
|
||||||
|
})
|
||||||
|
t.Run("loadbase64", func(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadbase64",
|
||||||
|
"loadbase64 not_a_base64",
|
||||||
|
"loadbase64 "+base64.StdEncoding.EncodeToString(script))
|
||||||
|
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
e.checkNextLine(t, "READY: loaded 3 instructions")
|
||||||
|
})
|
||||||
|
|
||||||
|
src := `package kek
|
||||||
|
func Main(op string, args []interface{}) int {
|
||||||
|
a := args[0].(int)
|
||||||
|
b := args[1].(int)
|
||||||
|
if op == "add" {
|
||||||
|
return a + b
|
||||||
|
} else {
|
||||||
|
return a * b
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
tmpDir := path.Join(os.TempDir(), "vmcliloadtest")
|
||||||
|
require.NoError(t, os.Mkdir(tmpDir, os.ModePerm))
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
t.Run("loadgo", func(t *testing.T) {
|
||||||
|
filename := path.Join(tmpDir, "vmtestcontract.go")
|
||||||
|
require.NoError(t, ioutil.WriteFile(filename, []byte(src), os.ModePerm))
|
||||||
|
filenameErr := path.Join(tmpDir, "vmtestcontract_err.go")
|
||||||
|
require.NoError(t, ioutil.WriteFile(filenameErr, []byte(src+"invalid_token"), os.ModePerm))
|
||||||
|
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadgo",
|
||||||
|
"loadgo "+filenameErr,
|
||||||
|
"loadgo "+filename,
|
||||||
|
"run add 3 5")
|
||||||
|
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkNextLine(t, "Error:")
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||||
|
e.checkStack(t, 8)
|
||||||
|
})
|
||||||
|
t.Run("loadnef", func(t *testing.T) {
|
||||||
|
config.Version = "0.92.0-test"
|
||||||
|
|
||||||
|
script, err := compiler.Compile("test", strings.NewReader(src))
|
||||||
|
require.NoError(t, err)
|
||||||
|
nefFile, err := nef.NewFile(script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
filename := path.Join(tmpDir, "vmtestcontract.nef")
|
||||||
|
rawNef, err := nefFile.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, ioutil.WriteFile(filename, rawNef, os.ModePerm))
|
||||||
|
filenameErr := path.Join(tmpDir, "vmtestcontract_err.nef")
|
||||||
|
require.NoError(t, ioutil.WriteFile(filenameErr, append([]byte{1, 2, 3, 4}, rawNef...), os.ModePerm))
|
||||||
|
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadnef",
|
||||||
|
"loadnef "+filenameErr,
|
||||||
|
"loadnef "+filename,
|
||||||
|
"run add 3 5")
|
||||||
|
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkNextLine(t, "Error:")
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d* instructions")
|
||||||
|
e.checkStack(t, 8)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunWithDifferentArguments(t *testing.T) {
|
||||||
|
src := `package kek
|
||||||
|
func Main(op string, args []interface{}) interface{} {
|
||||||
|
switch op {
|
||||||
|
case "getbool":
|
||||||
|
return args[0].(bool)
|
||||||
|
case "getint":
|
||||||
|
return args[0].(int)
|
||||||
|
case "getstring":
|
||||||
|
return args[0].(string)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
filename := path.Join(os.TempDir(), "run_vmtestcontract.go")
|
||||||
|
require.NoError(t, ioutil.WriteFile(filename, []byte(src), os.ModePerm))
|
||||||
|
defer os.Remove(filename)
|
||||||
|
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadgo "+filename, "run getbool true",
|
||||||
|
"loadgo "+filename, "run getbool false",
|
||||||
|
"loadgo "+filename, "run getbool bool:invalid",
|
||||||
|
"loadgo "+filename, "run getint 123",
|
||||||
|
"loadgo "+filename, "run getint int:invalid",
|
||||||
|
"loadgo "+filename, "run getstring validstring",
|
||||||
|
)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkStack(t, true)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkStack(t, false)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkStack(t, 123)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d.* instructions")
|
||||||
|
e.checkStack(t, "validstring")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintOps(t *testing.T) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PUSH1)
|
||||||
|
emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize)
|
||||||
|
emit.Instruction(w.BinWriter, opcode.PUSHDATA1, []byte{3, 1, 2, 3})
|
||||||
|
script := w.Bytes()
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"ops",
|
||||||
|
"loadhex "+hex.EncodeToString(script),
|
||||||
|
"ops")
|
||||||
|
|
||||||
|
e.checkNextLine(t, ".*no program loaded")
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded %d instructions", len(script)))
|
||||||
|
e.checkNextLine(t, "INDEX.*OPCODE.*PARAMETER")
|
||||||
|
e.checkNextLine(t, "0.*PUSH1")
|
||||||
|
e.checkNextLine(t, "1.*SYSCALL.*System\\.Binary\\.Serialize")
|
||||||
|
e.checkNextLine(t, "6.*PUSHDATA1.*010203")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadAbort(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadhex "+hex.EncodeToString([]byte{byte(opcode.PUSH1), byte(opcode.ABORT)}),
|
||||||
|
"run",
|
||||||
|
)
|
||||||
|
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded 2 instructions"))
|
||||||
|
e.checkNextLine(t, "Error:.*at instruction 1.*ABORT")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBreakpoint(t *testing.T) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PUSH1, opcode.PUSH2, opcode.ADD, opcode.PUSH6, opcode.ADD)
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"break 3",
|
||||||
|
"cont",
|
||||||
|
"ip",
|
||||||
|
"loadhex "+hex.EncodeToString(w.Bytes()),
|
||||||
|
"break",
|
||||||
|
"break second",
|
||||||
|
"break 2",
|
||||||
|
"break 4",
|
||||||
|
"cont", "estack",
|
||||||
|
"run", "estack",
|
||||||
|
"cont",
|
||||||
|
)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "no program loaded")
|
||||||
|
e.checkNextLine(t, "no program loaded")
|
||||||
|
e.checkNextLine(t, "no program loaded")
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded 5 instructions"))
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
e.checkNextLine(t, "breakpoint added at instruction 2")
|
||||||
|
e.checkNextLine(t, "breakpoint added at instruction 4")
|
||||||
|
|
||||||
|
e.checkNextLine(t, "at breakpoint 2.*ADD")
|
||||||
|
e.checkStack(t, 1, 2)
|
||||||
|
|
||||||
|
e.checkNextLine(t, "at breakpoint 4.*ADD")
|
||||||
|
e.checkStack(t, 3, 6)
|
||||||
|
|
||||||
|
e.checkStack(t, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStep(t *testing.T) {
|
||||||
|
script := hex.EncodeToString([]byte{
|
||||||
|
byte(opcode.PUSH0), byte(opcode.PUSH1), byte(opcode.PUSH2), byte(opcode.PUSH3),
|
||||||
|
})
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"step",
|
||||||
|
"loadhex "+script,
|
||||||
|
"step invalid",
|
||||||
|
"step",
|
||||||
|
"step 2",
|
||||||
|
"ip", "step", "ip")
|
||||||
|
|
||||||
|
e.checkNextLine(t, "no program loaded")
|
||||||
|
e.checkNextLine(t, "READY: loaded \\d+ instructions")
|
||||||
|
e.checkError(t, ErrInvalidParameter)
|
||||||
|
e.checkNextLine(t, "at breakpoint 1.*PUSH1")
|
||||||
|
e.checkNextLine(t, "at breakpoint 3.*PUSH3")
|
||||||
|
e.checkNextLine(t, "instruction pointer at 3.*PUSH3")
|
||||||
|
e.checkNextLine(t, "execution has finished")
|
||||||
|
e.checkNextLine(t, "execution has finished")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnStepInto(t *testing.T) {
|
||||||
|
script := hex.EncodeToString([]byte{byte(opcode.ADD)})
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"stepover",
|
||||||
|
"loadhex "+script,
|
||||||
|
"stepover")
|
||||||
|
|
||||||
|
e.checkNextLine(t, "Error:.*no program loaded")
|
||||||
|
e.checkNextLine(t, "READY: loaded 1 instructions")
|
||||||
|
e.checkNextLine(t, "Error:")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepIntoOverOut(t *testing.T) {
|
||||||
|
script := hex.EncodeToString([]byte{
|
||||||
|
byte(opcode.PUSH2), byte(opcode.CALL), 4, byte(opcode.NOP), byte(opcode.RET),
|
||||||
|
byte(opcode.PUSH3), byte(opcode.ADD), byte(opcode.RET),
|
||||||
|
})
|
||||||
|
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"loadhex "+script,
|
||||||
|
"step", "stepover", "run",
|
||||||
|
"loadhex "+script,
|
||||||
|
"step", "stepinto", "step", "estack", "run",
|
||||||
|
"loadhex "+script,
|
||||||
|
"step", "stepinto", "stepout", "run")
|
||||||
|
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded \\d+ instructions"))
|
||||||
|
e.checkNextLine(t, "at breakpoint 1.*CALL")
|
||||||
|
e.checkNextLine(t, "instruction pointer at.*NOP")
|
||||||
|
e.checkStack(t, 5)
|
||||||
|
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded \\d+ instructions"))
|
||||||
|
e.checkNextLine(t, "at breakpoint.*CALL")
|
||||||
|
e.checkNextLine(t, "instruction pointer at.*PUSH3")
|
||||||
|
e.checkNextLine(t, "at breakpoint.*ADD")
|
||||||
|
e.checkStack(t, 2, 3)
|
||||||
|
e.checkStack(t, 5)
|
||||||
|
|
||||||
|
e.checkNextLine(t, fmt.Sprintf("READY: loaded \\d+ instructions"))
|
||||||
|
e.checkNextLine(t, "at breakpoint 1.*CALL")
|
||||||
|
e.checkNextLine(t, "instruction pointer at.*PUSH3")
|
||||||
|
e.checkNextLine(t, "instruction pointer at.*NOP")
|
||||||
|
e.checkStack(t, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `Parse` output is written via `tabwriter` so if any problems
|
||||||
|
// are encountered in this test, try to replace ' ' with '\\s+'.
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
t.Run("Integer", func(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t,
|
||||||
|
"parse",
|
||||||
|
"parse 6667")
|
||||||
|
|
||||||
|
e.checkError(t, ErrMissingParameter)
|
||||||
|
e.checkNextLine(t, "Integer to Hex.*0b1a")
|
||||||
|
e.checkNextLine(t, "Integer to Base64.*Cxo=")
|
||||||
|
e.checkNextLine(t, "Hex to String.*\"fg\"")
|
||||||
|
e.checkNextLine(t, "Hex to Integer.*26470")
|
||||||
|
e.checkNextLine(t, "Swap Endianness.*6766")
|
||||||
|
e.checkNextLine(t, "Base64 to String.*\"뮻\"")
|
||||||
|
e.checkNextLine(t, "Base64 to BigInteger.*-4477205")
|
||||||
|
e.checkNextLine(t, "String to Hex.*36363637")
|
||||||
|
e.checkNextLine(t, "String to Base64.*NjY2Nw==")
|
||||||
|
})
|
||||||
|
t.Run("Address", func(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t, "parse "+"NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
|
||||||
|
e.checkNextLine(t, "Address to BE ScriptHash.*aa8acf859d4fe402b34e673f2156821796a488eb")
|
||||||
|
e.checkNextLine(t, "Address to LE ScriptHash.*eb88a496178256213f674eb302e44f9d85cf8aaa")
|
||||||
|
e.checkNextLine(t, "Address to Base64.*(BE).*qorPhZ1P5AKzTmc/IVaCF5akiOs=")
|
||||||
|
e.checkNextLine(t, "Address to Base64.*(LE).*64iklheCViE/Z06zAuRPnYXPiqo=")
|
||||||
|
e.checkNextLine(t, "String to Hex.*4e6254694d3668387239396b70527462343238586373556b31547a4b656432675463")
|
||||||
|
e.checkNextLine(t, "String to Base64.*TmJUaU02aDhyOTlrcFJ0YjQyOFhjc1VrMVR6S2VkMmdUYw==")
|
||||||
|
})
|
||||||
|
t.Run("Uint160", func(t *testing.T) {
|
||||||
|
u := util.Uint160{66, 67, 68}
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t, "parse "+u.StringLE())
|
||||||
|
e.checkNextLine(t, "Integer to Hex.*b6c706")
|
||||||
|
e.checkNextLine(t, "Integer to Base64.*tscG")
|
||||||
|
e.checkNextLine(t, "BE ScriptHash to Address.*NKuyBkoGdZZSLyPbJEetheRhQKhATAzN2A")
|
||||||
|
e.checkNextLine(t, "LE ScriptHash to Address.*NRxLN7apYwKJihzMt4eSSnU9BJ77dp2TNj")
|
||||||
|
e.checkNextLine(t, "Hex to String")
|
||||||
|
e.checkNextLine(t, "Hex to Integer.*378293464438118320046642359484100328446970822656")
|
||||||
|
e.checkNextLine(t, "Swap Endianness.*4243440000000000000000000000000000000000")
|
||||||
|
e.checkNextLine(t, "Base64 to String.*")
|
||||||
|
e.checkNextLine(t, "Base64 to BigInteger.*376115185060690908522683414825349447309891933036899526770189324554358227")
|
||||||
|
e.checkNextLine(t, "String to Hex.*30303030303030303030303030303030303030303030303030303030303030303030343434333432")
|
||||||
|
e.checkNextLine(t, "String to Base64.*MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQ0NDM0Mg==")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintLogo(t *testing.T) {
|
||||||
|
e := newTestVMCLIWithLogo(t, true)
|
||||||
|
e.runProg(t)
|
||||||
|
require.True(t, strings.HasPrefix(e.out.String(), logo))
|
||||||
|
require.False(t, e.exit.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExit(t *testing.T) {
|
||||||
|
e := newTestVMCLI(t)
|
||||||
|
e.runProg(t, "exit")
|
||||||
|
require.True(t, e.exit.Load())
|
||||||
|
}
|
Loading…
Reference in a new issue