package gmeasure import ( "fmt" "sort" "github.com/onsi/gomega/gmeasure/table" ) /* 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" . */ type RankingCriteria uint const ( LowerMeanIsBetter RankingCriteria = iota HigherMeanIsBetter LowerMedianIsBetter HigherMedianIsBetter LowerMinIsBetter HigherMinIsBetter LowerMaxIsBetter HigherMaxIsBetter ) 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"}) func (s RankingCriteria) String() string { return rcEnumSupport.String(uint(s)) } func (s *RankingCriteria) UnmarshalJSON(b []byte) error { out, err := rcEnumSupport.UnmarshJSON(b) *s = RankingCriteria(out) return err } func (s RankingCriteria) MarshalJSON() ([]byte, error) { return rcEnumSupport.MarshJSON(uint(s)) } /* Ranking ranks a set of Stats by a specified RankingCritera. Use RankStats to create a Ranking. 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. */ type Ranking struct { Criteria RankingCriteria Stats []Stats } /* RankStats creates a new ranking of the passed-in stats according to the passed-in criteria. */ func RankStats(criteria RankingCriteria, stats ...Stats) Ranking { sort.Slice(stats, func(i int, j int) bool { switch criteria { case LowerMeanIsBetter: return stats[i].FloatFor(StatMean) < stats[j].FloatFor(StatMean) case HigherMeanIsBetter: return stats[i].FloatFor(StatMean) > stats[j].FloatFor(StatMean) case LowerMedianIsBetter: return stats[i].FloatFor(StatMedian) < stats[j].FloatFor(StatMedian) case HigherMedianIsBetter: return stats[i].FloatFor(StatMedian) > stats[j].FloatFor(StatMedian) case LowerMinIsBetter: return stats[i].FloatFor(StatMin) < stats[j].FloatFor(StatMin) case HigherMinIsBetter: return stats[i].FloatFor(StatMin) > stats[j].FloatFor(StatMin) case LowerMaxIsBetter: return stats[i].FloatFor(StatMax) < stats[j].FloatFor(StatMax) case HigherMaxIsBetter: return stats[i].FloatFor(StatMax) > stats[j].FloatFor(StatMax) } return false }) out := Ranking{ Criteria: criteria, Stats: stats, } return out } /* 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" */ func (c Ranking) Winner() Stats { if len(c.Stats) == 0 { return Stats{} } return c.Stats[0] } func (c Ranking) report(enableStyling bool) string { if len(c.Stats) == 0 { return "Empty Ranking" } t := table.NewTable() t.TableStyle.EnableTextStyling = enableStyling t.AppendRow(table.R( table.C("Experiment"), table.C("Name"), table.C("N"), table.C("Min"), table.C("Median"), table.C("Mean"), table.C("StdDev"), table.C("Max"), table.Divider("="), "{{bold}}", )) for idx, stats := range c.Stats { name := stats.MeasurementName if stats.Units != "" { name = name + " [" + stats.Units + "]" } experimentName := stats.ExperimentName style := stats.Style if idx == 0 { style = "{{bold}}" + style name += "\n*Winner*" experimentName += "\n*Winner*" } r := table.R(style) t.AppendRow(r) r.AppendCell(table.C(experimentName), table.C(name)) r.AppendCell(stats.cells()...) } out := fmt.Sprintf("Ranking Criteria: %s\n", c.Criteria) if enableStyling { out = "{{bold}}" + out + "{{/}}" } out += t.Render() return out } /* ColorableString generates a styled report that includes a table of the rank-ordered Stats It is called automatically by Ginkgo's reporting infrastructure when the Ranking is registered as a ReportEntry via AddReportEntry. */ func (c Ranking) ColorableString() string { return c.report(true) } /* String generates an unstyled report that includes a table of the rank-ordered Stats */ func (c Ranking) String() string { return c.report(false) }