diff --git a/examples/gno.land/r/demo/tests/tests.gno b/examples/gno.land/r/demo/tests/tests.gno index dd2518e2e49..fb49b2273ae 100644 --- a/examples/gno.land/r/demo/tests/tests.gno +++ b/examples/gno.land/r/demo/tests/tests.gno @@ -3,7 +3,7 @@ package tests import ( "std" - "gno.land/r/demo/tests/subtests" + rsubtests "gno.land/r/demo/tests/subtests" ) var counter int @@ -78,8 +78,8 @@ func GetPrevRealm() std.Realm { return std.PrevRealm() } -func GetPSubtestsPrevRealm() std.Realm { - return subtests.GetPrevRealm() +func GetRSubtestsPrevRealm() std.Realm { + return rsubtests.GetPrevRealm() } func Exec(fn func()) { diff --git a/examples/gno.land/r/demo/tests/tests_test.gno b/examples/gno.land/r/demo/tests/tests_test.gno index 6efc3f35672..3dcbeecf18c 100644 --- a/examples/gno.land/r/demo/tests/tests_test.gno +++ b/examples/gno.land/r/demo/tests/tests_test.gno @@ -1,6 +1,7 @@ package tests import ( + "std" "testing" "gno.land/p/demo/testutils" @@ -30,3 +31,18 @@ func TestAssertOriginCall(t *testing.T) { AssertOriginCall() }() } + +func TestPrevRealm(t *testing.T) { + var ( + user1Addr = std.DerivePkgAddr("user1.gno") + rTestsAddr = std.DerivePkgAddr("gno.land/r/demo/tests") + ) + // When a single realm in the frames, PrevRealm returns the user + if addr := GetPrevRealm().Addr(); addr != user1Addr { + t.Errorf("want GetPrevRealm().Addr==%s, got %s", user1Addr, addr) + } + // When 2 or more realms in the frames, PrevRealm returns the second to last + if addr := GetRSubtestsPrevRealm().Addr(); addr != rTestsAddr { + t.Errorf("want GetRSubtestsPrevRealm().Addr==%s, got %s", rTestsAddr, addr) + } +} diff --git a/gnovm/cmd/gno/main_test.go b/gnovm/cmd/gno/main_test.go index 6b9a6f5c0d8..bc4fd8eb4b8 100644 --- a/gnovm/cmd/gno/main_test.go +++ b/gnovm/cmd/gno/main_test.go @@ -92,7 +92,7 @@ func testMainCaseRun(t *testing.T, tc []testMainCase) { require.False(t, recoverShouldBeEmpty, "should panic") require.True(t, errShouldBeEmpty, "should not return an error") if test.recoverShouldContain != "" { - require.Contains(t, output, test.recoverShouldContain, "recover should contain") + require.Regexpf(t, test.recoverShouldContain, output, "recover should contain") } if test.recoverShouldBe != "" { require.Equal(t, test.recoverShouldBe, output, "recover should be") diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index f5e180e694a..b99645df0b4 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -14,13 +14,16 @@ import ( "text/template" "time" + "go.uber.org/multierr" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/gnovm/tests" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/random" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/testutils" - "go.uber.org/multierr" ) type testCfg struct { @@ -240,11 +243,9 @@ func gnoTestPkg( stdin = io.In stdout = io.Out stderr = io.Err + errs error ) - filter := splitRegexp(runFlag) - var errs error - mode := tests.ImportModeStdlibsOnly if cfg.withNativeFallback { // XXX: display a warn? @@ -267,14 +268,23 @@ func gnoTestPkg( // testing with *_test.gno if len(unittestFiles) > 0 { - memPkg := gno.ReadMemPackage(pkgPath, pkgPath) + // Determine gnoPkgPath by reading gno.mod + var gnoPkgPath string + modfile, err := gnomod.ParseAt(pkgPath) + if err == nil { + gnoPkgPath = modfile.Module.Mod.Path + } else { + // unable to read pkgPath from gno.mod, generate a random realm path + gnoPkgPath = gno.GnoRealmPkgsPrefixBefore + random.RandStr(8) + } + memPkg := gno.ReadMemPackage(pkgPath, gnoPkgPath) // tfiles, ifiles := gno.ParseMemPackageTests(memPkg) tfiles, ifiles := parseMemPackageTests(memPkg) // run test files in pkg { - m := tests.TestMachine(testStore, stdout, "main") + m := tests.TestMachine(testStore, stdout, gnoPkgPath) if printRuntimeMetrics { // from tm2/pkg/sdk/vm/keeper.go // XXX: make maxAllocTx configurable. @@ -305,6 +315,7 @@ func gnoTestPkg( // testing with *_filetest.gno { + filter := splitRegexp(runFlag) for _, testFile := range filetestFiles { testFileName := filepath.Base(testFile) testName := "file/" + testFileName @@ -377,7 +388,7 @@ func runTestFiles( } m.RunFiles(files.Files...) - n := gno.MustParseFile("testmain.go", testmain) + n := gno.MustParseFile("main_test.gno", testmain) m.RunFiles(n) for _, test := range testFuncs.Tests { diff --git a/gnovm/cmd/gno/test_test.go b/gnovm/cmd/gno/test_test.go index 9059bc365dc..67df26facd7 100644 --- a/gnovm/cmd/gno/test_test.go +++ b/gnovm/cmd/gno/test_test.go @@ -153,7 +153,7 @@ func TestTest(t *testing.T) { }, { args: []string{"test", "--verbose", "../../tests/integ/native-lib"}, - recoverShouldContain: "./../../tests/integ/native-lib/contract.gno:1: unknown import path net", + recoverShouldContain: "gno.land/r/\\w{8}/contract.gno:1: unknown import path net", }, { args: []string{"test", "--verbose", "--with-native-fallback", "../../tests/integ/native-lib"}, @@ -161,11 +161,11 @@ func TestTest(t *testing.T) { }, { args: []string{"test", "--verbose", "../../tests/integ/unknown-lib"}, - recoverShouldContain: "./../../tests/integ/unknown-lib/contract.gno:1: unknown import path foobarbaz", + recoverShouldContain: "gno.land/r/\\w{8}/contract.gno:1: unknown import path foobarbaz", }, { args: []string{"test", "--verbose", "--with-native-fallback", "../../tests/integ/unknown-lib"}, - recoverShouldContain: "./../../tests/integ/unknown-lib/contract.gno:1: unknown import path foobarbaz", + recoverShouldContain: "gno.land/r/\\w{8}/contract.gno:1: unknown import path foobarbaz", }, { args: []string{"test", "--verbose", "--print-runtime-metrics", "../../../examples/gno.land/p/demo/ufmt"}, diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index df8640a65cb..21216828ce4 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -70,6 +70,9 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { // tries to parse gno mod file given the filename, using Parse and Validate from // the gnomod package +// +// TODO(tb): replace by `gnomod.ParseAt` ? The key difference is the latter +// looks for gno.mod in parent directories, while this function doesn't. func parseGnoMod(fname string) (*gnomod.File, error) { file, err := os.Stat(fname) if err != nil { diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 40f770c7720..567aea58284 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -1121,6 +1121,10 @@ func copyValueWithRefs(parent Object, val Value) Value { } case *FuncValue: source := toRefNode(cv.Source) + if strings.HasSuffix(source.Location.File, "_test.gno") { + // Ignore _test files + return nil + } var closure Value if cv.Closure != nil { closure = toRefValue(parent, cv.Closure) @@ -1502,7 +1506,7 @@ func isUnsaved(oo Object) bool { func IsRealmPath(pkgPath string) bool { // TODO: make it more distinct to distinguish from normal paths. - if strings.HasPrefix(pkgPath, "gno.land/r/") { + if strings.HasPrefix(pkgPath, GnoRealmPkgsPrefixBefore) { return true } else { return false diff --git a/gnovm/pkg/gnomod/parse.go b/gnovm/pkg/gnomod/parse.go index a23a5a06b46..5bda3c31f70 100644 --- a/gnovm/pkg/gnomod/parse.go +++ b/gnovm/pkg/gnomod/parse.go @@ -2,6 +2,8 @@ package gnomod import ( "fmt" + "os" + "path/filepath" "reflect" "strings" @@ -9,6 +11,37 @@ import ( "golang.org/x/mod/module" ) +// ParseAt parses, validates and returns a gno.mod file located at dir or at +// dir's parents. +func ParseAt(dir string) (*File, error) { + ferr := func(err error) (*File, error) { + return nil, fmt.Errorf("parsing gno.mod at %s: %w", dir, err) + } + + // FindRootDir requires absolute path, make sure its the case + absDir, err := filepath.Abs(dir) + if err != nil { + return ferr(err) + } + rd, err := FindRootDir(absDir) + if err != nil { + return ferr(err) + } + fname := filepath.Join(rd, "gno.mod") + b, err := os.ReadFile(fname) + if err != nil { + return ferr(err) + } + gm, err := Parse(fname, b) + if err != nil { + return ferr(err) + } + if err := gm.Validate(); err != nil { + return ferr(err) + } + return gm, nil +} + // Parse parses and returns a gno.mod file. // // - file is the name of the file, used in positions and errors. diff --git a/gnovm/tests/files/zrealm_crossrealm11.gno b/gnovm/tests/files/zrealm_crossrealm11.gno index ecb3555c29c..b250b07bbac 100644 --- a/gnovm/tests/files/zrealm_crossrealm11.gno +++ b/gnovm/tests/files/zrealm_crossrealm11.gno @@ -65,7 +65,7 @@ func main() { }, { callStackAdd: " -> r/demo/tests -> r/demo/tests/subtests", - callerFn: rtests.GetPSubtestsPrevRealm, + callerFn: rtests.GetRSubtestsPrevRealm, }, { callStackAdd: " -> p/demo/tests -> r/demo/tests", diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index bc07acea1b4..cfb1f08c6f4 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -972,7 +972,7 @@ func main() { // }, // "FileName": "tests.gno", // "IsMethod": false, -// "Name": "GetPSubtestsPrevRealm", +// "Name": "GetRSubtestsPrevRealm", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index 58d62f02d8d..0af77f9c59c 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -104,7 +104,8 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri Output: stdout, Store: store, }) - return m2.RunMemPackage(memPkg, true) + save := pkgPath != "testing" // never save the "testing" package + return m2.RunMemPackage(memPkg, save) } }