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

     1  package sortByName
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     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 TestSortByName(t *testing.T) {
    26  	now32 := int64(time.Now().Unix())
    27  
    28  	tests := []th.EvalTestItem{
    29  		{
    30  			"sortByName(metric*)",
    31  			map[parser.MetricRequest][]*types.MetricData{
    32  				{Metric: "metric*", From: 0, Until: 1}: {
    33  					types.MakeMetricData("metricX", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    34  					types.MakeMetricData("metricA", []float64{0, 1, 0, 0, 0, 0}, 1, now32),
    35  					types.MakeMetricData("metricB", []float64{0, 0, 2, 0, 0, 0}, 1, now32),
    36  					types.MakeMetricData("metricC", []float64{0, 0, 0, 3, 0, 0}, 1, now32),
    37  				},
    38  			},
    39  			[]*types.MetricData{
    40  				types.MakeMetricData("metricA", []float64{0, 1, 0, 0, 0, 0}, 1, now32),
    41  				types.MakeMetricData("metricB", []float64{0, 0, 2, 0, 0, 0}, 1, now32),
    42  				types.MakeMetricData("metricC", []float64{0, 0, 0, 3, 0, 0}, 1, now32),
    43  				types.MakeMetricData("metricX", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    44  			},
    45  		},
    46  		{
    47  			"sortByName(metric*,natural=true)",
    48  			map[parser.MetricRequest][]*types.MetricData{
    49  				{Metric: "metric*", From: 0, Until: 1}: {
    50  					types.MakeMetricData("metric1", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    51  					types.MakeMetricData("metric12", []float64{0, 1, 0, 0, 0, 0}, 1, now32),
    52  					types.MakeMetricData("metric1234567890", []float64{0, 0, 0, 5, 0, 0}, 1, now32),
    53  					types.MakeMetricData("metric2", []float64{0, 0, 2, 0, 0, 0}, 1, now32),
    54  					types.MakeMetricData("metric11", []float64{0, 0, 0, 3, 0, 0}, 1, now32),
    55  					types.MakeMetricData("metric", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    56  				},
    57  			},
    58  			[]*types.MetricData{
    59  				types.MakeMetricData("metric", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    60  				types.MakeMetricData("metric1", []float64{0, 0, 0, 0, 0, 0}, 1, now32),
    61  				types.MakeMetricData("metric2", []float64{0, 0, 2, 0, 0, 0}, 1, now32),
    62  				types.MakeMetricData("metric11", []float64{0, 0, 0, 3, 0, 0}, 1, now32),
    63  				types.MakeMetricData("metric12", []float64{0, 1, 0, 0, 0, 0}, 1, now32),
    64  				types.MakeMetricData("metric1234567890", []float64{0, 0, 0, 5, 0, 0}, 1, now32),
    65  			},
    66  		},
    67  		{
    68  			"sortByName(metric.foo.*)",
    69  			map[parser.MetricRequest][]*types.MetricData{
    70  				parser.MetricRequest{
    71  					Metric: "metric.foo.*",
    72  					From:   0,
    73  					Until:  1,
    74  				}: {
    75  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
    76  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
    77  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
    78  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
    79  				},
    80  			},
    81  			[]*types.MetricData{ // 100 is placed between 1 and 2 because it is alphabetical sort
    82  				types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
    83  				types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
    84  				types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
    85  				types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
    86  			},
    87  		},
    88  		{
    89  			"sortByName(metric.foo.*, true)",
    90  			map[parser.MetricRequest][]*types.MetricData{
    91  				parser.MetricRequest{
    92  					Metric: "metric.foo.*",
    93  					From:   0,
    94  					Until:  1,
    95  				}: {
    96  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
    97  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
    98  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
    99  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   100  				},
   101  			},
   102  			[]*types.MetricData{ // "natural" sort method considers that metrics contain numbers
   103  				types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
   104  				types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
   105  				types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
   106  				types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   107  			},
   108  		},
   109  		{
   110  			"sortByName(metric.foo.*, natural=false, reverse=true)",
   111  			map[parser.MetricRequest][]*types.MetricData{
   112  				parser.MetricRequest{
   113  					Metric: "metric.foo.*",
   114  					From:   0,
   115  					Until:  1,
   116  				}: {
   117  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
   118  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
   119  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
   120  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   121  				},
   122  			},
   123  			[]*types.MetricData{ // alphabetical reverse sort
   124  				types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
   125  				types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
   126  				types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   127  				types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
   128  			},
   129  		},
   130  		{
   131  			"sortByName(metric.foo.*, true, true)",
   132  			map[parser.MetricRequest][]*types.MetricData{
   133  				parser.MetricRequest{
   134  					Metric: "metric.foo.*",
   135  					From:   0,
   136  					Until:  1,
   137  				}: {
   138  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
   139  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
   140  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
   141  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   142  				},
   143  			},
   144  			[]*types.MetricData{ // "natural" reverse sort
   145  				types.MakeMetricData("metric.foo.x100", []float64{1}, 1, now32),
   146  				types.MakeMetricData("metric.foo.x99", []float64{1}, 1, now32),
   147  				types.MakeMetricData("metric.foo.x2", []float64{1}, 1, now32),
   148  				types.MakeMetricData("metric.foo.x1", []float64{1}, 1, now32),
   149  			},
   150  		},
   151  	}
   152  
   153  	for _, tt := range tests {
   154  		testName := tt.Target
   155  		t.Run(testName, func(t *testing.T) {
   156  			eval := th.EvaluatorFromFunc(md[0].F)
   157  			th.TestEvalExpr(t, eval, &tt)
   158  		})
   159  	}
   160  
   161  }
   162  
   163  func BenchmarkSortByName(b *testing.B) {
   164  	benchmarks := []struct {
   165  		target string
   166  		M      map[parser.MetricRequest][]*types.MetricData
   167  	}{
   168  		{
   169  			target: "sortByName(metric.foo.*)",
   170  			M: map[parser.MetricRequest][]*types.MetricData{
   171  				parser.MetricRequest{
   172  					Metric: "metric.foo.*",
   173  					From:   0,
   174  					Until:  1,
   175  				}: {
   176  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, 1),
   177  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, 1),
   178  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, 1),
   179  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, 1),
   180  				},
   181  			},
   182  		},
   183  		{
   184  			target: "sortByName(metric.foo.*, true)",
   185  			M: map[parser.MetricRequest][]*types.MetricData{
   186  				parser.MetricRequest{
   187  					Metric: "metric.foo.*",
   188  					From:   0,
   189  					Until:  1,
   190  				}: {
   191  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, 1),
   192  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, 1),
   193  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, 1),
   194  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, 1),
   195  				},
   196  			},
   197  		},
   198  		{
   199  			target: "sortByName(metric.foo.*, natural=false, reverse=true)",
   200  			M: map[parser.MetricRequest][]*types.MetricData{
   201  				parser.MetricRequest{
   202  					Metric: "metric.foo.*",
   203  					From:   0,
   204  					Until:  1,
   205  				}: {
   206  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, 1),
   207  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, 1),
   208  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, 1),
   209  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, 1),
   210  				},
   211  			},
   212  		},
   213  		{
   214  			target: "sortByName(metric.foo.*, true, true)",
   215  			M: map[parser.MetricRequest][]*types.MetricData{
   216  				parser.MetricRequest{
   217  					Metric: "metric.foo.*",
   218  					From:   0,
   219  					Until:  1,
   220  				}: {
   221  					types.MakeMetricData("metric.foo.x99", []float64{1}, 1, 1),
   222  					types.MakeMetricData("metric.foo.x1", []float64{1}, 1, 1),
   223  					types.MakeMetricData("metric.foo.x2", []float64{1}, 1, 1),
   224  					types.MakeMetricData("metric.foo.x100", []float64{1}, 1, 1),
   225  				},
   226  			},
   227  		},
   228  	}
   229  
   230  	eval := th.EvaluatorFromFunc(md[0].F)
   231  
   232  	for _, bm := range benchmarks {
   233  		b.Run(bm.target, func(b *testing.B) {
   234  			exp, _, err := parser.ParseExpr(bm.target)
   235  			if err != nil {
   236  				b.Fatalf("failed to parse %s: %+v", bm.target, err)
   237  			}
   238  
   239  			b.ResetTimer()
   240  
   241  			for i := 0; i < b.N; i++ {
   242  				g, err := eval.Eval(context.Background(), exp, 0, 1, bm.M)
   243  				if err != nil {
   244  					b.Fatalf("failed to eval %s: %+v", bm.target, err)
   245  				}
   246  				_ = g
   247  			}
   248  		})
   249  	}
   250  }