12166-subtests: address comments of @ChrisHines
- expanded benchmark example
- rational of proposal as opposed to expanding to top-level funcs
Change-Id: I932066eec3067699eda39f9aa33b6fcca68c66c2
Reviewed-on: https://go-review.googlesource.com/14808
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/design/12166-subtests.md b/design/12166-subtests.md
index c19beab..71dc0ac 100644
--- a/design/12166-subtests.md
+++ b/design/12166-subtests.md
@@ -236,16 +236,17 @@
### Example
The following code shows the use of two levels of subbenchmarks.
+It is based on a possible rewrite of
+golang.org/x/text/unicode/norm/normalize_test.go.
```go
func BenchmarkMethod(b *testing.B) {
for _, tt := range allMethods {
b.Run(tt.name, func(b *testing.B) {
- for _, d := range smallSet {
- s := []byte(d.Data)
- fn := tt.f(NFC, s) // initialize the test.
- b.Run(d.Name, func(b *testing.B) {
- b.SetBytes(int64(len(s)))
+ for _, d := range textdata {
+ fn := tt.f(NFC, []byte(d.data)) // initialize the test
+ b.Run(d.name, func(b *testing.B) {
+ b.SetBytes(int64(len(d.data)))
for i := 0; i < b.N; i++ {
fn()
}
@@ -254,6 +255,31 @@
})
}
}
+
+var allMethods = []struct {
+ name string
+ f func(to Form, b []byte) func()
+}{{"Transform", func(f Form, b []byte) func() {
+ buf := make([]byte, 4*len(b))
+ return func() {
+ f.Transform(buf, b, true)
+ }
+}, {"Iter", func(f Form, b []byte) func() {
+ iter := Iter{}
+ return func() {
+ for iter.Init(f, b); !iter.Done(); iter.Next() {
+ }
+ }
+}, {
+ ...
+}}
+
+var textdata = []struct { name, data string }{
+ {"small_change", "No\u0308rmalization"},
+ {"small_no_change", "nörmalization"},
+ {"ascii", ascii},
+ {"all", txt_all},
+}
```
Note that there is some initialization code above the second Run.
@@ -366,6 +392,42 @@
## Rationale
+### Alternative
+One alternative to the given proposal is to define variants of tests as
+top-level tests or benchmarks that call helper functions.
+For example, the use case explained above could be written as:
+
+```go
+func doSum(t *testing.T, a, b, sum int) {
+ if got := a + b; got != sum {
+ t.Errorf("got %d; want %d", got, sum)
+ }
+}
+
+func TestSumA1B2(t *testing.T) { doSum(t, 1, 2, 3) }
+func TestSumA1B1(t *testing.T) { doSum(t, 1, 1, 2) }
+func TestSumA2B1(t *testing.T) { doSum(t, 2, 1, 3) }
+```
+
+This approach can work well for smaller sets, but starts to get tedious for
+larger sets.
+Some disadvantages of this approach:
+
+1. considerably more typing for larger test sets (less code, much larger test cases),
+1. duplication of information in test name and test values,
+1. may get unwieldy if a Cartesian product of multiple tables is used as source,
+1. doesn't work well with dynamic table sources,
+1. does not allow for the same flexibility of inserting setup and teardown code
+ as using Run,
+1. does not allow for the same flexibility in parallelism as using Run,
+1. no ability to terminate early a subgroup of tests.
+
+Some of these objections can be addressed by generating the test cases.
+It seems, though, that addressing anything beyond point 1 and 2 with generation
+would require more complexity than the addition of Run introduces.
+Overall, it seems that the benefits of the proposed addition outweigh the
+benefits of an approach using generation as well as expanding tests by hand.
+
### Subtest semantics
A _subtest_ refers to a call to Run and a _test function_ refers to the function
f passed to Run.