github.com/m3db/m3@v1.5.0/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 }