Deep dive into Go 1.18 minor changes in testing package
Tags: go, test
Published: 2021-12-01 02:37:37

## Key takeaways

- Go 1.18 will be released in February 2022
- A minor change ([testing: skip extra -count iterations if there are no tests](https://go-review.googlesource.com/c/go/+/356669/)) will be added in testing package
- Added a fix for missing tests in `runTests()` function in testing.go

## 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](https://github.com/golang/go/issues/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](https://github.com/golang/go/issues/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](https://go-review.googlesource.com/c/go/+/356669/)

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.

```go
package bench

import (
	"testing"
)

var a int

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

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]
```

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgelepiu33mohij0xzys.png)

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://dev-to-uploads.s3.amazonaws.com/uploads/articles/7p0zucmrnpw25woke3k1.png)

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.

```go
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
}
```

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.

```go
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
		}
	}
}
```

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.

```go
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)...
}
```

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".

```go
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
}
```

## 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](https://go-review.googlesource.com/c/go/+/356669/)) will be added in testing package.

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