Skip to content

Commit

Permalink
test(gnovm): test performance improvements (gnolang#3210)
Browse files Browse the repository at this point in the history
Fix master CI runs, and miscellaneous improvements locally, too.

- ci: switch to using `-covermode=set` rather than atomic, as it
significantly degrades performance while not being shown on codecov.
[more
info](gnolang#3210 (comment))
- gnolang tests: use `t.Parallel()` to parallelize known "long" tests,
both in `-short` and long versions.
- stdlibs: provide `unicode` native shims for some common functions used
in some standard library tests. This may lead to some small
inconsistencies between on-chain behaviour and off-chain should the
`unicode` packages diverge; but I think we might we might want to
consider a native-based `unicode` stdlib, anyway.
- thanks to these improvements, there is no longer the need to run
`-short` on PRs, as the CI runs in ~9 mins, ie. 8 minutes less than the
gno.land tests.
  • Loading branch information
thehowl authored and r3v4s committed Dec 10, 2024
1 parent 2effa82 commit 49fef5d
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 22 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/gnovm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ jobs:
uses: ./.github/workflows/main_template.yml
with:
modulepath: "gnovm"
# in pull requests, append -short so that the CI runs quickly.
tests-extra-args: ${{ github.event_name == 'pull_request' && '-short' || '' }}
secrets:
codecov-token: ${{ secrets.CODECOV_TOKEN }}
fmt:
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ jobs:
# Craft a filter flag based on the module path to avoid expanding coverage on unrelated tags.
export filter="-pkg=github.com/gnolang/gno/${{ inputs.modulepath }}/..."
# codecov only supports "boolean" coverage (whether a line is
# covered or not); so using -covermode=count or atomic would be
# pointless here.
# XXX: Simplify coverage of txtar - the current setup is a bit
# confusing and meticulous. There will be some improvements in Go
# 1.23 regarding coverage, so we can use this as a workaround until
# then.
go test -covermode=atomic -timeout ${{ inputs.tests-timeout }} ${{ inputs.tests-extra-args }} ./... -test.gocoverdir=$GOCOVERDIR
go test -covermode=set -timeout ${{ inputs.tests-timeout }} ${{ inputs.tests-extra-args }} ./... -test.gocoverdir=$GOCOVERDIR
# Print results
(set +x; echo 'go coverage results:')
Expand Down
79 changes: 60 additions & 19 deletions gnovm/pkg/gnolang/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,26 @@ func (nopReader) Read(p []byte) (int, error) { return 0, io.EOF }
// fix a specific test:
// go test -run TestFiles/'^bin1.gno' -short -v -update-golden-tests .
func TestFiles(t *testing.T) {
t.Parallel()

rootDir, err := filepath.Abs("../../../")
require.NoError(t, err)

opts := &test.TestOptions{
RootDir: rootDir,
Output: io.Discard,
Error: io.Discard,
Sync: *withSync,
newOpts := func() *test.TestOptions {
o := &test.TestOptions{
RootDir: rootDir,
Output: io.Discard,
Error: io.Discard,
Sync: *withSync,
}
o.BaseStore, o.TestStore = test.Store(
rootDir, true,
nopReader{}, o.WriterForStore(), io.Discard,
)
return o
}
opts.BaseStore, opts.TestStore = test.Store(
rootDir, true,
nopReader{}, opts.WriterForStore(), io.Discard,
)
// sharedOpts is used for all "short" tests.
sharedOpts := newOpts()

dir := "../../tests/"
fsys := os.DirFS(dir)
Expand All @@ -59,7 +66,8 @@ func TestFiles(t *testing.T) {
return nil
}
subTestName := path[len("files/"):]
if strings.HasSuffix(path, "_long.gno") && testing.Short() {
isLong := strings.HasSuffix(path, "_long.gno")
if isLong && testing.Short() {
t.Run(subTestName, func(t *testing.T) {
t.Skip("skipping in -short")
})
Expand All @@ -73,6 +81,12 @@ func TestFiles(t *testing.T) {

var criticalError error
t.Run(subTestName, func(t *testing.T) {
opts := sharedOpts
if isLong {
// Long tests are run in parallel, and with their own store.
t.Parallel()
opts = newOpts()
}
changed, err := opts.RunFiletest(path, content)
if err != nil {
t.Fatal(err.Error())
Expand All @@ -94,16 +108,24 @@ 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)

var capture bytes.Buffer
out := io.Writer(&capture)
if testing.Verbose() {
out = os.Stdout
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
}
opts := test.NewTestOptions(rootDir, nopReader{}, out, out)
opts.Verbose = true
sharedCapture, sharedOpts := newOpts()

dir := "../../stdlibs/"
fsys := os.DirFS(dir)
Expand All @@ -118,12 +140,31 @@ func TestStdlibs(t *testing.T) {
fp := filepath.Join(dir, path)
memPkg := gnolang.ReadMemPackage(fp, path)
t.Run(strings.ReplaceAll(memPkg.Path, "/", "-"), func(t *testing.T) {
if testing.Short() {
switch memPkg.Path {
case "bytes", "strconv", "regexp/syntax":
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())
Expand Down
2 changes: 2 additions & 0 deletions gnovm/pkg/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,8 @@ func (opts *TestOptions) runTestFiles(
if opts.Metrics {
alloc = gno.NewAllocator(math.MaxInt64)
}
// reset store ops, if any - we only need them for some filetests.
opts.TestStore.SetLogStoreOps(false)

// Check if we already have the package - it may have been eagerly
// loaded.
Expand Down
2 changes: 2 additions & 0 deletions gnovm/stdlibs/bytes/compare_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ func TestCompareIdenticalSlice(t *testing.T) {
}

func TestCompareBytes(t *testing.T) {
t.Skip("This test takes very long to run on Gno at time of writing, even in its short form")

lengths := make([]int, 0) // lengths to test in ascending order
for i := 0; i <= 128; i++ {
lengths = append(lengths, i)
Expand Down
114 changes: 114 additions & 0 deletions gnovm/tests/stdlibs/generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions gnovm/tests/stdlibs/unicode/natives.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package unicode

// Optimized as native bindings in tests.

func IsPrint(r rune) bool
func IsGraphic(r rune) bool
func SimpleFold(r rune) rune
func IsUpper(r rune) bool
8 changes: 8 additions & 0 deletions gnovm/tests/stdlibs/unicode/natives.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package unicode

import "unicode"

func IsPrint(r rune) bool { return unicode.IsPrint(r) }
func IsGraphic(r rune) bool { return unicode.IsGraphic(r) }
func SimpleFold(r rune) rune { return unicode.SimpleFold(r) }
func IsUpper(r rune) bool { return unicode.IsUpper(r) }

0 comments on commit 49fef5d

Please sign in to comment.