1 package http2interop 2 3 import ( 4 "path" 5 "runtime" 6 "strings" 7 "sync" 8 "testing" 9 ) 10 11 // When a test is skipped or fails, runtime.Goexit() is called which destroys the callstack. 12 // This means the name of the test case is lost, so we need to grab a copy of pc before. 13 func Report(t testing.TB) { 14 // If the goroutine panics, Fatal()s, or Skip()s, the function name is at the 3rd callstack 15 // layer. On success, its at 1st. Since it's hard to check which happened, just try both. 16 pcs := make([]uintptr, 10) 17 total := runtime.Callers(1, pcs) 18 var name string 19 for _, pc := range pcs[:total] { 20 fn := runtime.FuncForPC(pc) 21 fullName := fn.Name() 22 if strings.HasPrefix(path.Ext(fullName), ".Test") { 23 // Skip the leading . 24 name = string([]byte(path.Ext(fullName))[1:]) 25 break 26 } 27 } 28 if name == "" { 29 return 30 } 31 32 allCaseInfos.lock.Lock() 33 defer allCaseInfos.lock.Unlock() 34 allCaseInfos.Cases = append(allCaseInfos.Cases, &caseInfo{ 35 Name: name, 36 Passed: !t.Failed() && !t.Skipped(), 37 Skipped: t.Skipped(), 38 Fatal: t.Failed() && !strings.HasPrefix(name, "TestSoon"), 39 }) 40 } 41 42 type caseInfo struct { 43 Name string `json:"name"` 44 Passed bool `json:"passed"` 45 Skipped bool `json:"skipped,omitempty"` 46 Fatal bool `json:"fatal,omitempty"` 47 } 48 49 type caseInfos struct { 50 lock sync.Mutex 51 Cases []*caseInfo `json:"cases"` 52 } 53 54 var ( 55 allCaseInfos = caseInfos{} 56 ) 57