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
}
func getOne ( ctx context . Context ) ( string , error ) {
return "one" , nil
}
func getTwo ( ctx context . Context ) ( string , error ) {
return "two" , nil
}
func getDyn ( ctx context . Context ) ( string , error ) {
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
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-09 10:44:32 +02:00
func ( r TestResource ) getInput ( 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 ( )
vm := NewVm ( & st , & rs , ca , nil , 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 ( )
pg := render . NewPage ( ca , rs )
vm := NewVm ( & st , & rs , ca , nil , pg )
st . Down ( "barbarbar" )
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-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-09 10:44:32 +02:00
m , err := ca . Get ( )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
2023-04-09 10:44:32 +02:00
r , err := pg . RenderTemplate ( "foo" , m , 0 )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
expect := "inky pinky blinky clyde"
if r != expect {
t . Errorf ( "Expected %v, got %v" , [ ] byte ( expect ) , [ ] byte ( r ) )
}
2023-04-09 10:44:32 +02:00
r , err = pg . RenderTemplate ( "bar" , m , 0 )
2023-04-02 13:12:06 +02:00
if err == nil {
t . Errorf ( "expected error for render of bar: %v" , err )
}
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 {
t . Error ( err )
}
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 {
t . Error ( err )
}
2023-04-09 10:44:32 +02:00
m , err = ca . Get ( )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
2023-04-09 10:44:32 +02:00
r , err = pg . RenderTemplate ( "bar" , m , 0 )
2023-04-02 13:12:06 +02:00
if err != nil {
t . Error ( err )
}
expect = "inky pinky one blinky two clyde"
if r != expect {
t . Errorf ( "Expected %v, got %v" , expect , r )
}
}
func TestRunMultiple ( t * testing . T ) {
st := state . NewState ( 5 )
rs := TestResource { }
2023-04-09 10:44:32 +02:00
ca := cache . NewCache ( )
vm := NewVm ( & st , & rs , ca , nil , nil )
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 ( )
pg := render . NewPage ( ca , rs )
vm := NewVm ( & st , & rs , ca , nil , pg )
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 10:44:32 +02:00
r , err := pg . Val ( "dyn" )
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
}
if r != "three" {
2023-04-06 10:54:51 +02:00
t . Fatalf ( "expected result 'three', 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
}
2023-04-09 10:44:32 +02:00
r , err = pg . Val ( "dyn" )
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
}
log . Printf ( "dun now %s" , r )
if r != "baz" {
2023-04-06 10:54:51 +02:00
t . Fatalf ( "expected result 'baz', got %v" , r )
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 ( )
vm := NewVm ( & st , & rs , ca , nil , nil )
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 ( )
vm := NewVm ( & st , & rs , ca , nil , nil )
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 ( )
pg := render . NewPage ( ca , rs )
vm := NewVm ( & st , & rs , ca , nil , pg )
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 ( )
mn := render . NewMenu ( )
vm := NewVm ( & st , & rs , ca , mn , 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 ( )
mn := render . NewMenu ( )
vm := NewVm ( & st , & rs , ca , mn , 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 10:44:32 +02:00
r , err := mn . Render ( 0 )
2023-04-03 00:53:21 +02:00
if err != nil {
t . Fatal ( err )
}
expect := "0:one\n1:two"
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 ( )
mn := render . NewMenu ( )
vm := NewVm ( & st , & rs , ca , mn , 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 10:44:32 +02:00
r , err := mn . Render ( 0 )
2023-04-03 10:11:44 +02:00
if err != nil {
t . Fatal ( err )
}
expect := "0:one\n1:two"
if r != expect {
t . Fatalf ( "expected:\n\t%s\ngot:\n\t%s\n" , expect , r )
}
}
2023-04-06 10:54:51 +02:00