From 25fb6de647256cdfb4c171894449bfbc08d8c131 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Fri, 16 Feb 2024 12:55:20 +0100 Subject: [PATCH] test(gnovm/pkg/gnolang): improve `TestPrecompile` (#1659) This test actually only tested the private `precompileAST()` function, not the public `Precompile()` function. The change makes it call the public function and adds some more cases described in comments like: - unknow-realm (no error) - syntax error - blacklist (was already done) - multiple files (not possible, `Precompile()` is single file only) Also used multiple-line string for file and expected content, for better readability. Relates to #1636 The test can be run with ``` go test ./gnovm/pkg/gnolang/ -v -run Precompile ```
Contributors' checklist... - [x] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [ ] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [ ] Added references to related issues and PRs - [x] Provided any useful hints for running manual tests - [ ] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md).
--- gnovm/pkg/gnolang/precompile_test.go | 328 +++++++++++++++++++++------ 1 file changed, 260 insertions(+), 68 deletions(-) diff --git a/gnovm/pkg/gnolang/precompile_test.go b/gnovm/pkg/gnolang/precompile_test.go index e2cb5e92d86..cc24636e5ed 100644 --- a/gnovm/pkg/gnolang/precompile_test.go +++ b/gnovm/pkg/gnolang/precompile_test.go @@ -1,94 +1,286 @@ package gnolang import ( - "bytes" - "errors" - "go/format" - "go/parser" - "go/token" + "go/ast" + "strings" "testing" - "github.com/stretchr/testify/assert" + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" ) func TestPrecompile(t *testing.T) { t.Parallel() cases := []struct { - name string - source string - expectedOutput string - expectedPreprocessorError error + name string + tags string + source string + expectedOutput string + expectedImports []*ast.ImportSpec + expectedError string }{ { - name: "hello", - source: "package foo\nfunc hello() string { return \"world\"}", - expectedOutput: "package foo\nfunc hello() string { return \"world\"}", - }, { - name: "use-std", - source: "package foo\nimport \"std\"\nfunc hello() string { _ = std.Foo\nreturn \"world\"}", - expectedOutput: "package foo\nimport \"github.com/gnolang/gno/gnovm/stdlibs/stdshim\"\nfunc hello() string { _ = std.Foo\nreturn \"world\"}", - }, { - name: "use-realm", - source: "package foo\nimport \"gno.land/r/users\"\nfunc foo() { _ = users.Register}", - expectedOutput: "package foo\nimport \"github.com/gnolang/gno/examples/gno.land/r/users\"\nfunc foo() { _ = users.Register}", - }, { - name: "use-avl", - source: "package foo\nimport \"gno.land/p/demo/avl\"\nfunc foo() { _ = avl.Tree}", - expectedOutput: "package foo\nimport \"github.com/gnolang/gno/examples/gno.land/p/demo/avl\"\nfunc foo() { _ = avl.Tree}", - }, { - name: "use-named-std", - source: "package foo\nimport bar \"std\"\nfunc hello() string { _ = bar.Foo\nreturn \"world\"}", - expectedOutput: "package foo\nimport bar \"github.com/gnolang/gno/gnovm/stdlibs/stdshim\"\nfunc hello() string { _ = bar.Foo\nreturn \"world\"}", - }, { - name: "blacklisted-package", - source: "package foo\nimport \"reflect\"\nfunc foo() { _ = reflect.ValueOf}", - expectedPreprocessorError: errors.New(`import "reflect" is not in the whitelist`), - }, { - name: "whitelisted-package", - source: "package foo\nimport \"regexp\"\nfunc foo() { _ = regexp.MatchString}", - expectedOutput: "package foo\nimport \"regexp\"\nfunc foo() { _ = regexp.MatchString}", + name: "hello", + source: ` +package foo + +func hello() string { return "world"} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +func hello() string { return "world" } +`, + }, + { + name: "hello with tags", + tags: "gno", + source: ` +package foo + +func hello() string { return "world"} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package foo + +func hello() string { return "world" } +`, + }, + { + name: "use-std", + source: ` +package foo + +import "std" + +func hello() string { + _ = std.Foo + return "world" +} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import "github.com/gnolang/gno/gnovm/stdlibs/stdshim" + +func hello() string { + _ = std.Foo + return "world" +} +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 21, + Kind: 9, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/stdshim"`, + }, + EndPos: 26, + }, + }, + }, + { + name: "use-realm", + source: ` +package foo + +import "gno.land/r/users" + +func foo() { _ = users.Register} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import "github.com/gnolang/gno/examples/gno.land/r/users" + +func foo() { _ = users.Register } +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 21, + Kind: 9, + Value: `"github.com/gnolang/gno/examples/gno.land/r/users"`, + }, + EndPos: 39, + }, + }, + }, + { + name: "use-avl", + source: ` +package foo + +import "gno.land/p/demo/avl" + +func foo() { _ = avl.Tree } +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import "github.com/gnolang/gno/examples/gno.land/p/demo/avl" + +func foo() { _ = avl.Tree } +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 21, + Kind: 9, + Value: `"github.com/gnolang/gno/examples/gno.land/p/demo/avl"`, + }, + EndPos: 42, + }, + }, + }, + { + name: "use-named-std", + source: ` +package foo + +import bar "std" + +func hello() string { + _ = bar.Foo + return "world" +} +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import bar "github.com/gnolang/gno/gnovm/stdlibs/stdshim" + +func hello() string { + _ = bar.Foo + return "world" +} +`, + expectedImports: []*ast.ImportSpec{ + { + Name: &ast.Ident{ + NamePos: 21, + Name: "bar", + }, + Path: &ast.BasicLit{ + ValuePos: 25, + Kind: 9, + Value: `"github.com/gnolang/gno/gnovm/stdlibs/stdshim"`, + }, + EndPos: 30, + }, + }, + }, + { + name: "blacklisted-package", + source: ` +package foo + +import "reflect" + +func foo() { _ = reflect.ValueOf } +`, + expectedError: `import "reflect" is not in the whitelist`, + }, + { + name: "syntax-error", + source: ` +package foo + +invalid +`, + expectedError: `parse: foo.gno:3:1: expected declaration, found invalid`, + }, + { + name: "unknown-realm", + source: ` +package foo + +import "gno.land/p/demo/unknownxyz" +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import "github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz" +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 21, + Kind: 9, + Value: `"github.com/gnolang/gno/examples/gno.land/p/demo/unknownxyz"`, + }, + EndPos: 49, + }, + }, + }, + { + name: "whitelisted-package", + source: ` +package foo + +import "regexp" + +func foo() { _ = regexp.MatchString } +`, + expectedOutput: ` +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +package foo + +import "regexp" + +func foo() { _ = regexp.MatchString } +`, + expectedImports: []*ast.ImportSpec{ + { + Path: &ast.BasicLit{ + ValuePos: 21, + Kind: 9, + Value: `"regexp"`, + }, + }, + }, }, - // multiple files - // syntax error - // unknown realm? - // blacklist - // etc } for _, c := range cases { c := c // scopelint t.Run(c.name, func(t *testing.T) { t.Parallel() + // "\n" is added for better test case readability, now trim it + source := strings.TrimPrefix(c.source, "\n") + + res, err := Precompile(source, c.tags, "foo.gno") - // parse gno - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "foo.go", c.source, parser.ParseComments) - assert.NoError(t, err) - - // call preprocessor - transformed, err := precompileAST(fset, f, true) - if c.expectedPreprocessorError == nil { - assert.NoError(t, err) - } else { - assert.Equal(t, err, c.expectedPreprocessorError) + if c.expectedError != "" { + require.EqualError(t, err, c.expectedError) + return } - // generate go - var buf bytes.Buffer - err = format.Node(&buf, fset, transformed) - assert.NoError(t, err) - got := buf.Bytes() - - // check output - if c.expectedOutput != "" { - expect, err := format.Source([]byte(c.expectedOutput)) - if !bytes.Equal(expect, got) { - t.Logf("got:\n%s", got) - t.Logf("expect:\n%s", expect) - t.Fatal("mismatch") - } - assert.NoError(t, err) + if c.expectedError != "" { + require.EqualError(t, err, c.expectedError) + return } + require.NoError(t, err) + expectedOutput := strings.TrimPrefix(c.expectedOutput, "\n") + assert.Equal(t, res.Translated, expectedOutput, "wrong output") + assert.Equal(t, res.Imports, c.expectedImports, "wrong imports") }) } }