github.com/m3db/m3@v1.5.0/src/dbnode/encoding/series_iterator_accumulator_test.go (about)

     1  // Copyright (c) 2020 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 encoding
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/ts"
    28  	"github.com/m3db/m3/src/x/ident"
    29  	xtest "github.com/m3db/m3/src/x/test"
    30  	xtime "github.com/m3db/m3/src/x/time"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  type testAccumulatorSeries struct {
    37  	id          string
    38  	nsID        string
    39  	start       xtime.UnixNano
    40  	end         xtime.UnixNano
    41  	input       []accumulatorInput
    42  	expected    []testValue
    43  	expectedErr *testSeriesErr
    44  
    45  	expectedFirstAnnotation ts.Annotation
    46  }
    47  
    48  type accumulatorInput struct {
    49  	values []testValue
    50  	id     string
    51  	err    error
    52  }
    53  
    54  func TestSeriesIteratorAccumulator(t *testing.T) {
    55  	start := xtime.Now().Truncate(time.Minute)
    56  	end := start.Add(time.Minute)
    57  
    58  	values := []accumulatorInput{
    59  		{
    60  			values: []testValue{
    61  				{1.0, start.Add(-1 * time.Second), xtime.Second, []byte{4}},
    62  				{2.0, start.Add(1 * time.Second), xtime.Second, nil},
    63  			},
    64  			id: "foo0",
    65  		},
    66  		{
    67  			values: []testValue{
    68  				{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    69  				{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    70  				{3.0, start.Add(6 * time.Second), xtime.Second, nil},
    71  			},
    72  			id: "foo1",
    73  		},
    74  		{
    75  			values: []testValue{
    76  				{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    77  				{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    78  				{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    79  			},
    80  			id: "foo2",
    81  		},
    82  		{
    83  			values: []testValue{
    84  				{3.0, start.Add(1 * time.Millisecond), xtime.Second, []byte{5}},
    85  				{4.0, start.Add(4 * time.Second), xtime.Second, nil},
    86  				{5.0, start.Add(5 * time.Second), xtime.Second, nil},
    87  			},
    88  			id: "foo3",
    89  		},
    90  	}
    91  
    92  	ex := []testValue{
    93  		{3.0, start.Add(1 * time.Millisecond), xtime.Second, []byte{5}},
    94  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
    95  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
    96  		{3.0, start.Add(3 * time.Second), xtime.Second, nil},
    97  		{4.0, start.Add(4 * time.Second), xtime.Second, nil},
    98  		{5.0, start.Add(5 * time.Second), xtime.Second, nil},
    99  		{3.0, start.Add(6 * time.Second), xtime.Second, nil},
   100  	}
   101  
   102  	test := testAccumulatorSeries{
   103  		id:                      "foo0",
   104  		nsID:                    "bar",
   105  		start:                   start,
   106  		end:                     end,
   107  		input:                   values,
   108  		expected:                ex,
   109  		expectedFirstAnnotation: []byte{4},
   110  	}
   111  
   112  	assertTestSeriesAccumulatorIterator(t, test)
   113  }
   114  
   115  func TestSingleSeriesIteratorAccumulator(t *testing.T) {
   116  	start := xtime.Now().Truncate(time.Minute)
   117  	end := start.Add(time.Minute)
   118  
   119  	values := []accumulatorInput{
   120  		{
   121  			values: []testValue{
   122  				{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   123  				{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   124  				{3.0, start.Add(6 * time.Second), xtime.Second, nil},
   125  			},
   126  			id: "foobar",
   127  		},
   128  	}
   129  
   130  	ex := []testValue{
   131  		{1.0, start.Add(1 * time.Second), xtime.Second, []byte{1, 2, 3}},
   132  		{2.0, start.Add(2 * time.Second), xtime.Second, nil},
   133  		{3.0, start.Add(6 * time.Second), xtime.Second, nil},
   134  	}
   135  
   136  	test := testAccumulatorSeries{
   137  		id:                      "foobar",
   138  		nsID:                    "bar",
   139  		start:                   start,
   140  		end:                     end,
   141  		input:                   values,
   142  		expected:                ex,
   143  		expectedFirstAnnotation: []byte{1, 2, 3},
   144  	}
   145  
   146  	assertTestSeriesAccumulatorIterator(t, test)
   147  }
   148  
   149  type newTestSeriesAccumulatorIteratorResult struct {
   150  	iter        *seriesIteratorAccumulator
   151  	seriesIters []SeriesIterator
   152  }
   153  
   154  func newTestSeriesAccumulatorIterator(
   155  	t *testing.T,
   156  	series testAccumulatorSeries,
   157  ) newTestSeriesAccumulatorIteratorResult {
   158  	iters := make([]SeriesIterator, 0, len(series.input))
   159  	var acc SeriesIteratorAccumulator
   160  	for _, r := range series.input {
   161  		multiIter := newTestMultiIterator(
   162  			r.values,
   163  			r.err,
   164  		)
   165  
   166  		iter := NewSeriesIterator(SeriesIteratorOptions{
   167  			ID:        ident.StringID(r.id),
   168  			Namespace: ident.StringID(series.nsID),
   169  			Tags: ident.NewTagsIterator(ident.NewTags(
   170  				ident.StringTag("foo", "bar"), ident.StringTag("qux", "quz"),
   171  			)),
   172  			StartInclusive: series.start,
   173  			EndExclusive:   series.end,
   174  			Replicas:       []MultiReaderIterator{multiIter},
   175  		}, nil)
   176  
   177  		iters = append(iters, iter)
   178  		if acc == nil {
   179  			a, err := NewSeriesIteratorAccumulator(iter)
   180  			require.NoError(t, err)
   181  			acc = a
   182  		} else {
   183  			err := acc.Add(iter)
   184  			require.NoError(t, err)
   185  		}
   186  	}
   187  
   188  	accumulator, ok := acc.(*seriesIteratorAccumulator)
   189  	require.True(t, ok)
   190  	return newTestSeriesAccumulatorIteratorResult{
   191  		iter:        accumulator,
   192  		seriesIters: iters,
   193  	}
   194  }
   195  
   196  func assertTestSeriesAccumulatorIterator(
   197  	t *testing.T,
   198  	series testAccumulatorSeries,
   199  ) {
   200  	newSeriesIter := newTestSeriesAccumulatorIterator(t, series)
   201  	iter := newSeriesIter.iter
   202  
   203  	checkTags := func() {
   204  		tags := iter.Tags()
   205  		require.NotNil(t, tags)
   206  		require.True(t, tags.Next())
   207  		assert.True(t, tags.Current().Equal(ident.StringTag("foo", "bar")))
   208  		require.True(t, tags.Next())
   209  		assert.True(t, tags.Current().Equal(ident.StringTag("qux", "quz")))
   210  		assert.False(t, tags.Next())
   211  		assert.NoError(t, tags.Err())
   212  		tags.Rewind()
   213  	}
   214  
   215  	checkTags()
   216  	assert.Equal(t, series.id, iter.ID().String())
   217  	assert.Equal(t, series.nsID, iter.Namespace().String())
   218  	assert.Equal(t, series.start, iter.Start())
   219  	assert.Equal(t, series.end, iter.End())
   220  	for i := 0; i < len(series.expected); i++ {
   221  		next := iter.Next()
   222  		if series.expectedErr != nil && i == series.expectedErr.atIdx {
   223  			assert.False(t, next)
   224  			break
   225  		}
   226  		require.True(t, next)
   227  		dp, unit, annotation := iter.Current()
   228  		expected := series.expected[i]
   229  		assert.Equal(t, expected.value, dp.Value)
   230  		assert.Equal(t, expected.t, dp.TimestampNanos)
   231  		assert.Equal(t, expected.unit, unit)
   232  		assert.Equal(t, expected.annotation, annotation)
   233  		assert.Equal(t, series.expectedFirstAnnotation, iter.FirstAnnotation())
   234  		checkTags()
   235  	}
   236  	// Ensure further calls to next false
   237  	for i := 0; i < 2; i++ {
   238  		assert.False(t, iter.Next())
   239  	}
   240  	if series.expectedErr == nil {
   241  		assert.NoError(t, iter.Err())
   242  	} else {
   243  		assert.Equal(t, series.expectedErr.err, iter.Err())
   244  	}
   245  
   246  	assert.Equal(t, series.id, iter.id.String())
   247  	assert.Equal(t, series.nsID, iter.nsID.String())
   248  
   249  	iter.Close()
   250  	for _, seriesIter := range newSeriesIter.seriesIters {
   251  		seriesIter.Close()
   252  	}
   253  
   254  	// Check that the tag iterator was closed.
   255  	tagIter := iter.Tags()
   256  	require.NotNil(t, tagIter)
   257  	assert.False(t, tagIter.Next())
   258  }
   259  
   260  func TestAccumulatorMocked(t *testing.T) {
   261  	ctrl := xtest.NewController(t)
   262  	defer ctrl.Finish()
   263  
   264  	start := xtime.Now()
   265  	annotation := ts.Annotation{5, 6, 7}
   266  	base := NewMockSeriesIterator(ctrl)
   267  	base.EXPECT().ID().Return(ident.StringID("base")).AnyTimes()
   268  	base.EXPECT().Namespace().Return(ident.StringID("ns")).AnyTimes()
   269  	base.EXPECT().FirstAnnotation().Return(annotation)
   270  	base.EXPECT().Next().Return(true)
   271  	dp := ts.Datapoint{TimestampNanos: start, Value: 88}
   272  	base.EXPECT().Current().Return(dp, xtime.Second, annotation).AnyTimes()
   273  	base.EXPECT().Next().Return(false)
   274  	base.EXPECT().Start().Return(start)
   275  	base.EXPECT().End().Return(start.Add(time.Hour))
   276  	base.EXPECT().Err().Return(nil).AnyTimes()
   277  
   278  	it, err := NewSeriesIteratorAccumulator(base)
   279  	require.NoError(t, err)
   280  
   281  	i := 0
   282  	for it.Next() {
   283  		ac, timeUnit, annot := it.Current()
   284  		assert.Equal(t, dp, ac)
   285  		assert.Equal(t, xtime.Second, timeUnit)
   286  		assert.Equal(t, annotation, annot)
   287  		assert.Equal(t, annotation, it.FirstAnnotation())
   288  		i++
   289  	}
   290  
   291  	assert.Equal(t, 1, i)
   292  	it.Close()
   293  }