Skip to content

Commit

Permalink
test(gnovm): improve gnovm/pkg/gnolang test coverage (#2143)
Browse files Browse the repository at this point in the history
No new tests have been added yet, but existing tests in
gnovm/tests/files are now executed in the context of the package which
contains the relevant interpreter virtual machine and parser code. We
have now a better baseline to measure and complete the code coverage.

This represents more than 800 tests, which takes 10s on my macbook air
m1. Those tests are actually run twice, as I haven't yet removed the
original execution from gnovm/tests.

The testing code is substantially simplified compared to its counterpart
in gnovm/tests, but has not yet been deduplicated. This will be done in
further commits.

In `gnovm/pkg/golang`, `go test -cover` went from 34.2% to 66.7%.

Related to #1121, #1145 and probably more
<!-- please provide a detailed description of the changes made in this
pull request. -->

<details><summary>Contributors' checklist...</summary>

- [*] 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
- [*] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [*] Added references to related issues and PRs
- [*] 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).
</details>

---------

Co-authored-by: Manfred Touron <[email protected]>
  • Loading branch information
mvertes and moul authored May 30, 2024
1 parent 454ae2d commit 9c60a23
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 35 deletions.
59 changes: 38 additions & 21 deletions gnovm/pkg/gnolang/debugger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,49 +23,65 @@ type writeNopCloser struct{ io.Writer }

func (writeNopCloser) Close() error { return nil }

func eval(debugAddr, in, file string) (string, string, error) {
out := bytes.NewBufferString("")
err := bytes.NewBufferString("")
// TODO (Marc): move evalTest to gnovm/tests package and remove code duplicates
func evalTest(debugAddr, in, file string) (out, err string) {
bout := bytes.NewBufferString("")
berr := bytes.NewBufferString("")
stdin := bytes.NewBufferString(in)
stdout := writeNopCloser{out}
stderr := writeNopCloser{err}
stdout := writeNopCloser{bout}
stderr := writeNopCloser{berr}
debug := in != "" || debugAddr != ""
mode := tests.ImportModeNativePreferred
if strings.HasSuffix(file, "_stdlibs.gno") {
mode = tests.ImportModeStdlibsPreferred
}

defer func() {
if r := recover(); r != nil {
err = fmt.Sprintf("%v", r)
}
out = strings.TrimSpace(out)
err = strings.TrimSpace(strings.ReplaceAll(err, "../../tests/files/", "files/"))
}()

testStore := tests.TestStore(gnoenv.RootDir(), "", stdin, stdout, stderr, tests.ImportModeStdlibsPreferred)
testStore := tests.TestStore(gnoenv.RootDir(), "../../tests/files", stdin, stdout, stderr, mode)

f := gnolang.MustReadFile(file)

m := gnolang.NewMachineWithOptions(gnolang.MachineOptions{
PkgPath: string(f.PkgName),
Input: stdin,
Output: stdout,
Store: testStore,
Debug: true,
DebugAddr: debugAddr,
PkgPath: string(f.PkgName),
Input: stdin,
Output: stdout,
Store: testStore,
Context: tests.TestContext(string(f.PkgName), nil),
Debug: debug,
})

defer m.Release()

if debugAddr != "" {
if err := m.Debugger.Serve(debugAddr); err != nil {
return "", "", err
if e := m.Debugger.Serve(debugAddr); e != nil {
err = e.Error()
return
}
}

m.RunFiles(f)
ex, _ := gnolang.ParseExpr("main()")
m.Eval(ex)
return out.String(), err.String(), nil
out, err = bout.String(), berr.String()
return
}

func runDebugTest(t *testing.T, targetPath string, tests []dtest) {
t.Helper()

for _, test := range tests {
t.Run("", func(t *testing.T) {
out, err, _ := eval("", test.in, targetPath)
out, err := evalTest("", test.in, targetPath)
t.Log("in:", test.in, "out:", out, "err:", err)
if !strings.Contains(out, test.out) {
t.Errorf("result does not contain \"%s\", got \"%s\"", test.out, out)
t.Errorf("unexpected output\nwant\"%s\"\n got \"%s\"", test.out, out)
}
})
}
Expand All @@ -77,7 +93,7 @@ func TestDebug(t *testing.T) {
cont2 := "break 21\ncontinue\n"

runDebugTest(t, debugTarget, []dtest{
{in: "", out: "Welcome to the Gnovm debugger. Type 'help' for list of commands."},
{in: "\n", out: "Welcome to the Gnovm debugger. Type 'help' for list of commands."},
{in: "help\n", out: "The following commands are available"},
{in: "h\n", out: "The following commands are available"},
{in: "help b\n", out: "Set a breakpoint."},
Expand Down Expand Up @@ -154,7 +170,7 @@ func TestRemoteDebug(t *testing.T) {
retry int
)

go eval(debugAddress, "", debugTarget)
go evalTest(debugAddress, "", debugTarget)

for retry = 100; retry > 0; retry-- {
conn, err = net.Dial("tcp", debugAddress)
Expand All @@ -177,8 +193,9 @@ func TestRemoteDebug(t *testing.T) {
}

func TestRemoteError(t *testing.T) {
_, _, err := eval(":xxx", "", debugTarget)
if !strings.Contains(err.Error(), "tcp/xxx: unknown port") {
_, err := evalTest(":xxx", "", debugTarget)
t.Log("err:", err)
if !strings.Contains(err, "tcp/xxx: unknown port") {
t.Error(err)
}
}
62 changes: 62 additions & 0 deletions gnovm/pkg/gnolang/eval_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package gnolang_test

import (
"os"
"path"
"strings"
"testing"
)

func TestEvalFiles(t *testing.T) {
dir := "../../tests/files"
files, err := os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
for _, f := range files {
wantOut, wantErr, ok := testData(dir, f)
if !ok {
continue
}
t.Run(f.Name(), func(t *testing.T) {
out, err := evalTest("", "", path.Join(dir, f.Name()))

if wantErr != "" && !strings.Contains(err, wantErr) ||
wantErr == "" && err != "" {
t.Fatalf("unexpected error\nWant: %s\n Got: %s", wantErr, err)
}
if wantOut != "" && out != wantOut {
t.Fatalf("unexpected output\nWant: %s\n Got: %s", wantOut, out)
}
})
}
}

// testData returns the expected output and error string, and true if entry is valid.
func testData(dir string, f os.DirEntry) (testOut, testErr string, ok bool) {
if f.IsDir() {
return "", "", false
}
name := path.Join(dir, f.Name())
if !strings.HasSuffix(name, ".gno") || strings.HasSuffix(name, "_long.gno") {
return "", "", false
}
buf, err := os.ReadFile(name)
if err != nil {
return "", "", false
}
str := string(buf)
if strings.Contains(str, "// PKGPATH:") {
return "", "", false
}
return commentFrom(str, "\n// Output:"), commentFrom(str, "\n// Error:"), true
}

// commentFrom returns the content from a trailing comment block in s starting with delim.
func commentFrom(s, delim string) string {
index := strings.Index(s, delim)
if index < 0 {
return ""
}
return strings.TrimSpace(strings.ReplaceAll(s[index+len(delim):], "\n// ", "\n"))
}
1 change: 0 additions & 1 deletion gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ type MachineOptions struct {
CheckTypes bool // not yet used
ReadOnly bool
Debug bool
DebugAddr string // debugger io stream address (stdin/stdout if empty)
Input io.Reader // used for default debugger input only
Output io.Writer // default os.Stdout
Store Store // default NewStore(Alloc, nil, nil)
Expand Down
5 changes: 3 additions & 2 deletions gnovm/tests/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestMachine(store gno.Store, stdout io.Writer, pkgPath string) *gno.Machine
}

func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAlloc int64, send std.Coins) *gno.Machine {
ctx := testContext(pkgPath, send)
ctx := TestContext(pkgPath, send)
m := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "", // set later.
Output: stdout,
Expand All @@ -47,7 +47,8 @@ func testMachineCustom(store gno.Store, pkgPath string, stdout io.Writer, maxAll
return m
}

func testContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
// TestContext returns a TestExecContext. Usable for test purpose only.
func TestContext(pkgPath string, send std.Coins) *teststd.TestExecContext {
// FIXME: create a better package to manage this, with custom constructors
pkgAddr := gno.DerivePkgAddr(pkgPath) // the addr of the pkgPath called.
caller := gno.DerivePkgAddr("user1.gno")
Expand Down
2 changes: 1 addition & 1 deletion gnovm/tests/files/convert4.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ func main() {
println(int(nil))
}

// error: cannot convert (undefined) to int
// Error: cannot convert (undefined) to int
2 changes: 1 addition & 1 deletion gnovm/tests/files/convert5.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ func main() {
println(ints)
}

// error: cannot convert (undefined) to int
// Error: cannot convert (undefined) to int
6 changes: 3 additions & 3 deletions gnovm/tests/files/float5_stdlibs.gno
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ func main() {
println(math.MaxFloat64, math.Float64bits(math.MaxFloat64))
}

// NOTE: 0x7f7fffff is 2139095039
// NOTE: 0x7fefffffffffffff is 9218868437227405311

// Output:
// 3.4028234663852886e+38 2139095039
// 1.7976931348623157e+308 9218868437227405311

// NOTE: 0x7f7fffff is 2139095039
// NOTE: 0x7fefffffffffffff is 9218868437227405311
2 changes: 1 addition & 1 deletion gnovm/tests/files/switch8.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ func main() {
}

// Error:
// 5:2: fallthrough statement out of place
// fallthrough statement out of place
2 changes: 1 addition & 1 deletion gnovm/tests/files/switch8b.gno
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ func main() {
}

// Error:
// 10:3: cannot fallthrough final case in switch
// cannot fallthrough final case in switch
2 changes: 1 addition & 1 deletion gnovm/tests/files/switch8c.gno
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ func main() {
}

// Error:
// 7:3: fallthrough statement out of place
// fallthrough statement out of place
2 changes: 1 addition & 1 deletion gnovm/tests/files/switch9.gno
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ func main() {
}

// Error:
// 9:3: cannot fallthrough in type switch
// cannot fallthrough in type switch
4 changes: 2 additions & 2 deletions gnovm/tests/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
baseDir := filepath.Join(filesPath, "extern", pkgPath[len(testPath):])
memPkg := gno.ReadMemPackage(baseDir, pkgPath)
send := std.Coins{}
ctx := testContext(pkgPath, send)
ctx := TestContext(pkgPath, send)
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Expand Down Expand Up @@ -390,7 +390,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri
}

send := std.Coins{}
ctx := testContext(pkgPath, send)
ctx := TestContext(pkgPath, send)
m2 := gno.NewMachineWithOptions(gno.MachineOptions{
PkgPath: "test",
Output: stdout,
Expand Down

0 comments on commit 9c60a23

Please sign in to comment.