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