mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
testing: finish implementation of subtests
API not exposed yet. Change-Id: Iaba0adc0fa1ae8075e6b56796f99ee8db9177a78 Reviewed-on: https://go-review.googlesource.com/18896 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
1857bfca13
commit
2330ae8cf8
2 changed files with 249 additions and 0 deletions
|
|
@ -5,6 +5,8 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -104,6 +106,229 @@ func TestTestContext(t *T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: remove this stub when API is exposed
|
||||
func (t *T) Run(name string, f func(t *T)) bool { return t.run(name, f) }
|
||||
|
||||
func TestTRun(t *T) {
|
||||
realTest := t
|
||||
testCases := []struct {
|
||||
desc string
|
||||
ok bool
|
||||
maxPar int
|
||||
f func(*T)
|
||||
}{{
|
||||
desc: "failnow skips future sequential and parallel tests at same level",
|
||||
ok: false,
|
||||
maxPar: 1,
|
||||
f: func(t *T) {
|
||||
ranSeq := false
|
||||
ranPar := false
|
||||
t.Run("", func(t *T) {
|
||||
t.Run("par", func(t *T) {
|
||||
t.Parallel()
|
||||
ranPar = true
|
||||
})
|
||||
t.Run("seq", func(t *T) {
|
||||
ranSeq = true
|
||||
})
|
||||
t.FailNow()
|
||||
t.Run("seq", func(t *T) {
|
||||
realTest.Error("test must be skipped")
|
||||
})
|
||||
t.Run("par", func(t *T) {
|
||||
t.Parallel()
|
||||
realTest.Error("test must be skipped.")
|
||||
})
|
||||
})
|
||||
if !ranPar {
|
||||
realTest.Error("parallel test was not run")
|
||||
}
|
||||
if !ranSeq {
|
||||
realTest.Error("sequential test was not run")
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "failure in parallel test propagates upwards",
|
||||
ok: false,
|
||||
maxPar: 1,
|
||||
f: func(t *T) {
|
||||
t.Run("", func(t *T) {
|
||||
t.Parallel()
|
||||
t.Run("par", func(t *T) {
|
||||
t.Parallel()
|
||||
t.Fail()
|
||||
})
|
||||
})
|
||||
},
|
||||
}, {
|
||||
desc: "use Run to locally synchronize parallelism",
|
||||
ok: true,
|
||||
maxPar: 1,
|
||||
f: func(t *T) {
|
||||
var count uint32
|
||||
t.Run("waitGroup", func(t *T) {
|
||||
for i := 0; i < 4; i++ {
|
||||
t.Run("par", func(t *T) {
|
||||
t.Parallel()
|
||||
atomic.AddUint32(&count, 1)
|
||||
})
|
||||
}
|
||||
})
|
||||
if count != 4 {
|
||||
t.Errorf("count was %d; want 4", count)
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "run no more than *parallel tests concurrently",
|
||||
ok: true,
|
||||
maxPar: 4,
|
||||
f: func(t *T) {
|
||||
max := 0
|
||||
in := make(chan int)
|
||||
out := make(chan int)
|
||||
ctx := t.context
|
||||
t.Run("wait", func(t *T) {
|
||||
t.Run("controller", func(t *T) {
|
||||
// Verify sequential tests don't skew counts.
|
||||
t.Run("seq1", func(t *T) {})
|
||||
t.Run("seq2", func(t *T) {})
|
||||
t.Run("seq3", func(t *T) {})
|
||||
t.Parallel()
|
||||
for i := 0; i < 80; i++ {
|
||||
ctx.mu.Lock()
|
||||
if ctx.running > max {
|
||||
max = ctx.running
|
||||
}
|
||||
ctx.mu.Unlock()
|
||||
<-in
|
||||
// force a minimum to avoid a race, although it works
|
||||
// without it.
|
||||
if i >= ctx.maxParallel-2 { // max - this - 1
|
||||
out <- i
|
||||
}
|
||||
}
|
||||
close(out)
|
||||
})
|
||||
// Ensure we don't exceed the maximum even with nested parallelism.
|
||||
for i := 0; i < 2; i++ {
|
||||
t.Run("", func(t *T) {
|
||||
t.Parallel()
|
||||
for j := 0; j < 40; j++ {
|
||||
t.Run("", func(t *T) {
|
||||
t.Run("seq1", func(t *T) {})
|
||||
t.Run("seq2", func(t *T) {})
|
||||
t.Parallel()
|
||||
in <- j
|
||||
<-out
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
if max != ctx.maxParallel {
|
||||
realTest.Errorf("max: got %d; want: %d", max, ctx.maxParallel)
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "alternate sequential and parallel",
|
||||
// Sequential tests should partake in the counting of running threads.
|
||||
// Otherwise, if one runs parallel subtests in sequential tests that are
|
||||
// itself subtests of parallel tests, the counts can get askew.
|
||||
ok: true,
|
||||
maxPar: 1,
|
||||
f: func(t *T) {
|
||||
t.Run("a", func(t *T) {
|
||||
t.Parallel()
|
||||
t.Run("b", func(t *T) {
|
||||
// Sequential: ensure running count is decremented.
|
||||
t.Run("c", func(t *T) {
|
||||
t.Parallel()
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
},
|
||||
}, {
|
||||
desc: "alternate sequential and parallel",
|
||||
// Sequential tests should partake in the counting of running threads.
|
||||
// Otherwise, if one runs parallel subtests in sequential tests that are
|
||||
// itself subtests of parallel tests, the counts can get askew.
|
||||
ok: true,
|
||||
maxPar: 2,
|
||||
f: func(t *T) {
|
||||
for i := 0; i < 2; i++ {
|
||||
t.Run("a", func(t *T) {
|
||||
t.Parallel()
|
||||
time.Sleep(time.Nanosecond)
|
||||
for i := 0; i < 2; i++ {
|
||||
t.Run("b", func(t *T) {
|
||||
time.Sleep(time.Nanosecond)
|
||||
for i := 0; i < 2; i++ {
|
||||
t.Run("c", func(t *T) {
|
||||
t.Parallel()
|
||||
time.Sleep(time.Nanosecond)
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}, {
|
||||
desc: "stress test",
|
||||
ok: true,
|
||||
maxPar: 4,
|
||||
f: func(t *T) {
|
||||
t.Parallel()
|
||||
for i := 0; i < 12; i++ {
|
||||
t.Run("a", func(t *T) {
|
||||
t.Parallel()
|
||||
time.Sleep(time.Nanosecond)
|
||||
for i := 0; i < 12; i++ {
|
||||
t.Run("b", func(t *T) {
|
||||
time.Sleep(time.Nanosecond)
|
||||
for i := 0; i < 12; i++ {
|
||||
t.Run("c", func(t *T) {
|
||||
t.Parallel()
|
||||
time.Sleep(time.Nanosecond)
|
||||
t.Run("d1", func(t *T) {})
|
||||
t.Run("d2", func(t *T) {})
|
||||
t.Run("d3", func(t *T) {})
|
||||
t.Run("d4", func(t *T) {})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}}
|
||||
for _, tc := range testCases {
|
||||
ctx := newTestContext(tc.maxPar)
|
||||
root := &T{
|
||||
common: common{
|
||||
barrier: make(chan bool),
|
||||
w: ioutil.Discard,
|
||||
},
|
||||
context: ctx,
|
||||
}
|
||||
ok := root.Run(tc.desc, tc.f)
|
||||
ctx.release()
|
||||
|
||||
if ok != tc.ok {
|
||||
t.Errorf("%s:ok: got %v; want %v", tc.desc, ok, tc.ok)
|
||||
}
|
||||
if ok != !root.Failed() {
|
||||
t.Errorf("%s:root failed: got %v; want %v", tc.desc, !ok, root.Failed())
|
||||
}
|
||||
if ctx.running != 0 || ctx.numWaiting != 0 {
|
||||
t.Errorf("%s:running and waiting non-zero: got %d and %d", tc.desc, ctx.running, ctx.numWaiting)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this stub when API is exposed
|
||||
func (b *B) Run(name string, f func(b *B)) bool { return b.runBench(name, f) }
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue