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  }