-
Notifications
You must be signed in to change notification settings - Fork 388
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(gnovm): add gno test parallel package testing #3431
base: master
Are you sure you want to change the base?
Changes from all commits
0913df7
eebf546
a191829
b682b1a
707182e
5599bad
a09ec1e
302c66e
62240c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 need to make the output buffered for each test if maxWorkers > 1, so that the full output of a package is all printed together rather than being mixed with that of other tests. |
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -7,7 +7,9 @@ | |||||||
goio "io" | ||||||||
"log" | ||||||||
"path/filepath" | ||||||||
"runtime" | ||||||||
"strings" | ||||||||
"sync" | ||||||||
"time" | ||||||||
|
||||||||
"github.com/gnolang/gno/gnovm/pkg/gnoenv" | ||||||||
|
@@ -16,13 +18,15 @@ | |||||||
"github.com/gnolang/gno/gnovm/pkg/test" | ||||||||
"github.com/gnolang/gno/tm2/pkg/commands" | ||||||||
"github.com/gnolang/gno/tm2/pkg/random" | ||||||||
"golang.org/x/sync/semaphore" | ||||||||
) | ||||||||
|
||||||||
type testCfg struct { | ||||||||
verbose bool | ||||||||
rootDir string | ||||||||
run string | ||||||||
timeout time.Duration | ||||||||
parallel int | ||||||||
updateGoldenTests bool | ||||||||
printRuntimeMetrics bool | ||||||||
printEvents bool | ||||||||
|
@@ -123,6 +127,13 @@ | |||||||
"test name filtering pattern", | ||||||||
) | ||||||||
|
||||||||
fs.IntVar( | ||||||||
&c.parallel, | ||||||||
"parallel", | ||||||||
0, | ||||||||
"number of packages to run concurrently; 0 = GOMAXPROCS", | ||||||||
) | ||||||||
|
||||||||
fs.DurationVar( | ||||||||
&c.timeout, | ||||||||
"timeout", | ||||||||
|
@@ -181,15 +192,19 @@ | |||||||
if cfg.verbose { | ||||||||
stdout = io.Out() | ||||||||
} | ||||||||
opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err()) | ||||||||
opts.RunFlag = cfg.run | ||||||||
opts.Sync = cfg.updateGoldenTests | ||||||||
opts.Verbose = cfg.verbose | ||||||||
opts.Metrics = cfg.printRuntimeMetrics | ||||||||
opts.Events = cfg.printEvents | ||||||||
|
||||||||
buildErrCount := 0 | ||||||||
testErrCount := 0 | ||||||||
|
||||||||
var buildErrCount, testErrCount uint64 | ||||||||
|
||||||||
maxWorkers := runtime.GOMAXPROCS(0) | ||||||||
if cfg.parallel > 0 { | ||||||||
maxWorkers = cfg.parallel | ||||||||
} | ||||||||
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.
Suggested change
|
||||||||
sem := semaphore.NewWeighted(int64(maxWorkers)) | ||||||||
|
||||||||
ctx, cancel := context.WithCancel(context.Background()) | ||||||||
defer cancel() | ||||||||
|
||||||||
var wg sync.WaitGroup | ||||||||
for _, pkg := range subPkgs { | ||||||||
if len(pkg.TestGnoFiles) == 0 && len(pkg.FiletestGnoFiles) == 0 { | ||||||||
io.ErrPrintfln("? %s \t[no test files]", pkg.Dir) | ||||||||
|
@@ -211,26 +226,52 @@ | |||||||
|
||||||||
memPkg := gno.MustReadMemPackage(pkg.Dir, gnoPkgPath) | ||||||||
|
||||||||
startedAt := time.Now() | ||||||||
hasError := catchRuntimeError(gnoPkgPath, io.Err(), func() { | ||||||||
err = test.Test(memPkg, pkg.Dir, opts) | ||||||||
}) | ||||||||
var syncWrite sync.Mutex | ||||||||
|
||||||||
duration := time.Since(startedAt) | ||||||||
dstr := fmtDuration(duration) | ||||||||
wg.Add(1) | ||||||||
go func() { | ||||||||
defer wg.Done() | ||||||||
|
||||||||
if hasError || err != nil { | ||||||||
if err != nil { | ||||||||
io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err) | ||||||||
if err := sem.Acquire(ctx, 1); err != nil { | ||||||||
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. This could be a channel where you write at the beginning |
||||||||
return | ||||||||
} | ||||||||
io.ErrPrintfln("FAIL") | ||||||||
io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr) | ||||||||
io.ErrPrintfln("FAIL") | ||||||||
testErrCount++ | ||||||||
} else { | ||||||||
io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr) | ||||||||
} | ||||||||
defer sem.Release(1) | ||||||||
|
||||||||
opts := test.NewTestOptions(cfg.rootDir, io.In(), stdout, io.Err()) | ||||||||
opts.RunFlag = cfg.run | ||||||||
opts.Sync = cfg.updateGoldenTests | ||||||||
opts.Verbose = cfg.verbose | ||||||||
opts.Metrics = cfg.printRuntimeMetrics | ||||||||
opts.Events = cfg.printEvents | ||||||||
|
||||||||
startedAt := time.Now() | ||||||||
hasError := catchRuntimeError(gnoPkgPath, io.Err(), func() { | ||||||||
err = test.Test(memPkg, pkg.Dir, opts) | ||||||||
}) | ||||||||
|
||||||||
duration := time.Since(startedAt) | ||||||||
dstr := fmtDuration(duration) | ||||||||
|
||||||||
syncWrite.Lock() | ||||||||
defer syncWrite.Unlock() | ||||||||
|
||||||||
if hasError || err != nil { | ||||||||
if err != nil { | ||||||||
io.ErrPrintfln("%s: test pkg: %v", pkg.Dir, err) | ||||||||
} | ||||||||
io.ErrPrintfln("FAIL") | ||||||||
io.ErrPrintfln("FAIL %s \t%s", pkg.Dir, dstr) | ||||||||
io.ErrPrintfln("FAIL") | ||||||||
|
||||||||
testErrCount++ | ||||||||
} else { | ||||||||
io.ErrPrintfln("ok %s \t%s", pkg.Dir, dstr) | ||||||||
} | ||||||||
}() | ||||||||
} | ||||||||
|
||||||||
wg.Wait() | ||||||||
|
||||||||
if testErrCount > 0 || buildErrCount > 0 { | ||||||||
io.ErrPrintfln("FAIL") | ||||||||
return fmt.Errorf("FAIL: %d build errors, %d test errors", buildErrCount, testErrCount) | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
package gnolang_test | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"fmt" | ||
"io" | ||
|
@@ -11,7 +10,6 @@ import ( | |
"strings" | ||
"testing" | ||
|
||
"github.com/gnolang/gno/gnovm/pkg/gnolang" | ||
"github.com/gnolang/gno/gnovm/pkg/test" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
@@ -107,76 +105,76 @@ func TestFiles(t *testing.T) { | |
} | ||
|
||
// TestStdlibs tests all the standard library packages. | ||
func TestStdlibs(t *testing.T) { | ||
t.Parallel() | ||
|
||
rootDir, err := filepath.Abs("../../../") | ||
require.NoError(t, err) | ||
|
||
newOpts := func() (capture *bytes.Buffer, opts *test.TestOptions) { | ||
var out io.Writer | ||
if testing.Verbose() { | ||
out = os.Stdout | ||
} else { | ||
capture = new(bytes.Buffer) | ||
out = capture | ||
} | ||
opts = test.NewTestOptions(rootDir, nopReader{}, out, out) | ||
opts.Verbose = true | ||
return | ||
} | ||
sharedCapture, sharedOpts := newOpts() | ||
|
||
dir := "../../stdlibs/" | ||
fsys := os.DirFS(dir) | ||
err = fs.WalkDir(fsys, ".", func(path string, de fs.DirEntry, err error) error { | ||
switch { | ||
case err != nil: | ||
return err | ||
case !de.IsDir() || path == ".": | ||
return nil | ||
} | ||
|
||
fp := filepath.Join(dir, path) | ||
memPkg := gnolang.MustReadMemPackage(fp, path) | ||
t.Run(strings.ReplaceAll(memPkg.Path, "/", "-"), func(t *testing.T) { | ||
capture, opts := sharedCapture, sharedOpts | ||
switch memPkg.Path { | ||
// Excluded in short | ||
case | ||
"bufio", | ||
"bytes", | ||
"strconv": | ||
if testing.Short() { | ||
t.Skip("Skipped because of -short, and this stdlib is very long currently.") | ||
} | ||
fallthrough | ||
// Run using separate store, as it's faster | ||
case | ||
"math/rand", | ||
"regexp", | ||
"regexp/syntax", | ||
"sort": | ||
t.Parallel() | ||
capture, opts = newOpts() | ||
} | ||
|
||
if capture != nil { | ||
capture.Reset() | ||
} | ||
|
||
err := test.Test(memPkg, "", opts) | ||
if !testing.Verbose() { | ||
t.Log(capture.String()) | ||
} | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
}) | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
// func TestStdlibs(t *testing.T) { | ||
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. If we don't have this, then the tests in the standard libraries doesn't count as coverage, and I think it should |
||
// t.Parallel() | ||
|
||
// rootDir, err := filepath.Abs("../../../") | ||
// require.NoError(t, err) | ||
|
||
// newOpts := func() (capture *bytes.Buffer, opts *test.TestOptions) { | ||
// var out io.Writer | ||
// if testing.Verbose() { | ||
// out = os.Stdout | ||
// } else { | ||
// capture = new(bytes.Buffer) | ||
// out = capture | ||
// } | ||
// opts = test.NewTestOptions(rootDir, nopReader{}, out, out) | ||
// opts.Verbose = true | ||
// return | ||
// } | ||
// sharedCapture, sharedOpts := newOpts() | ||
|
||
// dir := "../../stdlibs/" | ||
// fsys := os.DirFS(dir) | ||
// err = fs.WalkDir(fsys, ".", func(path string, de fs.DirEntry, err error) error { | ||
// switch { | ||
// case err != nil: | ||
// return err | ||
// case !de.IsDir() || path == ".": | ||
// return nil | ||
// } | ||
|
||
// fp := filepath.Join(dir, path) | ||
// memPkg := gnolang.MustReadMemPackage(fp, path) | ||
// t.Run(strings.ReplaceAll(memPkg.Path, "/", "-"), func(t *testing.T) { | ||
// capture, opts := sharedCapture, sharedOpts | ||
// switch memPkg.Path { | ||
// // Excluded in short | ||
// case | ||
// "bufio", | ||
// "bytes", | ||
// "strconv": | ||
// if testing.Short() { | ||
// t.Skip("Skipped because of -short, and this stdlib is very long currently.") | ||
// } | ||
// fallthrough | ||
// // Run using separate store, as it's faster | ||
// case | ||
// "math/rand", | ||
// "regexp", | ||
// "regexp/syntax", | ||
// "sort": | ||
// t.Parallel() | ||
// capture, opts = newOpts() | ||
// } | ||
|
||
// if capture != nil { | ||
// capture.Reset() | ||
// } | ||
|
||
// err := test.Test(memPkg, "", opts) | ||
// if !testing.Verbose() { | ||
// t.Log(capture.String()) | ||
// } | ||
// if err != nil { | ||
// t.Error(err) | ||
// } | ||
// }) | ||
|
||
// return nil | ||
// }) | ||
// if err != nil { | ||
// t.Fatal(err) | ||
// } | ||
// } |
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.
why specify this?