github.com/go-graphite/carbonapi@v0.17.0/expr/functions/exponentialMovingAverage/function_test.go (about) 1 package exponentialMovingAverage 2 3 import ( 4 "context" 5 "math" 6 "testing" 7 8 "github.com/go-graphite/carbonapi/expr/interfaces" 9 "github.com/go-graphite/carbonapi/expr/metadata" 10 "github.com/go-graphite/carbonapi/expr/types" 11 "github.com/go-graphite/carbonapi/pkg/parser" 12 th "github.com/go-graphite/carbonapi/tests" 13 ) 14 15 var ( 16 md []interfaces.FunctionMetadata = New("") 17 ) 18 19 func init() { 20 for _, m := range md { 21 metadata.RegisterFunction(m.Name, m.F) 22 } 23 } 24 25 func TestExponentialMovingAverage(t *testing.T) { 26 const from = 100 27 const step = 10 28 29 tests := []th.EvalTestItemWithRange{ 30 { 31 Target: "exponentialMovingAverage(metric1,'30s')", 32 M: map[parser.MetricRequest][]*types.MetricData{ 33 {Metric: "metric1", From: from - 30, Until: from + step*6}: {types.MakeMetricData("metric1", []float64{2, 4, 6, 8, 12, 14, 16, 18, 20}, step, from-30)}, 34 }, 35 Want: []*types.MetricData{ 36 types.MakeMetricData("exponentialMovingAverage(metric1,\"30s\")", []float64{4, 4.258065, 4.757544, 5.353832, 6.040681, 6.81225, 7.663073}, step, from).SetTag("exponentialMovingAverage", `"30s"`), 37 }, 38 From: from, 39 Until: from + step*6, 40 }, 41 { 42 Target: "exponentialMovingAverage(empty,3)", 43 M: map[parser.MetricRequest][]*types.MetricData{ 44 // When the window is an integer, the original from-until range is used to get the step. 45 // That's why two requests are made. 46 {Metric: "empty", From: from, Until: from + step*4}: {}, 47 {Metric: "empty", From: from - step*3, Until: from + step*4}: {}, 48 }, 49 Want: []*types.MetricData{}, 50 From: from, 51 Until: from + step*4, 52 }, 53 { 54 Target: "exponentialMovingAverage(metric_changes_rollup,4)", 55 M: map[parser.MetricRequest][]*types.MetricData{ 56 {Metric: "metric_changes_rollup", From: from, Until: from + step*6}: {types.MakeMetricData("metric_changes_rollup", []float64{8, 12, 14, 16, 18, 20}, step, from)}, 57 // when querying for the preview window, the store changes the rollup and the step changes 58 {Metric: "metric_changes_rollup", From: from - step*4, Until: from + step*6}: {types.MakeMetricData("metric_changes_rollup", []float64{10, 20}, step*10, from-step*4)}, 59 }, 60 Want: []*types.MetricData{ 61 // since the input is shorter than the window, the result should be just the average 62 types.MakeMetricData("exponentialMovingAverage(metric_changes_rollup,4)", []float64{15}, step*10, from).SetTag("exponentialMovingAverage", "4"), 63 }, 64 From: from, 65 Until: from + step*6, 66 }, 67 { 68 // copied from Graphite Web 69 Target: "exponentialMovingAverage(halfNone,10)", 70 M: map[parser.MetricRequest][]*types.MetricData{ 71 {Metric: "halfNone", From: from, Until: from + 10}: {types.MakeMetricData("halfNone", append(append(append(nans(10), rangeFloats(0, 5, 1)...), math.NaN()), rangeFloats(5, 9, 1)...), 1, from)}, 72 {Metric: "halfNone", From: from - 10, Until: from + 10}: {types.MakeMetricData("halfNone", append(append(append(nans(10), rangeFloats(0, 5, 1)...), math.NaN()), rangeFloats(5, 9, 1)...), 1, from-10)}, 73 }, 74 Want: []*types.MetricData{ 75 types.MakeMetricData("exponentialMovingAverage(halfNone,10)", []float64{0, 0.0, 0.181818, 0.512397, 0.964688, 1.516563, math.NaN(), 2.149915, 2.849931, 3.604489, 4.403673}, 1, from).SetTag("exponentialMovingAverage", `10`), 76 }, 77 From: from, 78 Until: from + 10, 79 }, 80 // copied from Graphite Web 81 { 82 Target: `exponentialMovingAverage(collectd.test-db0.load.value,"-30s")`, 83 M: map[parser.MetricRequest][]*types.MetricData{ 84 {Metric: "collectd.test-db0.load.value", From: from - 30, Until: from + 30}: {types.MakeMetricData("collectd.test-db0.load.value", rangeFloats(0, 60, 1), 1, from-30)}, 85 }, 86 Want: []*types.MetricData{ 87 types.MakeMetricData("exponentialMovingAverage(collectd.test-db0.load.value,\"-30s\")", []float64{ 88 14.5, 15.5, 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, 24.5, 25.5, 26.5, 27.5, 28.5, 29.5, 30.5, 31.5, 32.5, 33.5, 34.5, 35.5, 36.5, 37.5, 38.5, 39.5, 40.5, 41.5, 42.5, 43.5, 44.5, 89 }, 1, from).SetTag("exponentialMovingAverage", `"-30s"`), 90 }, 91 From: from, 92 Until: from + 30, 93 }, 94 } 95 96 for _, tt := range tests { 97 tt := tt 98 testName := tt.Target 99 t.Run(testName, func(t *testing.T) { 100 eval := th.EvaluatorFromFunc(md[0].F) 101 th.TestEvalExprWithRange(t, eval, &tt) 102 }) 103 } 104 } 105 106 func nans(n int) []float64 { 107 res := make([]float64, n) 108 for i := range res { 109 res[i] = math.NaN() 110 } 111 return res 112 } 113 114 func rangeFloats(start, end, step float64) []float64 { 115 res := make([]float64, 0, int((end-start)/step)) 116 for i := start; i < end; i += step { 117 res = append(res, i) 118 } 119 return res 120 } 121 122 func BenchmarkExponentialMovingAverage(b *testing.B) { 123 target := "exponentialMovingAverage(metric1,3)" 124 metrics := map[parser.MetricRequest][]*types.MetricData{ 125 {Metric: "metric[1234]", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{2, 4, 6, 8, 12, 14, 16, 18, 20}, 1, 0)}, 126 } 127 128 eval := th.EvaluatorFromFunc(md[0].F) 129 exp, _, err := parser.ParseExpr(target) 130 if err != nil { 131 b.Fatalf("failed to parse %s: %+v", target, err) 132 } 133 134 b.ResetTimer() 135 for n := 0; n < b.N; n++ { 136 g, err := eval.Eval(context.Background(), exp, 0, 1, metrics) 137 if err != nil { 138 b.Fatalf("failed to eval %s: %+v", target, err) 139 } 140 _ = g 141 } 142 } 143 144 func BenchmarkExponentialMovingAverageStr(b *testing.B) { 145 target := "exponentialMovingAverage(metric1,'3s')" 146 metrics := map[parser.MetricRequest][]*types.MetricData{ 147 {Metric: "metric[1234]", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{2, 4, 6, 8, 12, 14, 16, 18, 20}, 1, 0)}, 148 } 149 150 eval := th.EvaluatorFromFunc(md[0].F) 151 exp, _, err := parser.ParseExpr(target) 152 if err != nil { 153 b.Fatalf("failed to parse %s: %+v", target, err) 154 } 155 156 b.ResetTimer() 157 for n := 0; n < b.N; n++ { 158 g, err := eval.Eval(context.Background(), exp, 0, 1, metrics) 159 if err != nil { 160 b.Fatalf("failed to eval %s: %+v", target, err) 161 } 162 _ = g 163 } 164 }