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 }