github.com/onsi/gomega@v1.32.0/gmeasure/rank.go (about) 1 package gmeasure 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/onsi/gomega/gmeasure/table" 8 ) 9 10 /* 11 RankingCriteria is an enum representing the criteria by which Stats should be ranked. The enum names should be self explanatory. e.g. LowerMeanIsBetter means that Stats with lower mean values are considered more beneficial, with the lowest mean being declared the "winner" . 12 */ 13 type RankingCriteria uint 14 15 const ( 16 LowerMeanIsBetter RankingCriteria = iota 17 HigherMeanIsBetter 18 LowerMedianIsBetter 19 HigherMedianIsBetter 20 LowerMinIsBetter 21 HigherMinIsBetter 22 LowerMaxIsBetter 23 HigherMaxIsBetter 24 ) 25 26 var rcEnumSupport = newEnumSupport(map[uint]string{uint(LowerMeanIsBetter): "Lower Mean is Better", uint(HigherMeanIsBetter): "Higher Mean is Better", uint(LowerMedianIsBetter): "Lower Median is Better", uint(HigherMedianIsBetter): "Higher Median is Better", uint(LowerMinIsBetter): "Lower Mins is Better", uint(HigherMinIsBetter): "Higher Min is Better", uint(LowerMaxIsBetter): "Lower Max is Better", uint(HigherMaxIsBetter): "Higher Max is Better"}) 27 28 func (s RankingCriteria) String() string { return rcEnumSupport.String(uint(s)) } 29 func (s *RankingCriteria) UnmarshalJSON(b []byte) error { 30 out, err := rcEnumSupport.UnmarshJSON(b) 31 *s = RankingCriteria(out) 32 return err 33 } 34 func (s RankingCriteria) MarshalJSON() ([]byte, error) { return rcEnumSupport.MarshJSON(uint(s)) } 35 36 /* 37 Ranking ranks a set of Stats by a specified RankingCritera. Use RankStats to create a Ranking. 38 39 When using Ginkgo, you can register Rankings as Report Entries via AddReportEntry. This will emit a formatted table representing the Stats in rank-order when Ginkgo generates the report. 40 */ 41 type Ranking struct { 42 Criteria RankingCriteria 43 Stats []Stats 44 } 45 46 /* 47 RankStats creates a new ranking of the passed-in stats according to the passed-in criteria. 48 */ 49 func RankStats(criteria RankingCriteria, stats ...Stats) Ranking { 50 sort.Slice(stats, func(i int, j int) bool { 51 switch criteria { 52 case LowerMeanIsBetter: 53 return stats[i].FloatFor(StatMean) < stats[j].FloatFor(StatMean) 54 case HigherMeanIsBetter: 55 return stats[i].FloatFor(StatMean) > stats[j].FloatFor(StatMean) 56 case LowerMedianIsBetter: 57 return stats[i].FloatFor(StatMedian) < stats[j].FloatFor(StatMedian) 58 case HigherMedianIsBetter: 59 return stats[i].FloatFor(StatMedian) > stats[j].FloatFor(StatMedian) 60 case LowerMinIsBetter: 61 return stats[i].FloatFor(StatMin) < stats[j].FloatFor(StatMin) 62 case HigherMinIsBetter: 63 return stats[i].FloatFor(StatMin) > stats[j].FloatFor(StatMin) 64 case LowerMaxIsBetter: 65 return stats[i].FloatFor(StatMax) < stats[j].FloatFor(StatMax) 66 case HigherMaxIsBetter: 67 return stats[i].FloatFor(StatMax) > stats[j].FloatFor(StatMax) 68 } 69 return false 70 }) 71 72 out := Ranking{ 73 Criteria: criteria, 74 Stats: stats, 75 } 76 77 return out 78 } 79 80 /* 81 Winner returns the Stats with the most optimal rank based on the specified ranking criteria. For example, if the RankingCriteria is LowerMaxIsBetter then the Stats with the lowest value or duration for StatMax will be returned as the "winner" 82 */ 83 func (c Ranking) Winner() Stats { 84 if len(c.Stats) == 0 { 85 return Stats{} 86 } 87 return c.Stats[0] 88 } 89 90 func (c Ranking) report(enableStyling bool) string { 91 if len(c.Stats) == 0 { 92 return "Empty Ranking" 93 } 94 t := table.NewTable() 95 t.TableStyle.EnableTextStyling = enableStyling 96 t.AppendRow(table.R( 97 table.C("Experiment"), table.C("Name"), table.C("N"), table.C("Min"), table.C("Median"), table.C("Mean"), table.C("StdDev"), table.C("Max"), 98 table.Divider("="), 99 "{{bold}}", 100 )) 101 102 for idx, stats := range c.Stats { 103 name := stats.MeasurementName 104 if stats.Units != "" { 105 name = name + " [" + stats.Units + "]" 106 } 107 experimentName := stats.ExperimentName 108 style := stats.Style 109 if idx == 0 { 110 style = "{{bold}}" + style 111 name += "\n*Winner*" 112 experimentName += "\n*Winner*" 113 } 114 r := table.R(style) 115 t.AppendRow(r) 116 r.AppendCell(table.C(experimentName), table.C(name)) 117 r.AppendCell(stats.cells()...) 118 119 } 120 out := fmt.Sprintf("Ranking Criteria: %s\n", c.Criteria) 121 if enableStyling { 122 out = "{{bold}}" + out + "{{/}}" 123 } 124 out += t.Render() 125 return out 126 } 127 128 /* 129 ColorableString generates a styled report that includes a table of the rank-ordered Stats 130 It is called automatically by Ginkgo's reporting infrastructure when the Ranking is registered as a ReportEntry via AddReportEntry. 131 */ 132 func (c Ranking) ColorableString() string { 133 return c.report(true) 134 } 135 136 /* 137 String generates an unstyled report that includes a table of the rank-ordered Stats 138 */ 139 func (c Ranking) String() string { 140 return c.report(false) 141 }