Skip to content

Commit c7dcf1f

Browse files
authored
Add histogram conversion test cases and test data (#374)
1 parent db57f66 commit c7dcf1f

File tree

13 files changed

+1859
-0
lines changed

13 files changed

+1859
-0
lines changed

pkg/aws/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ../../Makefile.Common

pkg/aws/cloudwatch/datatypes.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package cloudwatch // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch"
5+
6+
// HistogramDataPoint is a single data point that describes the values of a Histogram.
7+
// The values of a histogram are represented by equal-length series of values and counts. Each value/count pair
8+
// is a single bucket of the Histogram.
9+
type HistogramDataPoint interface {
10+
ValuesAndCounts() ([]float64, []float64)
11+
Sum() float64
12+
SampleCount() float64
13+
Minimum() float64
14+
Maximum() float64
15+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package histograms // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/aws/cloudwatch/histograms"
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"math"
10+
11+
"go.opentelemetry.io/collector/pdata/pmetric"
12+
)
13+
14+
func CheckValidity(dp pmetric.HistogramDataPoint) error {
15+
errs := []error{}
16+
17+
bounds := dp.ExplicitBounds()
18+
bucketCounts := dp.BucketCounts()
19+
20+
// Check counts length matches boundaries + 1
21+
// special case: no bucketCounts and no boundaries is still valid
22+
if bucketCounts.Len() != bounds.Len()+1 && bucketCounts.Len() != 0 && bounds.Len() != 0 {
23+
errs = append(errs, fmt.Errorf("bucket counts length (%d) doesn't match boundaries length (%d) + 1",
24+
bucketCounts.Len(), bounds.Len()))
25+
}
26+
27+
if dp.HasMax() && dp.HasMin() && dp.Min() > dp.Max() {
28+
errs = append(errs, fmt.Errorf("min %f is greater than max %f", dp.Min(), dp.Max()))
29+
}
30+
31+
if dp.HasMax() {
32+
errs = append(errs, checkNanInf(dp.Max(), "max"))
33+
}
34+
if dp.HasMin() {
35+
errs = append(errs, checkNanInf(dp.Min(), "min"))
36+
}
37+
if dp.HasSum() {
38+
errs = append(errs, checkNanInf(dp.Sum(), "sum"))
39+
}
40+
41+
if bounds.Len() > 0 {
42+
// Check boundaries are in ascending order
43+
for i := 1; i < bounds.Len(); i++ {
44+
if bounds.At(i) <= bounds.At(i-1) {
45+
errs = append(errs, fmt.Errorf("boundaries not in ascending order: bucket index %d (%v) <= bucket index %d %v",
46+
i, bounds.At(i), i-1, bounds.At(i-1)))
47+
}
48+
errs = append(errs, checkNanInf(bounds.At(i), fmt.Sprintf("boundary %d", i)))
49+
}
50+
}
51+
52+
return errors.Join(errs...)
53+
}
54+
55+
func checkNanInf(value float64, name string) error {
56+
errs := []error{}
57+
if math.IsNaN(value) {
58+
errs = append(errs, errors.New(name+" is NaN"))
59+
}
60+
if math.IsInf(value, 0) {
61+
errs = append(errs, errors.New(name+" is +/-inf"))
62+
}
63+
return errors.Join(errs...)
64+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package histograms
5+
6+
import (
7+
"math"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"go.opentelemetry.io/collector/pdata/pmetric"
12+
)
13+
14+
func TestCheckValidity(t *testing.T) {
15+
tests := []struct {
16+
name string
17+
dp pmetric.HistogramDataPoint
18+
valid bool
19+
}{
20+
{
21+
name: "Boundaries Not Ascending",
22+
dp: func() pmetric.HistogramDataPoint {
23+
dp := pmetric.NewHistogramDataPoint()
24+
dp.SetCount(100)
25+
dp.SetSum(5000)
26+
dp.SetMin(10.0)
27+
dp.SetMax(200.0)
28+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 40, 100, 150}) // 40 < 50
29+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
30+
return dp
31+
}(),
32+
valid: false,
33+
},
34+
{
35+
name: "Counts Length Mismatch",
36+
dp: func() pmetric.HistogramDataPoint {
37+
dp := pmetric.NewHistogramDataPoint()
38+
dp.SetCount(100)
39+
dp.SetSum(5000)
40+
dp.SetMin(10.0)
41+
dp.SetMax(200.0)
42+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 75, 100})
43+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2}) // Should be 5 counts for 4 boundaries
44+
return dp
45+
}(),
46+
valid: false,
47+
},
48+
{
49+
name: "min greater than max",
50+
dp: func() pmetric.HistogramDataPoint {
51+
dp := pmetric.NewHistogramDataPoint()
52+
dp.SetCount(100)
53+
dp.SetSum(5000)
54+
dp.SetMin(200.0)
55+
dp.SetMax(10.0)
56+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 70, 100, 150})
57+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
58+
return dp
59+
}(),
60+
valid: false,
61+
},
62+
{
63+
name: "Inf min",
64+
dp: func() pmetric.HistogramDataPoint {
65+
dp := pmetric.NewHistogramDataPoint()
66+
dp.SetCount(100)
67+
dp.SetSum(5000)
68+
dp.SetMin(math.Inf(-1))
69+
dp.SetMax(10.0)
70+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 70, 100, 150})
71+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
72+
return dp
73+
}(),
74+
valid: false,
75+
},
76+
{
77+
name: "NaN min",
78+
dp: func() pmetric.HistogramDataPoint {
79+
dp := pmetric.NewHistogramDataPoint()
80+
dp.SetCount(100)
81+
dp.SetSum(5000)
82+
dp.SetMin(math.NaN())
83+
dp.SetMax(10.0)
84+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 70, 100, 150})
85+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
86+
return dp
87+
}(),
88+
valid: false,
89+
},
90+
{
91+
name: "Inf max",
92+
dp: func() pmetric.HistogramDataPoint {
93+
dp := pmetric.NewHistogramDataPoint()
94+
dp.SetCount(100)
95+
dp.SetSum(5000)
96+
dp.SetMin(10.0)
97+
dp.SetMax(math.Inf(1))
98+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 70, 100, 150})
99+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
100+
return dp
101+
}(),
102+
valid: false,
103+
},
104+
{
105+
name: "NaN max",
106+
dp: func() pmetric.HistogramDataPoint {
107+
dp := pmetric.NewHistogramDataPoint()
108+
dp.SetCount(100)
109+
dp.SetSum(5000)
110+
dp.SetMin(10.0)
111+
dp.SetMax(math.NaN())
112+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 70, 100, 150})
113+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
114+
return dp
115+
}(),
116+
valid: false,
117+
},
118+
{
119+
name: "NaN Sum",
120+
dp: func() pmetric.HistogramDataPoint {
121+
dp := pmetric.NewHistogramDataPoint()
122+
dp.SetCount(100)
123+
dp.SetSum(math.NaN())
124+
dp.SetMin(10.0)
125+
dp.SetMax(200.0)
126+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 75, 100, 150})
127+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
128+
return dp
129+
}(),
130+
valid: false,
131+
},
132+
{
133+
name: "Inf Sum",
134+
dp: func() pmetric.HistogramDataPoint {
135+
dp := pmetric.NewHistogramDataPoint()
136+
dp.SetCount(100)
137+
dp.SetSum(math.Inf(1))
138+
dp.SetMin(10.0)
139+
dp.SetMax(200.0)
140+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 75, 100, 150})
141+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
142+
return dp
143+
}(),
144+
valid: false,
145+
},
146+
{
147+
name: "NaN Boundary",
148+
dp: func() pmetric.HistogramDataPoint {
149+
dp := pmetric.NewHistogramDataPoint()
150+
dp.SetCount(100)
151+
dp.SetSum(5000)
152+
dp.SetMin(10.0)
153+
dp.SetMax(200.0)
154+
dp.ExplicitBounds().FromRaw([]float64{25, math.NaN(), 75, 100, 150})
155+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
156+
return dp
157+
}(),
158+
valid: false,
159+
},
160+
{
161+
name: "Inf Boundary",
162+
dp: func() pmetric.HistogramDataPoint {
163+
dp := pmetric.NewHistogramDataPoint()
164+
dp.SetCount(100)
165+
dp.SetSum(5000)
166+
dp.SetMin(10.0)
167+
dp.SetMax(200.0)
168+
dp.ExplicitBounds().FromRaw([]float64{25, 50, math.Inf(1), 100, 150})
169+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
170+
return dp
171+
}(),
172+
valid: false,
173+
},
174+
}
175+
176+
for _, tt := range tests {
177+
t.Run(tt.name, func(t *testing.T) {
178+
assert.Error(t, CheckValidity(tt.dp))
179+
})
180+
}
181+
}
182+
183+
func BenchmarkCheckValidity(b *testing.B) {
184+
dp := pmetric.NewHistogramDataPoint()
185+
dp.SetCount(100)
186+
dp.SetSum(5000)
187+
dp.SetMin(10.0)
188+
dp.SetMax(200.0)
189+
dp.ExplicitBounds().FromRaw([]float64{25, 50, 75, 100, 150})
190+
dp.BucketCounts().FromRaw([]uint64{20, 30, 25, 15, 8, 2})
191+
192+
b.ResetTimer()
193+
for i := 0; i < b.N; i++ {
194+
assert.NoError(b, CheckValidity(dp))
195+
}
196+
}

0 commit comments

Comments
 (0)