Skip to content

Commit

Permalink
new testing setup for cue helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
verdverm committed May 26, 2020
1 parent f5dd77d commit c174d4b
Show file tree
Hide file tree
Showing 27 changed files with 1,047 additions and 262 deletions.
32 changes: 24 additions & 8 deletions lib/cuetils/from-args.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package cuetils

import (
"cuelang.org/go/cue/load"

"github.com/hofstadter-io/hof/cmd/hof/flags"
)

// CueRuntimeFromArgs builds up a CueRuntime
// by processing the args passed in
func CueRuntimeFromArgs(args []string) (crt *CueRuntime, err error) {
func CueRuntimeFromEntrypoints(entrypoints []string) (crt *CueRuntime, err error) {
crt = &CueRuntime{
Entrypoints: args,
Entrypoints: entrypoints,
CueConfig: &load.Config{
ModuleRoot: "",
Module: "",
Expand All @@ -29,14 +31,28 @@ func CueRuntimeFromArgs(args []string) (crt *CueRuntime, err error) {

// CueRuntimeFromArgsAndFlags builds up a CueRuntime
// by processing the args passed in AND the current flag values
func CueRuntimeFromArgsAndFlags(args []string) (crt *CueRuntime, err error) {
crt = &CueRuntime{
Entrypoints: args,
func CueRuntimeFromEntrypointsAndFlags(entrypoints []string) (crt *CueRuntime, err error) {
cfg := &load.Config{
ModuleRoot: "",
Module: "",
Package: "",
Dir: "",
BuildTags: []string{},
Tests: false,
Tools: false,
DataFiles: false,
Overlay: map[string]load.Source{},
}

// package?
if flags.RootPackagePflag != "" {
cfg.Package = flags.RootPackagePflag
}

// XXX TODO XXX
// Buildup out arg to load.Instances second arg
// Add this configuration to our runtime struct
crt = &CueRuntime{
Entrypoints: entrypoints,
CueConfig: cfg,
}

err = crt.Load()

Expand Down
53 changes: 51 additions & 2 deletions lib/cuetils/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,57 @@ import (
"os"
"strings"

// "cuelang.org/go/cue"
"cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
// "cuelang.org/go/cue/format"
"cuelang.org/go/cue/format"
"golang.org/x/text/language"
"golang.org/x/text/message"
)


func PrintCueValue(val cue.Value) (string, error) {
node := val.Syntax(
cue.Attributes(true),
cue.Concrete(false),
cue.Definitions(true),
cue.Docs(true),
cue.Hidden(true),
cue.Final(),
cue.Optional(false),
)

bytes, err := format.Node(
node,
format.TabIndent(false),
format.UseSpaces(2),
format.Simplify(),
)
if err != nil {
return "", err
}

return string(bytes), nil
}

func ValueToSyntaxString(val cue.Value) (string, error) {
src, err := format.Node(val.Syntax())
str := string(src)
return str, err
}

func (CRT *CueRuntime) ParseCueExpr(expr string) (cue.Value, error) {
inst, err := CRT.CueRuntime.Compile("", expr)
if err != nil {
return cue.Value{}, err
}
val := inst.Value()
if val.Err() != nil {
return val, val.Err()
}

return val, nil
}

func (CRT *CueRuntime) PrintValue() error {
// Get top level struct from cuelang
S, err := CRT.CueValue.Struct()
Expand Down Expand Up @@ -74,3 +118,8 @@ func PrintCueError(err error) {
fmt.Println(s)

}
func (CR *CueRuntime) PrintCueErrors() {
for _, err := range CR.CueErrors {
PrintCueError(err)
}
}
55 changes: 55 additions & 0 deletions lib/cuetils/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,37 @@ import (
"cuelang.org/go/cue/load"
)

type CueSyntaxOptions struct {
Attributes bool
Concrete bool
Definitions bool
Docs bool
Hidden bool
Optional bool
}

func (CSO CueSyntaxOptions) MakeOpts() []cue.Option {
return []cue.Option{
cue.Attributes(CSO.Attributes),
cue.Concrete(CSO.Concrete),
cue.Definitions(CSO.Definitions),
cue.Docs(CSO.Docs),
cue.Hidden(CSO.Hidden),
cue.Optional(CSO.Optional),
}
}

var (
DefaultSyntaxOpts = CueSyntaxOptions{
Attributes: true,
Concrete: false,
Definitions: true,
Docs: true,
Hidden: true,
Optional: true,
}
)

type CueRuntime struct {

Entrypoints []string
Expand All @@ -23,13 +54,37 @@ type CueRuntime struct {
CueConfig *load.Config
BuildInstances []*build.Instance
CueErrors []error
FieldOpts []cue.Option

CueInstance *cue.Instance
CueValue cue.Value
Value interface{}

}

func (CRT *CueRuntime) ConvertToValue(in interface{}) (cue.Value, error) {
O, ook := in.(cue.Value)
if !ook {
switch T := in.(type) {
case string:
i, err := CRT.CueRuntime.Compile("", in)
if err != nil {
return O, err
}
v := i.Value()
if v.Err() != nil {
return v, v.Err()
}
O = v

default:
return O, fmt.Errorf("unknown type %v in convertToValue(in)", T)
}
}

return O, nil
}

func (CRT *CueRuntime) Load() (err error) {
return CRT.load()
}
Expand Down
189 changes: 189 additions & 0 deletions lib/cuetils/tsuite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package cuetils

import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"

"cuelang.org/go/cue"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type TestOpFunc func(name string, args cue.Value) (cue.Value, error)

type TestSuite struct {
suite.Suite

Op TestOpFunc

CRT *CueRuntime

TestdataDirs []string
Entrypoints []string
}

func NewTestSuite(testdirs []string, op TestOpFunc) *TestSuite {
ts := new(TestSuite)
ts.TestdataDirs = testdirs
ts.Op = op
return ts
}

var DEFAULT_TESTDATA_DIRS = []string{"testdata"}

func (TS *TestSuite) DoTestOp(name string, args cue.Value) (cue.Value, error) {
if TS.Op == nil {
return cue.Value{}, fmt.Errorf("DoTestOpFunc not implemented")
}
return TS.Op(name, args)
}

func (TS *TestSuite) SetupCue() (err error) {
if len(TS.Entrypoints) == 0 {
err := TS.SetupEntrypoints()
if err != nil {
return err
}
}

TS.CRT, err = CueRuntimeFromEntrypoints(TS.Entrypoints)
if err != nil {
TS.CRT.PrintCueErrors()
}

return err
}

func (TS *TestSuite) SetupEntrypoints() (err error) {
if len(TS.TestdataDirs) == 0 {
TS.TestdataDirs = DEFAULT_TESTDATA_DIRS
}

entrypoints := []string{}
for _, dir := range TS.TestdataDirs {
fis, err := ioutil.ReadDir(dir)
if err != nil {
return err
}
for _, fi := range fis {
if strings.HasSuffix(fi.Name(), ".cue") {
entrypoints = append(entrypoints, filepath.Join(dir, fi.Name()))
}
}
}

TS.Entrypoints = entrypoints
return nil
}

func (TS *TestSuite) RunCases(paths []string) (err error) {
for _, p := range paths {
err = TS.RunCase(p)
if err != nil {
return err
}
}
return nil
}

func (TS *TestSuite) RunCase(path string) (err error) {
CV := TS.CRT.CueValue
CS, err := CV.Struct()
assert.Nil(TS.T(), err, fmt.Sprintf("Test data should be a struct, but is a %q", CV.Kind()))

fi, err := CS.FieldByName(path, true)
assert.Nil(TS.T(), err, "Getting test cases should not return an error")

V := fi.Value
S, err := V.Struct()
assert.Nil(TS.T(), err, fmt.Sprintf("Test case %q should be a struct, but is a %q", path, V.Kind()))

iter := S.Fields()
for iter.Next() {
label := iter.Label()
value := iter.Value()
TS.RunGroup(path, label, value)
}

return nil
}

func (TS *TestSuite) RunGroup(cases, group string, V cue.Value) (err error) {
name := strings.Join([]string{cases, group}, "/")

S, err := V.Struct()
assert.Nil(TS.T(), err, fmt.Sprintf("Test group %q should be a struct, but is a %q", name, V.Kind()))
iter := S.Fields()
for iter.Next() {
label := iter.Label()
value := iter.Value()
TS.RunTestCase(cases, group, label, value)
}
return nil
}

func (TS *TestSuite) RunTestCase(cases, group, test string, V cue.Value) (err error) {
name := strings.Join([]string{cases, group, test}, "/")

vSyn, err := PrintCueValue(V)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should print without an issue", name))
if err != nil {
return err
}

S, err := V.Struct()
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should be a struct, but is a %q", name, V.Kind()))
if err != nil {
return err
}

args, err := S.FieldByName("args", true)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should have an 'args' field", name))
if err != nil {
return err
}

ex, err := S.FieldByName("ex", true)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should have an 'ex' field", name))
if err != nil {
return err
}

exSyn, err := PrintCueValue(ex.Value)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should syntax the 'ex' field", name))
if err != nil {
return err
}

ret, err := TS.DoTestOp(name, args.Value)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should oper without an error", name))
if err != nil {
PrintCueError(err)
return err
}

retSyn, err := PrintCueValue(ret)
assert.Nil(TS.T(), err, fmt.Sprintf("Test item %q should syntax the op return", name))
if err != nil {
return err
}

assert.True(TS.T(), exSyn == retSyn, fmt.Sprintf("Test item %q should return syntax that matches expected", name))
if exSyn != retSyn {
fmt.Println("======== ERROR =========")
fmt.Println(name)
fmt.Println("-------- INPUT ---------")
fmt.Println(vSyn)
fmt.Println("-------- EXPECT --------")
fmt.Println(exSyn)
fmt.Println("-------- OUTPUT --------")
fmt.Println(retSyn)
fmt.Println("========================")
return fmt.Errorf("Result did not match expected in:", name)
}

return nil
}

0 comments on commit c174d4b

Please sign in to comment.