Add more tests
This commit is contained in:
		
							
								
								
									
										52
									
								
								expr_test.go
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								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() | ||||
| } | ||||
|   | ||||
							
								
								
									
										124
									
								
								func_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								func_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										248
									
								
								structs_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								structs_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										48
									
								
								test_utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								test_utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user