From d049cbe6ed30f26d1ed8b20800b41925970a6446 Mon Sep 17 00:00:00 2001 From: Danilo Pantani Date: Thu, 12 Dec 2024 04:02:31 -0300 Subject: [PATCH] remove genesis/testcase placeholder --- ignite/pkg/xast/function.go | 113 +++++-- ignite/pkg/xast/function_test.go | 300 +++++++++++++++++- .../types/genesis_test.go.plush | 1 - ignite/templates/module/placeholders.go | 3 - ignite/templates/typed/list/genesis.go | 83 ++--- ignite/templates/typed/list/list.go | 2 +- ignite/templates/typed/map/map.go | 47 ++- ignite/templates/typed/singleton/singleton.go | 5 +- 8 files changed, 458 insertions(+), 96 deletions(-) diff --git a/ignite/pkg/xast/function.go b/ignite/pkg/xast/function.go index 630b388022..a304d007fb 100644 --- a/ignite/pkg/xast/function.go +++ b/ignite/pkg/xast/function.go @@ -15,13 +15,14 @@ import ( type ( // functionOpts represent the options for functions. functionOpts struct { - newParams []param - body string - newLines []line - insideCall []call - insideStruct []str - appendCode []string - returnVars []string + newParams []param + body string + newLines []line + insideCall []call + insideStruct []str + appendTestCase []string + appendCode []string + returnVars []string } // FunctionOptions configures code generation. @@ -67,6 +68,13 @@ func ReplaceFuncBody(body string) FunctionOptions { } } +// AppendFuncTestCase append test a new test case, if exists, of a function in Go source code content. +func AppendFuncTestCase(testCase string) FunctionOptions { + return func(c *functionOpts) { + c.appendTestCase = append(c.appendTestCase, testCase) + } +} + // AppendFuncCode append code before the end or the return, if exists, of a function in Go source code content. func AppendFuncCode(code string) FunctionOptions { return func(c *functionOpts) { @@ -119,13 +127,14 @@ func NewFuncReturn(returnVars ...string) FunctionOptions { func newFunctionOptions() functionOpts { return functionOpts{ - newParams: make([]param, 0), - body: "", - newLines: make([]line, 0), - insideCall: make([]call, 0), - insideStruct: make([]str, 0), - appendCode: make([]string, 0), - returnVars: make([]string, 0), + newParams: make([]param, 0), + body: "", + newLines: make([]line, 0), + insideCall: make([]call, 0), + insideStruct: make([]str, 0), + appendTestCase: make([]string, 0), + appendCode: make([]string, 0), + returnVars: make([]string, 0), } } @@ -148,23 +157,20 @@ func ModifyFunction(fileContent, functionName string, functions ...FunctionOptio // Parse the content of the new function into an ast. var newFunctionBody *ast.BlockStmt if opts.body != "" { - newFuncContent := fmt.Sprintf("package p; func _() { %s }", strings.TrimSpace(opts.body)) - newContent, err := parser.ParseFile(fileSet, "", newFuncContent, parser.ParseComments) + newFunctionBody, err = codeToBlockStmt(fileSet, opts.body) if err != nil { return "", err } - newFunctionBody = newContent.Decls[0].(*ast.FuncDecl).Body } // Parse the content of the append code an ast. appendCode := make([]ast.Stmt, 0) for _, codeToInsert := range opts.appendCode { - newFuncContent := fmt.Sprintf("package p; func _() { %s }", strings.TrimSpace(codeToInsert)) - newContent, err := parser.ParseFile(fileSet, "", newFuncContent, parser.ParseComments) + body, err := codeToBlockStmt(fileSet, codeToInsert) if err != nil { return "", err } - appendCode = append(appendCode, newContent.Decls[0].(*ast.FuncDecl).Body.List...) + appendCode = append(appendCode, body.List...) } // Parse the content of the return vars into an ast. @@ -235,6 +241,7 @@ func ModifyFunction(fileContent, functionName string, functions ...FunctionOptio // Check if the function has the code you want to replace. if newFunctionBody != nil { funcDecl.Body = newFunctionBody + funcDecl.Body.Rbrace = funcDecl.Body.Pos() // Re-adjust positions if necessary. } // Add the new code at line. @@ -390,6 +397,37 @@ func ModifyFunction(fileContent, functionName string, functions ...FunctionOptio return false } + // Locate the `tests` variable inside the function + for _, stmt := range funcDecl.Body.List { + assignStmt, ok := stmt.(*ast.AssignStmt) + if !ok || len(assignStmt.Lhs) == 0 { + continue + } + + // Check if the `tests` variable is being declared + ident, ok := assignStmt.Lhs[0].(*ast.Ident) + if !ok || ident.Name != "tests" { + continue + } + + // Find the composite literal (slice) for the `tests` variable + compositeLit, ok := assignStmt.Rhs[0].(*ast.CompositeLit) + if !ok { + continue + } + + for _, testCase := range opts.appendTestCase { + // Parse the new test case into an AST expression + testCaseStmt, err := structToBlockStmt(testCase) + if err != nil { + errInspect = err + return false + } + // Append the new test case to the list + compositeLit.Elts = append(compositeLit.Elts, testCaseStmt) + } + } + // everything is ok, mark as found and stop the inspect found = true return false @@ -410,3 +448,38 @@ func ModifyFunction(fileContent, functionName string, functions ...FunctionOptio // Return the modified content. return buf.String(), nil } + +func codeToBlockStmt(fileSet *token.FileSet, code string) (*ast.BlockStmt, error) { + newFuncContent := toCode(code) + newContent, err := parser.ParseFile(fileSet, "", newFuncContent, parser.ParseComments) + if err != nil { + return nil, err + } + return newContent.Decls[0].(*ast.FuncDecl).Body, nil +} + +func toCode(code string) string { + return fmt.Sprintf("package p; func _() { %s }", strings.TrimSpace(code)) +} + +func structToBlockStmt(code string) (ast.Expr, error) { + newFuncContent := toStruct(code) + newContent, err := parser.ParseExpr(newFuncContent) + if err != nil { + return nil, err + } + newCompositeList, ok := newContent.(*ast.CompositeLit) + if !ok { + return nil, errors.New("not a composite literal") + } + + if len(newCompositeList.Elts) != 1 { + return nil, errors.New("composite literal has more than one element or zero") + } + + return newCompositeList.Elts[0], nil +} + +func toStruct(code string) string { + return fmt.Sprintf(`struct {}{ %s }`, strings.TrimSpace(code)) +} diff --git a/ignite/pkg/xast/function_test.go b/ignite/pkg/xast/function_test.go index ffbed93dcf..111cf89e5c 100644 --- a/ignite/pkg/xast/function_test.go +++ b/ignite/pkg/xast/function_test.go @@ -1,6 +1,7 @@ package xast import ( + "fmt" "strconv" "strings" "testing" @@ -26,6 +27,28 @@ func anotherFunction() bool { p := bla.NewParam() p.CallSomething("Another call") return true +} + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } }` type args struct { @@ -56,6 +79,10 @@ func anotherFunction() bool { AppendInsideFuncCall("SimpleCall", "bla", -1), AppendInsideFuncCall("Println", strconv.Quote("test"), -1), AppendInsideFuncStruct("Param", "Bar", strconv.Quote("bar"), -1), + AppendFuncTestCase(`{ + desc: "valid first genesis state", + genState: GenesisState{}, + }`), }, }, want: `package main @@ -76,6 +103,28 @@ func anotherFunction(param1 string) bool { Param{Baz: baz, Foo: foo, Bar: "bar"} return 1 } + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} `, }, { @@ -97,6 +146,150 @@ func main() { } func anotherFunction() bool { return false } + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} +`, + }, + { + name: "add a new test case", + args: args{ + fileContent: existingContent, + functionName: "TestValidate", + functions: []FunctionOptions{ + AppendFuncTestCase(`{ + desc: "valid genesis state", + genState: GenesisState{}, + }`), + }, + }, + want: `package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Hello, world!") + New(param1, param2) +} + +func anotherFunction() bool { + p := bla.NewParam() + p.CallSomething("Another call") + return true +} + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, { + + desc: "valid genesis state", + + genState: GenesisState{}}, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} +`, + }, + { + name: "add two test cases", + args: args{ + fileContent: existingContent, + functionName: "TestValidate", + functions: []FunctionOptions{ + AppendFuncTestCase(`{ + desc: "valid first genesis state", + genState: GenesisState{}, + }`), + AppendFuncTestCase(`{ + desc: "valid second genesis state", + genState: GenesisState{}, + }`), + }, + }, + want: `package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Hello, world!") + New(param1, param2) +} + +func anotherFunction() bool { + p := bla.NewParam() + p.CallSomething("Another call") + return true +} + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, { + + desc: "valid first genesis state", + + genState: GenesisState{}, + }, { + + desc: "valid second genesis state", + + genState: GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} `, }, { @@ -131,6 +324,28 @@ func anotherFunction() bool { return true } + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} `, }, { @@ -170,6 +385,28 @@ func anotherFunction() bool { p.CallSomething("test2", "Another call", "test1") return true } + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} `, }, { @@ -185,7 +422,30 @@ func anotherFunction() bool { Param{Baz: baz, Foo: foo} Client{baz, foo} return true -}`, +} + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} +`, functionName: "anotherFunction", functions: []FunctionOptions{ AppendInsideFuncStruct("Param", "Bar", "bar", -1), @@ -204,8 +464,44 @@ func anotherFunction() bool { Client{bar, baz, foo} return true } + +func TestValidate(t *testing.T) { + tests := []struct { + desc string + genState types.GenesisState + }{ + { + desc: "default is valid", + genState: types.DefaultGenesis(), + }, + { + desc: "valid genesis state", + genState: types.GenesisState{}, + }, + } + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + err := tc.genState.Validate() + require.NoError(t, err) + }) + } +} `, }, + { + name: "function without test case assertion", + args: args{ + fileContent: existingContent, + functionName: "anotherFunction", + functions: []FunctionOptions{ + AppendFuncTestCase(`{ + desc: "valid second genesis state", + genState: GenesisState{}, + }`), + }, + }, + want: fmt.Sprintln(existingContent), + }, { name: "params out of range", args: args{ @@ -285,7 +581,7 @@ func anotherFunction() bool { functionName: "anotherFunction", functions: []FunctionOptions{AppendInsideFuncCall("NewParam", "9#.(c", 0)}, }, - err: errors.New("format.Node internal error (13:21: illegal character U+0023 '#' (and 2 more errors))"), + err: errors.New("format.Node internal error (13:21: illegal character U+0023 '#' (and 4 more errors))"), }, { name: "call params out of range", diff --git a/ignite/templates/module/create/files/base/x/{{moduleName}}/types/genesis_test.go.plush b/ignite/templates/module/create/files/base/x/{{moduleName}}/types/genesis_test.go.plush index 5a6d2911c4..064c6830c6 100644 --- a/ignite/templates/module/create/files/base/x/{{moduleName}}/types/genesis_test.go.plush +++ b/ignite/templates/module/create/files/base/x/{{moduleName}}/types/genesis_test.go.plush @@ -25,7 +25,6 @@ func TestGenesisState_Validate(t *testing.T) { }, valid: true, }, - // this line is used by starport scaffolding # types/genesis/testcase } for _, tc := range tests { t.Run(tc.desc, func(t *testing.T) { diff --git a/ignite/templates/module/placeholders.go b/ignite/templates/module/placeholders.go index d2cb60cc45..7bc358efb8 100644 --- a/ignite/templates/module/placeholders.go +++ b/ignite/templates/module/placeholders.go @@ -14,7 +14,4 @@ const ( PlaceholderIBCKeysName = "// this line is used by starport scaffolding # ibc/keys/name" PlaceholderIBCKeysPort = "// this line is used by starport scaffolding # ibc/keys/port" PlaceholderIBCNewModule = "// this line is used by starport scaffolding # ibc/app/module" - - // Genesis test - PlaceholderTypesGenesisTestcase = "// this line is used by starport scaffolding # types/genesis/testcase" ) diff --git a/ignite/templates/typed/list/genesis.go b/ignite/templates/typed/list/genesis.go index f4691482ac..ca088ab170 100644 --- a/ignite/templates/typed/list/genesis.go +++ b/ignite/templates/typed/list/genesis.go @@ -7,19 +7,17 @@ import ( "github.com/gobuffalo/genny/v2" "github.com/ignite/cli/v29/ignite/pkg/errors" - "github.com/ignite/cli/v29/ignite/pkg/placeholder" "github.com/ignite/cli/v29/ignite/pkg/protoanalysis/protoutil" "github.com/ignite/cli/v29/ignite/pkg/xast" - "github.com/ignite/cli/v29/ignite/templates/module" "github.com/ignite/cli/v29/ignite/templates/typed" ) -func genesisModify(replacer placeholder.Replacer, opts *typed.Options, g *genny.Generator) { +func genesisModify(opts *typed.Options, g *genny.Generator) { g.RunFn(genesisProtoModify(opts)) g.RunFn(genesisTypesModify(opts)) g.RunFn(genesisModuleModify(opts)) g.RunFn(genesisTestsModify(opts)) - g.RunFn(genesisTypesTestsModify(replacer, opts)) + g.RunFn(genesisTypesTestsModify(opts)) } // Modifies the genesis.proto file to add a new field. @@ -224,7 +222,7 @@ require.Equal(t, genesisState.%[1]vCount, got.%[1]vCount)`, opts.TypeName.UpperC } } -func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) genny.RunFn { +func genesisTypesTestsModify(opts *typed.Options) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "types/genesis_test.go") f, err := r.Disk.Find(path) @@ -232,31 +230,10 @@ func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) return err } - // add parameter to the struct into the new method. - content, err := xast.ModifyFunction( - f.String(), - "TestGenesisState_Validate", - xast.AppendInsideFuncStruct( - "GenesisState", - fmt.Sprintf("%[1]vList", opts.TypeName.UpperCamel), - fmt.Sprintf("[]types.%[1]v{{ Id: 0 }, { Id: 1 }}", opts.TypeName.UpperCamel), - -1, - ), - xast.AppendInsideFuncStruct( - "GenesisState", - fmt.Sprintf("%[1]vCount", opts.TypeName.UpperCamel), - "2", - -1, - ), - ) - if err != nil { - return err - } - - templateTests := `{ - desc: "duplicated %[2]v", + templateTestDuplicated := `{ + desc: "duplicated %[1]v", genState: &types.GenesisState{ - %[3]vList: []types.%[3]v{ + %[2]vList: []types.%[2]v{ { Id: 0, }, @@ -266,27 +243,53 @@ func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) }, }, valid: false, -}, -{ - desc: "invalid %[2]v count", +}` + replacementTestDuplicated := fmt.Sprintf( + templateTestDuplicated, + opts.TypeName.LowerCamel, + opts.TypeName.UpperCamel, + ) + + templateTestInvalidCount := `{ + desc: "invalid %[1]v count", genState: &types.GenesisState{ - %[3]vList: []types.%[3]v{ + %[2]vList: []types.%[2]v{ { Id: 1, }, }, - %[3]vCount: 0, + %[2]vCount: 0, }, valid: false, -}, -%[1]v` - replacementTests := fmt.Sprintf( - templateTests, - module.PlaceholderTypesGenesisTestcase, +}` + replacementInvalidCount := fmt.Sprintf( + templateTestInvalidCount, opts.TypeName.LowerCamel, opts.TypeName.UpperCamel, ) - content = replacer.Replace(content, module.PlaceholderTypesGenesisTestcase, replacementTests) + + // add parameter to the struct into the new method. + content, err := xast.ModifyFunction( + f.String(), + "TestGenesisState_Validate", + xast.AppendInsideFuncStruct( + "GenesisState", + fmt.Sprintf("%[1]vList", opts.TypeName.UpperCamel), + fmt.Sprintf("[]types.%[1]v{{ Id: 0 }, { Id: 1 }}", opts.TypeName.UpperCamel), + -1, + ), + xast.AppendInsideFuncStruct( + "GenesisState", + fmt.Sprintf("%[1]vCount", opts.TypeName.UpperCamel), + "2", + -1, + ), + xast.AppendFuncTestCase(replacementTestDuplicated), + xast.AppendFuncTestCase(replacementInvalidCount), + ) + if err != nil { + return err + } newFile := genny.NewFileS(path, content) return r.File(newFile) diff --git a/ignite/templates/typed/list/list.go b/ignite/templates/typed/list/list.go index debbb1925e..23dadf485e 100644 --- a/ignite/templates/typed/list/list.go +++ b/ignite/templates/typed/list/list.go @@ -58,7 +58,7 @@ func NewGenerator(replacer placeholder.Replacer, opts *typed.Options) (*genny.Ge g.RunFn(clientCliQueryModify(replacer, opts)) // Genesis modifications - genesisModify(replacer, opts, g) + genesisModify(opts, g) if !opts.NoMessage { // Modifications for new messages diff --git a/ignite/templates/typed/map/map.go b/ignite/templates/typed/map/map.go index 0904c0950c..6a5f9249db 100644 --- a/ignite/templates/typed/map/map.go +++ b/ignite/templates/typed/map/map.go @@ -16,7 +16,6 @@ import ( "github.com/ignite/cli/v29/ignite/pkg/xast" "github.com/ignite/cli/v29/ignite/pkg/xgenny" "github.com/ignite/cli/v29/ignite/templates/field/datatype" - "github.com/ignite/cli/v29/ignite/templates/module" "github.com/ignite/cli/v29/ignite/templates/typed" ) @@ -83,7 +82,7 @@ func NewGenerator(replacer placeholder.Replacer, opts *typed.Options) (*genny.Ge g.RunFn(genesisTypesModify(opts)) g.RunFn(genesisModuleModify(opts)) g.RunFn(genesisTestsModify(opts)) - g.RunFn(genesisTypesTestsModify(replacer, opts)) + g.RunFn(genesisTypesTestsModify(opts)) // Modifications for new messages if !opts.NoMessage { @@ -499,7 +498,7 @@ func genesisTestsModify(opts *typed.Options) genny.RunFn { } } -func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) genny.RunFn { +func genesisTypesTestsModify(opts *typed.Options) genny.RunFn { return func(r *genny.Runner) error { path := filepath.Join(opts.AppPath, "x", opts.ModuleName, "types/genesis_test.go") f, err := r.Disk.Find(path) @@ -513,6 +512,25 @@ func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) sampleIndexes[i] = opts.Index.GenesisArgs(i) } + templateDuplicated := `{ + desc: "duplicated %[1]v", + genState: &types.GenesisState{ + %[2]vList: []types.%[2]v{ + { + %[3]v}, + { + %[3]v}, + }, + }, + valid: false, +}` + replacementDuplicated := fmt.Sprintf( + templateDuplicated, + opts.TypeName.LowerCamel, + opts.TypeName.UpperCamel, + sampleIndexes[0], + ) + // add parameter to the struct into the new method. content, err := xast.ModifyFunction( f.String(), @@ -528,33 +546,12 @@ func genesisTypesTestsModify(replacer placeholder.Replacer, opts *typed.Options) ), -1, ), + xast.AppendFuncTestCase(replacementDuplicated), ) if err != nil { return err } - templateDuplicated := `{ - desc: "duplicated %[2]v", - genState: &types.GenesisState{ - %[3]vList: []types.%[3]v{ - { - %[4]v}, - { - %[4]v}, - }, - }, - valid: false, -}, -%[1]v` - replacementDuplicated := fmt.Sprintf( - templateDuplicated, - module.PlaceholderTypesGenesisTestcase, - opts.TypeName.LowerCamel, - opts.TypeName.UpperCamel, - sampleIndexes[0], - ) - content = replacer.Replace(content, module.PlaceholderTypesGenesisTestcase, replacementDuplicated) - newFile := genny.NewFileS(path, content) return r.File(newFile) } diff --git a/ignite/templates/typed/singleton/singleton.go b/ignite/templates/typed/singleton/singleton.go index d5d959ac6f..dcb6ada5f9 100644 --- a/ignite/templates/typed/singleton/singleton.go +++ b/ignite/templates/typed/singleton/singleton.go @@ -358,11 +358,8 @@ func genesisTypesTestsModify(opts *typed.Options) genny.RunFn { content, err := xast.ModifyFunction( f.String(), "TestGenesisState_Validate", - xast.AppendInsideFuncStruct( - "GenesisState", - opts.TypeName.UpperCamel, + xast.AppendFuncTestCase( fmt.Sprintf("&types.%[1]v{ %[2]v }", opts.TypeName.UpperCamel, sampleFields), - -1, ), ) if err != nil {