github.com/go-graphite/carbonapi@v0.17.0/tests/helper.go (about)

     1  package tests
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ansel1/merry"
    10  	zipperTypes "github.com/go-graphite/carbonapi/zipper/types"
    11  	pb "github.com/go-graphite/protocol/carbonapi_v3_pb"
    12  
    13  	"github.com/go-graphite/carbonapi/expr/helper"
    14  	"github.com/go-graphite/carbonapi/expr/interfaces"
    15  	"github.com/go-graphite/carbonapi/expr/metadata"
    16  	"github.com/go-graphite/carbonapi/expr/types"
    17  	"github.com/go-graphite/carbonapi/pkg/parser"
    18  	"github.com/go-graphite/carbonapi/tests/compare"
    19  )
    20  
    21  type FuncEvaluator struct {
    22  	eval func(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error)
    23  }
    24  
    25  func (evaluator *FuncEvaluator) Fetch(_ context.Context, _ []parser.Expr, _, _ int64, values map[parser.MetricRequest][]*types.MetricData) (map[parser.MetricRequest][]*types.MetricData, error) {
    26  	return values, nil
    27  }
    28  
    29  func (evaluator *FuncEvaluator) Eval(ctx context.Context, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    30  	if e.IsName() {
    31  		return values[parser.MetricRequest{Metric: e.Target(), From: from, Until: until}], nil
    32  	} else if e.IsConst() {
    33  		p := types.MetricData{
    34  			FetchResponse: pb.FetchResponse{
    35  				Name:   e.Target(),
    36  				Values: []float64{e.FloatValue()},
    37  			},
    38  			Tags: map[string]string{"name": e.Target()},
    39  		}
    40  		return []*types.MetricData{&p}, nil
    41  	}
    42  	// evaluate the function
    43  
    44  	// all functions have arguments -- check we do too
    45  	if e.ArgsLen() == 0 {
    46  		return nil, parser.ErrMissingArgument
    47  	}
    48  
    49  	if evaluator.eval != nil {
    50  		return evaluator.eval(context.Background(), evaluator, e, from, until, values)
    51  	}
    52  
    53  	return nil, helper.ErrUnknownFunction(e.Target())
    54  }
    55  
    56  func DummyEvaluator() interfaces.Evaluator {
    57  	e := &FuncEvaluator{
    58  		eval: nil,
    59  	}
    60  
    61  	return e
    62  }
    63  
    64  func EvaluatorFromFunc(function interfaces.Function) interfaces.Evaluator {
    65  	e := &FuncEvaluator{
    66  		eval: function.Do,
    67  	}
    68  
    69  	return e
    70  }
    71  
    72  func EvaluatorFromFuncWithMetadata(metadata map[string]interfaces.Function) interfaces.Evaluator {
    73  	e := &FuncEvaluator{
    74  		eval: func(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    75  			if f, ok := metadata[e.Target()]; ok {
    76  				return f.Do(context.Background(), eval, e, from, until, values)
    77  			}
    78  			return nil, fmt.Errorf("unknown function: %v", e.Target())
    79  		},
    80  	}
    81  	return e
    82  }
    83  
    84  func DeepClone(original map[parser.MetricRequest][]*types.MetricData) map[parser.MetricRequest][]*types.MetricData {
    85  	clone := map[parser.MetricRequest][]*types.MetricData{}
    86  	for key, originalMetrics := range original {
    87  		copiedMetrics := make([]*types.MetricData, 0, len(originalMetrics))
    88  		for _, originalMetric := range originalMetrics {
    89  			copiedMetric := types.MetricData{
    90  				FetchResponse: pb.FetchResponse{
    91  					Name:                    originalMetric.Name,
    92  					StartTime:               originalMetric.StartTime,
    93  					StopTime:                originalMetric.StopTime,
    94  					StepTime:                originalMetric.StepTime,
    95  					Values:                  make([]float64, len(originalMetric.Values)),
    96  					PathExpression:          originalMetric.PathExpression,
    97  					ConsolidationFunc:       originalMetric.ConsolidationFunc,
    98  					XFilesFactor:            originalMetric.XFilesFactor,
    99  					HighPrecisionTimestamps: originalMetric.HighPrecisionTimestamps,
   100  					AppliedFunctions:        make([]string, len(originalMetric.AppliedFunctions)),
   101  					RequestStartTime:        originalMetric.RequestStartTime,
   102  					RequestStopTime:         originalMetric.RequestStopTime,
   103  				},
   104  				GraphOptions:      originalMetric.GraphOptions,
   105  				ValuesPerPoint:    originalMetric.ValuesPerPoint,
   106  				Tags:              make(map[string]string),
   107  				AggregateFunction: originalMetric.AggregateFunction,
   108  			}
   109  
   110  			copy(copiedMetric.Values, originalMetric.Values)
   111  			copy(copiedMetric.AppliedFunctions, originalMetric.AppliedFunctions)
   112  			copiedMetrics = append(copiedMetrics, &copiedMetric)
   113  			for k, v := range originalMetric.Tags {
   114  				copiedMetric.Tags[k] = v
   115  			}
   116  		}
   117  
   118  		clone[key] = copiedMetrics
   119  	}
   120  
   121  	return clone
   122  }
   123  
   124  func DeepEqual(t *testing.T, target string, original, modified map[parser.MetricRequest][]*types.MetricData, compareTags bool) {
   125  	for key := range original {
   126  		if len(original[key]) == len(modified[key]) {
   127  			for i := range original[key] {
   128  				if !compare.MetricDataIsEqual(original[key][i], modified[key][i], compareTags) {
   129  					t.Errorf(
   130  						"%s: source data was modified key %v index %v original:\n%v\n modified:\n%v",
   131  						target,
   132  						key,
   133  						i,
   134  						original[key][i],
   135  						modified[key][i],
   136  					)
   137  				}
   138  			}
   139  		} else {
   140  			t.Errorf(
   141  				"%s: source data was modified key %v original length %d, new length %d",
   142  				target,
   143  				key,
   144  				len(original[key]),
   145  				len(modified[key]),
   146  			)
   147  		}
   148  	}
   149  }
   150  
   151  type SummarizeEvalTestItem struct {
   152  	Target string
   153  	M      map[parser.MetricRequest][]*types.MetricData
   154  	Want   []float64
   155  	From   int64
   156  	Until  int64
   157  	Name   string
   158  	Step   int64
   159  	Start  int64
   160  	Stop   int64
   161  }
   162  
   163  func InitTestSummarize() (int64, int64, int64) {
   164  	t0, err := time.Parse(time.UnixDate, "Wed Sep 10 10:32:00 CEST 2014")
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  
   169  	tenThirtyTwo := t0.Unix()
   170  
   171  	t0, err = time.Parse(time.UnixDate, "Wed Sep 10 10:59:00 CEST 2014")
   172  	if err != nil {
   173  		panic(err)
   174  	}
   175  
   176  	tenFiftyNine := t0.Unix()
   177  
   178  	t0, err = time.Parse(time.UnixDate, "Wed Sep 10 10:30:00 CEST 2014")
   179  	if err != nil {
   180  		panic(err)
   181  	}
   182  
   183  	tenThirty := t0.Unix()
   184  
   185  	return tenThirtyTwo, tenFiftyNine, tenThirty
   186  }
   187  
   188  func TestSummarizeEvalExpr(t *testing.T, eval interfaces.Evaluator, tt *SummarizeEvalTestItem) {
   189  	t.Run(tt.Name, func(t *testing.T) {
   190  		originalMetrics := DeepClone(tt.M)
   191  		exp, _, _ := parser.ParseExpr(tt.Target)
   192  		g, err := eval.Eval(context.Background(), exp, tt.From, tt.Until, tt.M)
   193  		if err != nil {
   194  			t.Errorf("failed to eval %v: %+v", tt.Name, err)
   195  			return
   196  		}
   197  		DeepEqual(t, g[0].Name, originalMetrics, tt.M, false)
   198  		if g[0].StepTime != tt.Step {
   199  			t.Errorf("bad Step for %s:\ngot  %d\nwant %d", g[0].Name, g[0].StepTime, tt.Step)
   200  		}
   201  		if g[0].StartTime != tt.Start {
   202  			t.Errorf("bad Start for %s: got %s want %s", g[0].Name, time.Unix(g[0].StartTime, 0).Format(time.StampNano), time.Unix(tt.Start, 0).Format(time.StampNano))
   203  		}
   204  		if g[0].StopTime != tt.Stop {
   205  			t.Errorf("bad Stop for %s: got %s want %s", g[0].Name, time.Unix(g[0].StopTime, 0).Format(time.StampNano), time.Unix(tt.Stop, 0).Format(time.StampNano))
   206  		}
   207  
   208  		if !compare.NearlyEqual(g[0].Values, tt.Want) {
   209  			t.Errorf("failed: %s:\ngot  %+v,\nwant %+v", g[0].Name, g[0].Values, tt.Want)
   210  		}
   211  		if g[0].Name != tt.Name {
   212  			t.Errorf("bad Name for %+v: got %v, want %v", g, g[0].Name, tt.Name)
   213  		}
   214  		if _, ok := g[0].Tags["name"]; !ok {
   215  			t.Errorf("metric with name %v doesn't contain 'name' tag", g[0].Name)
   216  		}
   217  	})
   218  }
   219  
   220  type MultiReturnEvalTestItem struct {
   221  	Target  string
   222  	M       map[parser.MetricRequest][]*types.MetricData
   223  	Name    string
   224  	Results map[string][]*types.MetricData
   225  }
   226  
   227  func TestMultiReturnEvalExpr(t *testing.T, eval interfaces.Evaluator, tt *MultiReturnEvalTestItem) {
   228  	originalMetrics := DeepClone(tt.M)
   229  	exp, _, err := parser.ParseExpr(tt.Target)
   230  	if err != nil {
   231  		t.Errorf("failed to parse %v: %+v", tt.Target, err)
   232  		return
   233  	}
   234  	g, err := eval.Eval(context.Background(), exp, 0, 1, tt.M)
   235  	if err != nil {
   236  		t.Errorf("failed to eval %v: %+v", tt.Name, err)
   237  		return
   238  	}
   239  	DeepEqual(t, tt.Name, originalMetrics, tt.M, true)
   240  	if len(g) == 0 {
   241  		t.Errorf("returned no data %v", tt.Name)
   242  		return
   243  	}
   244  	if g[0] == nil {
   245  		t.Errorf("returned no value %v", tt.Name)
   246  		return
   247  	}
   248  	if g[0].StepTime == 0 {
   249  		t.Errorf("missing Step for %+v", g)
   250  	}
   251  	if len(g) != len(tt.Results) {
   252  		t.Errorf("unexpected results len: got %d, want %d for %s", len(g), len(tt.Results), tt.Target)
   253  	}
   254  	for i, actual := range g {
   255  		if actual == nil {
   256  			t.Errorf("result[%d] mismatch, got nil", i)
   257  			continue
   258  		}
   259  		wants, ok := tt.Results[actual.Name]
   260  		if !ok {
   261  			t.Errorf("missing result Name: %v", actual.Name)
   262  			continue
   263  		}
   264  
   265  		if wants[0].Name != actual.Name {
   266  			t.Errorf("result Name mismatch, got\n%#v,\nwant\n%#v", actual.Name, wants[0].Name)
   267  		}
   268  
   269  		for k, v := range wants[0].Tags {
   270  			if aTag, ok := actual.Tags[k]; ok {
   271  				if aTag != v {
   272  					t.Errorf("metric %+v with name '%s' tag['%s'] value '%s' not equal '%s'", actual, actual.Name, k, aTag, v)
   273  				}
   274  			} else {
   275  				t.Errorf("metric %+v with name %v doesn't contain '%s' tag", actual, actual.Name, k)
   276  			}
   277  		}
   278  
   279  		for k := range actual.Tags {
   280  			if _, ok := wants[0].Tags[k]; !ok {
   281  				t.Errorf("metric %+v with name %v contain unwanted '%s' tag", actual, actual.Name, k)
   282  			}
   283  		}
   284  
   285  		if !compare.NearlyEqual(wants[0].Values, actual.Values) ||
   286  			wants[0].StartTime != actual.StartTime ||
   287  			wants[0].StopTime != actual.StopTime ||
   288  			wants[0].StepTime != actual.StepTime {
   289  			t.Errorf("result mismatch, got\n%#v,\nwant\n%#v", actual, wants)
   290  		}
   291  	}
   292  }
   293  
   294  type RewriteTestResult struct {
   295  	Rewritten bool
   296  	Targets   []string
   297  	Err       error
   298  }
   299  
   300  type RewriteTestItem struct {
   301  	//E    parser.Expr
   302  	Target string
   303  	M      map[parser.MetricRequest][]*types.MetricData
   304  	Want   RewriteTestResult
   305  }
   306  
   307  type RewriteTestError struct {
   308  	//E    parser.Expr
   309  	Target string
   310  	M      map[parser.MetricRequest][]*types.MetricData
   311  	Want   error
   312  }
   313  
   314  func rewriteExpr(e parser.Expr, eval interfaces.Evaluator, from, until int64, values map[parser.MetricRequest][]*types.MetricData) (bool, []string, error) {
   315  	if e.IsFunc() {
   316  		metadata.FunctionMD.RLock()
   317  		f, ok := metadata.FunctionMD.RewriteFunctions[e.Target()]
   318  		metadata.FunctionMD.RUnlock()
   319  		if ok {
   320  			return f.Do(context.Background(), eval, e, from, until, values)
   321  		}
   322  	}
   323  	return false, nil, nil
   324  }
   325  
   326  func TestRewriteExpr(t *testing.T, eval interfaces.Evaluator, tt *RewriteTestItem) {
   327  	originalMetrics := DeepClone(tt.M)
   328  	testName := tt.Target
   329  	exp, _, err := parser.ParseExpr(tt.Target)
   330  	if err != nil {
   331  		t.Fatalf("failed to parse %s: %+v", tt.Target, err)
   332  	}
   333  
   334  	rewritten, targets, err := rewriteExpr(exp, eval, 0, 1, tt.M)
   335  	if err != tt.Want.Err {
   336  		if err == nil || tt.Want.Err == nil || !merry.Is(err, tt.Want.Err) {
   337  			t.Fatalf("unexpected error while calling rewrite for '%s': got '%+v', expected '%+v'", testName, err, tt.Want.Err)
   338  		}
   339  	}
   340  	if rewritten != tt.Want.Rewritten {
   341  		t.Fatalf("unexpected result for rewritten for '%s': got '%v', expected '%v'", testName, rewritten, tt.Want.Rewritten)
   342  		return
   343  	}
   344  
   345  	if len(targets) != len(tt.Want.Targets) {
   346  		t.Fatalf("%s returned a different number of metrics, actual %v, Want %v", testName, len(targets), len(tt.Want.Targets))
   347  	}
   348  	DeepEqual(t, testName, originalMetrics, tt.M, false)
   349  
   350  	for i, want := range tt.Want.Targets {
   351  		if want != targets[i] {
   352  			t.Errorf("unexpected result for rewrite for '%s': got='%s', expected='%s'", testName, targets[i], want)
   353  		}
   354  	}
   355  }
   356  
   357  type EvalTestItem struct {
   358  	//E    parser.Expr
   359  	Target string
   360  	M      map[parser.MetricRequest][]*types.MetricData
   361  	Want   []*types.MetricData
   362  }
   363  
   364  type EvalTestItemWithCustomValidation struct {
   365  	Target    string
   366  	M         map[parser.MetricRequest][]*types.MetricData
   367  	Validator func(*testing.T, []*types.MetricData)
   368  	From      int64
   369  	Until     int64
   370  }
   371  
   372  type EvalTestItemWithError struct {
   373  	Target string
   374  	M      map[parser.MetricRequest][]*types.MetricData
   375  	Want   []*types.MetricData
   376  	Error  error
   377  }
   378  
   379  type EvalTestItemWithRange struct {
   380  	Target string
   381  	M      map[parser.MetricRequest][]*types.MetricData
   382  	Want   []*types.MetricData
   383  	From   int64
   384  	Until  int64
   385  }
   386  
   387  func (r *EvalTestItemWithRange) TestItem() *EvalTestItem {
   388  	return &EvalTestItem{
   389  		Target: r.Target,
   390  		M:      r.M,
   391  		Want:   r.Want,
   392  	}
   393  }
   394  
   395  func TestEvalExprWithCustomValidation(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItemWithCustomValidation) {
   396  	exp, _, err := parser.ParseExpr(tt.Target)
   397  	if err != nil {
   398  		t.Errorf("failed to parse %s: %+v", tt.Target, err)
   399  	}
   400  	g, err := eval.Eval(context.Background(), exp, tt.From, tt.Until, tt.M)
   401  	if err != nil {
   402  		t.Errorf("failed to eval %s: %+v", tt.Target, err)
   403  	}
   404  	tt.Validator(t, g)
   405  }
   406  
   407  func TestEvalExprModifiedOrigin(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItem, from, until int64, strictOrder, compareTags bool) error {
   408  	testName := tt.Target
   409  	exp, _, err := parser.ParseExpr(tt.Target)
   410  	if err != nil {
   411  		t.Errorf("failed to parse %s: %+v", tt.Target, err)
   412  		return nil
   413  	}
   414  	g, err := eval.Eval(context.Background(), exp, from, until, tt.M)
   415  	if err != nil {
   416  		return err
   417  	}
   418  	if len(g) != len(tt.Want) {
   419  		t.Errorf("%s returned a different number of metrics, actual %v, Want %v", testName, len(g), len(tt.Want))
   420  	}
   421  
   422  	for i, want := range tt.Want {
   423  		if i > len(g)-1 || g[i] == nil {
   424  			t.Errorf("returned no value %+s[%d]: want %+v", tt.Target, i, want)
   425  			return nil
   426  		}
   427  		actual := g[i]
   428  		if compareTags {
   429  			if _, ok := actual.Tags["name"]; !ok {
   430  				t.Errorf("metric[%d] %+v with name %v doesn't contain 'name' tag", i, actual, actual.Name)
   431  			}
   432  			for k, v := range want.Tags {
   433  				if aTag, ok := actual.Tags[k]; ok {
   434  					if aTag != v {
   435  						t.Errorf("metric[%d] %+v with name '%s' tag['%s'] value '%s' not equal '%s'", i, actual, actual.Name, k, aTag, v)
   436  					}
   437  				} else {
   438  					t.Errorf("metric[%d] %+v with name %v doesn't contain '%s' tag", i, actual, actual.Name, k)
   439  				}
   440  			}
   441  			for k := range actual.Tags {
   442  				if _, ok := want.Tags[k]; !ok {
   443  					t.Errorf("metric[%d] %+v with name %v contain unwanted '%s' tag", i, actual, actual.Name, k)
   444  				}
   445  			}
   446  		}
   447  		if actual.StepTime == 0 {
   448  			t.Errorf("missing Step for %+v", g)
   449  		}
   450  		if actual.Name != want.Name {
   451  			t.Errorf("bad Name for %s metric[%d]: got %s, Want %s", testName, i, actual.Name, want.Name)
   452  		}
   453  		if actual.ConsolidationFunc != want.ConsolidationFunc {
   454  			t.Errorf("different ConsolidationFunc for %s metric[%d] %s: got %v, Want %v", testName, i, actual.Name, actual.ConsolidationFunc, want.ConsolidationFunc)
   455  		}
   456  		if !compare.NearlyEqualMetrics(actual, want) {
   457  			t.Errorf("different values for %s metric[%d] %s: got %v, Want %v", testName, i, actual.Name, actual.Values, want.Values)
   458  		}
   459  		if actual.StepTime != want.StepTime {
   460  			t.Errorf("different StepTime for %s metric[%d] %s: got %v, Want %v", testName, i, actual.Name, actual.StepTime, want.StepTime)
   461  		}
   462  		if actual.StartTime != want.StartTime {
   463  			t.Errorf("different StartTime for %s metric[%d] %s: got %v, Want %v", testName, i, actual.Name, actual.StartTime, want.StartTime)
   464  		}
   465  		if actual.StopTime != want.StopTime {
   466  			t.Errorf("different StopTime for %s metric[%d] %s: got %v, Want %v", testName, i, actual.Name, actual.StopTime, want.StopTime)
   467  		}
   468  	}
   469  	return nil
   470  }
   471  
   472  func TestEvalExpr(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItem) {
   473  	TestEvalExprWithOptions(t, eval, tt, true)
   474  }
   475  
   476  func TestEvalExprWithOptions(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItem, compareTags bool) {
   477  	originalMetrics := DeepClone(tt.M)
   478  	err := TestEvalExprModifiedOrigin(t, eval, tt, 0, 1, false, compareTags)
   479  	if err != nil {
   480  		t.Errorf("unexpected error while evaluating %s: got `%+v`", tt.Target, err)
   481  		return
   482  	}
   483  	DeepEqual(t, tt.Target, originalMetrics, tt.M, true)
   484  }
   485  
   486  func TestEvalExprResult(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItem) {
   487  	err := TestEvalExprModifiedOrigin(t, eval, tt, 0, 1, false, true)
   488  	if err != nil {
   489  		t.Errorf("unexpected error while evaluating %s: got `%+v`", tt.Target, err)
   490  		return
   491  	}
   492  	//
   493  }
   494  
   495  func TestEvalExprWithRange(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItemWithRange) {
   496  	originalMetrics := DeepClone(tt.M)
   497  	tt2 := tt.TestItem()
   498  	err := TestEvalExprModifiedOrigin(t, eval, tt2, tt.From, tt.Until, false, true)
   499  	if err != nil {
   500  		t.Errorf("unexpected error while evaluating %s: got `%+v`", tt.Target, err)
   501  		return
   502  	}
   503  	DeepEqual(t, tt.Target, originalMetrics, tt.M, true)
   504  }
   505  
   506  func TestEvalExprWithError(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItemWithError) {
   507  	originalMetrics := DeepClone(tt.M)
   508  	tt2 := &EvalTestItem{
   509  		Target: tt.Target,
   510  		M:      tt.M,
   511  		Want:   tt.Want,
   512  	}
   513  	err := TestEvalExprModifiedOrigin(t, eval, tt2, 0, 1, false, true)
   514  	if !merry.Is(err, tt.Error) {
   515  		t.Errorf("unexpected error while evaluating %s: got `%+v`, expected `%+v`", tt.Target, err, tt.Error)
   516  		return
   517  	}
   518  	DeepEqual(t, tt.Target, originalMetrics, tt.M, true)
   519  }
   520  
   521  func TestEvalExprOrdered(t *testing.T, eval interfaces.Evaluator, tt *EvalTestItem) {
   522  	originalMetrics := DeepClone(tt.M)
   523  	err := TestEvalExprModifiedOrigin(t, eval, tt, 0, 1, true, true)
   524  	if err != nil {
   525  		t.Errorf("unexpected error while evaluating %s: got `%+v`", tt.Target, err)
   526  		return
   527  	}
   528  	DeepEqual(t, tt.Target, originalMetrics, tt.M, true)
   529  }
   530  
   531  type TestZipper struct {
   532  	M map[parser.MetricRequest][]*types.MetricData
   533  }
   534  
   535  func NewTestZipper(m map[parser.MetricRequest][]*types.MetricData) TestZipper {
   536  	return TestZipper{M: m}
   537  }
   538  
   539  func (zp TestZipper) Find(ctx context.Context, request pb.MultiGlobRequest) (*pb.MultiGlobResponse, *zipperTypes.Stats, merry.Error) {
   540  	return nil, nil, zipperTypes.ErrNotImplementedYet
   541  }
   542  
   543  func (zp TestZipper) Info(ctx context.Context, metrics []string) (*pb.ZipperInfoResponse, *zipperTypes.Stats, merry.Error) {
   544  	return nil, nil, zipperTypes.ErrNotImplementedYet
   545  }
   546  
   547  func (zp TestZipper) RenderCompat(ctx context.Context, metrics []string, from, until int64) ([]*types.MetricData, *zipperTypes.Stats, merry.Error) {
   548  	return nil, nil, zipperTypes.ErrNotImplementedYet
   549  }
   550  
   551  func (zp TestZipper) Render(ctx context.Context, request pb.MultiFetchRequest) ([]*types.MetricData, *zipperTypes.Stats, merry.Error) {
   552  	var resp []*types.MetricData
   553  	for _, r := range request.Metrics {
   554  		metricRequest := parser.MetricRequest{Metric: r.PathExpression, From: r.StartTime, Until: r.StopTime}
   555  		if v, ok := zp.M[metricRequest]; ok {
   556  			resp = append(resp, v...)
   557  		}
   558  	}
   559  	return resp, nil, nil
   560  }
   561  
   562  func (zp TestZipper) TagNames(ctx context.Context, query string, limit int64) ([]string, merry.Error) {
   563  	return nil, zipperTypes.ErrNotImplementedYet
   564  }
   565  
   566  func (zp TestZipper) TagValues(ctx context.Context, query string, limit int64) ([]string, merry.Error) {
   567  	return nil, zipperTypes.ErrNotImplementedYet
   568  }
   569  
   570  func (zp TestZipper) ScaleToCommonStep() bool {
   571  	return false
   572  }
   573  
   574  func GenerateValues(start, stop, step int64) (values []float64) {
   575  	for i := start; i < stop; i += step {
   576  		values = append(values, float64(i))
   577  	}
   578  	return
   579  }