-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: fix gno test
for _test.gno
and _filetest.gno
files
#945
Changes from all commits
35d5064
022d434
986a7c8
56aecb4
c037e22
07cc842
8c87cc6
6911013
95a335d
1e6ce13
7cb7960
d96e536
9d42cc0
65f9d1e
d2d04f6
5d7ea7c
3b729be
038d8c5
5c0e053
d5ef003
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. better to leave it as pkgPaths so as to differentiate from file paths. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but they can be file paths too. e.g. Later I'm also planning to support patterns in args. e.g.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do the renaming in a new PR so we can focus on names. Suggestion:
Suggested change
// targetsFromPatterns returns a list of local dirs or single files
// intended to be used by gno commands such as `gno test`.
func targetsFromPatterns(args []string) []target {
// TODO: support './...'
} |
||||||
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 { | ||||||
|
@@ -148,75 +152,72 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error { | |||||
}() | ||||||
} | ||||||
|
||||||
subPkgs, err := gnomod.SubPkgsFromPaths(paths) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we CAN have a function called "SubPkgsFromDir" (singular), but not "SubPkgsFromDirs" or "SubPkgsFromPath" or "SubPkgsFromPaths". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on how The main difference is that they are only supporting dirs and not single file, while we want our CLI to basically support this at the end: I think we should consider that we have:
Edit: @harry-hov found this which is better: https://github.com/golang/go/blob/master/src/cmd/go/internal/load/pkg.go#L2816; it consists of looking for packages, but creating virtual packages of a single file when a file is passed instead of a directory or pattern. |
||||||
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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my main point is that here, we should use pkg.Path, which is not the same as any Dir. because "PkgPath/pkgPath/pkgpath/pkg.Path" are all the same "package path" identifier, whereas anything that ends with "Dir" is a filesystem location. They can diverge. Also a package path starts with a domain unless it is special, like a stdlibs package or something special like "main". Dir is a different beast based on the filesystem. Either absolute or relative, not something that starts with "gno.land" nor "main". It might be "." or "./something" though, or "/something". We should ideally make sure all Dirs start with a dot or slash. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree we should print pkg.Path instead of Dir. original var Will fix this. But in another PR. (Out of scope for this PR) |
||||||
} | ||||||
precompileOpts := newPrecompileOptions(&precompileCfg{ | ||||||
output: tempdirRoot, | ||||||
}) | ||||||
err := precompilePkg(importPath(pkgPath), precompileOpts) | ||||||
err := precompilePkg(importPath(pkg.Dir), precompileOpts) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. importPath should take pkg.Path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I see that importPath is declared as a there can be some function that converts a package path to a file path, Other than such a function, we shouldn't be converting package path Names to file paths or dir paths with type conversions, because nobody should be doing such a thing without calling the official function for such conversions (above). An "import path" I don't see why we need to create such a name, because it is nothing more than package paths. So ImportPkgPaths is less confusing. Or just Imports of Names. Any path that is not a package path, should be clearly named as such, by calling it a "file path" or a "dir path" that is a string, never a Name type. Anything like package path, pkgPath, pkg.Path, are all package paths. If you want to associate a file path or a dir path it will have to be called pkg.FilePath or pkg.DirPath. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, |
||||||
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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GnoTests string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or TestFiles, FiletestFiles? |
||||||
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 { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be better also to rename this pkgDir |
||
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 | ||
} | ||
harry-hov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
paths = append(paths, pkg) | ||
return nil | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,40 +5,32 @@ import ( | |
"io/fs" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
type Pkg struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think type Module would be better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initially had the same thought. But we don't seem to use word module anywhere. |
||
name string | ||
path string | ||
draft bool | ||
requires []string | ||
Dir string // absolute path to package dir | ||
harry-hov marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dir here is fine but there should also be a PkgPath. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have Maybe rename it to Path? ( |
||
Name string // package name | ||
Requires []string // dependencies | ||
Draft bool // whether the package is a draft | ||
zivkovicmilos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
type SubPkg struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't have anything called Sub*. |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ImportPaths []Name // I think ImportPkgPath is just too long. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have Don't you think
|
||
|
||
GnoFiles []string // .gno source files (excluding TestGnoFiles, FiletestGnoFiles) | ||
TestGnoFiles []string // _test.gno source files | ||
FiletestGnoFiles []string // _filetest.gno source files | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe BTW we might support other langauges that compile to .gno, like .gnoffee There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original thought process behind this is to have pkg in a structured/sorted/filtered way. If we want to support another language we can easily add another field here. This way we can apply specific operations on them accordingly. |
||
} | ||
|
||
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) | ||
|
@@ -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 { | ||
|
@@ -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 | ||
} | ||
|
@@ -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) | ||
|
@@ -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 | ||
} | ||
} | ||
|
@@ -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) { | ||
ajnavarro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} | ||
|
||
zivkovicmilos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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) { | ||
ajnavarro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this usage of Dir is OK since you need to file location to load.