1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
package mapr
import (
"strconv"
"testing"
)
func TestGroupSetResultPercentageAndPercentile(t *testing.T) {
query, err := NewQuery("select percentage(value),percentile(value) from stats group by host order by percentage(value)")
if err != nil {
t.Fatalf("Unable to parse query: %v", err)
}
groupSet := NewGroupSet()
setA := groupSet.GetSet("host-a")
if err := setA.Aggregate("percentage(value)", Percentage, "10", false); err != nil {
t.Fatalf("Unable to aggregate percentage for host-a: %v", err)
}
if err := setA.Aggregate("percentile(value)", Percentile, "10", false); err != nil {
t.Fatalf("Unable to aggregate percentile for host-a: %v", err)
}
setB := groupSet.GetSet("host-b")
if err := setB.Aggregate("percentage(value)", Percentage, "30", false); err != nil {
t.Fatalf("Unable to aggregate percentage for host-b: %v", err)
}
if err := setB.Aggregate("percentile(value)", Percentile, "30", false); err != nil {
t.Fatalf("Unable to aggregate percentile for host-b: %v", err)
}
setC := groupSet.GetSet("host-c")
if err := setC.Aggregate("percentage(value)", Percentage, "20", false); err != nil {
t.Fatalf("Unable to aggregate percentage for host-c: %v", err)
}
if err := setC.Aggregate("percentile(value)", Percentile, "20", false); err != nil {
t.Fatalf("Unable to aggregate percentile for host-c: %v", err)
}
rows, _, err := groupSet.result(query, false)
if err != nil {
t.Fatalf("Unable to build result rows: %v", err)
}
if len(rows) != 3 {
t.Fatalf("Expected 3 result rows, got %d", len(rows))
}
if rows[0].groupKey != "host-b" {
t.Fatalf("Expected rows to be ordered by percentage descending, first row=%s", rows[0].groupKey)
}
valuesByGroup := map[string][]float64{}
for _, row := range rows {
parsedValues := make([]float64, 0, len(row.values))
for _, value := range row.values {
parsedValue, err := strconv.ParseFloat(value, 64)
if err != nil {
t.Fatalf("Unable to parse result value %q: %v", value, err)
}
parsedValues = append(parsedValues, parsedValue)
}
valuesByGroup[row.groupKey] = parsedValues
}
assertAlmostEqual(t, valuesByGroup["host-a"][0], 16.6666666667, 0.0001, "host-a percentage")
assertAlmostEqual(t, valuesByGroup["host-a"][1], 33.3333333333, 0.0001, "host-a percentile")
assertAlmostEqual(t, valuesByGroup["host-b"][0], 50.0, 0.0001, "host-b percentage")
assertAlmostEqual(t, valuesByGroup["host-b"][1], 100.0, 0.0001, "host-b percentile")
assertAlmostEqual(t, valuesByGroup["host-c"][0], 33.3333333333, 0.0001, "host-c percentage")
assertAlmostEqual(t, valuesByGroup["host-c"][1], 66.6666666667, 0.0001, "host-c percentile")
}
func TestGroupSetPercentageReturnsZeroWhenTotalIsZero(t *testing.T) {
query, err := NewQuery("select percentage(value) from stats group by host")
if err != nil {
t.Fatalf("Unable to parse query: %v", err)
}
groupSet := NewGroupSet()
for _, host := range []string{"host-a", "host-b"} {
set := groupSet.GetSet(host)
if err := set.Aggregate("percentage(value)", Percentage, "0", false); err != nil {
t.Fatalf("Unable to aggregate percentage for %s: %v", host, err)
}
}
rows, _, err := groupSet.result(query, false)
if err != nil {
t.Fatalf("Unable to build result rows: %v", err)
}
if len(rows) != 2 {
t.Fatalf("Expected 2 result rows, got %d", len(rows))
}
for _, row := range rows {
if len(row.values) != 1 {
t.Fatalf("Expected one result value, got %d for %s", len(row.values), row.groupKey)
}
value, err := strconv.ParseFloat(row.values[0], 64)
if err != nil {
t.Fatalf("Unable to parse percentage result %q: %v", row.values[0], err)
}
assertAlmostEqual(t, value, 0.0, 0.0001, row.groupKey+" percentage")
}
}
func TestPercentileRank(t *testing.T) {
sortedValues := []float64{10, 20, 30}
tests := []struct {
name string
value float64
expected float64
}{
{name: "below minimum", value: 5, expected: 0},
{name: "first bucket", value: 10, expected: 33.3333333333},
{name: "middle bucket", value: 20, expected: 66.6666666667},
{name: "maximum", value: 30, expected: 100},
{name: "above maximum", value: 40, expected: 100},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertAlmostEqual(t, percentileRank(tt.value, sortedValues), tt.expected, 0.0001, tt.name)
})
}
assertAlmostEqual(t, percentileRank(10, []float64{10, 10, 30}), 66.6666666667, 0.0001, "duplicate percentile rank")
if got := percentileRank(10, nil); got != 0 {
t.Fatalf("Expected empty percentile input to return 0, got %f", got)
}
}
func assertAlmostEqual(t *testing.T, got, expected, tolerance float64, label string) {
t.Helper()
diff := got - expected
if diff < 0 {
diff = -diff
}
if diff > tolerance {
t.Fatalf("Unexpected %s: got=%f expected=%f tolerance=%f", label, got, expected, tolerance)
}
}
|