github.com/thanos-io/thanos@v0.32.5/internal/cortex/querier/series/series_set_test.go (about) 1 // Copyright (c) The Cortex Authors. 2 // Licensed under the Apache License 2.0. 3 4 package series 5 6 import ( 7 "math/rand" 8 "testing" 9 10 "github.com/prometheus/common/model" 11 "github.com/prometheus/prometheus/model/labels" 12 "github.com/prometheus/prometheus/storage" 13 "github.com/prometheus/prometheus/tsdb/chunkenc" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestConcreteSeriesSet(t *testing.T) { 18 series1 := &ConcreteSeries{ 19 labels: labels.FromStrings("foo", "bar"), 20 samples: []model.SamplePair{{Value: 1, Timestamp: 2}}, 21 } 22 series2 := &ConcreteSeries{ 23 labels: labels.FromStrings("foo", "baz"), 24 samples: []model.SamplePair{{Value: 3, Timestamp: 4}}, 25 } 26 c := NewConcreteSeriesSet([]storage.Series{series2, series1}) 27 require.True(t, c.Next()) 28 require.Equal(t, series1, c.At()) 29 require.True(t, c.Next()) 30 require.Equal(t, series2, c.At()) 31 require.False(t, c.Next()) 32 } 33 34 func TestMatrixToSeriesSetSortsMetricLabels(t *testing.T) { 35 matrix := model.Matrix{ 36 { 37 Metric: model.Metric{ 38 model.MetricNameLabel: "testmetric", 39 "e": "f", 40 "a": "b", 41 "g": "h", 42 "c": "d", 43 }, 44 Values: []model.SamplePair{{Timestamp: 0, Value: 0}}, 45 }, 46 } 47 ss := MatrixToSeriesSet(matrix) 48 require.True(t, ss.Next()) 49 require.NoError(t, ss.Err()) 50 51 l := ss.At().Labels() 52 require.Equal(t, labels.Labels{ 53 {Name: string(model.MetricNameLabel), Value: "testmetric"}, 54 {Name: "a", Value: "b"}, 55 {Name: "c", Value: "d"}, 56 {Name: "e", Value: "f"}, 57 {Name: "g", Value: "h"}, 58 }, l) 59 } 60 61 func TestDeletedSeriesIterator(t *testing.T) { 62 cs := ConcreteSeries{labels: labels.FromStrings("foo", "bar")} 63 // Insert random stuff from (0, 1000). 64 for i := 0; i < 1000; i++ { 65 cs.samples = append(cs.samples, model.SamplePair{Timestamp: model.Time(i), Value: model.SampleValue(rand.Float64())}) 66 } 67 68 cases := []struct { 69 r []model.Interval 70 }{ 71 {r: []model.Interval{{Start: 1, End: 20}}}, 72 {r: []model.Interval{{Start: 1, End: 10}, {Start: 12, End: 20}, {Start: 21, End: 23}, {Start: 25, End: 30}}}, 73 {r: []model.Interval{{Start: 1, End: 10}, {Start: 12, End: 20}, {Start: 20, End: 30}}}, 74 {r: []model.Interval{{Start: 1, End: 10}, {Start: 12, End: 23}, {Start: 25, End: 30}}}, 75 {r: []model.Interval{{Start: 1, End: 23}, {Start: 12, End: 20}, {Start: 25, End: 30}}}, 76 {r: []model.Interval{{Start: 1, End: 23}, {Start: 12, End: 20}, {Start: 25, End: 3000}}}, 77 {r: []model.Interval{{Start: 0, End: 2000}}}, 78 {r: []model.Interval{{Start: 500, End: 2000}}}, 79 {r: []model.Interval{{Start: 0, End: 200}}}, 80 {r: []model.Interval{{Start: 1000, End: 20000}}}, 81 } 82 83 for _, c := range cases { 84 i := int64(-1) 85 it := NewDeletedSeriesIterator(NewConcreteSeriesIterator(&cs), c.r) 86 ranges := c.r[:] 87 88 for it.Next() != chunkenc.ValNone { 89 i++ 90 for _, tr := range ranges { 91 if inbound(model.Time(i), tr) { 92 i = int64(tr.End + 1) 93 ranges = ranges[1:] 94 } 95 } 96 97 require.Equal(t, true, i < 1000) 98 99 ts, v := it.At() 100 require.Equal(t, int64(cs.samples[i].Timestamp), ts) 101 require.Equal(t, float64(cs.samples[i].Value), v) 102 } 103 104 // There has been an extra call to Next(). 105 i++ 106 for _, tr := range ranges { 107 if inbound(model.Time(i), tr) { 108 i = int64(tr.End + 1) 109 ranges = ranges[1:] 110 } 111 } 112 113 require.Equal(t, true, i >= 1000) 114 require.NoError(t, it.Err()) 115 } 116 } 117 118 func TestDeletedIterator_WithSeek(t *testing.T) { 119 cs := ConcreteSeries{labels: labels.FromStrings("foo", "bar")} 120 // Insert random stuff from (0, 1000). 121 for i := 0; i < 1000; i++ { 122 cs.samples = append(cs.samples, model.SamplePair{Timestamp: model.Time(i), Value: model.SampleValue(rand.Float64())}) 123 } 124 125 cases := []struct { 126 r []model.Interval 127 seek int64 128 valueType chunkenc.ValueType 129 seekedTs int64 130 }{ 131 {r: []model.Interval{{Start: 1, End: 20}}, seek: 1, valueType: chunkenc.ValFloat, seekedTs: 21}, 132 {r: []model.Interval{{Start: 1, End: 20}}, seek: 20, valueType: chunkenc.ValFloat, seekedTs: 21}, 133 {r: []model.Interval{{Start: 1, End: 20}}, seek: 10, valueType: chunkenc.ValFloat, seekedTs: 21}, 134 {r: []model.Interval{{Start: 1, End: 20}}, seek: 999, valueType: chunkenc.ValFloat, seekedTs: 999}, 135 {r: []model.Interval{{Start: 1, End: 20}}, seek: 1000, valueType: chunkenc.ValNone}, 136 {r: []model.Interval{{Start: 1, End: 23}, {Start: 24, End: 40}, {Start: 45, End: 3000}}, seek: 1, valueType: chunkenc.ValFloat, seekedTs: 41}, 137 {r: []model.Interval{{Start: 5, End: 23}, {Start: 24, End: 40}, {Start: 41, End: 3000}}, seek: 5, valueType: chunkenc.ValNone}, 138 {r: []model.Interval{{Start: 0, End: 2000}}, seek: 10, valueType: chunkenc.ValNone}, 139 {r: []model.Interval{{Start: 500, End: 2000}}, seek: 10, valueType: chunkenc.ValFloat, seekedTs: 10}, 140 {r: []model.Interval{{Start: 500, End: 2000}}, seek: 501, valueType: chunkenc.ValNone}, 141 } 142 143 for _, c := range cases { 144 it := NewDeletedSeriesIterator(NewConcreteSeriesIterator(&cs), c.r) 145 146 require.Equal(t, c.valueType, it.Seek(c.seek)) 147 if c.valueType != chunkenc.ValNone { 148 ts, _ := it.At() 149 require.Equal(t, c.seekedTs, ts) 150 } 151 } 152 } 153 154 func inbound(t model.Time, interval model.Interval) bool { 155 return interval.Start <= t && t <= interval.End 156 }