github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/linear/clamp_test.go (about)

     1  // Copyright (c) 2018 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 linear
    22  
    23  import (
    24  	"math"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/query/executor/transform"
    29  	"github.com/m3db/m3/src/query/models"
    30  	"github.com/m3db/m3/src/query/parser"
    31  	"github.com/m3db/m3/src/query/test"
    32  	"github.com/m3db/m3/src/query/test/compare"
    33  	"github.com/m3db/m3/src/query/test/executor"
    34  
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    37  )
    38  
    39  // nolint
    40  func expectedClampVals(values [][]float64, num float64, fn func(x, y float64) float64) [][]float64 {
    41  	expected := make([][]float64, 0, len(values))
    42  	for _, val := range values {
    43  		v := make([]float64, len(val))
    44  		for i, ev := range val {
    45  			v[i] = fn(ev, num)
    46  		}
    47  
    48  		expected = append(expected, v)
    49  	}
    50  	return expected
    51  }
    52  
    53  func TestClampMin(t *testing.T) {
    54  	values, bounds := test.GenerateValuesAndBounds(nil, nil)
    55  	values[0][0] = math.NaN()
    56  
    57  	block := test.NewBlockFromValues(bounds, values)
    58  	c, sink := executor.NewControllerWithSink(parser.NodeID(rune(1)))
    59  	clampOp, err := NewClampOp([]interface{}{3.0}, ClampMinType)
    60  	require.NoError(t, err)
    61  
    62  	op, ok := clampOp.(transform.Params)
    63  	require.True(t, ok)
    64  
    65  	node := op.Node(c, transform.Options{})
    66  	err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), block)
    67  	require.NoError(t, err)
    68  	expected := expectedClampVals(values, 3.0, math.Max)
    69  	assert.Len(t, sink.Values, 2)
    70  	compare.EqualsWithNans(t, expected, sink.Values)
    71  }
    72  
    73  func TestClampMax(t *testing.T) {
    74  	values, bounds := test.GenerateValuesAndBounds(nil, nil)
    75  	values[0][0] = math.NaN()
    76  
    77  	block := test.NewBlockFromValues(bounds, values)
    78  	c, sink := executor.NewControllerWithSink(parser.NodeID(rune(1)))
    79  	clampOp, err := NewClampOp([]interface{}{3.0}, ClampMaxType)
    80  	require.NoError(t, err)
    81  
    82  	op, ok := clampOp.(transform.Params)
    83  	require.True(t, ok)
    84  
    85  	node := op.Node(c, transform.Options{})
    86  	err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), block)
    87  	require.NoError(t, err)
    88  	expected := expectedClampVals(values, 3.0, math.Min)
    89  	assert.Len(t, sink.Values, 2)
    90  	compare.EqualsWithNans(t, expected, sink.Values)
    91  }
    92  
    93  func TestClampFailsParse(t *testing.T) {
    94  	_, err := NewClampOp([]interface{}{}, "bad")
    95  	assert.Error(t, err)
    96  }
    97  
    98  func runClamp(t *testing.T, args []interface{},
    99  	opType string, vals []float64) []float64 {
   100  	bounds := models.Bounds{
   101  		StepSize: step,
   102  		Duration: step * time.Duration(len(vals)),
   103  	}
   104  
   105  	v := [][]float64{vals}
   106  	block := test.NewBlockFromValues(bounds, v)
   107  	c, sink := executor.NewControllerWithSink(parser.NodeID(rune(1)))
   108  	roundOp, err := NewClampOp(args, opType)
   109  	require.NoError(t, err)
   110  
   111  	op, ok := roundOp.(transform.Params)
   112  	require.True(t, ok)
   113  
   114  	node := op.Node(c, transform.Options{})
   115  	err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), block)
   116  	require.NoError(t, err)
   117  	require.Len(t, sink.Values, 1)
   118  
   119  	return sink.Values[0]
   120  }
   121  
   122  func TestClampWithArgs(t *testing.T) {
   123  	var (
   124  		v     = []float64{math.NaN(), 0, 1, 2, 3, math.Inf(1), math.Inf(-1)}
   125  		exMax = []float64{math.NaN(), 0, 1, 2, 2, 2, math.Inf(-1)}
   126  		exMin = []float64{math.NaN(), 2, 2, 2, 3, math.Inf(1), 2}
   127  	)
   128  
   129  	max := runClamp(t, toArgs(2), ClampMaxType, v)
   130  	compare.EqualsWithNans(t, exMax, max)
   131  
   132  	min := runClamp(t, toArgs(2), ClampMinType, v)
   133  	compare.EqualsWithNans(t, exMin, min)
   134  }