github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/graphite/common/transform_test.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package common
    22  
    23  import (
    24  	"fmt"
    25  	"math"
    26  	"testing"
    27  	"time"
    28  
    29  	xtest "github.com/m3db/m3/src/query/graphite/testing"
    30  	"github.com/m3db/m3/src/query/graphite/ts"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func testRenamer(series *ts.Series) string {
    37  	return fmt.Sprintf("test %v", series.Name())
    38  }
    39  
    40  func TestAbsolute(t *testing.T) {
    41  	ctx := NewTestContext()
    42  	defer ctx.Close()
    43  
    44  	var (
    45  		vals = []float64{-2, 0, 42, math.NaN()}
    46  		step = 100
    47  		now  = time.Now()
    48  	)
    49  	input := ts.SeriesList{
    50  		Values: []*ts.Series{
    51  			ts.NewSeries(ctx, "vodka", now, NewTestSeriesValues(ctx, step, vals)),
    52  		},
    53  	}
    54  
    55  	r, err := Transform(ctx, input, NewStatelessTransformer(math.Abs), testRenamer)
    56  	require.NoError(t, err)
    57  
    58  	output := r.Values
    59  	require.Equal(t, 1, len(output))
    60  
    61  	abs := output[0]
    62  	require.Equal(t, len(vals), abs.Len())
    63  	assert.Equal(t, step, abs.MillisPerStep())
    64  	assert.Equal(t, now, abs.StartTime())
    65  	assert.Equal(t, "test vodka", abs.Name())
    66  
    67  	absVals := make([]float64, len(vals))
    68  	for i := 0; i < abs.Len(); i++ {
    69  		absVals[i] = abs.ValueAt(i)
    70  	}
    71  	xtest.Equalish(t, []float64{2, 0, 42, math.NaN()}, absVals)
    72  }
    73  
    74  func TestOffset(t *testing.T) {
    75  	ctx := NewTestContext()
    76  	defer ctx.Close()
    77  
    78  	var (
    79  		tests = []struct {
    80  			inputs []float64
    81  			factor float64
    82  			output []float64
    83  		}{
    84  			{
    85  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0}, 2.5,
    86  				[]float64{2.5, 3.5, 4.5, math.NaN(), 5.5},
    87  			},
    88  			{
    89  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0}, -0.5,
    90  				[]float64{-0.5, 0.5, 1.5, math.NaN(), 2.5},
    91  			},
    92  		}
    93  
    94  		startTime = time.Now()
    95  		step      = 100
    96  	)
    97  
    98  	for _, test := range tests {
    99  		input := ts.SeriesList{
   100  			Values: []*ts.Series{
   101  				ts.NewSeries(ctx, "foo", startTime, NewTestSeriesValues(ctx, step, test.inputs)),
   102  			},
   103  		}
   104  
   105  		r, err := Transform(ctx, input, NewStatelessTransformer(Offset(test.factor)), testRenamer)
   106  		require.NoError(t, err)
   107  
   108  		output := r.Values
   109  		require.EqualValues(t, 1, len(output))
   110  		require.Equal(t, len(test.inputs), output[0].Len())
   111  
   112  		assert.Equal(t, step, output[0].MillisPerStep())
   113  		assert.Equal(t, startTime, output[0].StartTime())
   114  		assert.Equal(t, "test foo", output[0].Name())
   115  
   116  		for step := 0; step < output[0].Len(); step++ {
   117  			v := output[0].ValueAt(step)
   118  			xtest.EqualWithNaNs(t, float64(test.output[step]), float64(v), "invalid value for %d", step)
   119  		}
   120  	}
   121  
   122  }
   123  
   124  func TestScale(t *testing.T) {
   125  	ctx := NewTestContext()
   126  	defer ctx.Close()
   127  
   128  	var (
   129  		tests = []struct {
   130  			inputs []float64
   131  			scale  float64
   132  			output []float64
   133  		}{
   134  			{
   135  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0}, 2.5,
   136  				[]float64{0, 2.5, 5.0, math.NaN(), 7.5},
   137  			},
   138  			{
   139  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0}, 0.5,
   140  				[]float64{0, 0.5, 1.0, math.NaN(), 1.5},
   141  			},
   142  		}
   143  
   144  		startTime = time.Now()
   145  		step      = 100
   146  	)
   147  
   148  	for _, test := range tests {
   149  		input := ts.SeriesList{
   150  			Values: []*ts.Series{
   151  				ts.NewSeries(ctx, "foo", startTime, NewTestSeriesValues(ctx, step, test.inputs)),
   152  			},
   153  		}
   154  
   155  		r, err := Transform(ctx, input, NewStatelessTransformer(Scale(test.scale)), testRenamer)
   156  		require.NoError(t, err)
   157  
   158  		output := r.Values
   159  		require.EqualValues(t, 1, len(output))
   160  		require.Equal(t, len(test.inputs), output[0].Len())
   161  
   162  		assert.EqualValues(t, step, output[0].MillisPerStep())
   163  		assert.Equal(t, startTime, output[0].StartTime())
   164  		assert.Equal(t, "test foo", output[0].Name())
   165  
   166  		for step := 0; step < output[0].Len(); step++ {
   167  			v := output[0].ValueAt(step)
   168  			xtest.EqualWithNaNs(t, float64(test.output[step]), float64(v), "invalid value for %d", step)
   169  		}
   170  	}
   171  }
   172  
   173  func TestTransformNull(t *testing.T) {
   174  	ctx := NewTestContext()
   175  	defer ctx.Close()
   176  
   177  	var (
   178  		tests = []struct {
   179  			inputs       []float64
   180  			defaultValue float64
   181  			output       []float64
   182  		}{
   183  			{
   184  				[]float64{0, math.NaN(), 2.0, math.NaN(), 3.0}, 42.5,
   185  				[]float64{0, 42.5, 2.0, 42.5, 3.0},
   186  			},
   187  			{
   188  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0}, -0.5,
   189  				[]float64{0, 1.0, 2.0, -0.5, 3.0},
   190  			},
   191  		}
   192  
   193  		startTime = time.Now()
   194  		step      = 100
   195  	)
   196  
   197  	for _, test := range tests {
   198  		input := ts.SeriesList{
   199  			Values: []*ts.Series{
   200  				ts.NewSeries(ctx, "foo", startTime, NewTestSeriesValues(ctx, step, test.inputs)),
   201  			},
   202  		}
   203  
   204  		r, err := Transform(ctx, input, NewStatelessTransformer(TransformNull(test.defaultValue)), testRenamer)
   205  		require.NoError(t, err)
   206  
   207  		output := r.Values
   208  		require.EqualValues(t, 1, len(output))
   209  		require.Equal(t, len(test.inputs), output[0].Len())
   210  
   211  		assert.EqualValues(t, step, output[0].MillisPerStep())
   212  		assert.Equal(t, startTime, output[0].StartTime())
   213  		assert.Equal(t, "test foo", output[0].Name())
   214  
   215  		for step := 0; step < output[0].Len(); step++ {
   216  			v := output[0].ValueAt(step)
   217  			assert.Equal(t, test.output[step], v, "invalid value for %d", step)
   218  		}
   219  	}
   220  }
   221  
   222  func TestIsNonNull(t *testing.T) {
   223  	ctx := NewTestContext()
   224  	defer ctx.Close()
   225  
   226  	var (
   227  		tests = []struct {
   228  			inputs []float64
   229  			output []float64
   230  		}{
   231  			{
   232  				[]float64{0, math.NaN(), 2.0, math.NaN(), 3.0},
   233  				[]float64{1, 0, 1, 0, 1},
   234  			},
   235  			{
   236  				[]float64{0, 1.0, 2.0, math.NaN(), 3.0},
   237  				[]float64{1, 1, 1, 0, 1},
   238  			},
   239  		}
   240  
   241  		startTime = time.Now()
   242  		step      = 100
   243  	)
   244  
   245  	for _, test := range tests {
   246  		input := ts.SeriesList{
   247  			Values: []*ts.Series{
   248  				ts.NewSeries(ctx, "foo", startTime, NewTestSeriesValues(ctx, step, test.inputs)),
   249  			},
   250  		}
   251  
   252  		r, err := Transform(ctx, input, NewStatelessTransformer(IsNonNull()), testRenamer)
   253  		require.NoError(t, err)
   254  
   255  		output := r.Values
   256  		require.EqualValues(t, 1, len(output))
   257  		require.Equal(t, len(test.inputs), output[0].Len())
   258  
   259  		assert.EqualValues(t, step, output[0].MillisPerStep())
   260  		assert.Equal(t, startTime, output[0].StartTime())
   261  		assert.Equal(t, "test foo", output[0].Name())
   262  
   263  		for step := 0; step < output[0].Len(); step++ {
   264  			v := output[0].ValueAt(step)
   265  			assert.Equal(t, test.output[step], v, "invalid value for %d", step)
   266  		}
   267  	}
   268  }
   269  
   270  func TestStdev(t *testing.T) {
   271  	ctx := NewTestContext()
   272  	defer ctx.Close()
   273  
   274  	nan := math.NaN()
   275  	startTime := ctx.StartTime
   276  	stepSize := 10000
   277  	inputs := []struct {
   278  		name        string
   279  		startTime   time.Time
   280  		stepInMilli int
   281  		values      []float64
   282  	}{
   283  		{
   284  			"foo",
   285  			startTime,
   286  			stepSize,
   287  			[]float64{1.0, 2.0, 3.0, 4.0, nan, nan, nan, 5.0, 6.0, nan, nan},
   288  		},
   289  	}
   290  
   291  	inputSeries := make([]*ts.Series, 0, len(inputs))
   292  	for _, input := range inputs {
   293  		series := ts.NewSeries(
   294  			ctx,
   295  			input.name,
   296  			input.startTime,
   297  			NewTestSeriesValues(ctx, input.stepInMilli, input.values),
   298  		)
   299  		inputSeries = append(inputSeries, series)
   300  	}
   301  	expected := []TestSeries{
   302  		TestSeries{Name: "foo | stddev 3", Data: []float64{0.0, 0.5, 0.8165, 0.8165, 0.5, 0.0, nan, 0.0, 0.5, 0.5, 0.0}},
   303  	}
   304  	input := ts.SeriesList{Values: inputSeries}
   305  	results, err := Stdev(ctx, input, 3, 0.1, func(series *ts.Series, points int) string {
   306  		return fmt.Sprintf("%s | stddev %d", series.Name(), points)
   307  	})
   308  	require.Nil(t, err)
   309  	CompareOutputsAndExpected(t, stepSize, startTime, expected, results.Values)
   310  }
   311  
   312  func TestPerSecond(t *testing.T) {
   313  	ctx := NewTestContext()
   314  	defer ctx.Close()
   315  
   316  	nan := math.NaN()
   317  	startTime := ctx.StartTime
   318  	stepSize := 1000
   319  	inputs := []struct {
   320  		name        string
   321  		startTime   time.Time
   322  		stepInMilli int
   323  		values      []float64
   324  	}{
   325  		{
   326  			"foo",
   327  			startTime,
   328  			stepSize,
   329  			[]float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0},
   330  		},
   331  		{
   332  			"foo",
   333  			startTime,
   334  			stepSize,
   335  			[]float64{1.0, 2.0, 4.0, 7.0, 11.0, 16.0, 22.0, 29.0, 37.0, 46.0},
   336  		},
   337  		{
   338  			"foo",
   339  			startTime,
   340  			stepSize,
   341  			[]float64{1.0, 2.0, 3.0, 4.0, 5.0, 1.0, 2.0, 3.0, 4.0, 5.0},
   342  		},
   343  		{
   344  			"foo",
   345  			startTime,
   346  			stepSize,
   347  			[]float64{nan, nan, nan, 4.0, 5.0, 1.0, 2.0, 3.0, 4.0, 5.0},
   348  		},
   349  		{
   350  			"foo",
   351  			startTime,
   352  			stepSize,
   353  			[]float64{1.0, 2.0, 3.0, nan, nan, nan, 7.0, 8.0, 9.0, 10.0},
   354  		},
   355  	}
   356  
   357  	inputSeries := make([]*ts.Series, 0, len(inputs))
   358  	for _, input := range inputs {
   359  		series := ts.NewSeries(
   360  			ctx,
   361  			input.name,
   362  			input.startTime,
   363  			NewTestSeriesValues(ctx, input.stepInMilli, input.values),
   364  		)
   365  		inputSeries = append(inputSeries, series)
   366  	}
   367  	expected := []TestSeries{
   368  		TestSeries{Name: "foo | perSecond", Data: []float64{nan, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}},
   369  		TestSeries{Name: "foo | perSecond", Data: []float64{nan, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}},
   370  		TestSeries{Name: "foo | perSecond", Data: []float64{nan, 1.0, 1.0, 1.0, 1.0, nan, 1.0, 1.0, 1.0, 1.0}},
   371  		TestSeries{Name: "foo | perSecond", Data: []float64{nan, nan, nan, nan, 1.0, nan, 1.0, 1.0, 1.0, 1.0}},
   372  		TestSeries{Name: "foo | perSecond", Data: []float64{nan, 1.0, 1.0, nan, nan, nan, 1.0, 1.0, 1.0, 1.0}},
   373  	}
   374  	input := ts.SeriesList{Values: inputSeries}
   375  	results, err := PerSecond(ctx, input, func(series *ts.Series) string {
   376  		return fmt.Sprintf("%s | perSecond", series.Name())
   377  	})
   378  	require.Nil(t, err)
   379  	CompareOutputsAndExpected(t, stepSize, startTime, expected, results.Values)
   380  }