2023-04-02 13:12:06 +02:00
package vm
import (
"bytes"
"context"
"fmt"
"log"
"testing"
2023-04-09 10:44:32 +02:00
"git.defalsify.org/festive/cache"
"git.defalsify.org/festive/render"
2023-04-02 13:12:06 +02:00
"git.defalsify.org/festive/resource"
"git.defalsify.org/festive/state"
)
var dynVal = "three"
type TestResource struct {
2023-04-03 00:53:21 +02:00
resource . MenuResource
2023-04-02 13:12:06 +02:00
state * state . State
}
2023-04-10 06:30:57 +02:00
func getOne ( sym string , ctx context . Context ) ( string , error ) {
2023-04-02 13:12:06 +02:00
return "one" , nil
}
2023-04-10 06:30:57 +02:00
func getTwo ( sym string , ctx context . Context ) ( string , error ) {
2023-04-02 13:12:06 +02:00
return "two" , nil
}
2023-04-10 06:30:57 +02:00
func getDyn ( sym string , ctx context . Context ) ( string , error ) {
2023-04-02 13:12:06 +02:00
return dynVal , nil
}
type TestStatefulResolver struct {
state * state . State
}
2023-04-09 10:44:32 +02:00
func ( r TestResource ) GetTemplate ( sym string ) ( string , error ) {
2023-04-02 13:12:06 +02:00
switch sym {
case "foo" :
return "inky pinky blinky clyde" , nil
case "bar" :
return "inky pinky {{.one}} blinky {{.two}} clyde" , nil
case "baz" :
return "inky pinky {{.baz}} blinky clyde" , nil
case "three" :
return "{{.one}} inky pinky {{.three}} blinky clyde {{.two}}" , nil
2023-04-09 16:35:26 +02:00
case "root" :
return "root" , nil
2023-04-02 13:12:06 +02:00
case "_catch" :
return "aiee" , nil
}
panic ( fmt . Sprintf ( "unknown symbol %s" , sym ) )
return "" , fmt . Errorf ( "unknown symbol %s" , sym )
}
2023-04-09 10:44:32 +02:00
func ( r TestResource ) FuncFor ( sym string ) ( resource . EntryFunc , error ) {
2023-04-02 13:12:06 +02:00
switch sym {
case "one" :
return getOne , nil
case "two" :
return getTwo , nil
case "dyn" :
return getDyn , nil
case "arg" :
return r . getInput , nil
}
return nil , fmt . Errorf ( "invalid function: '%s'" , sym )
}
2023-04-10 06:30:57 +02:00
func ( r TestResource ) getInput ( sym string , ctx context . Context ) ( string , error ) {
2023-04-02 13:12:06 +02:00
v , err := r . state . GetInput ( )
return string ( v ) , err
}
2023-04-09 10:44:32 +02:00
func ( r TestResource ) GetCode ( sym string ) ( [ ] byte , error ) {
2023-04-06 12:49:52 +02:00
var b [ ] byte
if sym == "_catch" {
b = NewLine ( b , MOUT , [ ] string { "0" , "repent" } , nil , nil )
b = NewLine ( b , HALT , nil , nil , nil )
}
return b , nil
2023-04-02 13:12:06 +02:00
}
func TestRun ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-02 14:59:40 +02:00
b := NewLine ( nil , MOVE , [ ] string { "foo" } , nil , nil )
2023-04-06 12:49:52 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
_ , err := vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-02 14:59:40 +02:00
t . Errorf ( "run error: %v" , err )
2023-04-02 13:12:06 +02:00
}
b = [ ] byte { 0x01 , 0x02 }
2023-04-09 10:44:32 +02:00
_ , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err == nil {
t . Errorf ( "no error on invalid opcode" )
}
}
func TestRunLoadRender ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-09 10:44:32 +02:00
2023-04-09 16:35:26 +02:00
st . Down ( "bar" )
2023-04-02 14:59:40 +02:00
2023-04-02 13:12:06 +02:00
var err error
2023-04-02 14:59:40 +02:00
b := NewLine ( nil , LOAD , [ ] string { "one" } , [ ] byte { 0x0a } , nil )
2023-04-09 16:35:26 +02:00
b = NewLine ( b , LOAD , [ ] string { "two" } , [ ] byte { 0x0a } , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-09 16:35:26 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-09 16:35:26 +02:00
r , err := vm . Render ( )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-09 16:35:26 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-09 16:35:26 +02:00
expect := "inky pinky one blinky two clyde"
2023-04-02 13:12:06 +02:00
if r != expect {
2023-04-09 16:35:26 +02:00
t . Fatalf ( "Expected\n\t%s\ngot\n\t%s\n" , expect , r )
2023-04-02 13:12:06 +02:00
}
2023-04-02 14:59:40 +02:00
b = NewLine ( nil , LOAD , [ ] string { "two" } , [ ] byte { 0x0a } , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 14:59:40 +02:00
if err != nil {
2023-04-09 16:35:26 +02:00
t . Fatal ( err )
2023-04-02 14:59:40 +02:00
}
b = NewLine ( nil , MAP , [ ] string { "one" } , nil , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
_ , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-09 16:35:26 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-09 16:35:26 +02:00
r , err = vm . Render ( )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-09 16:35:26 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
expect = "inky pinky one blinky two clyde"
if r != expect {
2023-04-09 16:35:26 +02:00
t . Fatalf ( "Expected %v, got %v" , expect , r )
2023-04-02 13:12:06 +02:00
}
}
func TestRunMultiple ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-09 10:44:32 +02:00
2023-04-08 09:14:14 +02:00
b := NewLine ( nil , MOVE , [ ] string { "test" } , nil , nil )
b = NewLine ( b , LOAD , [ ] string { "one" } , [ ] byte { 0x00 } , nil )
2023-04-02 14:59:40 +02:00
b = NewLine ( b , LOAD , [ ] string { "two" } , [ ] byte { 42 } , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
b , err := vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
2023-04-02 14:59:40 +02:00
if len ( b ) > 0 {
t . Errorf ( "expected empty code" )
}
2023-04-02 13:12:06 +02:00
}
func TestRunReload ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
szr := render . NewSizer ( 128 )
vm := NewVm ( & st , & rs , ca , szr )
2023-04-09 10:44:32 +02:00
2023-04-06 10:54:51 +02:00
b := NewLine ( nil , MOVE , [ ] string { "root" } , nil , nil )
b = NewLine ( b , LOAD , [ ] string { "dyn" } , nil , [ ] uint8 { 0 } )
2023-04-02 13:12:06 +02:00
b = NewLine ( b , MAP , [ ] string { "dyn" } , nil , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
_ , err := vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-06 10:54:51 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-09 16:35:26 +02:00
r , err := vm . Render ( )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-06 10:54:51 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-09 16:35:26 +02:00
if r != "root" {
t . Fatalf ( "expected result 'root', got %v" , r )
2023-04-02 13:12:06 +02:00
}
dynVal = "baz"
2023-04-06 10:54:51 +02:00
b = NewLine ( nil , RELOAD , [ ] string { "dyn" } , nil , nil )
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
_ , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-06 10:54:51 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
}
func TestHalt ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-09 10:44:32 +02:00
2023-04-08 09:14:14 +02:00
b := NewLine ( nil , MOVE , [ ] string { "root" } , nil , nil )
b = NewLine ( b , LOAD , [ ] string { "one" } , nil , [ ] uint8 { 0 } )
2023-04-02 13:12:06 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
b = NewLine ( b , MOVE , [ ] string { "foo" } , nil , nil )
var err error
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
2023-04-07 12:31:30 +02:00
r , _ := st . Where ( )
2023-04-02 13:12:06 +02:00
if r == "foo" {
t . Fatalf ( "Expected where-symbol not to be 'foo'" )
}
if ! bytes . Equal ( b [ : 2 ] , [ ] byte { 0x00 , MOVE } ) {
t . Fatalf ( "Expected MOVE instruction, found '%v'" , b )
}
}
func TestRunArg ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-09 10:44:32 +02:00
2023-04-02 13:12:06 +02:00
input := [ ] byte ( "bar" )
_ = st . SetInput ( input )
2023-04-06 10:54:51 +02:00
bi := NewLine ( nil , INCMP , [ ] string { "bar" , "baz" } , nil , nil )
bi = NewLine ( bi , HALT , nil , nil , nil )
2023-04-09 10:44:32 +02:00
b , err := vm . Run ( bi , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
l := len ( b )
if l != 0 {
t . Errorf ( "expected empty remainder, got length %v: %v" , l , b )
}
2023-04-07 12:31:30 +02:00
r , _ := st . Where ( )
2023-04-02 13:12:06 +02:00
if r != "baz" {
t . Errorf ( "expected where-state baz, got %v" , r )
}
}
func TestRunInputHandler ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-02 13:12:06 +02:00
_ = st . SetInput ( [ ] byte ( "baz" ) )
bi := NewLine ( [ ] byte { } , INCMP , [ ] string { "bar" , "aiee" } , nil , nil )
bi = NewLine ( bi , INCMP , [ ] string { "baz" , "foo" } , nil , nil )
2023-04-02 14:59:40 +02:00
bi = NewLine ( bi , LOAD , [ ] string { "one" } , [ ] byte { 0x00 } , nil )
bi = NewLine ( bi , LOAD , [ ] string { "two" } , [ ] byte { 0x03 } , nil )
2023-04-02 13:12:06 +02:00
bi = NewLine ( bi , MAP , [ ] string { "one" } , nil , nil )
bi = NewLine ( bi , MAP , [ ] string { "two" } , nil , nil )
2023-04-06 10:54:51 +02:00
bi = NewLine ( bi , HALT , nil , nil , nil )
2023-04-02 13:12:06 +02:00
var err error
2023-04-09 10:44:32 +02:00
_ , err = vm . Run ( bi , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Fatal ( err )
}
2023-04-07 12:31:30 +02:00
r , _ := st . Where ( )
2023-04-02 13:12:06 +02:00
if r != "foo" {
t . Fatalf ( "expected where-sym 'foo', got '%v'" , r )
}
}
func TestRunArgInvalid ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-02 13:12:06 +02:00
_ = st . SetInput ( [ ] byte ( "foo" ) )
var err error
2023-04-06 10:54:51 +02:00
st . Down ( "root" )
b := NewLine ( nil , INCMP , [ ] string { "bar" , "baz" } , nil , nil )
2023-04-02 13:12:06 +02:00
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-02 13:12:06 +02:00
if err != nil {
2023-04-06 10:54:51 +02:00
t . Fatal ( err )
2023-04-02 13:12:06 +02:00
}
2023-04-07 12:31:30 +02:00
r , _ := st . Where ( )
2023-04-02 13:12:06 +02:00
if r != "_catch" {
2023-04-06 10:54:51 +02:00
t . Fatalf ( "expected where-state _catch, got %v" , r )
2023-04-02 13:12:06 +02:00
}
}
2023-04-03 00:53:21 +02:00
func TestRunMenu ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-03 00:53:21 +02:00
var err error
b := NewLine ( nil , MOVE , [ ] string { "foo" } , nil , nil )
b = NewLine ( b , MOUT , [ ] string { "0" , "one" } , nil , nil )
b = NewLine ( b , MOUT , [ ] string { "1" , "two" } , nil , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-03 00:53:21 +02:00
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-03 00:53:21 +02:00
if err != nil {
t . Error ( err )
}
l := len ( b )
if l != 0 {
t . Errorf ( "expected empty remainder, got length %v: %v" , l , b )
}
2023-04-09 16:35:26 +02:00
r , err := vm . Render ( )
2023-04-03 00:53:21 +02:00
if err != nil {
t . Fatal ( err )
}
2023-04-09 16:35:26 +02:00
expect := "inky pinky blinky clyde\n0:one\n1:two"
2023-04-03 00:53:21 +02:00
if r != expect {
t . Fatalf ( "expected:\n\t%s\ngot:\n\t%s\n" , expect , r )
}
}
2023-04-03 10:11:44 +02:00
func TestRunMenuBrowse ( t * testing . T ) {
log . Printf ( "This test is incomplete, it must check the output of a menu browser once one is implemented. For now it only checks whether it can execute the runner endpoints for the instrucitons." )
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
2023-04-09 16:35:26 +02:00
vm := NewVm ( & st , & rs , ca , nil )
2023-04-03 10:11:44 +02:00
var err error
b := NewLine ( nil , MOVE , [ ] string { "foo" } , nil , nil )
b = NewLine ( b , MOUT , [ ] string { "0" , "one" } , nil , nil )
b = NewLine ( b , MOUT , [ ] string { "1" , "two" } , nil , nil )
2023-04-06 10:54:51 +02:00
b = NewLine ( b , HALT , nil , nil , nil )
2023-04-03 10:11:44 +02:00
2023-04-09 10:44:32 +02:00
b , err = vm . Run ( b , context . TODO ( ) )
2023-04-03 10:11:44 +02:00
if err != nil {
t . Error ( err )
}
l := len ( b )
if l != 0 {
t . Errorf ( "expected empty remainder, got length %v: %v" , l , b )
}
2023-04-09 16:35:26 +02:00
r , err := vm . Render ( )
2023-04-03 10:11:44 +02:00
if err != nil {
t . Fatal ( err )
}
2023-04-09 16:35:26 +02:00
expect := "inky pinky blinky clyde\n0:one\n1:two"
2023-04-03 10:11:44 +02:00
if r != expect {
t . Fatalf ( "expected:\n\t%s\ngot:\n\t%s\n" , expect , r )
}
}
2023-04-06 10:54:51 +02:00
2023-04-10 08:54:52 +02:00
func TestRunReturn ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
ca := cache . NewCache ( )
vm := NewVm ( & st , & rs , ca , nil )
var err error
st . Down ( "root" )
st . SetInput ( [ ] byte ( "0" ) )
b := NewLine ( nil , INCMP , [ ] string { "0" , "bar" } , nil , nil )
b = NewLine ( b , HALT , nil , nil , nil )
b = NewLine ( b , INCMP , [ ] string { "1" , "_" } , nil , nil )
b = NewLine ( b , HALT , nil , nil , nil )
ctx := context . TODO ( )
b , err = vm . Run ( b , ctx )
if err != nil {
t . Fatal ( err )
}
location , _ := st . Where ( )
if location != "bar" {
t . Fatalf ( "expected location 'bar', got '%s'" , location )
}
st . SetInput ( [ ] byte ( "1" ) )
b , err = vm . Run ( b , ctx )
if err != nil {
t . Fatal ( err )
}
location , _ = st . Where ( )
if location != "root" {
t . Fatalf ( "expected location 'foo', got '%s'" , location )
}
}