640 lines
17 KiB
Go
640 lines
17 KiB
Go
package parser_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.sharkk.net/Sharkk/Mako/parser"
|
|
)
|
|
|
|
func TestBasicStructDefinition(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string,
|
|
age: number
|
|
}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
stmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
if stmt.Name != "Person" {
|
|
t.Errorf("expected struct name 'Person', got %s", stmt.Name)
|
|
}
|
|
|
|
if len(stmt.Fields) != 2 {
|
|
t.Fatalf("expected 2 fields, got %d", len(stmt.Fields))
|
|
}
|
|
|
|
// Test first field
|
|
if stmt.Fields[0].Name != "name" {
|
|
t.Errorf("expected field name 'name', got %s", stmt.Fields[0].Name)
|
|
}
|
|
if stmt.Fields[0].TypeHint.Type == parser.TypeUnknown {
|
|
t.Fatal("expected type hint for name field")
|
|
}
|
|
if stmt.Fields[0].TypeHint.Type != parser.TypeString {
|
|
t.Errorf("expected type string, got %v", stmt.Fields[0].TypeHint.Type)
|
|
}
|
|
|
|
// Test second field
|
|
if stmt.Fields[1].Name != "age" {
|
|
t.Errorf("expected field name 'age', got %s", stmt.Fields[1].Name)
|
|
}
|
|
if stmt.Fields[1].TypeHint.Type == parser.TypeUnknown {
|
|
t.Fatal("expected type hint for age field")
|
|
}
|
|
if stmt.Fields[1].TypeHint.Type != parser.TypeNumber {
|
|
t.Errorf("expected type number, got %v", stmt.Fields[1].TypeHint.Type)
|
|
}
|
|
}
|
|
|
|
func TestEmptyStructDefinition(t *testing.T) {
|
|
input := `struct Empty {}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
stmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
if stmt.Name != "Empty" {
|
|
t.Errorf("expected struct name 'Empty', got %s", stmt.Name)
|
|
}
|
|
|
|
if len(stmt.Fields) != 0 {
|
|
t.Errorf("expected 0 fields, got %d", len(stmt.Fields))
|
|
}
|
|
}
|
|
|
|
func TestComplexStructDefinition(t *testing.T) {
|
|
input := `struct Complex {
|
|
id: number,
|
|
name: string,
|
|
active: bool,
|
|
data: table,
|
|
callback: function,
|
|
optional: any
|
|
}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
stmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
expectedTypes := []parser.Type{parser.TypeNumber, parser.TypeString, parser.TypeBool, parser.TypeTable, parser.TypeFunction, parser.TypeAny}
|
|
expectedNames := []string{"id", "name", "active", "data", "callback", "optional"}
|
|
|
|
if len(stmt.Fields) != len(expectedTypes) {
|
|
t.Fatalf("expected %d fields, got %d", len(expectedTypes), len(stmt.Fields))
|
|
}
|
|
|
|
for i, field := range stmt.Fields {
|
|
if field.Name != expectedNames[i] {
|
|
t.Errorf("field %d: expected name '%s', got '%s'", i, expectedNames[i], field.Name)
|
|
}
|
|
if field.TypeHint.Type == parser.TypeUnknown {
|
|
t.Fatalf("field %d: expected type hint", i)
|
|
}
|
|
if field.TypeHint.Type != expectedTypes[i] {
|
|
t.Errorf("field %d: expected type %v, got %v", i, expectedTypes[i], field.TypeHint.Type)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMethodDefinition(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string,
|
|
age: number
|
|
}
|
|
|
|
fn Person.getName(): string
|
|
return self.name
|
|
end
|
|
|
|
fn Person.setAge(newAge: number)
|
|
self.age = newAge
|
|
end`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 3 {
|
|
t.Fatalf("expected 3 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// First statement: struct definition
|
|
structStmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
if structStmt.Name != "Person" {
|
|
t.Errorf("expected struct name 'Person', got %s", structStmt.Name)
|
|
}
|
|
|
|
// Second statement: getter method
|
|
method1, ok := program.Statements[1].(*parser.MethodDefinition)
|
|
if !ok {
|
|
t.Fatalf("expected MethodDefinition, got %T", program.Statements[1])
|
|
}
|
|
if method1.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d, got %d", structStmt.ID, method1.StructID)
|
|
}
|
|
if method1.MethodName != "getName" {
|
|
t.Errorf("expected method name 'getName', got %s", method1.MethodName)
|
|
}
|
|
if method1.Function.ReturnType.Type == parser.TypeUnknown {
|
|
t.Fatal("expected return type for getName method")
|
|
}
|
|
if method1.Function.ReturnType.Type != parser.TypeString {
|
|
t.Errorf("expected return type string, got %v", method1.Function.ReturnType.Type)
|
|
}
|
|
if len(method1.Function.Parameters) != 0 {
|
|
t.Errorf("expected 0 parameters, got %d", len(method1.Function.Parameters))
|
|
}
|
|
|
|
// Third statement: setter method
|
|
method2, ok := program.Statements[2].(*parser.MethodDefinition)
|
|
if !ok {
|
|
t.Fatalf("expected MethodDefinition, got %T", program.Statements[2])
|
|
}
|
|
if method2.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d, got %d", structStmt.ID, method2.StructID)
|
|
}
|
|
if method2.MethodName != "setAge" {
|
|
t.Errorf("expected method name 'setAge', got %s", method2.MethodName)
|
|
}
|
|
if method2.Function.ReturnType.Type != parser.TypeUnknown {
|
|
t.Errorf("expected no return type for setAge method, got %v", method2.Function.ReturnType.Type)
|
|
}
|
|
if len(method2.Function.Parameters) != 1 {
|
|
t.Fatalf("expected 1 parameter, got %d", len(method2.Function.Parameters))
|
|
}
|
|
if method2.Function.Parameters[0].Name != "newAge" {
|
|
t.Errorf("expected parameter name 'newAge', got %s", method2.Function.Parameters[0].Name)
|
|
}
|
|
if method2.Function.Parameters[0].TypeHint.Type == parser.TypeUnknown {
|
|
t.Fatal("expected type hint for newAge parameter")
|
|
}
|
|
if method2.Function.Parameters[0].TypeHint.Type != parser.TypeNumber {
|
|
t.Errorf("expected parameter type number, got %v", method2.Function.Parameters[0].TypeHint.Type)
|
|
}
|
|
}
|
|
|
|
func TestStructConstructor(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string,
|
|
age: number
|
|
}
|
|
|
|
person = Person{name = "John", age = 30}
|
|
empty = Person{}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 3 {
|
|
t.Fatalf("expected 3 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
structStmt := program.Statements[0].(*parser.StructStatement)
|
|
|
|
// Second statement: constructor with fields
|
|
assign1, ok := program.Statements[1].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected Assignment, got %T", program.Statements[1])
|
|
}
|
|
|
|
constructor1, ok := assign1.Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("expected StructConstructor, got %T", assign1.Value)
|
|
}
|
|
|
|
if constructor1.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d, got %d", structStmt.ID, constructor1.StructID)
|
|
}
|
|
|
|
if len(constructor1.Fields) != 2 {
|
|
t.Fatalf("expected 2 fields, got %d", len(constructor1.Fields))
|
|
}
|
|
|
|
// Check name field
|
|
nameKey, ok := constructor1.Fields[0].Key.(*parser.Identifier)
|
|
if !ok {
|
|
t.Fatalf("expected Identifier for name key, got %T", constructor1.Fields[0].Key)
|
|
}
|
|
if nameKey.Value != "name" {
|
|
t.Errorf("expected key 'name', got %s", nameKey.Value)
|
|
}
|
|
testStringLiteral(t, constructor1.Fields[0].Value, "John")
|
|
|
|
// Check age field
|
|
ageKey, ok := constructor1.Fields[1].Key.(*parser.Identifier)
|
|
if !ok {
|
|
t.Fatalf("expected Identifier for age key, got %T", constructor1.Fields[1].Key)
|
|
}
|
|
if ageKey.Value != "age" {
|
|
t.Errorf("expected key 'age', got %s", ageKey.Value)
|
|
}
|
|
testNumberLiteral(t, constructor1.Fields[1].Value, 30)
|
|
|
|
// Third statement: empty constructor
|
|
assign2, ok := program.Statements[2].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected Assignment, got %T", program.Statements[2])
|
|
}
|
|
|
|
constructor2, ok := assign2.Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("expected StructConstructor, got %T", assign2.Value)
|
|
}
|
|
|
|
if constructor2.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d, got %d", structStmt.ID, constructor2.StructID)
|
|
}
|
|
|
|
if len(constructor2.Fields) != 0 {
|
|
t.Errorf("expected 0 fields, got %d", len(constructor2.Fields))
|
|
}
|
|
}
|
|
|
|
func TestNestedStructTypes(t *testing.T) {
|
|
input := `struct Address {
|
|
street: string,
|
|
city: string
|
|
}
|
|
|
|
struct Person {
|
|
name: string,
|
|
address: Address
|
|
}
|
|
|
|
person = Person{
|
|
name = "John",
|
|
address = Address{street = "Main St", city = "NYC"}
|
|
}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 3 {
|
|
t.Fatalf("expected 3 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
addressStruct := program.Statements[0].(*parser.StructStatement)
|
|
|
|
// Check Person struct has Address field type
|
|
personStruct, ok := program.Statements[1].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[1])
|
|
}
|
|
|
|
addressField := personStruct.Fields[1]
|
|
if addressField.Name != "address" {
|
|
t.Errorf("expected field name 'address', got %s", addressField.Name)
|
|
}
|
|
if addressField.TypeHint.Type != parser.TypeStruct {
|
|
t.Errorf("expected field type struct, got %v", addressField.TypeHint.Type)
|
|
}
|
|
if addressField.TypeHint.StructID != addressStruct.ID {
|
|
t.Errorf("expected struct ID %d, got %d", addressStruct.ID, addressField.TypeHint.StructID)
|
|
}
|
|
|
|
// Check nested constructor
|
|
assign, ok := program.Statements[2].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected Assignment, got %T", program.Statements[2])
|
|
}
|
|
|
|
personConstructor, ok := assign.Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("expected StructConstructor, got %T", assign.Value)
|
|
}
|
|
|
|
// Check the nested Address constructor
|
|
addressConstructor, ok := personConstructor.Fields[1].Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("expected nested StructConstructor, got %T", personConstructor.Fields[1].Value)
|
|
}
|
|
|
|
if addressConstructor.StructID != addressStruct.ID {
|
|
t.Errorf("expected nested struct ID %d, got %d", addressStruct.ID, addressConstructor.StructID)
|
|
}
|
|
|
|
if len(addressConstructor.Fields) != 2 {
|
|
t.Errorf("expected 2 fields in nested constructor, got %d", len(addressConstructor.Fields))
|
|
}
|
|
}
|
|
|
|
func TestStructIntegrationWithProgram(t *testing.T) {
|
|
input := `struct Point {
|
|
x: number,
|
|
y: number
|
|
}
|
|
|
|
fn Point.distance(other: Point): number
|
|
dx = self.x - other.x
|
|
dy = self.y - other.y
|
|
return (dx * dx + dy * dy)
|
|
end
|
|
|
|
p1 = Point{x = 0, y = 0}
|
|
p2 = Point{x = 3, y = 4}
|
|
|
|
if p1.distance(p2) then
|
|
echo "Distance calculated"
|
|
end
|
|
|
|
for i = 1, 10 do
|
|
point = Point{x = i, y = i * 2}
|
|
echo point.x
|
|
end`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 6 {
|
|
t.Fatalf("expected 6 statements, got %d", len(program.Statements))
|
|
}
|
|
|
|
// Verify struct definition
|
|
structStmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
if structStmt.Name != "Point" {
|
|
t.Errorf("expected struct name 'Point', got %s", structStmt.Name)
|
|
}
|
|
|
|
// Verify method definition
|
|
methodStmt, ok := program.Statements[1].(*parser.MethodDefinition)
|
|
if !ok {
|
|
t.Fatalf("expected MethodDefinition, got %T", program.Statements[1])
|
|
}
|
|
if methodStmt.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d, got %d", structStmt.ID, methodStmt.StructID)
|
|
}
|
|
if methodStmt.MethodName != "distance" {
|
|
t.Errorf("expected method name 'distance', got %s", methodStmt.MethodName)
|
|
}
|
|
|
|
// Verify constructors
|
|
for i := 2; i <= 3; i++ {
|
|
assign, ok := program.Statements[i].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("statement %d: expected Assignment, got %T", i, program.Statements[i])
|
|
}
|
|
constructor, ok := assign.Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("statement %d: expected StructConstructor, got %T", i, assign.Value)
|
|
}
|
|
if constructor.StructID != structStmt.ID {
|
|
t.Errorf("statement %d: expected struct ID %d, got %d", i, structStmt.ID, constructor.StructID)
|
|
}
|
|
}
|
|
|
|
// Verify if statement with method call
|
|
ifStmt, ok := program.Statements[4].(*parser.IfStatement)
|
|
if !ok {
|
|
t.Fatalf("expected IfStatement, got %T", program.Statements[4])
|
|
}
|
|
callExpr, ok := ifStmt.Condition.(*parser.CallExpression)
|
|
if !ok {
|
|
t.Fatalf("expected CallExpression in if condition, got %T", ifStmt.Condition)
|
|
}
|
|
dotExpr, ok := callExpr.Function.(*parser.DotExpression)
|
|
if !ok {
|
|
t.Fatalf("expected DotExpression for method call, got %T", callExpr.Function)
|
|
}
|
|
if dotExpr.Key != "distance" {
|
|
t.Errorf("expected method name 'distance', got %s", dotExpr.Key)
|
|
}
|
|
|
|
// Verify for loop with struct creation
|
|
forStmt, ok := program.Statements[5].(*parser.ForStatement)
|
|
if !ok {
|
|
t.Fatalf("expected ForStatement, got %T", program.Statements[5])
|
|
}
|
|
if len(forStmt.Body) != 2 {
|
|
t.Errorf("expected 2 statements in for body, got %d", len(forStmt.Body))
|
|
}
|
|
|
|
// Check struct constructor in loop
|
|
loopAssign, ok := forStmt.Body[0].(*parser.Assignment)
|
|
if !ok {
|
|
t.Fatalf("expected Assignment in loop, got %T", forStmt.Body[0])
|
|
}
|
|
loopConstructor, ok := loopAssign.Value.(*parser.StructConstructor)
|
|
if !ok {
|
|
t.Fatalf("expected StructConstructor in loop, got %T", loopAssign.Value)
|
|
}
|
|
if loopConstructor.StructID != structStmt.ID {
|
|
t.Errorf("expected struct ID %d in loop, got %d", structStmt.ID, loopConstructor.StructID)
|
|
}
|
|
}
|
|
|
|
func TestStructErrorCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expectedErrorSubstring string
|
|
}{
|
|
{
|
|
name: "missing field type",
|
|
input: `struct Person {
|
|
name
|
|
}`,
|
|
expectedErrorSubstring: "struct fields require type annotation",
|
|
},
|
|
{
|
|
name: "missing struct name",
|
|
input: `struct {
|
|
name: string
|
|
}`,
|
|
expectedErrorSubstring: "expected struct name",
|
|
},
|
|
{
|
|
name: "missing opening brace",
|
|
input: `struct Person
|
|
name: string
|
|
}`,
|
|
expectedErrorSubstring: "expected '{' after struct name",
|
|
},
|
|
{
|
|
name: "missing closing brace",
|
|
input: `struct Person {
|
|
name: string`,
|
|
expectedErrorSubstring: "expected next token to be }, got end of file instead",
|
|
},
|
|
{
|
|
name: "invalid field type",
|
|
input: `struct Person {
|
|
name: invalidtype
|
|
}`,
|
|
expectedErrorSubstring: "invalid type name 'invalidtype'",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
l := parser.NewLexer(tt.input)
|
|
p := parser.NewParser(l)
|
|
_ = p.ParseProgram()
|
|
|
|
if !p.HasErrors() {
|
|
t.Fatalf("expected parser errors, but got none")
|
|
}
|
|
|
|
errors := p.ErrorStrings()
|
|
found := false
|
|
for _, err := range errors {
|
|
if containsSubstring(err, tt.expectedErrorSubstring) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("expected error containing '%s', got errors: %v", tt.expectedErrorSubstring, errors)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSingleLineStruct(t *testing.T) {
|
|
input := `struct Person { name: string, age: number }`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
if len(program.Statements) != 1 {
|
|
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
|
|
}
|
|
|
|
stmt, ok := program.Statements[0].(*parser.StructStatement)
|
|
if !ok {
|
|
t.Fatalf("expected StructStatement, got %T", program.Statements[0])
|
|
}
|
|
|
|
if stmt.Name != "Person" {
|
|
t.Errorf("expected struct name 'Person', got %s", stmt.Name)
|
|
}
|
|
|
|
if len(stmt.Fields) != 2 {
|
|
t.Fatalf("expected 2 fields, got %d", len(stmt.Fields))
|
|
}
|
|
|
|
if stmt.Fields[0].Name != "name" || stmt.Fields[0].TypeHint.Type != parser.TypeString {
|
|
t.Errorf("expected first field 'name: string', got '%s: %v'",
|
|
stmt.Fields[0].Name, stmt.Fields[0].TypeHint.Type)
|
|
}
|
|
|
|
if stmt.Fields[1].Name != "age" || stmt.Fields[1].TypeHint.Type != parser.TypeNumber {
|
|
t.Errorf("expected second field 'age: number', got '%s: %v'",
|
|
stmt.Fields[1].Name, stmt.Fields[1].TypeHint.Type)
|
|
}
|
|
}
|
|
|
|
func TestStructString(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string,
|
|
age: number
|
|
}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
stmt := program.Statements[0].(*parser.StructStatement)
|
|
str := stmt.String()
|
|
|
|
expected := "struct Person {\n\tname: string,\n\tage: number\n}"
|
|
if str != expected {
|
|
t.Errorf("expected string representation:\n%s\ngot:\n%s", expected, str)
|
|
}
|
|
}
|
|
|
|
func TestMethodString(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string
|
|
}
|
|
|
|
fn Person.getName(): string
|
|
return self.name
|
|
end`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
method := program.Statements[1].(*parser.MethodDefinition)
|
|
str := method.String()
|
|
|
|
if !containsSubstring(str, "fn <struct>.getName") {
|
|
t.Errorf("expected method string to contain 'fn <struct>.getName', got: %s", str)
|
|
}
|
|
if !containsSubstring(str, ": string") {
|
|
t.Errorf("expected method string to contain return type, got: %s", str)
|
|
}
|
|
}
|
|
|
|
func TestConstructorString(t *testing.T) {
|
|
input := `struct Person {
|
|
name: string,
|
|
age: number
|
|
}
|
|
|
|
person = Person{name = "John", age = 30}`
|
|
|
|
l := parser.NewLexer(input)
|
|
p := parser.NewParser(l)
|
|
program := p.ParseProgram()
|
|
checkParserErrors(t, p)
|
|
|
|
assign := program.Statements[1].(*parser.Assignment)
|
|
constructor := assign.Value.(*parser.StructConstructor)
|
|
str := constructor.String()
|
|
|
|
expected := `<struct>{name = "John", age = 30.00}`
|
|
if str != expected {
|
|
t.Errorf("expected constructor string:\n%s\ngot:\n%s", expected, str)
|
|
}
|
|
}
|