github.com/thiagoyeds/go-cloud@v0.26.0/internal/testing/test-summary/test-summary.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Summarizes the output of go test. 16 // Run like so: 17 // go test -json ./... | test-summary 18 package main 19 20 import ( 21 "bufio" 22 "encoding/json" 23 "flag" 24 "fmt" 25 "io" 26 "log" 27 "os" 28 "path/filepath" 29 "strings" 30 "time" 31 ) 32 33 var ( 34 progress = flag.Bool("progress", false, "display test progress") 35 verbose = flag.Bool("verbose", false, "display all test output") 36 ) 37 38 // TestEvent is copied from "go doc test2json". 39 type TestEvent struct { 40 Time time.Time // encodes as an RFC3339-format string 41 Action string 42 Package string 43 Test string 44 Elapsed float64 // seconds 45 Output string 46 } 47 48 func main() { 49 flag.Parse() 50 s, fails, err := run(os.Stdin) 51 if err != nil { 52 log.Fatal(err) 53 } 54 fmt.Println(s) 55 if fails { 56 os.Exit(1) 57 } 58 } 59 60 func run(r io.Reader) (msg string, failures bool, err error) { 61 counts := map[string]int{} 62 scanner := bufio.NewScanner(bufio.NewReader(r)) 63 64 // Collects tests that failed. 65 var failedTests []string 66 67 // Stores output produced by each test. 68 testOutputs := map[string][]string{} 69 70 start := time.Now() 71 for scanner.Scan() { 72 // When the build fails, go test -json doesn't emit a valid JSON value, only 73 // a line of output starting with FAIL. Report a more reasonable error in 74 // this case. 75 if strings.HasPrefix(scanner.Text(), "FAIL") { 76 return "", true, fmt.Errorf("No test output: %q", scanner.Text()) 77 } 78 79 var event TestEvent 80 if err := json.Unmarshal(scanner.Bytes(), &event); err != nil { 81 return "", false, fmt.Errorf("%q: %v", scanner.Text(), err) 82 } 83 testpath := filepath.Join(event.Package, event.Test) 84 85 // The Test field, if non-empty, specifies the test, example, or benchmark 86 // function that caused the event. Events for the overall package test do 87 // not set Test. 88 if event.Action == "fail" && event.Test != "" { 89 failedTests = append(failedTests, testpath) 90 } 91 92 if event.Action == "output" { 93 if *verbose { 94 fmt.Print(event.Output) 95 } 96 testOutputs[testpath] = append(testOutputs[testpath], event.Output) 97 } 98 99 // We don't want to count package passes/fails because these don't 100 // represent specific tests being run. However, skips of an entire package 101 // are not duplicated with individual test skips. 102 if event.Test != "" || event.Action == "skip" { 103 counts[event.Action]++ 104 } 105 106 // For failed tests, print all the output we collected for them before 107 // the "fail" event. 108 if event.Action == "fail" { 109 fmt.Println(strings.Join(testOutputs[testpath], "")) 110 } 111 112 if *progress { 113 // Only print progress for fail events for packages and tests, or 114 // pass events for packages only (not individual tests, since this is 115 // too noisy). 116 if event.Action == "fail" || (event.Test == "" && event.Action == "pass") { 117 fmt.Printf("%s %s (%.2fs)\n", event.Action, testpath, event.Elapsed) 118 } 119 } 120 } 121 if err := scanner.Err(); err != nil { 122 return "", false, err 123 } 124 p := counts["pass"] 125 f := counts["fail"] 126 s := counts["skip"] 127 128 summary := fmt.Sprintf("ran %d; passed %d; failed %d; skipped %d (in %.1f sec)", p+f+s, p, f, s, time.Since(start).Seconds()) 129 if len(failedTests) > 0 { 130 var sb strings.Builder 131 sb.WriteString("Failures (reporting up to 10):\n") 132 for i := 0; i < len(failedTests) && i < 10; i++ { 133 fmt.Fprintf(&sb, " %s\n", failedTests[i]) 134 } 135 if len(failedTests) > 10 { 136 sb.WriteString(" ...\n") 137 } 138 sb.WriteString(summary) 139 summary = sb.String() 140 } 141 142 return summary, f > 0, nil 143 }