// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package promql import ( "fmt" "math" "testing" "github.com/stretchr/testify/require" "github.com/prometheus/prometheus/model/histogram" "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/util/annotations" ) func TestKahanSumInc(t *testing.T) { testCases := map[string]struct { first float64 second float64 expected float64 }{ "+Inf + anything = +Inf": { first: math.Inf(1), second: 2.0, expected: math.Inf(1), }, "-Inf + anything = -Inf": { first: math.Inf(-1), second: 2.0, expected: math.Inf(-1), }, "+Inf + -Inf = NaN": { first: math.Inf(1), second: math.Inf(-1), expected: math.NaN(), }, "NaN + anything = NaN": { first: math.NaN(), second: 2, expected: math.NaN(), }, "NaN + Inf = NaN": { first: math.NaN(), second: math.Inf(1), expected: math.NaN(), }, "NaN + -Inf = NaN": { first: math.NaN(), second: math.Inf(-1), expected: math.NaN(), }, } runTest := func(t *testing.T, a, b, expected float64) { t.Run(fmt.Sprintf("%v + %v = %v", a, b, expected), func(t *testing.T) { sum, c := kahanSumInc(b, a, 0) result := sum + c if math.IsNaN(expected) { require.Truef(t, math.IsNaN(result), "expected result to be NaN, but got %v (from %v + %v)", result, sum, c) } else { require.Equalf(t, expected, result, "expected result to be %v, but got %v (from %v + %v)", expected, result, sum, c) } }) } for name, testCase := range testCases { t.Run(name, func(t *testing.T) { runTest(t, testCase.first, testCase.second, testCase.expected) runTest(t, testCase.second, testCase.first, testCase.expected) }) } } func newAnnotations(errs ...error) annotations.Annotations { var annos annotations.Annotations for _, err := range errs { annos.Add(err) } return annos } // TODO(juliusmh): this test ensures histogramRate sets correct annotations. // This test can be removed in favor of a user facing promqltest when // https://github.com/prometheus/prometheus/issues/15346 is resolved. func TestHistogramRate_Annotations(t *testing.T) { const metricName = "test" pos := posrange.PositionRange{} for _, tc := range []struct { name string points []HPoint wantAnnotations annotations.Annotations }{ { name: "empty histograms", points: []HPoint{ {H: &histogram.FloatHistogram{}}, {H: &histogram.FloatHistogram{}}, }, wantAnnotations: newAnnotations( annotations.NewNativeHistogramNotGaugeWarning(metricName, pos), ), }, { name: "counter reset hint collision", points: []HPoint{ {H: &histogram.FloatHistogram{CounterResetHint: histogram.NotCounterReset}}, {H: &histogram.FloatHistogram{CounterResetHint: histogram.CounterReset}}, }, wantAnnotations: newAnnotations( annotations.NewNativeHistogramNotGaugeWarning(metricName, pos), annotations.NewHistogramCounterResetCollisionWarning(pos, annotations.HistogramSub), ), }, } { t.Run(tc.name, func(t *testing.T) { _, annos := histogramRate(tc.points, false, metricName, pos) require.Equal(t, tc.wantAnnotations, annos) }) } }