Add persisted state engine runner
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.defalsify.org/festive/cache"
|
||||
"git.defalsify.org/festive/resource"
|
||||
"git.defalsify.org/festive/state"
|
||||
@@ -11,7 +13,11 @@ func NewDefaultEngine(dir string) Engine {
|
||||
st := state.NewState(0)
|
||||
rs := resource.NewFsResource(dir)
|
||||
ca := cache.NewCache()
|
||||
return NewEngine(Config{}, &st, &rs, ca)
|
||||
cfg := Config{
|
||||
Root: "root",
|
||||
}
|
||||
ctx := context.TODO()
|
||||
return NewEngine(cfg, &st, &rs, ca, ctx)
|
||||
}
|
||||
|
||||
// NewSizedEngine is a convenience function to instantiate a filesystem-backed engine with a specified output constraint.
|
||||
@@ -21,6 +27,8 @@ func NewSizedEngine(dir string, size uint32) Engine {
|
||||
ca := cache.NewCache()
|
||||
cfg := Config{
|
||||
OutputSize: size,
|
||||
Root: "root",
|
||||
}
|
||||
return NewEngine(cfg, &st, &rs, ca)
|
||||
ctx := context.TODO()
|
||||
return NewEngine(cfg, &st, &rs, ca, ctx)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ import (
|
||||
// Config globally defines behavior of all components driven by the engine.
|
||||
type Config struct {
|
||||
OutputSize uint32 // Maximum size of output from a single rendered page
|
||||
SessionId string
|
||||
Root string
|
||||
FlagCount uint32
|
||||
CacheSize uint32
|
||||
}
|
||||
|
||||
// Engine is an execution engine that handles top-level errors when running client inputs against code in the bytecode buffer.
|
||||
@@ -24,10 +28,11 @@ type Engine struct {
|
||||
rs resource.Resource
|
||||
ca cache.Memory
|
||||
vm *vm.Vm
|
||||
initd bool
|
||||
}
|
||||
|
||||
// NewEngine creates a new Engine
|
||||
func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory) Engine {
|
||||
func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory, ctx context.Context) Engine {
|
||||
var szr *render.Sizer
|
||||
if cfg.OutputSize > 0 {
|
||||
szr = render.NewSizer(cfg.OutputSize)
|
||||
@@ -38,6 +43,9 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
|
||||
ca: ca,
|
||||
vm: vm.NewVm(st, rs, ca, szr),
|
||||
}
|
||||
if cfg.Root != "" {
|
||||
engine.Init(cfg.Root, ctx)
|
||||
}
|
||||
return engine
|
||||
}
|
||||
|
||||
@@ -45,6 +53,13 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
|
||||
//
|
||||
// It loads and executes code for the start node.
|
||||
func(en *Engine) Init(sym string, ctx context.Context) error {
|
||||
if en.initd {
|
||||
log.Printf("already initialized")
|
||||
return nil
|
||||
}
|
||||
if sym == "" {
|
||||
return fmt.Errorf("start sym empty")
|
||||
}
|
||||
err := en.st.SetInput([]byte{})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -55,6 +70,7 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
en.st.SetCode(b)
|
||||
en.initd = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,18 @@ func(fs FsWrapper) inky(sym string, ctx context.Context) (string, error) {
|
||||
return "tinkywinky", nil
|
||||
}
|
||||
|
||||
func(fs FsWrapper) pinky(sym string, ctx context.Context) (string, error) {
|
||||
return "xyzzy", nil
|
||||
}
|
||||
|
||||
func(fs FsWrapper) FuncFor(sym string) (resource.EntryFunc, error) {
|
||||
switch sym {
|
||||
case "one":
|
||||
return fs.one, nil
|
||||
case "inky":
|
||||
return fs.inky, nil
|
||||
case "pinky":
|
||||
return fs.pinky, nil
|
||||
}
|
||||
return nil, fmt.Errorf("function for %v not found", sym)
|
||||
}
|
||||
@@ -75,7 +81,7 @@ func TestEngineInit(t *testing.T) {
|
||||
rs := NewFsWrapper(dataDir, &st)
|
||||
ca := cache.NewCache().WithCacheSize(1024)
|
||||
|
||||
en := NewEngine(Config{}, &st, &rs, ca)
|
||||
en := NewEngine(Config{}, &st, &rs, ca, ctx)
|
||||
err := en.Init("root", ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -129,7 +135,7 @@ func TestEngineExecInvalidInput(t *testing.T) {
|
||||
rs := NewFsWrapper(dataDir, &st)
|
||||
ca := cache.NewCache().WithCacheSize(1024)
|
||||
|
||||
en := NewEngine(Config{}, &st, &rs, ca)
|
||||
en := NewEngine(Config{}, &st, &rs, ca, ctx)
|
||||
err := en.Init("root", ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -18,13 +18,8 @@ import (
|
||||
// Any error not handled by the engine will terminate the oop and return an error.
|
||||
//
|
||||
// Rendered output is written to the provided writer.
|
||||
func Loop(en *Engine, startSym string, ctx context.Context, reader io.Reader, writer io.Writer) error {
|
||||
err := en.Init(startSym, ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot init: %v\n", err)
|
||||
}
|
||||
|
||||
err = en.WriteResult(writer, ctx)
|
||||
func Loop(en *Engine, reader io.Reader, writer io.Writer, ctx context.Context) error {
|
||||
err := en.WriteResult(writer, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,8 +19,11 @@ func TestLoopTop(t *testing.T) {
|
||||
st := state.NewState(0)
|
||||
rs := resource.NewFsResource(dataDir)
|
||||
ca := cache.NewCache().WithCacheSize(1024)
|
||||
|
||||
en := NewEngine(Config{}, &st, &rs, ca)
|
||||
|
||||
cfg := Config{
|
||||
Root: "root",
|
||||
}
|
||||
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||
err := en.Init("root", ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -36,7 +39,7 @@ func TestLoopTop(t *testing.T) {
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||
|
||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
||||
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -53,7 +56,10 @@ func TestLoopBackForth(t *testing.T) {
|
||||
rs := resource.NewFsResource(dataDir)
|
||||
ca := cache.NewCache().WithCacheSize(1024)
|
||||
|
||||
en := NewEngine(Config{}, &st, &rs, ca)
|
||||
cfg := Config{
|
||||
Root: "root",
|
||||
}
|
||||
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||
err := en.Init("root", ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -70,7 +76,7 @@ func TestLoopBackForth(t *testing.T) {
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||
|
||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
||||
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -85,8 +91,9 @@ func TestLoopBrowse(t *testing.T) {
|
||||
|
||||
cfg := Config{
|
||||
OutputSize: 68,
|
||||
Root: "root",
|
||||
}
|
||||
en := NewEngine(cfg, &st, &rs, ca)
|
||||
en := NewEngine(cfg, &st, &rs, ca, ctx)
|
||||
err := en.Init("root", ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -104,7 +111,7 @@ func TestLoopBrowse(t *testing.T) {
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
log.Printf("running with input: %s", inputBuf.Bytes())
|
||||
|
||||
err = Loop(&en, "root", ctx, inputBuf, outputBuf)
|
||||
err = Loop(&en, inputBuf, outputBuf, ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
28
engine/persist.go
Normal file
28
engine/persist.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"git.defalsify.org/festive/persist"
|
||||
"git.defalsify.org/festive/resource"
|
||||
)
|
||||
|
||||
func RunPersisted(cfg Config, rs resource.Resource, pr persist.Persister, input []byte, w io.Writer, ctx context.Context) error {
|
||||
err := pr.Load(cfg.SessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st := pr.GetState()
|
||||
log.Printf("st %v", st)
|
||||
en := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx)
|
||||
|
||||
if len(input) > 0 {
|
||||
_, err = en.Exec(input, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
68
engine/persist_test.go
Normal file
68
engine/persist_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.defalsify.org/festive/cache"
|
||||
"git.defalsify.org/festive/persist"
|
||||
"git.defalsify.org/festive/state"
|
||||
)
|
||||
|
||||
func TestPersist(t *testing.T) {
|
||||
generateTestData(t)
|
||||
cfg := Config{
|
||||
OutputSize: 128,
|
||||
SessionId: "xyzzy",
|
||||
Root: "root",
|
||||
}
|
||||
rs := NewFsWrapper(dataDir, nil)
|
||||
|
||||
persistDir, err := ioutil.TempDir("", "festive_engine_persist")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
st := state.NewState(3)
|
||||
ca := cache.NewCache().WithCacheSize(1024)
|
||||
pr := persist.NewFsPersister(persistDir).WithContent(&st, ca)
|
||||
|
||||
w := bytes.NewBuffer(nil)
|
||||
ctx := context.TODO()
|
||||
|
||||
|
||||
err = RunPersisted(cfg, rs, pr, []byte{}, w, ctx)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := state.NewState(cfg.FlagCount)
|
||||
ca := cache.NewCache()
|
||||
if cfg.CacheSize > 0 {
|
||||
ca = ca.WithCacheSize(cfg.CacheSize)
|
||||
}
|
||||
pr = persist.NewFsPersister(persistDir).WithContent(&st, ca)
|
||||
err = pr.Save(cfg.SessionId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
pr = persist.NewFsPersister(persistDir)
|
||||
inputs := []string{
|
||||
"",
|
||||
"1",
|
||||
"2",
|
||||
"00",
|
||||
}
|
||||
for _, v := range inputs {
|
||||
err = RunPersisted(cfg, rs, pr, []byte(v), w, ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user