github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/functions/binary/unless_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 binary
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/query/block"
    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  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/stretchr/testify/assert"
    37  	"github.com/stretchr/testify/require"
    38  )
    39  
    40  func m(i int) indexMatcher {
    41  	return mm(i, i)
    42  }
    43  
    44  func mm(l, r int) indexMatcher {
    45  	return indexMatcher{lhsIndex: l, rhsIndex: r}
    46  }
    47  
    48  var distinctLeftTests = []struct {
    49  	name     string
    50  	lhs, rhs []block.SeriesMeta
    51  	expected []indexMatcher
    52  }{
    53  	{
    54  		"equal tags",
    55  		generateMetaDataWithTagsInRange(0, 5),
    56  		generateMetaDataWithTagsInRange(0, 5),
    57  		[]indexMatcher{m(0), m(1), m(2), m(3), m(4)},
    58  	},
    59  	{
    60  		"empty rhs",
    61  		generateMetaDataWithTagsInRange(0, 5),
    62  		[]block.SeriesMeta{},
    63  		[]indexMatcher{},
    64  	},
    65  	{
    66  		"empty lhs",
    67  		[]block.SeriesMeta{},
    68  		generateMetaDataWithTagsInRange(0, 5),
    69  		[]indexMatcher{},
    70  	},
    71  	{
    72  		"longer lhs",
    73  		generateMetaDataWithTagsInRange(-1, 6),
    74  		generateMetaDataWithTagsInRange(0, 5),
    75  		[]indexMatcher{mm(1, 0), mm(2, 1), mm(3, 2), mm(4, 3), mm(5, 4)},
    76  	},
    77  	{
    78  		"longer rhs",
    79  		generateMetaDataWithTagsInRange(0, 5),
    80  		generateMetaDataWithTagsInRange(-1, 6),
    81  		[]indexMatcher{mm(0, 1), mm(1, 2), mm(2, 3), mm(3, 4), mm(4, 5)},
    82  	},
    83  	{
    84  		"shorter lhs",
    85  		generateMetaDataWithTagsInRange(1, 4),
    86  		generateMetaDataWithTagsInRange(0, 5),
    87  		[]indexMatcher{mm(0, 1), mm(1, 2), mm(2, 3)},
    88  	},
    89  	{
    90  		"shorter rhs",
    91  		generateMetaDataWithTagsInRange(0, 5),
    92  		generateMetaDataWithTagsInRange(1, 4),
    93  		[]indexMatcher{mm(1, 0), mm(2, 1), mm(3, 2)},
    94  	},
    95  	{
    96  		"partial overlap",
    97  		generateMetaDataWithTagsInRange(0, 5),
    98  		generateMetaDataWithTagsInRange(1, 6),
    99  		[]indexMatcher{mm(1, 0), mm(2, 1), mm(3, 2), mm(4, 3)},
   100  	},
   101  	{
   102  		"no overlap",
   103  		generateMetaDataWithTagsInRange(0, 5),
   104  		generateMetaDataWithTagsInRange(6, 9),
   105  		[]indexMatcher{},
   106  	},
   107  }
   108  
   109  func TestMatchingIndices(t *testing.T) {
   110  	matching := VectorMatching{}
   111  	for _, tt := range distinctLeftTests {
   112  		t.Run(tt.name, func(t *testing.T) {
   113  			excluded := matchingIndices(matching, tt.lhs, tt.rhs)
   114  			assert.Equal(t, tt.expected, excluded)
   115  		})
   116  	}
   117  }
   118  
   119  var unlessTests = []struct {
   120  	name          string
   121  	lhsMeta       []block.SeriesMeta
   122  	lhs           [][]float64
   123  	rhsMeta       []block.SeriesMeta
   124  	rhs           [][]float64
   125  	expectedMetas []block.SeriesMeta
   126  	expected      [][]float64
   127  	err           error
   128  }{
   129  	{
   130  		"valid, equal tags",
   131  		test.NewSeriesMeta("a", 2),
   132  		[][]float64{{1, 2}, {10, 20}},
   133  		test.NewSeriesMeta("a", 2),
   134  		[][]float64{{3, 4}, {30, 40}},
   135  		test.NewSeriesMetaWithoutName("a", 2),
   136  		[][]float64{{nan, nan}, {nan, nan}},
   137  		nil,
   138  	},
   139  	{
   140  		"valid, some overlap right",
   141  		test.NewSeriesMeta("a", 2),
   142  		[][]float64{{nan, 2}, {10, 20}},
   143  		test.NewSeriesMeta("a", 3),
   144  		[][]float64{{3, nan}, {30, 40}, {50, 60}},
   145  		test.NewSeriesMetaWithoutName("a", 2),
   146  		[][]float64{{nan, 2}, {nan, nan}},
   147  		nil,
   148  	},
   149  	{
   150  		"valid, some overlap left",
   151  		test.NewSeriesMeta("a", 3),
   152  		[][]float64{{1, 2}, {10, 20}, {100, 200}},
   153  		test.NewSeriesMeta("a", 3)[1:],
   154  		[][]float64{{3, 4}, {nan, 40}},
   155  		test.NewSeriesMetaWithoutName("a", 3),
   156  		[][]float64{{1, 2}, {nan, nan}, {100, nan}},
   157  		nil,
   158  	},
   159  	{
   160  		"valid, some overlap both",
   161  		test.NewSeriesMeta("a", 3),
   162  		[][]float64{{1, 2}, {10, 20}, {100, 200}},
   163  		test.NewSeriesMeta("a", 4)[1:],
   164  		[][]float64{{3, nan}, {nan, 40}, {300, 400}},
   165  		test.NewSeriesMetaWithoutName("a", 3),
   166  		[][]float64{{1, 2}, {nan, 20}, {100, nan}},
   167  		nil,
   168  	},
   169  	{
   170  		"valid, different tags",
   171  		test.NewSeriesMeta("a", 2),
   172  		[][]float64{{1, 2}, {10, 20}},
   173  		test.NewSeriesMeta("b", 2),
   174  		[][]float64{{nan, 4}, {30, 40}},
   175  		test.NewSeriesMetaWithoutName("a", 2),
   176  		[][]float64{{1, 2}, {10, 20}},
   177  		nil,
   178  	},
   179  	{
   180  		"valid, different tags, longer rhs",
   181  		test.NewSeriesMeta("a", 2),
   182  		[][]float64{{1, 2}, {10, 20}},
   183  		test.NewSeriesMeta("b", 3),
   184  		[][]float64{{3, 4}, {30, 40}, {300, 400}},
   185  		test.NewSeriesMetaWithoutName("a", 2),
   186  		[][]float64{{1, 2}, {10, 20}},
   187  		nil,
   188  	},
   189  	{
   190  		"valid, different tags, longer lhs",
   191  		test.NewSeriesMeta("a", 3),
   192  		[][]float64{{1, 2}, {10, 20}, {100, 200}},
   193  		test.NewSeriesMeta("b", 2),
   194  		[][]float64{{3, 4}, {30, 40}},
   195  		test.NewSeriesMetaWithoutName("a", 3),
   196  		[][]float64{{1, 2}, {10, 20}, {100, 200}},
   197  		nil,
   198  	},
   199  	{
   200  		"mismatched step counts",
   201  		test.NewSeriesMeta("a", 2),
   202  		[][]float64{{1, 2, 3}, {10, 20, 30}},
   203  		test.NewSeriesMeta("b", 2),
   204  		[][]float64{{3, 4}, {30, 40}},
   205  		test.NewSeriesMetaWithoutName("a", 0),
   206  		[][]float64{},
   207  		errMismatchedStepCounts,
   208  	},
   209  }
   210  
   211  func TestUnless(t *testing.T) {
   212  	now := xtime.Now()
   213  	for _, tt := range unlessTests {
   214  		t.Run(tt.name, func(t *testing.T) {
   215  			op, err := NewOp(
   216  				UnlessType,
   217  				NodeParams{
   218  					LNode:                parser.NodeID(rune(0)),
   219  					RNode:                parser.NodeID(rune(1)),
   220  					VectorMatcherBuilder: emptyVectorMatcherBuilder,
   221  				},
   222  			)
   223  			require.NoError(t, err)
   224  
   225  			c, sink := executor.NewControllerWithSink(parser.NodeID(rune(2)))
   226  			node := op.(baseOp).Node(c, transform.Options{})
   227  			bounds := models.Bounds{
   228  				Start:    now,
   229  				Duration: time.Minute * time.Duration(len(tt.lhs[0])),
   230  				StepSize: time.Minute,
   231  			}
   232  
   233  			lhs := test.NewBlockFromValuesWithSeriesMeta(bounds, tt.lhsMeta, tt.lhs)
   234  			err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(0)), lhs)
   235  			require.NoError(t, err)
   236  			bounds = models.Bounds{
   237  				Start:    now,
   238  				Duration: time.Minute * time.Duration(len(tt.rhs[0])),
   239  				StepSize: time.Minute,
   240  			}
   241  
   242  			rhs := test.NewBlockFromValuesWithSeriesMeta(bounds, tt.rhsMeta, tt.rhs)
   243  			err = node.Process(models.NoopQueryContext(), parser.NodeID(rune(1)), rhs)
   244  			if tt.err != nil {
   245  				require.EqualError(t, err, tt.err.Error())
   246  				return
   247  			}
   248  
   249  			require.NoError(t, err)
   250  			compare.EqualsWithNans(t, tt.expected, sink.Values)
   251  			meta := sink.Meta
   252  			assert.Equal(t, 0, meta.Tags.Len())
   253  			assert.True(t, meta.Bounds.Equals(bounds))
   254  			assert.Equal(t, tt.expectedMetas, sink.Metas)
   255  		})
   256  	}
   257  }