DEV Community

Kazuki Higashiguchi
Kazuki Higashiguchi

Posted on

Deep dive into Go 1.18 minor changes in testing package

Key takeaways

Go 1.18

The Go team is hard working on Go 1.18, which will be released in February 2022. Rob Pike said the 1.18 release will include the biggest change to the language since Go's creation at Issue #48918.

The 1.18 release of the Go language is likely to include by far the biggest change to the language since its creation: parametric polymorphism, colloquially called generics.

In conjunction with implementation, Go team is also writing the release note of Go 1.18 (Issue#47694).

You can read the draft from the link below.

https://tip.golang.org/doc/go1.18

Minor changes in testing package

In this article, I will focus on the minor change of a standard package, testing.

testing: skip extra -count iterations if there are no tests

Specifically, the following points will be explained.

  • What was the issue?
  • What kind of implementation solved the problem?

Notice: It's still in the draft stage, so it's not certain if it will really be there when 1.18 version is released.

Issue: high start up cost with high -count and no tests

The background of this changes included in the Go 1.18 draft, is the following link.

https://github.com/golang/go/issues/49050

This issue reports that the benchmark does not start immediately under certain conditions.

The code to reproduce it is also available in the issue.

package bench

import (
    "testing"
)

var a int

func BenchmarkFoo(b *testing.B) {
    for i := 0; i < b.N; i++ {
        a += 1
    }
}
Enter fullscreen mode Exit fullscreen mode

Actually, it took about 4s to run the bench mark for the first time when I tried it in my environment(go version go1.17.1 darwin/amd64).

$ go test -bench=. -count=10000000
?       github.com/hgsgtk/go-snippets/bench     [no test files]
Enter fullscreen mode Exit fullscreen mode

How was this issue resolved

You can read the modified commit in the following link.

https://go-review.googlesource.com/c/go/+/356669/2/src/testing/testing.go

The commit in src/testing/testing.go

As a prerequisite, runTests() is a function in testing package invoked from testing.m.Run() or RunTests() function. RunTests() function is exported to be a part of the implementation of the "go test" command.

Internal structure of testing package

https://speakerdeck.com/hgsgtk/dive-deep-into-go-1-dot-15-changes-to-testing-package?slide=12

This commit adds an if statement when there is no test to run in the function.

if i > 0 && !ran {
    // There were no tests to run on the first
    // iteration. This won't change, so no reason
    // to keep trying.
    break
}
Enter fullscreen mode Exit fullscreen mode

Since the condition if i > 0 && !ran, !ran condition will be verified in the second and subsequent loops. In addition, ran has a bool value that indicates whether the test was run last time or not.

Here is the specific related code.

func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest, deadline time.Time) (ran, ok bool) {
    ok = true
    for _, procs := range cpuList {
        runtime.GOMAXPROCS(procs)
        for i := uint(0); i < *count; i++ {
            t := &T{
                common: common{
                    signal:  make(chan bool, 1),
                    barrier: make(chan bool),
                    w:       os.Stdout,
                },
                context: ctx,
            }
            // ... (omit)
            ran = ran || t.ran // <- here
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

https://go-review.googlesource.com/c/go/+/356669/2/src/testing/testing.go#1818

t.ran is ran field in testing.common structure which is embed in testing.T structure. It indicates whether test or benchmark (or one of its subtests) was executed or not.

type common struct {
    // (omit)...
    ran         bool                 // Test or benchmark (or one of its subtests) was executed.
    failed      bool                 // Test or benchmark has failed.
    skipped     bool                 // Test or benchmark has been skipped.
    done        bool                 // Test is finished and all subtests have completed.
    // (omit)...
}
Enter fullscreen mode Exit fullscreen mode

https://go.googlesource.com/go/+/refs/changes/69/356669/2/src/testing/testing.go#493

That's why this if statement means "there were no tests to run on the first iteration".

if i > 0 && !ran {
    // There were no tests to run on the first
    // iteration. This won't change, so no reason
    // to keep trying.
    break
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Go 1.18 will be released in February 2022. I'm looking forward to the release date. This article introduced a minor change (testing: skip extra -count iterations if there are no tests) will be added in testing package.

I hope this article will help you to understand the internal implementation related testing package.

Discussion (0)