1// Copyright 2019, OpenTelemetry Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package array 16 17import ( 18 "context" 19 "fmt" 20 "math" 21 "os" 22 "testing" 23 "unsafe" 24 25 "github.com/stretchr/testify/require" 26 27 "go.opentelemetry.io/otel/api/core" 28 ottest "go.opentelemetry.io/otel/internal/testing" 29 export "go.opentelemetry.io/otel/sdk/export/metric" 30 "go.opentelemetry.io/otel/sdk/export/metric/aggregator" 31 "go.opentelemetry.io/otel/sdk/metric/aggregator/test" 32) 33 34// Ensure struct alignment prior to running tests. 35func TestMain(m *testing.M) { 36 fields := []ottest.FieldOffset{ 37 { 38 Name: "Aggregator.ckptSum", 39 Offset: unsafe.Offsetof(Aggregator{}.ckptSum), 40 }, 41 } 42 if !ottest.Aligned8Byte(fields, os.Stderr) { 43 os.Exit(1) 44 } 45 46 os.Exit(m.Run()) 47} 48 49type updateTest struct { 50 count int 51 absolute bool 52} 53 54func (ut *updateTest) run(t *testing.T, profile test.Profile) { 55 descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind, !ut.absolute) 56 57 agg := New() 58 59 all := test.NewNumbers(profile.NumberKind) 60 61 for i := 0; i < ut.count; i++ { 62 x := profile.Random(+1) 63 all.Append(x) 64 test.CheckedUpdate(t, agg, x, descriptor) 65 66 if !ut.absolute { 67 y := profile.Random(-1) 68 all.Append(y) 69 test.CheckedUpdate(t, agg, y, descriptor) 70 } 71 } 72 73 ctx := context.Background() 74 agg.Checkpoint(ctx, descriptor) 75 76 all.Sort() 77 78 sum, err := agg.Sum() 79 require.Nil(t, err) 80 allSum := all.Sum() 81 require.InEpsilon(t, 82 (&allSum).CoerceToFloat64(profile.NumberKind), 83 sum.CoerceToFloat64(profile.NumberKind), 84 0.0000001, 85 "Same sum - absolute") 86 count, err := agg.Count() 87 require.Nil(t, err) 88 require.Equal(t, all.Count(), count, "Same count - absolute") 89 90 min, err := agg.Min() 91 require.Nil(t, err) 92 require.Equal(t, all.Min(), min, "Same min - absolute") 93 94 max, err := agg.Max() 95 require.Nil(t, err) 96 require.Equal(t, all.Max(), max, "Same max - absolute") 97 98 qx, err := agg.Quantile(0.5) 99 require.Nil(t, err) 100 require.Equal(t, all.Median(), qx, "Same median - absolute") 101} 102 103func TestArrayUpdate(t *testing.T) { 104 // Test with an odd an even number of measurements 105 for count := 999; count <= 1000; count++ { 106 t.Run(fmt.Sprint("Odd=", count%2 == 1), func(t *testing.T) { 107 // Test absolute and non-absolute 108 for _, absolute := range []bool{false, true} { 109 t.Run(fmt.Sprint("Absolute=", absolute), func(t *testing.T) { 110 ut := updateTest{ 111 count: count, 112 absolute: absolute, 113 } 114 115 // Test integer and floating point 116 test.RunProfiles(t, ut.run) 117 }) 118 } 119 }) 120 } 121} 122 123type mergeTest struct { 124 count int 125 absolute bool 126} 127 128func (mt *mergeTest) run(t *testing.T, profile test.Profile) { 129 ctx := context.Background() 130 131 descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind, !mt.absolute) 132 133 agg1 := New() 134 agg2 := New() 135 136 all := test.NewNumbers(profile.NumberKind) 137 138 for i := 0; i < mt.count; i++ { 139 x1 := profile.Random(+1) 140 all.Append(x1) 141 test.CheckedUpdate(t, agg1, x1, descriptor) 142 143 x2 := profile.Random(+1) 144 all.Append(x2) 145 test.CheckedUpdate(t, agg2, x2, descriptor) 146 147 if !mt.absolute { 148 y1 := profile.Random(-1) 149 all.Append(y1) 150 test.CheckedUpdate(t, agg1, y1, descriptor) 151 152 y2 := profile.Random(-1) 153 all.Append(y2) 154 test.CheckedUpdate(t, agg2, y2, descriptor) 155 } 156 } 157 158 agg1.Checkpoint(ctx, descriptor) 159 agg2.Checkpoint(ctx, descriptor) 160 161 test.CheckedMerge(t, agg1, agg2, descriptor) 162 163 all.Sort() 164 165 sum, err := agg1.Sum() 166 require.Nil(t, err) 167 allSum := all.Sum() 168 require.InEpsilon(t, 169 (&allSum).CoerceToFloat64(profile.NumberKind), 170 sum.CoerceToFloat64(profile.NumberKind), 171 0.0000001, 172 "Same sum - absolute") 173 count, err := agg1.Count() 174 require.Nil(t, err) 175 require.Equal(t, all.Count(), count, "Same count - absolute") 176 177 min, err := agg1.Min() 178 require.Nil(t, err) 179 require.Equal(t, all.Min(), min, "Same min - absolute") 180 181 max, err := agg1.Max() 182 require.Nil(t, err) 183 require.Equal(t, all.Max(), max, "Same max - absolute") 184 185 qx, err := agg1.Quantile(0.5) 186 require.Nil(t, err) 187 require.Equal(t, all.Median(), qx, "Same median - absolute") 188} 189 190func TestArrayMerge(t *testing.T) { 191 // Test with an odd an even number of measurements 192 for count := 999; count <= 1000; count++ { 193 t.Run(fmt.Sprint("Odd=", count%2 == 1), func(t *testing.T) { 194 // Test absolute and non-absolute 195 for _, absolute := range []bool{false, true} { 196 t.Run(fmt.Sprint("Absolute=", absolute), func(t *testing.T) { 197 mt := mergeTest{ 198 count: count, 199 absolute: absolute, 200 } 201 202 // Test integer and floating point 203 test.RunProfiles(t, mt.run) 204 }) 205 } 206 }) 207 } 208} 209 210func TestArrayErrors(t *testing.T) { 211 test.RunProfiles(t, func(t *testing.T, profile test.Profile) { 212 agg := New() 213 214 _, err := agg.Max() 215 require.Error(t, err) 216 require.Equal(t, err, aggregator.ErrEmptyDataSet) 217 218 _, err = agg.Min() 219 require.Error(t, err) 220 require.Equal(t, err, aggregator.ErrEmptyDataSet) 221 222 _, err = agg.Quantile(0.1) 223 require.Error(t, err) 224 require.Equal(t, err, aggregator.ErrEmptyDataSet) 225 226 ctx := context.Background() 227 228 descriptor := test.NewAggregatorTest(export.MeasureKind, profile.NumberKind, false) 229 230 test.CheckedUpdate(t, agg, core.Number(0), descriptor) 231 232 if profile.NumberKind == core.Float64NumberKind { 233 test.CheckedUpdate(t, agg, core.NewFloat64Number(math.NaN()), descriptor) 234 } 235 agg.Checkpoint(ctx, descriptor) 236 237 count, err := agg.Count() 238 require.Equal(t, int64(1), count, "NaN value was not counted") 239 require.Nil(t, err) 240 241 num, err := agg.Quantile(0) 242 require.Nil(t, err) 243 require.Equal(t, num, core.Number(0)) 244 245 _, err = agg.Quantile(-0.0001) 246 require.Error(t, err) 247 require.Equal(t, err, aggregator.ErrInvalidQuantile) 248 249 _, err = agg.Quantile(1.0001) 250 require.Error(t, err) 251 require.Equal(t, err, aggregator.ErrInvalidQuantile) 252 }) 253} 254 255func TestArrayFloat64(t *testing.T) { 256 for _, absolute := range []bool{false, true} { 257 t.Run(fmt.Sprint("Absolute=", absolute), func(t *testing.T) { 258 descriptor := test.NewAggregatorTest(export.MeasureKind, core.Float64NumberKind, !absolute) 259 260 fpsf := func(sign int) []float64 { 261 // Check behavior of a bunch of odd floating 262 // points except for NaN, which is invalid. 263 return []float64{ 264 0, 265 math.Inf(sign), 266 1 / math.Inf(sign), 267 1, 268 2, 269 1e100, 270 math.MaxFloat64, 271 math.SmallestNonzeroFloat64, 272 math.MaxFloat32, 273 math.SmallestNonzeroFloat32, 274 math.E, 275 math.Pi, 276 math.Phi, 277 math.Sqrt2, 278 math.SqrtE, 279 math.SqrtPi, 280 math.SqrtPhi, 281 math.Ln2, 282 math.Log2E, 283 math.Ln10, 284 math.Log10E, 285 } 286 } 287 288 all := test.NewNumbers(core.Float64NumberKind) 289 290 ctx := context.Background() 291 agg := New() 292 293 for _, f := range fpsf(1) { 294 all.Append(core.NewFloat64Number(f)) 295 test.CheckedUpdate(t, agg, core.NewFloat64Number(f), descriptor) 296 } 297 298 if !absolute { 299 for _, f := range fpsf(-1) { 300 all.Append(core.NewFloat64Number(f)) 301 test.CheckedUpdate(t, agg, core.NewFloat64Number(f), descriptor) 302 } 303 } 304 305 agg.Checkpoint(ctx, descriptor) 306 307 all.Sort() 308 309 sum, err := agg.Sum() 310 require.Nil(t, err) 311 allSum := all.Sum() 312 require.InEpsilon(t, (&allSum).AsFloat64(), sum.AsFloat64(), 0.0000001, "Same sum") 313 314 count, err := agg.Count() 315 require.Equal(t, all.Count(), count, "Same count") 316 require.Nil(t, err) 317 318 min, err := agg.Min() 319 require.Nil(t, err) 320 require.Equal(t, all.Min(), min, "Same min") 321 322 max, err := agg.Max() 323 require.Nil(t, err) 324 require.Equal(t, all.Max(), max, "Same max") 325 326 qx, err := agg.Quantile(0.5) 327 require.Nil(t, err) 328 require.Equal(t, all.Median(), qx, "Same median") 329 330 po, err := agg.Points() 331 require.Nil(t, err) 332 require.Equal(t, all.Len(), len(po), "Points() must have same length of updates") 333 for i := 0; i < len(po); i++ { 334 require.Equal(t, all.Points()[i], po[i], "Wrong point at position %d", i) 335 } 336 }) 337 } 338} 339