Commit 39b3d8c7 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 4afad355
......@@ -92,38 +92,19 @@ func routeEvent(event interface{}) (stream string) {
// verify calls tracetest.Verify on f with first preparing tracing setup and events delivery.
// It also verifies that tracetest detects errors as expected.
func verify(t *testing.T, f func(t *tracetest.T), targvExtra ...string) {
if os.Getenv("TRACETEST_EX_VERIFY_IN_SUBPROCESS") == "1" {
// XXX t.Helper() ?
verifyInSubprocess(t, func (t *testing.T) {
tracetest.Verify(t, func(t *tracetest.T) {
// setup tracing to deliver trace events to t.
pg := setupTracing(t)
t.Cleanup(pg.Done)
defer pg.Done()
// tell t to which stream an event should go.
t.SetEventRouter(routeEvent)
// run test code
f(t)
})
return
}
// spawn the test in subprocess and verify its output
outOK, ok := testExpect[t.Name()]
if !ok {
panic(fmt.Sprintf("testExpect[%q] not defined", t.Name()))
}
argv := []string{"-test.run="+t.Name()}
argv = append(argv, targvExtra...)
cmd := exec.Command(os.Args[0], argv...)
cmd.Env = append(os.Environ(), "TRACETEST_EX_VERIFY_IN_SUBPROCESS=1")
bout, err := cmd.CombinedOutput()
out := string(bout)
if err != nil {
t.Log(out)
t.Fatal(err) // XXX check first it is not "exit code"
}
// XXX verify out
_ = outOK
}, targvExtra...)
}
......@@ -162,26 +143,21 @@ func Test2ThreadsOK(t *testing.T) {
}
// XXX
// TestDeadlock demonstrates deadlock scenario hit by the checker.
func TestDeadlock(t *testing.T) {
verify(t, func(t *tracetest.T) {
var wg sync.WaitGroup
defer wg.Wait()
wg.Add(2)
wg.Add(1)
go func() { // thread1
defer wg.Done()
hi("T1·A")
}()
go func() { // thread2
defer wg.Done()
hi("T2·B")
}()
// the checker expects something on stream "t3", but there is
// the checker expects something on stream "t2", but there is
// no event sent there -> deadlock.
t.Expect("t3", eventHi("zzz"))
t.Expect("t2", eventHi("zzz"))
}, "-tracetest.deadtime=0.5s")
}
......@@ -190,24 +166,67 @@ func TestDeadlock(t *testing.T) {
// --------
// testExpect maps <test name> -> expected output
var testExpect = map[string]string{
"Test2ThreadsOK": "",
"TestDeadlock": `
// verifyInSubprocess runs f in subprocess and verifies that its output matches testOutput.
func verifyInSubprocess(t *testing.T, f func(t *testing.T), targvExtra ...string) {
t.Helper() // XXX ok?
if os.Getenv("TRACETEST_EX_VERIFY_IN_SUBPROCESS") == "1" {
f(t)
return
}
// spawn the test in subprocess and verify its output
expectOK, ok := testExpectMap[t.Name()]
if !ok {
panic(fmt.Sprintf("testExpectMap[%q] not defined", t.Name()))
}
argv := []string{"-test.run="+t.Name()}
argv = append(argv, targvExtra...)
cmd := exec.Command(os.Args[0], argv...)
cmd.Env = append(os.Environ(), "TRACETEST_EX_VERIFY_IN_SUBPROCESS=1")
bout, err := cmd.CombinedOutput()
out := string(bout)
e, ok := err.(*exec.ExitError)
if !ok {
// e.g. could not respawn at all
t.Log(out)
t.Fatal(err)
}
ecode := e.ExitCode()
bad := ""
badf := func(format string, argv ...interface{}) {
bad += fmt.Sprintf(format+"\n", argv...)
}
if ecode != expectOK.exitCode {
badf("exit code: %d ; expected: %d", ecode, expectOK.exitCode)
}
if out != expectOK.outputRe { // XXX match with re
badf("unexpected output:\n%s\nwant: %s\n", out, expectOK.outputRe)
}
if bad != "" {
t.Fatal(bad)
}
}
// testExpect describes what result to expect from a test.
type testExpect struct {
exitCode int
outputRe string
}
// testExpectMap maps <test name> -> testExpect.
var testExpectMap = map[string]testExpect{
"Test2ThreadsOK": {0, ""},
"TestDeadlock": {1, `
--- FAIL: TestDeadlock (.+s)
example_test.go:179: t3: recv: deadlock waiting for *tracetest_test.eventHi
example_test.go:179: test shutdown: #streams: 3, #(pending events): 2
example_test.go:159: t2: recv: deadlock waiting for *tracetest_test.eventHi
example_test.go:159: test shutdown: #streams: 2, #(pending events): 1
t1 <- tracetest_test.eventHi T1·A
t2 <- tracetest_test.eventHi T2·B
# t3
tracetest.go:175: t2: send: canceled (test failed)
.*
# t2
tracetest.go:175: t1: send: canceled (test failed)
.*
FAIL
exit status 1
`,
...
`},
}
......@@ -484,11 +484,14 @@ type Streams interface {
// ------ vvv = ok ------
// _testing_TB is alias for testing.TB that is non-public when embedded into a struct.
type _testing_TB = testing.TB
// T is similar to testing.T and is passed by Verify to tested function.
//
// See top-level package documentation for details.
type T struct {
testing.TB
_testing_TB
mu sync.Mutex
streamTab map[/*stream*/string]Chan // set to nil on test shutdown
......@@ -686,7 +689,7 @@ func (t *T) fatalfInNonMain(format string, argv ...interface{}) {
func (t *T) FailNow() {
t.Helper()
_ = t.closeStreamTab()
t.TB.FailNow()
t._testing_TB.FailNow()
}
func (t *T) Fatal(argv ...interface{}) {
......@@ -784,7 +787,7 @@ func (t *T) closeStreamTab() (nnak int) {
// Verify verifies a test system.
// XXX
func Verify(t testing.TB, f func(t *T)) {
tT := &T{TB: t, streamTab: make(map[string]Chan)}
tT := &T{_testing_TB: t, streamTab: make(map[string]Chan)}
// XXX
// verify in the end that no events are left unchecked / unconsumed,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment