The Go Blog
Participate in the 2016 Go User Survey and Company Questionnaire
13 December 2016
The Go project wants to hear from you!
The Go project wants to hear from you! Our goal is to create the best language for developing simple, reliable, scalable software. We are asking you to help by participating in a survey and if applicable, a company questionnaire.
The Go User Survey
Who: If you use Go, have ever used Go, have ever stopped using Go, or have any interest in the language, please help by sharing your feedback to improve Go for you and your fellow Gophers.
Where: Please take this 20-minute survey by December 22nd: Go User Survey 2016
The survey is anonymous and confidential.
Why: The Go project leadership depends on your feedback to guide the future of the Go project. Your responses will help to understand how we are doing and help plan improvements for the Go language, libraries, and tools.
After the survey closes, we will publish the anonymous aggregate results to the Go blog.
The Go Company Questionnaire
Who: If you are in a position to share details like “company name”, “if your company is hiring Go developers”, and “reasons your team or company adopted Go” then please help us by taking this questionnaire. We only need one response per company (or department for larger companies).
Where: Please take this 5-minute questionnaire by December 22nd: Go Company Questionnaire 2016
The questionnaire is confidential, but not anonymous.
Why: The Go project would like to better understand the companies using Go.
After the questionnaire closes the Go project leadership will use this information to better understand how companies are utilizing Go and in what ways the project can improve their experience. If you participate we may reach out for further information.
Spread the word!
Please help us spread the word by sharing this post on your social network feeds, at meetups, around your office and in other communities.
The survey & questionnaire close by December 22nd so please share today.
Go fonts
16 November 2016
An Announcement
The experimental user interface toolkit being built at
golang.org/x/exp/shiny
includes several text elements, but there is a problem with testing them:
What font should be used?
Answering this question led us to today's announcement,
the release of a family of high-quality WGL4 TrueType fonts,
created by the Bigelow & Holmes type foundry specifically for the Go project.
The font family, called Go (naturally), includes proportional- and fixed-width faces in normal, bold, and italic renderings. The fonts have been tested for technical uses, particularly programming. Go source code looks particularly good when displayed in Go fonts, as its name implies, with things like punctuation characters easily distinguishable and operators lined up and placed consistently:
Perhaps the most remarkable feature of the Go fonts is their license: They are licensed under the same open source license as the rest of the Go project's software, an unusually free arrangement for a high-quality font set.
Here are samples of the proportionally-spaced...
and monospaced fonts:
How to use them
If you just want the TTF files, run
git clone https://go.googlesource.com/image
and copy them from the subsequent image/font/gofont/ttfs directory.
If you want to use Go (the fonts) with Go (the software), each font is provided by a separate package.
To use the Go Regular font in a program, import golang.org/x/image/font/gofont/goregular, and write:
font, err := truetype.Parse(goregular.TTF)
The github.com/golang/freetype/truetype
package provides the truetype.Parse function today.
There is also work underway to add a TrueType package under golang.org/x
again licensed under the same open source license as the rest of the Go project's software.
We leave it to you to find some of the other unusual properties the fonts have, but for an overview of the fonts' design we asked Chuck Bigelow to provide some background. The remainder of this blog post is his response.
Notes on the fonts, by Chuck Bigelow
The Go fonts are divided into two sets, Go proportional, which is sans-serif, and Go Mono, which is slab-serif.
Go proportional fonts
Sans-serif
Go proportional fonts are sans-serif, like several popular fonts for screen displays. There is some evidence that some sans-serif faces at small sizes and low resolutions on screens are slightly more legible than their seriffed counterparts, while at large sizes, there is not a significant difference in legibility between sans and seriffed faces, at least in the pair tested. [1] (The bracketed numbers refer to the references listed at the end of this article.)
Style
Go sans-serif fonts are "humanist" rather than "grotesque" in style. This is an historical distinction, not an aesthetic judgment. Widely used sans-serif fonts like Helvetica and Arial are called grotesque because an early 19th century sans-serif typeface was named "Grotesque," and the name became generic.
The shapes of modern grotesque fonts like Helvetica are sculpted, with smooth, assimilated forms.
Humanist sans-serifs are derived from Humanist handwriting and early fonts of the Italian Renaissance and still show subtle traces of pen-written calligraphy. There is some evidence that humanist fonts are more legible than grotesque fonts. [2]
Italics
Go proportional italics have the same width metrics as the roman fonts. Go italics are oblique versions of the romans, with one noticeable exception: the italic lowercase 'a' is redesigned as a cursive single-story form to harmonize with the bowl shapes of the b d g p q set, in which the upright forms also adapt well to slanting, The addition of cursive 'a' makes the italics appear more lively than a simply slanted roman. Some typographers believe that slanted roman sans-serif italics are preferable to truly "cursive" sans Italics, in part because of history and design. [3]
The x-height
The x-height of a typeface is the height of the lowercase 'x' relative to the body size. The x-height of Go fonts is 53.0% of body size, a bit larger than the x-heights of Helvetica (52.3%) or Arial (51.9%), but the difference is usually unnoticeable at normal reading sizes. Typographers believe that larger x-heights contribute to greater legibility in small sizes and on screens. A study of "print size" (particularly x-height) and reading noted that types for reading on screens and for small sizes tend to have large x-heights. [4]
DIN Legibility Standard
The recent German DIN 1450 legibility standard recommends several features for font legibility, including differentiation of letter shapes to reduce confusion. The Go fonts conform to the 1450 standard by carefully differentiating zero from capital O; numeral 1 from capital I (eye) and lowercase l (ell); numeral 5 from capital S; and numeral 8 from capital B. The shapes of bowls of b d p q follow the natural asymmetries of legible Renaissance handwriting, aiding differentiation to reduce confusion. [5]
Weights
The Go proportional fonts come in three weights: Normal, Medium, and Bold. The Normal weight is strong enough that it maintains clarity on backlit screens, which often tend to erode letter features and thickness. The Medium weight has stem thickness 1.25 times the Normal, for greater sturdiness on bright screens or for users who prefer a sturdy font. The Bold weight has stem thickness 1.5 times the Normal, bold enough to be distinct from the normal weight. These Go fonts have CSS numerical weights of 400, 500, and 600. Although CSS specifies "Bold" as a 700 weight and 600 as Semibold or Demibold, the Go numerical weights match the actual progression of the ratios of stem thicknesses: Normal:Medium = 400:500; Normal:Bold = 400:600. The Bold weight name matches the use of “Bold” as the usual corresponding bold weight of a normal font. More discussion of the relationship of stem thicknesses, weight names, and CSS numbering is in [6].
WGL4 character set
The WGL4 character set, originally developed by Microsoft, is often used as an informal standard character set. WGL4 includes Western and Eastern European Latin characters plus Modern Greek and Cyrillic, with additional symbols, signs, and graphical characters, totalling more than 650 characters in all. The Go WGL4 fonts can be used to compose a wide range of languages. [7]
Metric compatibility with Arial and Helvetica
The Go sans-serif fonts are nearly metrically compatible with standard Helvetica or Arial characters. Texts set in Go occupy nearly the same space as texts in Helvetica or Arial (at the same size), but Go has a different look and texture because of its humanist style. Some Go letters with DIN legibility features are wider than corresponding letters in Helvetica or Arial, so some texts set in Go may take slightly more space.
Go Mono fonts
Monospaced
Go Mono fonts are monospaced—each letter has the same width as the other letters. Monospaced fonts have been used in programming since the beginning of computing and are still widely used because the typewriter regularity of their spacing makes text align in columns and rows, a style also found in Greek inscriptions of the 5th century BC. (The ancient Greeks didn't have typewriters or computer keyboards, but they did have great mathematicians and a great sense of symmetry and pattern that shaped their alphabet.)
Slab-serif
The Go Mono fonts have slab-shaped serifs, giving them a sturdy appearance.
Style
The underlying letter shapes of Go Mono are, like the Go sans-serif fonts, derived from humanist handwriting, but the monospacing and slab serifs tend to obscure the historical and stylistic connections.
Italics
Go Mono Italics are oblique versions of the romans, with the exception that the italic lowercase 'a' is redesigned as a cursive single-story form to harmonize with the bowl shapes of the b d g p q. The cursive 'a' makes the italics appear more lively than a simply slanted roman. As with many sans-serif fonts, it is believed that slanted roman slab-serifs fonts may be more legible than truly "cursive" italics.
The x-height
Go Mono fonts have the same x-height as Go sans-serif fonts, 53% of the body size. Go Mono looks almost 18% bigger than Courier, which has an x-height 45% of body size. Yet Go Mono has the same width as Courier, so the bigger look is gained with no loss of economy in characters per line.
DIN Legibility Standard
Go Mono fonts conform to the DIN 1450 standard by differentiating zero from capital O; numeral 1 from capital I (eye) and lowercase l (ell); numeral 5 from capital S; and numeral 8 from capital B. The shapes of bowls of b d p q follow the natural asymmetries of legible Renaissance handwriting, aiding differentiation and reducing confusion.
Weights
Go Mono fonts have two weights: Normal and Bold. The normal weight stem is the same as in Go Normal and thus maintains clarity on backlit screens, which tend to erode letter features and stem thickness. The bold stem thickness is 1.5 times thicker than the normal weight, hence the Bold Mono has the same stem thickness as Bold Go proportional. Because the letter width of monospaced bold is identical to the width of monospaced normal, the bold Mono appears slightly bolder than the proportional Go Bold, as more black pixels are put into the same area.)
Metric compatibility with popular monospaced fonts
Go Mono is metrically compatible with Courier and other monospaced fonts that match the "Pica" typewriter type widths of 10 characters per linear inch at 12 point. At 10 point, Go Mono fonts set 12 characters per inch. The TrueType fonts are scalable, of course, so Go Mono can be set at any size.
WGL4 character set
The Go Mono fonts offer the WGL4 character set often used as an informal standard character set. WGL4 includes Western and Eastern European Latin characters plus Modern Greek and Cyrillic, with additional symbols, signs, and graphical characters. The 650+ characters of the Go WGL4 sets can be used for a wide range of languages.
References
[1] Morris, R. A., Aquilante, K., Yager, D., & Bigelow, C. (2002, May). P‐13: Serifs Slow RSVP Reading at Very Small Sizes, but Don't Matter at Larger Sizes. In SID Symposium Digest of Technical Papers (Vol. 33, No. 1, pp. 244-247). Blackwell Publishing Ltd.
[2] Bryan Reimer et al. (2014) “Assessing the impact of typeface design in a text-rich automotive user interface”, Ergonomics, 57:11, 1643-1658. http://www.tandfonline.com/doi/abs/10.1080/00140139.2014.940000
[3] Adrian Frutiger - Typefaces: The Complete Works. H. Osterer and P. Stamm, editors. Birkhäuser, Basel, 2009, page 257.
[4] Legge, G. E., & Bigelow, C. A. (2011). Does print size matter for reading? A review of findings from vision science and typography. Journal of Vision, 11(5), 8-8. http://jov.arvojournals.org/article.aspx?articleid=2191906
[5] Charles Bigelow. "Oh, oh, zero!" TUGboat, Volume 34 (2013), No. 2. https://tug.org/TUGboat/tb34-2/tb107bigelow-zero.pdf https://tug.org/TUGboat/tb34-2/tb107bigelow-wang.pdf
[6] "Lucida Basic Font Weights" Bigelow & Holmes. http://lucidafonts.com/pages/facts
[7] WGL4 language coverage: Afrikaans, Albanian, Asu, Basque, Belarusian, Bemba, Bena, Bosnian, Bulgarian, Catalan, Chiga, Colognian, Cornish, Croatian, Czech, Danish, Embu, English, Esperanto, Estonian, Faroese, Filipino, Finnish, French, Friulian, Galician, Ganda, German, Greek, Gusii, Hungarian, Icelandic, Inari Sami, Indonesian, Irish, Italian, Jola-Fonyi, Kabuverdianu, Kalaallisut, Kalenjin, Kamba, Kikuyu, Kinyarwanda, Latvian, Lithuanian, Lower Sorbian, Luo, Luxembourgish, Luyia, Macedonian, Machame, Makhuwa-Meetto, Makonde, Malagasy, Malay, Maltese, Manx, Meru, Morisyen, North Ndebele, Northern Sami, Norwegian Bokmål, Norwegian Nynorsk, Nyankole, Oromo, Polish, Portuguese, Romanian, Romansh, Rombo, Rundi, Russian, Rwa, Samburu, Sango, Sangu, Scottish Gaelic, Sena, Serbian, Shambala, Shona, Slovak, Slovenian, Soga, Somali, Spanish, Swahili, Swedish, Swiss German, Taita, Teso, Turkish, Turkmen, Upper Sorbian, Vunjo, Walser, Welsh, Zulu
Jabberwocky in Go Regular
From en.wikipedia.org/wiki/Jabberwocky:
There is no Greek version listed. Instead, a pangram from clagnut.com/blog/2380/#Greek:
Seven years of Go
10 November 2016
Today marks seven years since we open-sourced our preliminary sketch of Go. With the help of the open source community, including more than a thousand individual contributors to the Go source repositories, Go has matured into a language used all over the world.
The most significant user-facing changes to Go over the past year are the addition of built-in support for HTTP/2 in Go 1.6 and the integration of the context package into the standard library in Go 1.7. But we’ve been making many less visible improvements. Go 1.7 changed the x86-64 compiler to use a new SSA-based back end, improving the performance of most Go programs by 10–20%. For Go 1.8, planned for release next February, we have changed the compilers for the other architectures to use the new back end too. We’ve also added new ports, to Android on 32-bit x86, Linux on 64-bit MIPS, and Linux on IBM z Systems. And we’ve developed new garbage-collection techniques that reduce typical “stop the world” pauses to under 100 microseconds. (Contrast that with Go 1.5’s big news of 10 milliseconds or less.)
This year kicked off with a global Go hackathon, the Gopher Gala, in January. Then there were Go conferences in India and Dubai in February, China and Japan in April, San Francisco in May, Denver in July, London in August, Paris last month, and Brazil this past weekend. And GothamGo in New York is next week. This year also saw more than 30 new Go user groups, eight new Women Who Go chapters, and four GoBridge workshops around the world.
We continue to be overwhelmed by and grateful for the enthusiasm and support of the Go community. Whether you participate by contributing changes, reporting bugs, sharing your expertise in design discussions, writing blog posts or books, running meetups, helping others learn or improve, open sourcing Go packages you wrote, or just being part of the Go community, the Go team thanks you for your help, your time, and your energy. Go would not be the success it is today without you.
Thank you, and here’s to another year of fun and success with Go!
Introducing HTTP Tracing
4 October 2016
Introduction
In Go 1.7 we introduced HTTP tracing, a facility to gather fine-grained
information throughout the lifecycle of an HTTP client request.
Support for HTTP tracing is provided by the net/http/httptrace
package. The collected information can be used for debugging latency issues,
service monitoring, writing adaptive systems, and more.
HTTP events
The httptrace package provides a number of hooks to gather information
during an HTTP round trip about a variety of events. These events include:
- Connection creation
- Connection reuse
- DNS lookups
- Writing the request to the wire
- Reading the response
Tracing events
You can enable HTTP tracing by putting an
*httptrace.ClientTrace
containing hook functions into a request's context.Context.
Various http.RoundTripper
implementations report the internal events by
looking for context's *httptrace.ClientTrace and calling the relevant hook functions.
The tracing is scoped to the request's context and users should
put a *httptrace.ClientTrace to the request context before they start a request.
req, _ := http.NewRequest("GET", "http://example.com", nil) trace := &httptrace.ClientTrace{ DNSDone: func(dnsInfo httptrace.DNSDoneInfo) { fmt.Printf("DNS Info: %+v\n", dnsInfo) }, GotConn: func(connInfo httptrace.GotConnInfo) { fmt.Printf("Got Conn: %+v\n", connInfo) }, } req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) if _, err := http.DefaultTransport.RoundTrip(req); err != nil { log.Fatal(err) }
During a round trip, http.DefaultTransport will invoke each hook
as an event happens. The program above will print the DNS
information as soon as the DNS lookup is complete. It will similarly print
connection information when a connection is established to the request's host.
Tracing with http.Client
The tracing mechanism is designed to trace the events in the lifecycle
of a single http.Transport.RoundTrip. However, a client may
make multiple round trips to complete an HTTP request. For example, in the case
of a URL redirection, the registered hooks will be called as many times as the
client follows HTTP redirects, making multiple requests.
Users are responsible for recognizing such events at the http.Client level.
The program below identifies the current request by using an
http.RoundTripper wrapper.
package main import ( "fmt" "log" "net/http" "net/http/httptrace" ) // transport is an http.RoundTripper that keeps track of the in-flight // request and implements hooks to report HTTP tracing events. type transport struct { current *http.Request } // RoundTrip wraps http.DefaultTransport.RoundTrip to keep track // of the current request. func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { t.current = req return http.DefaultTransport.RoundTrip(req) } // GotConn prints whether the connection has been used previously // for the current request. func (t *transport) GotConn(info httptrace.GotConnInfo) { fmt.Printf("Connection reused for %v? %v\n", t.current.URL, info.Reused) } func main() { t := &transport{} req, _ := http.NewRequest("GET", "https://google.com", nil) trace := &httptrace.ClientTrace{ GotConn: t.GotConn, } req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) client := &http.Client{Transport: t} if _, err := client.Do(req); err != nil { log.Fatal(err) } }
The program will follow the redirect of google.com to www.google.com and will output:
Connection reused for https://google.com? false Connection reused for https://www.google.com/? false
The Transport in the net/http package supports tracing of both HTTP/1
and HTTP/2 requests.
If you are an author of a custom http.RoundTripper implementation,
you can support tracing by checking the request context for an
*httptest.ClientTrace and invoking the relevant hooks as the events occur.
Conclusion
HTTP tracing is a valuable addition to Go for those who are interested in debugging HTTP request latency and writing tools for network debugging for outbound traffic. By enabling this new facility, we hope to see HTTP debugging, benchmarking and visualization tools from the community — such as httpstat.
Using Subtests and Sub-benchmarks
3 October 2016
Introduction
In Go 1.7, the testing package introduces a Run method on the
T and
B types
that allows for the creation of subtests and sub-benchmarks.
The introduction of subtests and sub-benchmarks enables better handling of
failures, fine-grained control of which tests to run from the command line,
control of parallelism, and often results in simpler and more maintainable code.
Table-driven tests basics
Before digging into the details, let's first discuss a common way of writing tests in Go. A series of related checks can be implemented by looping over a slice of test cases:
func TestTime(t *testing.T) {
testCases := []struct {
gmt string
loc string
want string
}{
{"12:31", "Europe/Zuri", "13:31"}, // incorrect location name
{"12:31", "America/New_York", "7:31"}, // should be 07:31
{"08:08", "Australia/Sydney", "18:08"},
}
for _, tc := range testCases {
loc, err := time.LoadLocation(tc.loc)
if err != nil {
t.Fatalf("could not load location %q", tc.loc)
}
gmt, _ := time.Parse("15:04", tc.gmt)
if got := gmt.In(loc).Format("15:04"); got != tc.want {
t.Errorf("In(%s, %s) = %s; want %s", tc.gmt, tc.loc, got, tc.want)
}
}
}This approach, commonly referred to as table-driven tests, reduces the amount of repetitive code compared to repeating the same code for each test and makes it straightforward to add more test cases.
Table-driven benchmarks
Before Go 1.7 it was not possible to use the same table-driven approach for benchmarks. A benchmark tests the performance of an entire function, so iterating over benchmarks would just measure all of them as a single benchmark.
A common workaround was to define separate top-level benchmarks
that each call a common function with different parameters.
For instance, before 1.7 the strconv package's benchmarks for AppendFloat
looked something like this:
func benchmarkAppendFloat(b *testing.B, f float64, fmt byte, prec, bitSize int) {
dst := make([]byte, 30)
b.ResetTimer() // Overkill here, but for illustrative purposes.
for i := 0; i < b.N; i++ {
AppendFloat(dst[:0], f, fmt, prec, bitSize)
}
}
func BenchmarkAppendFloatDecimal(b *testing.B) { benchmarkAppendFloat(b, 33909, 'g', -1, 64) }
func BenchmarkAppendFloat(b *testing.B) { benchmarkAppendFloat(b, 339.7784, 'g', -1, 64) }
func BenchmarkAppendFloatExp(b *testing.B) { benchmarkAppendFloat(b, -5.09e75, 'g', -1, 64) }
func BenchmarkAppendFloatNegExp(b *testing.B) { benchmarkAppendFloat(b, -5.11e-95, 'g', -1, 64) }
func BenchmarkAppendFloatBig(b *testing.B) { benchmarkAppendFloat(b, 123456789123456789123456789, 'g', -1, 64) }
...
Using the Run method available in Go 1.7, the same set of benchmarks is now
expressed as a single top-level benchmark:
func BenchmarkAppendFloat(b *testing.B) {
benchmarks := []struct{
name string
float float64
fmt byte
prec int
bitSize int
}{
{"Decimal", 33909, 'g', -1, 64},
{"Float", 339.7784, 'g', -1, 64},
{"Exp", -5.09e75, 'g', -1, 64},
{"NegExp", -5.11e-95, 'g', -1, 64},
{"Big", 123456789123456789123456789, 'g', -1, 64},
...
}
dst := make([]byte, 30)
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
AppendFloat(dst[:0], bm.float, bm.fmt, bm.prec, bm.bitSize)
}
})
}
}
Each invocation of the Run method creates a separate benchmark.
An enclosing benchmark function that calls a Run method is only run once and
is not measured.
The new code has more lines of code, but is more maintainable, more readable, and consistent with the table-driven approach commonly used for testing. Moreover, common setup code is now shared between runs while eliminating the need to reset the timer.
Table-driven tests using subtests
Go 1.7 also introduces a Run method for creating subtests.
This test is a rewritten version of our earlier example using subtests:
func TestTime(t *testing.T) {
testCases := []struct {
gmt string
loc string
want string
}{
{"12:31", "Europe/Zuri", "13:31"},
{"12:31", "America/New_York", "7:31"},
{"08:08", "Australia/Sydney", "18:08"},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("%s in %s", tc.gmt, tc.loc), func(t *testing.T) {
loc, err := time.LoadLocation(tc.loc)
if err != nil {
t.Fatal("could not load location")
}
gmt, _ := time.Parse("15:04", tc.gmt)
if got := gmt.In(loc).Format("15:04"); got != tc.want {
t.Errorf("got %s; want %s", got, tc.want)
}
})
}
}The first thing to note is the difference in output from the two implementations. The original implementation prints:
--- FAIL: TestTime (0.00s)
time_test.go:62: could not load location "Europe/Zuri"
Even though there are two errors, execution of the test halts on the call to
Fatalf and the second test never runs.
The implementation using Run prints both:
--- FAIL: TestTime (0.00s)
--- FAIL: TestTime/12:31_in_Europe/Zuri (0.00s)
time_test.go:84: could not load location
--- FAIL: TestTime/12:31_in_America/New_York (0.00s)
time_test.go:88: got 07:31; want 7:31
Fatal and its siblings causes a subtest to be skipped but not its parent or
subsequent subtests.
Another thing to note is the shorter error messages in the new implementation. Since the subtest name uniquely identifies the subtest there is no need to identify the test again within the error messages.
There are several other benefits to using subtests or sub-benchmarks, as clarified by the following sections.
Running specific tests or benchmarks
Both subtests and sub-benchmarks can be singled out on the command line using
the -run or -bench flag.
Both flags take a slash-separated list of regular expressions that match the
corresponding parts of the full name of the subtest or sub-benchmark.
The full name of a subtest or sub-benchmark is a slash-separated list of
its name and the names of all of its parents, starting with the top-level.
The name is the corresponding function name for top-level tests and benchmarks,
and the first argument to Run otherwise.
To avoid display and parsing issues, a name is sanitized by replacing spaces
with underscores and escaping non-printable characters.
The same sanitizing is applied to the regular expressions passed to
the -run or -bench flags.
A few examples:
Run tests that use a timezone in Europe:
$ go test -run=TestTime/"in Europe"
--- FAIL: TestTime (0.00s)
--- FAIL: TestTime/12:31_in_Europe/Zuri (0.00s)
time_test.go:85: could not load locationRun only tests for times after noon:
$ go test -run=Time/12:[0-9] -v
=== RUN TestTime
=== RUN TestTime/12:31_in_Europe/Zuri
=== RUN TestTime/12:31_in_America/New_York
--- FAIL: TestTime (0.00s)
--- FAIL: TestTime/12:31_in_Europe/Zuri (0.00s)
time_test.go:85: could not load location
--- FAIL: TestTime/12:31_in_America/New_York (0.00s)
time_test.go:89: got 07:31; want 7:31
Perhaps a bit surprising, using -run=TestTime/New_York won't match any tests.
This is because the slash present in the location names is treated as
a separator as well.
Instead use:
$ go test -run=Time//New_York
--- FAIL: TestTime (0.00s)
--- FAIL: TestTime/12:31_in_America/New_York (0.00s)
time_test.go:88: got 07:31; want 7:31
Note the // in the string passed to -run.
The / in time zone name America/New_York is handled as if it were
a separator resulting from a subtest.
The first regular expression of the pattern (TestTime) matches the top-level
test.
The second regular expression (the empty string) matches anything, in this case
the time and the continent part of the location.
The third regular expression (New_York) matches the city part of the location.
Treating slashes in names as separators allows the user to refactor hierarchies of tests without the need to change the naming. It also simplifies the escaping rules. The user should escape slashes in names, for instance by replacing them with backslashes, if this poses a problem.
A unique sequence number is appended to test names that are not unique.
So one could just pass an empty string to Run
if there is no obvious naming scheme for subtests and the subtests
can easily be identified by their sequence number.
Setup and Tear-down
Subtests and sub-benchmarks can be used to manage common setup and tear-down code:
func TestFoo(t *testing.T) {
// <setup code>
t.Run("A=1", func(t *testing.T) { ... })
t.Run("A=2", func(t *testing.T) { ... })
t.Run("B=1", func(t *testing.T) {
if !test(foo{B:1}) {
t.Fail()
}
})
// <tear-down code>
}
The setup and tear-down code will run if any of the enclosed subtests are run
and will run at most once.
This applies even if any of the subtests calls Skip, Fail, or Fatal.
Control of Parallelism
Subtests allow fine-grained control over parallelism. To understand how to use subtests in the way it is important to understand the semantics of parallel tests.
Each test is associated with a test function.
A test is called a parallel test if its test function calls the Parallel
method on its instance of testing.T.
A parallel test never runs concurrently with a sequential test and its execution
is suspended until its calling test function, that of the parent test,
has returned.
The -parallel flag defines the maximum number of parallel tests that can run
in parallel.
A test blocks until its test function returns and all of its subtests have completed. This means that the parallel tests that are run by a sequential test will complete before any other consecutive sequential test is run.
This behavior is identical for tests created by Run and top-level tests.
In fact, under the hood top-level tests are implemented as subtests of
a hidden master test.
Run a group of tests in parallel
The above semantics allows for running a group of tests in parallel with each other but not with other parallel tests:
func TestGroupedParallel(t *testing.T) {
for _, tc := range testCases {
tc := tc // capture range variable
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
if got := foo(tc.in); got != tc.out {
t.Errorf("got %v; want %v", got, tc.out)
}
...
})
}
}
The outer test will not complete until all parallel tests started by Run
have completed.
As a result, no other parallel tests can run in parallel to these parallel tests.
Note that we need to capture the range variable to ensure that tc gets bound to
the correct instance.
Cleaning up after a group of parallel tests
In the previous example we used the semantics to wait on a group of parallel tests to complete before commencing other tests. The same technique can be used to clean up after a group of parallel tests that share common resources:
func TestTeardownParallel(t *testing.T) {
// <setup code>
// This Run will not return until its parallel subtests complete.
t.Run("group", func(t *testing.T) {
t.Run("Test1", parallelTest1)
t.Run("Test2", parallelTest2)
t.Run("Test3", parallelTest3)
})
// <tear-down code>
}The behavior of waiting on a group of parallel tests is identical to that of the previous example.
Conclusion
Go 1.7's addition of subtests and sub-benchmarks allows you to write structured tests and benchmarks in a natural way that blends nicely into the existing tools. One way to think about this is that earlier versions of the testing package had a 1-level hierarchy: the package-level test was structured as a set of individual tests and benchmarks. Now that structure has been extended to those individual tests and benchmarks, recursively. In fact, in the implementation, the top-level tests and benchmarks are tracked as if they were subtests and sub-benchmarks of an implicit master test and benchmark: the treatment really is the same at all levels.
The ability for tests to define this structure enables fine-grained execution of specific test cases, shared setup and teardown, and better control over test parallelism. We are excited to see what other uses people find. Enjoy.
See the index for more articles.