gotest.tools/gotestsum@v1.11.0/internal/aggregate/slowest.go (about)

     1  package aggregate
     2  
     3  import (
     4  	"sort"
     5  	"time"
     6  
     7  	"gotest.tools/gotestsum/testjson"
     8  )
     9  
    10  // Slowest returns a slice of all tests with an elapsed time greater than
    11  // threshold. The slice is sorted by Elapsed time in descending order (slowest
    12  // test first).
    13  //
    14  // If there are multiple runs of a TestCase, all of them will be represented
    15  // by a single TestCase with the median elapsed time in the returned slice.
    16  func Slowest(exec *testjson.Execution, threshold time.Duration, num int) []testjson.TestCase {
    17  	if threshold == 0 && num == 0 {
    18  		return nil
    19  	}
    20  	pkgs := exec.Packages()
    21  	tests := make([]testjson.TestCase, 0, len(pkgs))
    22  	for _, pkg := range pkgs {
    23  		pkgTests := ByElapsed(exec.Package(pkg).TestCases(), median)
    24  		tests = append(tests, pkgTests...)
    25  	}
    26  	sort.Slice(tests, func(i, j int) bool {
    27  		return tests[i].Elapsed > tests[j].Elapsed
    28  	})
    29  	if num >= len(tests) {
    30  		return tests
    31  	}
    32  	if num > 0 {
    33  		return tests[:num]
    34  	}
    35  
    36  	end := sort.Search(len(tests), func(i int) bool {
    37  		return tests[i].Elapsed < threshold
    38  	})
    39  	return tests[:end]
    40  }
    41  
    42  // ByElapsed maps all test cases by name, and if there is more than one
    43  // instance of a TestCase, uses fn to select the elapsed time for the group.
    44  //
    45  // All cases are assumed to be part of the same package.
    46  func ByElapsed(cases []testjson.TestCase, fn func(times []time.Duration) time.Duration) []testjson.TestCase {
    47  	if len(cases) <= 1 {
    48  		return cases
    49  	}
    50  	pkg := cases[0].Package
    51  	// nolint: prealloc // size is not predictable
    52  	m := make(map[testjson.TestName][]time.Duration)
    53  	for _, tc := range cases {
    54  		m[tc.Test] = append(m[tc.Test], tc.Elapsed)
    55  	}
    56  	result := make([]testjson.TestCase, 0, len(m))
    57  	for name, timing := range m {
    58  		result = append(result, testjson.TestCase{
    59  			Package: pkg,
    60  			Test:    name,
    61  			Elapsed: fn(timing),
    62  		})
    63  	}
    64  	return result
    65  }
    66  
    67  func median(times []time.Duration) time.Duration {
    68  	switch len(times) {
    69  	case 0:
    70  		return 0
    71  	case 1:
    72  		return times[0]
    73  	}
    74  	sort.Slice(times, func(i, j int) bool {
    75  		return times[i] < times[j]
    76  	})
    77  	return times[len(times)/2]
    78  }