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  }