Skip to content

Commit

Permalink
feat: gno test support /... pattern (#1078)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Altered behavior of the `gno test` command

Adds support for `/...` pattern in `gno test` command. Now args can have
`/...` pattern in the directory path.

Using `gno test ./path/to/pkg` would trigger the execution of test files
solely within the specified package directory, excluding any
subdirectories like `./path/to/pkg/subpkg`.

To execute test files within subdirectories as well, use `gno test
./path/to/pkg/...`

It supports all variations of `/...` such as `./path/.../pkg`,
`./.../pkg`, ,`./.../path/...` and more


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

- [x] Added new tests, or not needed, or not feasible
- [x] Provided an example (e.g. screenshot) to aid review or the PR is
self-explanatory
- [x] Updated the official documentation or not needed
- [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message
was included in the description
- [x] 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).
</details>
  • Loading branch information
harry-hov authored Oct 12, 2023
1 parent eb27a8f commit a2971bf
Show file tree
Hide file tree
Showing 18 changed files with 411 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
with:
go-version: ${{ matrix.goversion }}
- run: go install -v ./gnovm/cmd/gno
- run: go run ./gnovm/cmd/gno test --verbose ./examples
- run: go run ./gnovm/cmd/gno test --verbose ./examples/...
lint:
strategy:
fail-fast: false
Expand Down
4 changes: 2 additions & 2 deletions examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ build: precompile

.PHONY: test
test:
go run ../gnovm/cmd/gno test --verbose .
go run ../gnovm/cmd/gno test --verbose ./...

.PHONY: lint
lint:
go run ../gnovm/cmd/gno lint $(OFFICIAL_PACKAGES)

.PHONY: test.sync
test.sync:
go run ../gnovm/cmd/gno test --verbose --update-golden-tests .
go run ../gnovm/cmd/gno test --verbose --update-golden-tests ./...

.PHONY: clean
clean:
Expand Down
6 changes: 3 additions & 3 deletions gnovm/cmd/gno/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func newTestCmd(io *commands.IO) *commands.Command {
'gno test' recompiles each package along with any files with names matching the
file pattern "*_test.gno" or "*_filetest.gno".
The only <package> supported for now is a directory (relative or absolute).
The <package> can be directory or file path (relative or absolute).
- "*_test.gno" files work like "*_test.go" files, but they contain only test
functions. Benchmark and fuzz functions aren't supported yet. Similarly, only
Expand Down Expand Up @@ -182,9 +182,9 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error {
cfg.rootDir = guessRootDir()
}

paths, err := gnoPackagesFromArgs(args)
paths, err := targetsFromPatterns(args)
if err != nil {
return fmt.Errorf("list package paths from args: %w", err)
return fmt.Errorf("list targets from patterns: %w", err)
}
if len(paths) == 0 {
io.ErrPrintln("no packages to test")
Expand Down
5 changes: 5 additions & 0 deletions gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@

gno test .

! stdout .+
stderr '[no test files]'

gno test ./...

! stdout .+
stderr 'no packages to test'
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
gno test .

! stdout .+
stderr '\? \./\. \[no test files\]'
stderr '\? \. \[no test files\]'

! gno test --precompile .

Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/error_correct.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ gno test -verbose .
stdout 'Machine\.RunMain\(\) panic: oups'
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- x_filetest.gno --
package main
Expand Down
4 changes: 2 additions & 2 deletions gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ stderr 'panic: fail on failing_filetest.gno: got unexpected error: beep boop'
! gno test -verbose --precompile .

stdout 'Machine.RunMain\(\) panic: beep boop'
stderr '=== PREC \./\.'
stderr '=== BUILD \./\.'
stderr '=== PREC \.'
stderr '=== BUILD \.'
stderr '=== RUN file/failing_filetest.gno'
stderr 'panic: fail on failing_filetest.gno: got unexpected error: beep boop'

Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/minim1.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
gno test .

! stdout .+
stderr '\? \./\. \[no test files\]'
stderr '\? \. \[no test files\]'

-- minim.gno --
package minim
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/minim2.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
gno test .

! stdout .+
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- minim.gno --
package minim
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/minim3.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
gno test .

! stdout .+
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- minim.gno --
package minim
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/output_correct.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ gno test -verbose .
! stdout .+ # stdout should be empty
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- x_filetest.gno --
package main
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/output_sync.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ gno test -verbose . -update-golden-tests
! stdout .+ # stdout should be empty
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

cmp x_filetest.gno x_filetest.gno.golden

Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ gno test -verbose .
! stdout .+ # stdout should be empty
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- x_filetest.gno --
// PKGPATH: gno.land/r/x
Expand Down
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ gno test -verbose . -update-golden-tests
! stdout .+ # stdout should be empty
stderr '=== RUN file/x_filetest.gno'
stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

cmp x_filetest.gno x_filetest.gno.golden

Expand Down
4 changes: 2 additions & 2 deletions gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
gno test .

! stdout .+
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

gno test -verbose .

! stdout .+
stderr '=== RUN file/valid_filetest.gno'
stderr '--- PASS: file/valid_filetest.gno \(\d\.\d\ds\)'
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

-- valid.gno --
package valid
Expand Down
7 changes: 6 additions & 1 deletion gnovm/cmd/gno/testdata/gno_test/valid_test.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
gno test .

! stdout .+
stderr 'ok \./\. \d\.\d\ds'
stderr 'ok \. \d\.\d\ds'

gno test ./...

! stdout .+
stderr 'ok \. \d\.\d\ds'

-- valid.gno --
package valid
Expand Down
84 changes: 84 additions & 0 deletions gnovm/cmd/gno/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -105,6 +106,89 @@ func gnoPackagesFromArgs(args []string) ([]string, error) {
return paths, nil
}

// targetsFromPatterns returns a list of target paths that match the patterns.
// Each pattern can represent a file or a directory, and if the pattern
// includes "/...", the "..." is treated as a wildcard, matching any string.
// Intended to be used by gno commands such as `gno test`.
func targetsFromPatterns(patterns []string) ([]string, error) {
paths := []string{}
for _, p := range patterns {
var match func(string) bool
patternLookup := false
dirToSearch := p

// Check if the pattern includes `/...`
if strings.Contains(p, "/...") {
index := strings.Index(p, "/...")
if index != -1 {
dirToSearch = p[:index] // Extract the directory path to search
}
match = matchPattern(strings.TrimPrefix(p, "./"))
patternLookup = true
}

info, err := os.Stat(dirToSearch)
if err != nil {
return nil, fmt.Errorf("invalid file or package path: %w", err)
}

// If the pattern is a file or a directory
// without `/...`, add it to the list.
if !info.IsDir() || !patternLookup {
paths = append(paths, p)
continue
}

// the pattern is a dir containing `/...`, walk the dir recursively and
// look for directories containing at least one .gno file and match pattern.
visited := map[string]bool{} // used to run the builder only once per folder.
err = filepath.WalkDir(dirToSearch, func(curpath string, f fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("%s: walk dir: %w", dirToSearch, err)
}
// Skip directories and non ".gno" files.
if f.IsDir() || !isGnoFile(f) {
return nil
}

parentDir := filepath.Dir(curpath)
if _, found := visited[parentDir]; found {
return nil
}

visited[parentDir] = true
if match(parentDir) {
paths = append(paths, parentDir)
}

return nil
})
if err != nil {
return nil, err
}
}
return paths, nil
}

// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Simplified version of go source's matchPatternInternal
// (see $GOROOT/src/cmd/internal/pkgpattern)
func matchPattern(pattern string) func(name string) bool {
re := regexp.QuoteMeta(pattern)
re = strings.Replace(re, `\.\.\.`, `.*`, -1)
// Special case: foo/... matches foo too.
if strings.HasSuffix(re, `/.*`) {
re = re[:len(re)-len(`/.*`)] + `(/.*)?`
}
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
return reg.MatchString(name)
}
}

func fmtDuration(d time.Duration) string {
return fmt.Sprintf("%.2fs", d.Seconds())
}
Expand Down
Loading

0 comments on commit a2971bf

Please sign in to comment.