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
|
package mapr
import (
"math"
"strconv"
"strings"
"testing"
)
// TestGroupSetAvgZeroSamplesDoesNotProduceNaN is a negative test that reproduces
// the bug where an empty aggregate set (Samples==0) causes 0/0 = NaN in the Avg
// case of resultSelect. This happens when the server creates a group-set entry
// via GetSet before confirming that any select fields matched, then serialises and
// sends the empty set to the client. The client-side resultSelect must guard the
// Avg division so that Samples==0 yields 0 instead of NaN.
func TestGroupSetAvgZeroSamplesDoesNotProduceNaN(t *testing.T) {
t.Parallel()
query, err := NewQuery("select avg(latency) from stats group by host")
if err != nil {
t.Fatalf("Unable to parse query: %v", err)
}
groupSet := NewGroupSet()
// Simulate what the server does when no log line fields match the select
// clause: GetSet creates the entry, but Samples stays 0 and FValues is
// never populated. This is the bug trigger — previously 0/0 = NaN.
_ = groupSet.GetSet("host-a")
rows, _, err := groupSet.result(query, false)
if err != nil {
t.Fatalf("result() returned unexpected error: %v", err)
}
if len(rows) != 1 {
t.Fatalf("Expected 1 result row (even for empty set), got %d", len(rows))
}
// Before the fix each floating-point value in the row was the string "NaN".
for _, row := range rows {
for _, v := range row.values {
trimmed := strings.TrimSpace(v)
f, parseErr := strconv.ParseFloat(trimmed, 64)
if parseErr != nil {
// Non-numeric values (e.g. integer count or last-string fields)
// are fine; only floating-point results can be NaN.
continue
}
if math.IsNaN(f) {
t.Errorf("avg on empty set produced NaN in output %q; expected 0", v)
}
}
}
}
// TestGroupSetAvgZeroSamplesResultOutputContainsNoNaN verifies that the
// higher-level Result method (which drives terminal output) also never emits
// "NaN" strings when aggregate sets have zero samples.
func TestGroupSetAvgZeroSamplesResultOutputContainsNoNaN(t *testing.T) {
t.Parallel()
query, err := NewQuery("select avg(latency) from stats group by host")
if err != nil {
t.Fatalf("Unable to parse query: %v", err)
}
groupSet := NewGroupSet()
// Empty set — Samples==0, no FValues populated.
_ = groupSet.GetSet("host-a")
output, _, err := groupSet.Result(query, 100, nil)
if err != nil {
t.Fatalf("Result() returned unexpected error: %v", err)
}
if strings.Contains(output, "NaN") {
t.Errorf("Result output must not contain 'NaN', got:\n%s", output)
}
}
|