From d9356a48a5f3af7ac5b6d9a1c8e7e31ce717c42f Mon Sep 17 00:00:00 2001 From: Elara6331 Date: Sun, 11 Feb 2024 19:04:07 -0800 Subject: [PATCH] Add more tests --- expr_test.go | 52 ---------- func_test.go | 124 ++++++++++++++++++++++++ structs_test.go | 248 ++++++++++++++++++++++++++++++++++++++++++++++++ test_utils.go | 48 ++++++++++ 4 files changed, 420 insertions(+), 52 deletions(-) create mode 100644 func_test.go create mode 100644 structs_test.go create mode 100644 test_utils.go diff --git a/expr_test.go b/expr_test.go index b22a570..8f05372 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1,9 +1,7 @@ package salix import ( - "strings" "testing" - "time" ) func TestAdd(t *testing.T) { @@ -139,59 +137,9 @@ func TestNot(t *testing.T) { } } -func TestIndex(t *testing.T) { - res := execStr(t, `#(x[0]) #(y["hello"])`, map[string]any{ - "x": []int{0, 1, 2}, - "y": map[string]string{"hello": "world"}, - }) - if res != "0 world" { - t.Errorf("Expected %q, got %q", "equal non-equal", res) - } -} - -func TestFuncCall(t *testing.T) { - res := execStr(t, `#(toUpper("hello"))`, nil) - if res != "HELLO" { - t.Errorf("Expected %q, got %q", "HELLO", res) - } -} - func TestAssignment(t *testing.T) { res := execStr(t, `#(x = 4)#(x)`, nil) if res != "4" { t.Errorf("Expected %q, got %q", "4", res) } } - -func TestMethodCall(t *testing.T) { - res := execStr(t, `#(t.String())`, map[string]any{ - "t": time.Date(2023, time.April, 26, 0, 0, 0, 0, time.UTC), - }) - if res != "2023-04-26 00:00:00 +0000 UTC" { - t.Errorf("Expected %q, got %q", "2023-04-26 00:00:00 +0000 UTC", res) - } -} - -func TestVariadic(t *testing.T) { - res := execStr(t, `#(sprintf("%s %d", x, y))`, map[string]any{ - "x": "test", - "y": 4, - }) - if res != "test 4" { - t.Errorf("Expected %q, got %q", "test 4", res) - } -} - -func execStr(t *testing.T, tmplStr string, vars map[string]any) string { - t.Helper() - tmpl, err := New().ParseString("test", tmplStr) - if err != nil { - t.Fatal(err) - } - sb := &strings.Builder{} - err = tmpl.WithVarMap(vars).Execute(sb) - if err != nil { - t.Fatal(err) - } - return sb.String() -} diff --git a/func_test.go b/func_test.go new file mode 100644 index 0000000..15771d0 --- /dev/null +++ b/func_test.go @@ -0,0 +1,124 @@ +package salix + +import ( + "errors" + "strings" + "testing" + + "go.elara.ws/salix/ast" +) + +func TestFuncCall(t *testing.T) { + const expected = 1 + 2i + fn := func() complex128 { return expected } + + // test() + ast := ast.FuncCall{ + Name: ast.Ident{Value: "test", Position: testPos(t)}, + Position: testPos(t), + } + + tmpl := testTmpl(t) + val, err := tmpl.execFuncCall(ast, map[string]any{"test": fn}) + if err != nil { + t.Fatalf("execFuncCall error: %s", err) + } + + if val.(complex128) != expected { + t.Errorf("Expected %v, got %v", expected, val) + } +} + +func TestFuncCallVariadic(t *testing.T) { + const expected = "Hello, World" + concat := func(params ...string) string { return strings.Join(params, ", ") } + + // concat("Hello", "World") + ast := ast.FuncCall{ + Name: ast.Ident{Value: "concat", Position: testPos(t)}, + Params: []ast.Node{ + ast.String{Value: "Hello", Position: testPos(t)}, + ast.String{Value: "World", Position: testPos(t)}, + }, + Position: testPos(t), + } + + tmpl := testTmpl(t) + val, err := tmpl.execFuncCall(ast, map[string]any{"concat": concat}) + if err != nil { + t.Fatalf("execFuncCall error: %s", err) + } + + if val.(string) != expected { + t.Errorf("Expected %q, got %q", expected, val) + } +} + +func TestFuncCallError(t *testing.T) { + var expectedErr = errors.New("expected error") + fn := func() error { return expectedErr } + + // test() + ast := ast.FuncCall{ + Name: ast.Ident{Value: "test", Position: testPos(t)}, + Position: testPos(t), + } + + tmpl := testTmpl(t) + _, err := tmpl.execFuncCall(ast, map[string]any{"test": fn}) + if !errors.Is(err, expectedErr) { + t.Errorf("Expected %q, got %q", expectedErr, err) + } +} + +func TestFuncCallMultiReturn(t *testing.T) { + const expected = "test" + fn := func() (string, error) { return expected, nil } + + // test() + ast := ast.FuncCall{ + Name: ast.Ident{Value: "test", Position: testPos(t)}, + Position: testPos(t), + } + + tmpl := testTmpl(t) + val, err := tmpl.execFuncCall(ast, map[string]any{"test": fn}) + if err != nil { + t.Fatalf("execFuncCall error: %s", err) + } + + if val.(string) != expected { + t.Errorf("Expected %q, got %q", expected, val) + } +} + +func TestFuncCallMultiReturnError(t *testing.T) { + var expectedErr = errors.New("expected error") + fn := func() (string, error) { return "", expectedErr } + + // test() + ast := ast.FuncCall{ + Name: ast.Ident{Value: "test", Position: testPos(t)}, + Position: testPos(t), + } + + tmpl := testTmpl(t) + _, err := tmpl.execFuncCall(ast, map[string]any{"test": fn}) + if !errors.Is(err, expectedErr) { + t.Errorf("Expected %q, got %q", expectedErr, err) + } +} + +func TestFuncCallNil(t *testing.T) { + // test() + ast := ast.FuncCall{ + Name: ast.Ident{Value: "test", Position: testPos(t)}, + Position: testPos(t), + } + + tmpl := testTmpl(t) + _, err := tmpl.execFuncCall(ast, map[string]any{"test": nil}) + if err == nil { + t.Error("Expected error, got nil") + } +} diff --git a/structs_test.go b/structs_test.go new file mode 100644 index 0000000..588c2ee --- /dev/null +++ b/structs_test.go @@ -0,0 +1,248 @@ +package salix + +import ( + "fmt" + "testing" + + "go.elara.ws/salix/ast" +) + +func TestSliceGetIndex(t *testing.T) { + testSlice := []any{1, "2", 3.0} + + tmpl := testTmpl(t) + for index, expected := range testSlice { + t.Run(fmt.Sprint(index), func(t *testing.T) { + // test[index] + ast := ast.Index{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Index: ast.Ident{Value: "index", Position: testPos(t)}, + Position: testPos(t), + } + + val, err := tmpl.getIndex(ast, map[string]any{"test": testSlice, "index": index}) + if err != nil { + t.Fatalf("getIndex error: %s", err) + } + + if val != expected { + t.Errorf("Expected %v, got %v", expected, val) + } + }) + } +} + +func TestSliceGetIndexOutOfRange(t *testing.T) { + testSlice := []any{} + tmpl := testTmpl(t) + + // test[0.0] + ast := ast.Index{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Index: ast.Float{Value: 0, Position: testPos(t)}, + Position: testPos(t), + } + + _, err := tmpl.getIndex(ast, map[string]any{"test": testSlice}) + if err == nil { + t.Errorf("Expected error, got nil") + } +} + +func TestMapGetIndex(t *testing.T) { + testMap := map[any]any{1: "2", 3.0: 4, "5": 6.0} + + tmpl := testTmpl(t) + for index, expected := range testMap { + t.Run(fmt.Sprint(index), func(t *testing.T) { + // test[index] + ast := ast.Index{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Index: ast.Ident{Value: "index", Position: testPos(t)}, + Position: testPos(t), + } + + val, err := tmpl.getIndex(ast, map[string]any{"test": testMap, "index": index}) + if err != nil { + t.Fatalf("getIndex error: %s", err) + } + + if val != expected { + t.Errorf("Expected %v, got %v", expected, val) + } + }) + } +} + +func TestMapGetIndexNotFound(t *testing.T) { + testMap := map[string]any{} + tmpl := testTmpl(t) + + // test["key"] + ast := ast.Index{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Index: ast.String{Value: "key", Position: testPos(t)}, + Position: testPos(t), + } + + _, err := tmpl.getIndex(ast, map[string]any{"test": testMap}) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestGetIndexNil(t *testing.T) { + tmpl := testTmpl(t) + + // test["key"] + ast := ast.Index{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Index: ast.String{Value: "key", Position: testPos(t)}, + Position: testPos(t), + } + + _, err := tmpl.getIndex(ast, map[string]any{"test": nil}) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestGetField(t *testing.T) { + testStruct := struct { + X int + Y string + Z struct{ A int } + }{ + X: 1, + Y: "2", + Z: struct{ A int }{A: 1}, + } + + testCases := map[string]any{ + "X": testStruct.X, + "Y": testStruct.Y, + "Z": testStruct.Z, + } + + tmpl := testTmpl(t) + for fieldName, expected := range testCases { + t.Run(fieldName, func(t *testing.T) { + // test.Field + ast := ast.FieldAccess{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: fieldName, Position: testPos(t)}, + Position: testPos(t), + } + + val, err := tmpl.getField(ast, map[string]any{"test": testStruct}) + if err != nil { + t.Fatalf("getField error: %s", err) + } + + if val != expected { + t.Errorf("Expected %v, got %v", expected, val) + } + }) + } +} + +func TestGetFieldNotFound(t *testing.T) { + testStruct := struct{}{} + tmpl := testTmpl(t) + + // test.Field + ast := ast.FieldAccess{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: "Field", Position: testPos(t)}, + Position: testPos(t), + } + + _, err := tmpl.getField(ast, map[string]any{"test": testStruct}) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestGetFieldNil(t *testing.T) { + tmpl := testTmpl(t) + + // test.Field + ast := ast.FieldAccess{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: "Field", Position: testPos(t)}, + Position: testPos(t), + } + + _, err := tmpl.getField(ast, map[string]any{"test": nil}) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestMethodCall(t *testing.T) { + executed := false + testStruct := struct{ Method func(bool) bool }{ + Method: func(b bool) bool { + executed = b + return executed + }, + } + + // test.Method(true) + ast := ast.MethodCall{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: "Method", Position: testPos(t)}, + Params: []ast.Node{ + ast.Bool{Value: true, Position: testPos(t)}, + }, + Position: testPos(t), + } + + tmpl := testTmpl(t) + val, err := tmpl.execMethodCall(ast, map[string]any{"test": testStruct}) + if err != nil { + t.Fatalf("execMethodCall error: %s", err) + } + + if !executed || !val.(bool) { + t.Error("Expected method to execute, got false") + } +} + +func TestMethodCallNotFound(t *testing.T) { + tmpl := testTmpl(t) + + // test.Method(true) + ast := ast.MethodCall{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: "Method", Position: testPos(t)}, + Params: []ast.Node{ + ast.Bool{Value: true, Position: testPos(t)}, + }, + Position: testPos(t), + } + + _, err := tmpl.execMethodCall(ast, map[string]any{"test": struct{}{}}) + if err == nil { + t.Error("Expected error, got nil") + } +} + +func TestMethodCallNil(t *testing.T) { + tmpl := testTmpl(t) + + // test.Method(true) + ast := ast.MethodCall{ + Value: ast.Ident{Value: "test", Position: testPos(t)}, + Name: ast.Ident{Value: "Method", Position: testPos(t)}, + Params: []ast.Node{ + ast.Bool{Value: true, Position: testPos(t)}, + }, + Position: testPos(t), + } + + _, err := tmpl.execMethodCall(ast, map[string]any{"test": nil}) + if err == nil { + t.Error("Expected error, got nil") + } +} diff --git a/test_utils.go b/test_utils.go new file mode 100644 index 0000000..622481d --- /dev/null +++ b/test_utils.go @@ -0,0 +1,48 @@ +package salix + +import ( + "strings" + "testing" + + "go.elara.ws/salix/ast" +) + +func testTmpl(t *testing.T) Template { + t.Helper() + + return Template{ + ns: &Namespace{ + tmpls: map[string]Template{}, + tags: map[string]Tag{}, + vars: map[string]any{}, + }, + name: t.Name(), + tags: map[string]Tag{}, + vars: map[string]any{}, + macros: map[string][]ast.Node{}, + } +} + +func testPos(t *testing.T) ast.Position { + t.Helper() + + return ast.Position{ + Name: t.Name(), + Line: -1, + Col: -1, + } +} + +func execStr(t *testing.T, tmplStr string, vars map[string]any) string { + t.Helper() + tmpl, err := New().ParseString("test", tmplStr) + if err != nil { + t.Fatal(err) + } + sb := &strings.Builder{} + err = tmpl.WithVarMap(vars).Execute(sb) + if err != nil { + t.Fatal(err) + } + return sb.String() +}