Skip to content

Commit

Permalink
feat: fix gno test for _test.gno and _filetest.gno files (#945)
Browse files Browse the repository at this point in the history
Fix bug where `gno test` command doesn't recognize 
`_test.gno`/`_filetest.gno` file if directly passed as arg.

Fixes another bug where `gno test` avoids absolute path in arguments.

Note: This PR may contain some code that will be used to support future
PR like #682 and PR that I will open after this to support patterns in
arg.
  • Loading branch information
harry-hov authored Aug 10, 2023
1 parent 60d6605 commit 5240e97
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 83 deletions.
2 changes: 1 addition & 1 deletion gno.land/cmd/gnoland/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func makeGenesisDoc(

for _, pkg := range nonDraftPkgs {
// open files in directory as MemPackage.
memPkg := gno.ReadMemPackage(pkg.Path(), pkg.Name())
memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name)
var tx std.Tx
tx.Msgs = []std.Msg{
vmm.MsgAddPackage{
Expand Down
51 changes: 26 additions & 25 deletions gnovm/cmd/gno/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,13 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error {
cfg.rootDir = guessRootDir()
}

pkgPaths, err := gnoPackagesFromArgs(args)
paths, err := gnoPackagesFromArgs(args)
if err != nil {
return fmt.Errorf("list packages from args: %w", err)
return fmt.Errorf("list package paths from args: %w", err)
}
if len(paths) == 0 {
io.ErrPrintln("no packages to test")
return nil
}

if cfg.timeout > 0 {
Expand All @@ -148,75 +152,72 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error {
}()
}

subPkgs, err := gnomod.SubPkgsFromPaths(paths)
if err != nil {
return fmt.Errorf("list sub packages: %w", err)
}

buildErrCount := 0
testErrCount := 0
for _, pkgPath := range pkgPaths {
for _, pkg := range subPkgs {
if cfg.precompile {
if verbose {
io.ErrPrintfln("=== PREC %s", pkgPath)
io.ErrPrintfln("=== PREC %s", pkg.Dir)
}
precompileOpts := newPrecompileOptions(&precompileCfg{
output: tempdirRoot,
})
err := precompilePkg(importPath(pkgPath), precompileOpts)
err := precompilePkg(importPath(pkg.Dir), precompileOpts)
if err != nil {
io.ErrPrintln(err)
io.ErrPrintln("FAIL")
io.ErrPrintfln("FAIL %s", pkgPath)
io.ErrPrintfln("FAIL %s", pkg.Dir)
io.ErrPrintln("FAIL")

buildErrCount++
continue
}

if verbose {
io.ErrPrintfln("=== BUILD %s", pkgPath)
io.ErrPrintfln("=== BUILD %s", pkg.Dir)
}
tempDir, err := ResolvePath(tempdirRoot, importPath(pkgPath))
tempDir, err := ResolvePath(tempdirRoot, importPath(pkg.Dir))
if err != nil {
return errors.New("cannot resolve build dir")
}
err = goBuildFileOrPkg(tempDir, defaultBuildOptions)
if err != nil {
io.ErrPrintln(err)
io.ErrPrintln("FAIL")
io.ErrPrintfln("FAIL %s", pkgPath)
io.ErrPrintfln("FAIL %s", pkg.Dir)
io.ErrPrintln("FAIL")

buildErrCount++
continue
}
}

unittestFiles, err := filepath.Glob(filepath.Join(pkgPath, "*_test.gno"))
if err != nil {
log.Fatal(err)
}
filetestFiles, err := filepath.Glob(filepath.Join(pkgPath, "*_filetest.gno"))
if err != nil {
log.Fatal(err)
}
if len(unittestFiles) == 0 && len(filetestFiles) == 0 {
io.ErrPrintfln("? %s \t[no test files]", pkgPath)
if len(pkg.TestGnoFiles) == 0 && len(pkg.FiletestGnoFiles) == 0 {
io.ErrPrintfln("? %s \t[no test files]", pkg.Dir)
continue
}

sort.Strings(unittestFiles)
sort.Strings(filetestFiles)
sort.Strings(pkg.TestGnoFiles)
sort.Strings(pkg.FiletestGnoFiles)

startedAt := time.Now()
err = gnoTestPkg(pkgPath, unittestFiles, filetestFiles, cfg, io)
err = gnoTestPkg(pkg.Dir, pkg.TestGnoFiles, pkg.FiletestGnoFiles, cfg, io)
duration := time.Since(startedAt)
dstr := fmtDuration(duration)

if err != nil {
io.ErrPrintfln("%s: test pkg: %v", pkgPath, err)
io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err)
io.ErrPrintfln("FAIL")
io.ErrPrintfln("FAIL %s \t%s", pkgPath, dstr)
io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr)
io.ErrPrintfln("FAIL")
testErrCount++
} else {
io.ErrPrintfln("ok %s \t%s", pkgPath, dstr)
io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr)
}
}
if testErrCount > 0 || buildErrCount > 0 {
Expand Down
8 changes: 7 additions & 1 deletion gnovm/cmd/gno/test_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ func TestTest(t *testing.T) {
errShouldContain: "no such file or directory",
},
{
args: []string{"test", "../../tests/integ/empty-dir"},
args: []string{"test", "../../tests/integ/empty-dir"},
stderrShouldContain: "no packages to test",
},
{
// FIXME: should have an output
Expand Down Expand Up @@ -98,6 +99,11 @@ func TestTest(t *testing.T) {
stdoutShouldContain: "RUN TestSprintf",
stderrShouldContain: "ok ./../../../examples/gno.land/p/demo/ufmt",
},
{
args: []string{"test", "../../../examples/gno.land/p/demo/ufmt/ufmt_test.gno"},
stdoutShouldContain: "RUN TestSprintf",
stderrShouldContain: "ok ../../../examples/gno.land/p/demo/ufmt",
},
{
args: []string{"test", "--verbose", "../../../examples/gno.land/p/demo/ufmt"},
stdoutShouldContain: "RUN TestSprintf",
Expand Down
11 changes: 8 additions & 3 deletions gnovm/cmd/gno/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,14 @@ func gnoPackagesFromArgs(args []string) ([]string, error) {
}
visited[parentDir] = true

// cannot use path.Join or filepath.Join, because we need
// to ensure that ./ is the prefix to pass to go build.
pkg := "./" + parentDir
pkg := parentDir
if !filepath.IsAbs(parentDir) {
// cannot use path.Join or filepath.Join, because we need
// to ensure that ./ is the prefix to pass to go build.
// if not absolute.
pkg = "./" + parentDir
}

paths = append(paths, pkg)
return nil
})
Expand Down
165 changes: 124 additions & 41 deletions gnovm/pkg/gnomod/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,32 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
)

type Pkg struct {
name string
path string
draft bool
requires []string
Dir string // absolute path to package dir
Name string // package name
Requires []string // dependencies
Draft bool // whether the package is a draft
}

type SubPkg struct {
Dir string // absolute path to package dir
ImportPath string // import path of package
Root string // Root dir containing this package, i.e dir containing gno.mod file
Imports []string // imports used by this package

GnoFiles []string // .gno source files (excluding TestGnoFiles, FiletestGnoFiles)
TestGnoFiles []string // _test.gno source files
FiletestGnoFiles []string // _filetest.gno source files
}

type (
PkgList []Pkg
SortedPkgList []Pkg
)

// Name returns the name of the package.
func (p Pkg) Name() string {
return p.name
}

// Path returns the path of the package.
func (p Pkg) Path() string {
return p.path
}

// Draft returns whether the package is a draft.
func (p Pkg) Draft() bool {
return p.draft
}

// Requires returns the required packages of the package.
func (p Pkg) Requires() []string {
return p.requires
}

// sortPkgs sorts the given packages by their dependencies.
func (pl PkgList) Sort() (SortedPkgList, error) {
visited := make(map[string]bool)
Expand All @@ -57,21 +49,21 @@ func (pl PkgList) Sort() (SortedPkgList, error) {

// visitNode visits a package's and its dependencies dependencies and adds them to the sorted list.
func visitPackage(pkg Pkg, pkgs []Pkg, visited, onStack map[string]bool, sortedPkgs *[]Pkg) error {
if onStack[pkg.name] {
return fmt.Errorf("cycle detected: %s", pkg.name)
if onStack[pkg.Name] {
return fmt.Errorf("cycle detected: %s", pkg.Name)
}
if visited[pkg.name] {
if visited[pkg.Name] {
return nil
}

visited[pkg.name] = true
onStack[pkg.name] = true
visited[pkg.Name] = true
onStack[pkg.Name] = true

// Visit package's dependencies
for _, req := range pkg.requires {
for _, req := range pkg.Requires {
found := false
for _, p := range pkgs {
if p.name != req {
if p.Name != req {
continue
}
if err := visitPackage(p, pkgs, visited, onStack, sortedPkgs); err != nil {
Expand All @@ -81,11 +73,11 @@ func visitPackage(pkg Pkg, pkgs []Pkg, visited, onStack map[string]bool, sortedP
break
}
if !found {
return fmt.Errorf("missing dependency '%s' for package '%s'", req, pkg.name)
return fmt.Errorf("missing dependency '%s' for package '%s'", req, pkg.Name)
}
}

onStack[pkg.name] = false
onStack[pkg.Name] = false
*sortedPkgs = append(*sortedPkgs, pkg)
return nil
}
Expand Down Expand Up @@ -120,10 +112,10 @@ func ListPkgs(root string) (PkgList, error) {
}

pkgs = append(pkgs, Pkg{
name: gnoMod.Module.Mod.Path,
path: path,
draft: gnoMod.Draft,
requires: func() []string {
Dir: path,
Name: gnoMod.Module.Mod.Path,
Draft: gnoMod.Draft,
Requires: func() []string {
var reqs []string
for _, req := range gnoMod.Require {
reqs = append(reqs, req.Mod.Path)
Expand All @@ -147,15 +139,15 @@ func (sp SortedPkgList) GetNonDraftPkgs() SortedPkgList {
draft := make(map[string]bool)

for _, pkg := range sp {
if pkg.draft {
draft[pkg.name] = true
if pkg.Draft {
draft[pkg.Name] = true
continue
}
dependsOnDraft := false
for _, req := range pkg.requires {
for _, req := range pkg.Requires {
if draft[req] {
dependsOnDraft = true
draft[pkg.name] = true
draft[pkg.Name] = true
break
}
}
Expand All @@ -165,3 +157,94 @@ func (sp SortedPkgList) GetNonDraftPkgs() SortedPkgList {
}
return res
}

// SubPkgsFromPaths returns a list of subpackages from the given paths.
func SubPkgsFromPaths(paths []string) ([]*SubPkg, error) {
for _, path := range paths {
fi, err := os.Stat(path)
if err != nil {
return nil, err
}
if fi.IsDir() {
continue
}
if filepath.Ext(path) != ".gno" {
return nil, fmt.Errorf("files must be .gno files: %s", path)
}

subPkg, err := GnoFileSubPkg(paths)
if err != nil {
return nil, err
}
return []*SubPkg{subPkg}, nil
}

subPkgs := make([]*SubPkg, 0, len(paths))
for _, path := range paths {
subPkg := SubPkg{}

matches, err := filepath.Glob(filepath.Join(path, "*.gno"))
if err != nil {
return nil, fmt.Errorf("failed to match pattern: %w", err)
}

subPkg.Dir = path
for _, match := range matches {
if strings.HasSuffix(match, "_test.gno") {
subPkg.TestGnoFiles = append(subPkg.TestGnoFiles, match)
continue
}

if strings.HasSuffix(match, "_filetest.gno") {
subPkg.FiletestGnoFiles = append(subPkg.FiletestGnoFiles, match)
continue
}
subPkg.GnoFiles = append(subPkg.GnoFiles, match)
}

subPkgs = append(subPkgs, &subPkg)
}

return subPkgs, nil
}

// GnoFileSubPkg returns a subpackage from the given .gno files.
func GnoFileSubPkg(files []string) (*SubPkg, error) {
subPkg := SubPkg{}
firstDir := ""
for _, file := range files {
if filepath.Ext(file) != ".gno" {
return nil, fmt.Errorf("files must be .gno files: %s", file)
}

fi, err := os.Stat(file)
if err != nil {
return nil, err
}
if fi.IsDir() {
return nil, fmt.Errorf("%s is a directory, should be a Gno file", file)
}

dir := filepath.Dir(file)
if firstDir == "" {
firstDir = dir
}
if dir != firstDir {
return nil, fmt.Errorf("all files must be in one directory; have %s and %s", firstDir, dir)
}

if strings.HasSuffix(file, "_test.gno") {
subPkg.TestGnoFiles = append(subPkg.TestGnoFiles, file)
continue
}

if strings.HasSuffix(file, "_filetest.gno") {
subPkg.FiletestGnoFiles = append(subPkg.FiletestGnoFiles, file)
continue
}
subPkg.GnoFiles = append(subPkg.GnoFiles, file)
}
subPkg.Dir = firstDir

return &subPkg, nil
}
Loading

0 comments on commit 5240e97

Please sign in to comment.