github.com/go-graphite/carbonapi@v0.17.0/expr/functions/aggregate/function_test.go (about)

     1  package aggregate
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"testing"
     7  	"time"
     8  
     9  	fconfig "github.com/go-graphite/carbonapi/expr/functions/config"
    10  	"github.com/go-graphite/carbonapi/expr/interfaces"
    11  	"github.com/go-graphite/carbonapi/expr/metadata"
    12  	"github.com/go-graphite/carbonapi/expr/types"
    13  	"github.com/go-graphite/carbonapi/pkg/parser"
    14  	th "github.com/go-graphite/carbonapi/tests"
    15  	"github.com/go-graphite/carbonapi/tests/compare"
    16  )
    17  
    18  var (
    19  	md []interfaces.FunctionMetadata = New("")
    20  )
    21  
    22  func init() {
    23  	for _, m := range md {
    24  		metadata.RegisterFunction(m.Name, m.F)
    25  	}
    26  }
    27  
    28  func TestAverageSeries(t *testing.T) {
    29  	fconfig.Config.ExtractTagsFromArgs = false
    30  
    31  	now32 := int64(time.Now().Unix())
    32  
    33  	tests := []th.EvalTestItem{
    34  		{
    35  			`aggregate(metric[123], "avg")`,
    36  			map[parser.MetricRequest][]*types.MetricData{
    37  				{"metric[123]", "", 0, 1}: {},
    38  			},
    39  			[]*types.MetricData{},
    40  		},
    41  		{
    42  			`aggregate(metric[123], "avg")`,
    43  			map[parser.MetricRequest][]*types.MetricData{
    44  				{"metric[123]", "", 0, 1}: {
    45  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
    46  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
    47  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
    48  				},
    49  			},
    50  			[]*types.MetricData{types.MakeMetricData("avgSeries(metric[123])",
    51  				[]float64{2, math.NaN(), 3, 4, 5, 5.5}, 1, now32).SetTag("aggregatedBy", "avg")},
    52  		},
    53  		{
    54  			`aggregate(metric[123], "avg_zero")`,
    55  			map[parser.MetricRequest][]*types.MetricData{
    56  				{"metric[123]", "", 0, 1}: {
    57  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 4, 4, 6}, 1, now32),
    58  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
    59  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
    60  				},
    61  			},
    62  			[]*types.MetricData{types.MakeMetricData("avg_zeroSeries(metric[123])",
    63  				[]float64{2, math.NaN(), 3, 3, 5, 4}, 1, now32).SetTag("aggregatedBy", "avg_zero")},
    64  		},
    65  		{
    66  			`aggregate(metric[123], "count")`,
    67  			map[parser.MetricRequest][]*types.MetricData{
    68  				{"metric[123]", "", 0, 1}: {
    69  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
    70  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
    71  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
    72  				},
    73  			},
    74  			[]*types.MetricData{types.MakeMetricData("countSeries(metric[123])",
    75  				[]float64{3, math.NaN(), 3, 2, 3, 2}, 1, now32).SetTag("aggregatedBy", "count")},
    76  		},
    77  		{
    78  			`aggregate(metric[123], "diff")`,
    79  			map[parser.MetricRequest][]*types.MetricData{
    80  				{"metric[123]", "", 0, 1}: {
    81  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
    82  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
    83  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
    84  				},
    85  			},
    86  			[]*types.MetricData{types.MakeMetricData("diffSeries(metric[123])",
    87  				[]float64{-4, math.NaN(), -5, -2, -7, -1}, 1, now32).SetTag("aggregatedBy", "diff")},
    88  		},
    89  		{
    90  			`aggregate(metric[123], "last")`,
    91  			map[parser.MetricRequest][]*types.MetricData{
    92  				{"metric[123]", "", 0, 1}: {
    93  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
    94  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
    95  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
    96  				},
    97  			},
    98  			[]*types.MetricData{types.MakeMetricData("lastSeries(metric[123])",
    99  				[]float64{3, math.NaN(), 4, 5, 6, 6}, 1, now32).SetTag("aggregatedBy", "last")},
   100  		},
   101  		{
   102  			`aggregate(metric[123], "current")`,
   103  			map[parser.MetricRequest][]*types.MetricData{
   104  				{"metric[123]", "", 0, 1}: {
   105  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
   106  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   107  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   108  				},
   109  			},
   110  			[]*types.MetricData{types.MakeMetricData("currentSeries(metric[123])",
   111  				[]float64{3, math.NaN(), 4, 5, 6, 6}, 1, now32).SetTag("aggregatedBy", "current")},
   112  		},
   113  		{
   114  			`aggregate(metric[123], "max")`,
   115  			map[parser.MetricRequest][]*types.MetricData{
   116  				{"metric[123]", "", 0, 1}: {
   117  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
   118  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   119  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   120  				},
   121  			},
   122  			[]*types.MetricData{types.MakeMetricData("maxSeries(metric[123])",
   123  				[]float64{3, math.NaN(), 4, 5, 6, 6}, 1, now32).SetTag("aggregatedBy", "max")},
   124  		},
   125  		{
   126  			`aggregate(metric[123], "min")`,
   127  			map[parser.MetricRequest][]*types.MetricData{
   128  				{"metric[123]", "", 0, 1}: {
   129  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   130  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   131  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   132  				},
   133  			},
   134  			[]*types.MetricData{types.MakeMetricData("minSeries(metric[123])",
   135  				[]float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "min")},
   136  		},
   137  		{
   138  			`aggregate(metric[123], "median")`,
   139  			map[parser.MetricRequest][]*types.MetricData{
   140  				{"metric[123]", "", 0, 1}: {
   141  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   142  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   143  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   144  				},
   145  			},
   146  			[]*types.MetricData{types.MakeMetricData("medianSeries(metric[123])",
   147  				[]float64{2, math.NaN(), 3, 4, 5, 5.5}, 1, now32).SetTag("aggregatedBy", "median")},
   148  		},
   149  		{
   150  			`aggregate(metric[123], "multiply")`,
   151  			map[parser.MetricRequest][]*types.MetricData{
   152  				{"metric[123]", "", 0, 1}: {
   153  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   154  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   155  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   156  				},
   157  			},
   158  			[]*types.MetricData{types.MakeMetricData("multiplySeries(metric[123])",
   159  				[]float64{6, math.NaN(), 24, math.NaN(), 120, math.NaN()}, 1, now32).SetTag("aggregatedBy", "multiply")},
   160  		},
   161  		{
   162  			`aggregate(metric[123], "range")`,
   163  			map[parser.MetricRequest][]*types.MetricData{
   164  				{"metric[123]", "", 0, 1}: {
   165  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   166  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   167  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   168  				},
   169  			},
   170  			[]*types.MetricData{types.MakeMetricData("rangeSeries(metric[123])",
   171  				[]float64{2, math.NaN(), 2, 2, 2, 1}, 1, now32).SetTag("aggregatedBy", "range")},
   172  		},
   173  		{
   174  			`aggregate(metric[123], "rangeOf")`,
   175  			map[parser.MetricRequest][]*types.MetricData{
   176  				{"metric[123]", "", 0, 1}: {
   177  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   178  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   179  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   180  				},
   181  			},
   182  			[]*types.MetricData{types.MakeMetricData("rangeOfSeries(metric[123])",
   183  				[]float64{2, math.NaN(), 2, 2, 2, 1}, 1, now32).SetTag("aggregatedBy", "rangeOf")},
   184  		},
   185  		{
   186  			`aggregate(metric[123], "sum")`,
   187  			map[parser.MetricRequest][]*types.MetricData{
   188  				{"metric[123]", "", 0, 1}: {
   189  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   190  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   191  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   192  				},
   193  			},
   194  			[]*types.MetricData{types.MakeMetricData("sumSeries(metric[123])",
   195  				[]float64{6, math.NaN(), 9, 8, 15, 11}, 1, now32).SetTag("aggregatedBy", "sum")},
   196  		},
   197  		{
   198  			`aggregate(metric[123], "total")`,
   199  			map[parser.MetricRequest][]*types.MetricData{
   200  				{"metric[123]", "", 0, 1}: {
   201  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   202  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   203  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   204  				},
   205  			},
   206  			[]*types.MetricData{types.MakeMetricData("totalSeries(metric[123])",
   207  				[]float64{6, math.NaN(), 9, 8, 15, 11}, 1, now32).SetTag("aggregatedBy", "total")},
   208  		},
   209  		{
   210  			`aggregate(metric[123], "avg", 0.7)`, // Test with xFilesFactor
   211  			map[parser.MetricRequest][]*types.MetricData{
   212  				{"metric[123]", "", 0, 1}: {
   213  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, math.NaN(), 4, 5}, 1, now32),
   214  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   215  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   216  				},
   217  			},
   218  			[]*types.MetricData{types.MakeMetricData("avgSeries(metric[123])",
   219  				[]float64{2, math.NaN(), 3, math.NaN(), 5, math.NaN()}, 1, now32).SetTag("aggregatedBy", "avg")},
   220  		},
   221  		{
   222  			`aggregate(metric[123], "sum", 0.5)`, // Test with xFilesFactor
   223  			map[parser.MetricRequest][]*types.MetricData{
   224  				{"metric[123]", "", 0, 1}: {
   225  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, math.NaN()}, 1, now32),
   226  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   227  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   228  				},
   229  			},
   230  			[]*types.MetricData{types.MakeMetricData("sumSeries(metric[123])",
   231  				[]float64{6, math.NaN(), 9, 8, 15, math.NaN()}, 1, now32).SetTag("aggregatedBy", "sum")},
   232  		},
   233  		{
   234  			`aggregate(metric[123], "max", 0.3)`,
   235  			map[parser.MetricRequest][]*types.MetricData{
   236  				{"metric[123]", "", 0, 1}: {
   237  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, math.NaN(), 4, 5}, 1, now32),
   238  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   239  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   240  				},
   241  			},
   242  			[]*types.MetricData{types.MakeMetricData("maxSeries(metric[123])",
   243  				[]float64{3, math.NaN(), 4, 5, 6, 6}, 1, now32).SetTag("aggregatedBy", "max")},
   244  		},
   245  		{
   246  			`stddevSeries(metric[123])`,
   247  			map[parser.MetricRequest][]*types.MetricData{
   248  				{"metric[123]", "", 0, 1}: {
   249  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   250  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   251  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   252  				},
   253  			},
   254  			[]*types.MetricData{types.MakeMetricData("stddevSeries(metric[123])",
   255  				[]float64{0.816496580927726, math.NaN(), 0.816496580927726, 1, 0.816496580927726, 0.5}, 1, now32).SetTag("aggregatedBy", "stddev")},
   256  		},
   257  		{
   258  			`stddevSeries(metric1,metric2,metric3)`,
   259  			map[parser.MetricRequest][]*types.MetricData{
   260  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 2, 3, 4, 5}, 1, now32)},
   261  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, 4, 6, 8, 10}, 1, now32)},
   262  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{1, 2, 3, 4, 5}, 1, now32)},
   263  			},
   264  			[]*types.MetricData{types.MakeMetricData("stddevSeries(metric1,metric2,metric3)",
   265  				[]float64{0.4714045207910317, 0.9428090415820634, 1.4142135623730951, 1.8856180831641267, 2.357022603955158}, 1, now32).SetTag("aggregatedBy", "stddev")},
   266  		},
   267  		{
   268  			`aggregate(metric[123], "stddev")`,
   269  			map[parser.MetricRequest][]*types.MetricData{
   270  				{Metric: "metric[123]", From: 0, Until: 1}: {
   271  					types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 6}, 1, now32),
   272  					types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 5}, 1, now32),
   273  					types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   274  				},
   275  			},
   276  			[]*types.MetricData{types.MakeMetricData("stddevSeries(metric[123])",
   277  				[]float64{0.816496580927726, math.NaN(), 0.816496580927726, 1, 0.816496580927726, 0.5}, 1, now32).SetTag("aggregatedBy", "stddev")},
   278  		},
   279  		{
   280  			`aggregate(metric[123], "stddev")`,
   281  			map[parser.MetricRequest][]*types.MetricData{
   282  				{Metric: "metric[123]", From: 0, Until: 1}: {
   283  					types.MakeMetricData("metric1", []float64{1, 2, 3, 4, 5}, 1, now32),
   284  					types.MakeMetricData("metric2", []float64{2, 4, 6, 8, 10}, 1, now32),
   285  					types.MakeMetricData("metric3", []float64{1, 2, 3, 4, 5}, 1, now32),
   286  				},
   287  			},
   288  			[]*types.MetricData{types.MakeMetricData("stddevSeries(metric[123])",
   289  				[]float64{0.4714045207910317, 0.9428090415820634, 1.4142135623730951, 1.8856180831641267, 2.357022603955158}, 1, now32).SetTag("aggregatedBy", "stddev")},
   290  		},
   291  
   292  		// sum
   293  		{
   294  			`sum(metric1,metric2)`,
   295  			map[parser.MetricRequest][]*types.MetricData{
   296  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{0, -1, 2, -3, 4, 5}, 1, now32)},
   297  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{0, 1, -2, 3, -4, -5}, 1, now32)},
   298  			},
   299  			[]*types.MetricData{types.MakeMetricData("sumSeries(metric1,metric2)",
   300  				[]float64{0, 0, 0, 0, 0, 0}, 1, now32).SetTag("aggregatedBy", "sum")},
   301  		},
   302  		{
   303  			"sum(metric1,metric2,metric3)",
   304  			map[parser.MetricRequest][]*types.MetricData{
   305  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 2, 3, 4, 5, math.NaN()}, 1, now32)},
   306  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, 3, math.NaN(), 5, 6, math.NaN()}, 1, now32)},
   307  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{3, 4, 5, 6, math.NaN(), math.NaN()}, 1, now32)},
   308  			},
   309  			[]*types.MetricData{types.MakeMetricData("sumSeries(metric1,metric2,metric3)", []float64{6, 9, 8, 15, 11, math.NaN()}, 1, now32).SetTag("aggregatedBy", "sum")},
   310  		},
   311  		{
   312  			"sum(metric1,metric2,metric3,metric4)",
   313  			map[parser.MetricRequest][]*types.MetricData{
   314  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 2, 3, 4, 5, math.NaN()}, 1, now32)},
   315  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, 3, math.NaN(), 5, 6, math.NaN()}, 1, now32)},
   316  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{3, 4, 5, 6, math.NaN(), math.NaN()}, 1, now32)},
   317  			},
   318  			[]*types.MetricData{types.MakeMetricData("sumSeries(metric1,metric2,metric3)", []float64{6, 9, 8, 15, 11, math.NaN()}, 1, now32).SetTag("aggregatedBy", "sum")},
   319  		},
   320  
   321  		// minMax
   322  		{
   323  			"maxSeries(metric1,metric2,metric3)",
   324  			map[parser.MetricRequest][]*types.MetricData{
   325  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32)},
   326  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32)},
   327  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32)},
   328  			},
   329  			[]*types.MetricData{types.MakeMetricData("maxSeries(metric1,metric2,metric3)",
   330  				[]float64{3, math.NaN(), 4, 5, 6, 6}, 1, now32).SetTag("aggregatedBy", "max")},
   331  		},
   332  		{
   333  			"minSeries(metric1,metric2,metric3)",
   334  			map[parser.MetricRequest][]*types.MetricData{
   335  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32)},
   336  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32)},
   337  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32)},
   338  			},
   339  			[]*types.MetricData{types.MakeMetricData("minSeries(metric1,metric2,metric3)",
   340  				[]float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "min")},
   341  		},
   342  
   343  		// avg
   344  		{
   345  			"averageSeries(metric1,metric2,metric3)",
   346  			map[parser.MetricRequest][]*types.MetricData{
   347  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32)},
   348  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32)},
   349  				{Metric: "metric3", From: 0, Until: 1}: {types.MakeMetricData("metric3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32)},
   350  			},
   351  			[]*types.MetricData{types.MakeMetricData("averageSeries(metric1,metric2,metric3)",
   352  				[]float64{2, math.NaN(), 3, 4, 5, 5.5}, 1, now32).SetTag("aggregatedBy", "average")},
   353  		},
   354  
   355  		// sumSeies with tags, common tags to all metrics
   356  		{
   357  			"sum(seriesByTag('tag2=value*', 'name=metric'))",
   358  			map[parser.MetricRequest][]*types.MetricData{
   359  				{Metric: "seriesByTag('tag2=value*', 'name=metric')", From: 0, Until: 1}: {
   360  					// No tags in common
   361  					types.MakeMetricData("metric;tag1=value1;tag2=value21", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
   362  					types.MakeMetricData("metric;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   363  					types.MakeMetricData("metric;tag2=value23;tag3=value3;tag4=val4", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   364  				},
   365  				{Metric: "metric", From: 0, Until: 1}: {types.MakeMetricData("metric", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   366  			},
   367  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2=value*', 'name=metric'))",
   368  				[]float64{6, math.NaN(), 9, 8, 15, 11}, 1, now32).SetTags(map[string]string{"aggregatedBy": "sum", "name": "metric"})},
   369  		},
   370  		{
   371  			"sum(seriesByTag('tag2!=value2*', 'name=metric.name'))",
   372  			map[parser.MetricRequest][]*types.MetricData{
   373  				{Metric: "seriesByTag('tag2!=value2*', 'name=metric.name')", From: 0, Until: 1}: {
   374  					// One tag in common
   375  					types.MakeMetricData("metric.name;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   376  					types.MakeMetricData("metric.name;tag2=value23;tag3=value3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   377  				},
   378  				{Metric: "metric.name", From: 0, Until: 1}: {types.MakeMetricData("metric.name", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   379  			},
   380  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2!=value2*', 'name=metric.name'))",
   381  				// []float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"name": "metric.name"})},
   382  				[]float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"aggregatedBy": "sum", "name": "metric.name", "tag3": "value3"})},
   383  		},
   384  		{
   385  			"sum(seriesByTag('tag2=value21'))",
   386  			map[parser.MetricRequest][]*types.MetricData{
   387  				{Metric: "seriesByTag('tag2=value21')", From: 0, Until: 1}: {
   388  					// All tags in common
   389  					types.MakeMetricData("metric;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   390  					types.MakeMetricData("metric;tag2=value22;tag3=value3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   391  				},
   392  				{Metric: "metric", From: 0, Until: 1}: {types.MakeMetricData("metric", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   393  			},
   394  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2=value21'))",
   395  				// []float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"name": "sumSeries", "tag2": "value21"})},
   396  				[]float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"aggregatedBy": "sum", "name": "metric", "tag2": "value22", "tag3": "value3"})},
   397  		},
   398  	}
   399  
   400  	for _, tt := range tests {
   401  		testName := tt.Target
   402  		t.Run(testName, func(t *testing.T) {
   403  			eval := th.EvaluatorFromFunc(md[0].F)
   404  			th.TestEvalExpr(t, eval, &tt)
   405  		})
   406  	}
   407  
   408  }
   409  
   410  func TestAverageSeriesExtractSeriesByTag(t *testing.T) {
   411  	fconfig.Config.ExtractTagsFromArgs = true
   412  
   413  	now32 := int64(time.Now().Unix())
   414  
   415  	tests := []th.EvalTestItem{
   416  		// sumSeies with tags, tags from first metric
   417  		{
   418  			"sum(seriesByTag('tag2=value*', 'name=metric'))",
   419  			map[parser.MetricRequest][]*types.MetricData{
   420  				{Metric: "seriesByTag('tag2=value*', 'name=metric')", From: 0, Until: 1}: {
   421  					types.MakeMetricData("metric;tag1=value1;tag2=value21", []float64{1, math.NaN(), 2, 3, 4, 5}, 1, now32),
   422  					types.MakeMetricData("metric;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   423  					types.MakeMetricData("metric;tag2=value23;tag3=value3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   424  				},
   425  				{Metric: "metric", From: 0, Until: 1}: {types.MakeMetricData("metric", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   426  			},
   427  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2=value*', 'name=metric'))",
   428  				[]float64{6, math.NaN(), 9, 8, 15, 11}, 1, now32).SetTags(map[string]string{"name": "metric", "tag2": "value*", "aggregatedBy": "sum"})},
   429  		},
   430  		{
   431  			"sum(seriesByTag('tag2!=value2*', 'name=metric.name'))",
   432  			map[parser.MetricRequest][]*types.MetricData{
   433  				{Metric: "seriesByTag('tag2!=value2*', 'name=metric.name')", From: 0, Until: 1}: {
   434  					types.MakeMetricData("metric.name;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   435  					types.MakeMetricData("metric.name;tag2=value23;tag3=value3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   436  				},
   437  				{Metric: "metric.name", From: 0, Until: 1}: {types.MakeMetricData("metric.name", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   438  			},
   439  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2!=value2*', 'name=metric.name'))",
   440  				[]float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"name": "metric.name", "aggregatedBy": "sum"})},
   441  		},
   442  		{
   443  			"sum(seriesByTag('tag2=value21'))",
   444  			map[parser.MetricRequest][]*types.MetricData{
   445  				{Metric: "seriesByTag('tag2=value21')", From: 0, Until: 1}: {
   446  					types.MakeMetricData("metric;tag2=value22;tag3=value3", []float64{2, math.NaN(), 3, math.NaN(), 5, 6}, 1, now32),
   447  					types.MakeMetricData("metric;tag2=value23;tag3=value3", []float64{3, math.NaN(), 4, 5, 6, math.NaN()}, 1, now32),
   448  				},
   449  				{Metric: "metric", From: 0, Until: 1}: {types.MakeMetricData("metric", []float64{2, math.NaN(), 3, math.NaN(), 5, 11}, 1, now32)},
   450  			},
   451  			[]*types.MetricData{types.MakeMetricData("sumSeries(seriesByTag('tag2=value21'))",
   452  				[]float64{5, math.NaN(), 7, 5, 11, 6}, 1, now32).SetTags(map[string]string{"name": "sumSeries", "tag2": "value21", "aggregatedBy": "sum"})},
   453  		},
   454  	}
   455  
   456  	for _, tt := range tests {
   457  		testName := tt.Target
   458  		t.Run(testName, func(t *testing.T) {
   459  			eval := th.EvaluatorFromFunc(md[0].F)
   460  			th.TestEvalExpr(t, eval, &tt)
   461  		})
   462  	}
   463  
   464  }
   465  
   466  func TestAverageSeriesAlign(t *testing.T) {
   467  	tests := []th.EvalTestItem{
   468  		// Indx      |   0  |   1  |   2  |   3  |   4  |
   469  		// commonStep  2
   470  		// Start  0 (1 - 1 % 2)
   471  		// metric1_2 |      |   1  |   3  |   5  |      |
   472  		// metric1_2 |   1  |      |   4  |      |      |
   473  		//
   474  		// metric2_1 |      |   1  |      |   5  |      |
   475  		// metric2_1 |   1  |      |   5  |      |      |
   476  
   477  		// sum       |   2  |      |   9  |      |      |
   478  		{
   479  			// timeseries with different length
   480  			Target: "sum(metric1_2,metric2_1)",
   481  			M: map[parser.MetricRequest][]*types.MetricData{
   482  				{Metric: "metric1_2", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 3, 5}, 1, 1)},
   483  				{Metric: "metric2_1", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{1, 5}, 2, 1)},
   484  			},
   485  			Want: []*types.MetricData{types.MakeMetricData("sumSeries(metric1_2,metric2_1)",
   486  				[]float64{2, 9}, 2, 0).SetTag("aggregatedBy", "sum")},
   487  		},
   488  		{
   489  			// First timeseries with broker StopTime
   490  			Target: "sum(metric1,metric2)",
   491  			M: map[parser.MetricRequest][]*types.MetricData{
   492  				{Metric: "metric1", From: 0, Until: 1}: {types.MakeMetricData("metric1", []float64{1, 3, 5, 8}, 1, 1).AppendStopTime(-1)},
   493  				{Metric: "metric2", From: 0, Until: 1}: {types.MakeMetricData("metric2", []float64{1, 5, 7}, 1, 1)},
   494  			},
   495  			Want: []*types.MetricData{types.MakeMetricData("sumSeries(metric1,metric2)",
   496  				[]float64{2, 8, 12, 8}, 1, 1).SetTag("aggregatedBy", "sum")},
   497  		},
   498  	}
   499  
   500  	for _, tt := range tests {
   501  		testName := tt.Target
   502  		t.Run(testName, func(t *testing.T) {
   503  			eval := th.EvaluatorFromFunc(md[0].F)
   504  			th.TestEvalExprResult(t, eval, &tt)
   505  		})
   506  	}
   507  
   508  }
   509  
   510  func BenchmarkAverageSeries(b *testing.B) {
   511  	target := "sum(metric*)"
   512  	metrics := map[parser.MetricRequest][]*types.MetricData{
   513  		{Metric: "metric*", From: 0, Until: 1}: {
   514  			types.MakeMetricData("metric2", compare.GenerateMetrics(2046, 1, 10, 1), 2, 1),
   515  			types.MakeMetricData("metric1", compare.GenerateMetrics(4096, 1, 10, 1), 1, 1),
   516  			types.MakeMetricData("metric3", compare.GenerateMetrics(1360, 1, 10, 1), 3, 1),
   517  		},
   518  	}
   519  
   520  	eval := th.EvaluatorFromFunc(md[0].F)
   521  	exp, _, err := parser.ParseExpr(target)
   522  	if err != nil {
   523  		b.Fatalf("failed to parse %s: %+v", target, err)
   524  	}
   525  
   526  	b.ResetTimer()
   527  	for n := 0; n < b.N; n++ {
   528  		g, err := eval.Eval(context.Background(), exp, 0, 1, metrics)
   529  		if err != nil {
   530  			b.Fatalf("failed to eval %s: %+v", target, err)
   531  		}
   532  		_ = g
   533  	}
   534  }