github.com/m3db/m3@v1.5.0/src/aggregator/aggregation/quantile/cm/stream_test.go (about) 1 // Copyright (c) 2016 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 cm 22 23 import ( 24 "math" 25 "math/rand" 26 "testing" 27 28 "github.com/stretchr/testify/require" 29 ) 30 31 const ( 32 testInsertAndCompressEvery = 100 33 ) 34 35 var ( 36 testQuantiles = []float64{0.5, 0.9, 0.99} 37 ) 38 39 func testStreamOptions() Options { 40 return NewOptions(). 41 SetEps(0.01). 42 SetCapacity(16) 43 } 44 45 func TestEmptyStream(t *testing.T) { 46 opts := testStreamOptions() 47 s := NewStream(opts) 48 s.ResetSetData(testQuantiles) 49 require.Equal(t, 0.0, s.Min()) 50 require.Equal(t, 0.0, s.Max()) 51 for _, q := range testQuantiles { 52 require.Equal(t, 0.0, s.Quantile(q)) 53 } 54 } 55 56 func TestStreamWithOnePositiveSample(t *testing.T) { 57 opts := testStreamOptions() 58 s := NewStream(opts) 59 s.ResetSetData(testQuantiles) 60 s.Add(100.0) 61 s.Flush() 62 63 require.Equal(t, 100.0, s.Min()) 64 require.Equal(t, 100.0, s.Max()) 65 for _, q := range testQuantiles { 66 require.Equal(t, 100.0, s.Quantile(q)) 67 } 68 } 69 70 func TestStreamWithOneNegativeSample(t *testing.T) { 71 opts := testStreamOptions() 72 s := NewStream(opts) 73 s.ResetSetData(testQuantiles) 74 s.Add(-100.0) 75 s.Flush() 76 77 require.Equal(t, -100.0, s.Min()) 78 require.Equal(t, -100.0, s.Max()) 79 for _, q := range testQuantiles { 80 require.Equal(t, -100.0, s.Quantile(q)) 81 } 82 } 83 84 func TestStreamWithThreeSamples(t *testing.T) { 85 opts := testStreamOptions() 86 s := NewStream(opts) 87 s.ResetSetData(testQuantiles) 88 for _, val := range []float64{100.0, 200.0, 300.0} { 89 s.Add(val) 90 } 91 s.Flush() 92 93 require.Equal(t, 100.0, s.Min()) 94 require.Equal(t, 300.0, s.Max()) 95 expected := []float64{200.0, 300.0, 300.0} 96 for i, q := range testQuantiles { 97 require.Equal(t, expected[i], s.Quantile(q)) 98 } 99 } 100 101 func TestStreamWithIncreasingSamplesNoPeriodicInsertCompress(t *testing.T) { 102 opts := testStreamOptions() 103 testStreamWithIncreasingSamples(t, opts) 104 } 105 106 func TestStreamWithIncreasingSamplesPeriodicInsertCompress(t *testing.T) { 107 opts := testStreamOptions().SetInsertAndCompressEvery(testInsertAndCompressEvery) 108 testStreamWithIncreasingSamples(t, opts) 109 } 110 111 func TestStreamWithDecreasingSamplesNoPeriodicInsertCompress(t *testing.T) { 112 opts := testStreamOptions() 113 testStreamWithDecreasingSamples(t, opts) 114 } 115 116 func TestStreamWithDecreasingSamplesPeriodicInsertCompress(t *testing.T) { 117 opts := testStreamOptions().SetInsertAndCompressEvery(testInsertAndCompressEvery) 118 testStreamWithDecreasingSamples(t, opts) 119 } 120 121 func TestStreamWithRandomSamplesNoPeriodicInsertCompress(t *testing.T) { 122 opts := testStreamOptions() 123 testStreamWithRandomSamples(t, opts) 124 } 125 126 func TestStreamWithRandomSamplesPeriodicInsertCompress(t *testing.T) { 127 opts := testStreamOptions().SetInsertAndCompressEvery(testInsertAndCompressEvery) 128 testStreamWithRandomSamples(t, opts) 129 } 130 131 func TestStreamWithSkewedDistributionNoPeriodicInsertCompress(t *testing.T) { 132 opts := testStreamOptions() 133 testStreamWithSkewedDistribution(t, opts) 134 } 135 136 func TestStreamWithSkewedDistributionPeriodicInsertCompress(t *testing.T) { 137 opts := testStreamOptions().SetInsertAndCompressEvery(testInsertAndCompressEvery) 138 testStreamWithSkewedDistribution(t, opts) 139 } 140 141 func TestStreamClose(t *testing.T) { 142 opts := testStreamOptions() 143 s := NewStream(opts) 144 s.ResetSetData(testQuantiles) 145 require.False(t, s.closed) 146 147 // Close the stream. 148 s.Close() 149 require.True(t, s.closed) 150 151 // Close the stream again, should be a no-op. 152 s.Close() 153 require.True(t, s.closed) 154 } 155 156 func testStreamWithIncreasingSamples(t *testing.T, opts Options) { 157 numSamples := 100000 158 s := NewStream(opts) 159 s.ResetSetData(testQuantiles) 160 for i := 0; i < numSamples; i++ { 161 s.Add(float64(i)) 162 } 163 s.Flush() 164 165 require.Equal(t, 0.0, s.Min()) 166 require.Equal(t, float64(numSamples-1), s.Max()) 167 margin := float64(numSamples) * opts.Eps() 168 for _, q := range testQuantiles { 169 val := s.Quantile(q) 170 require.True(t, val >= float64(numSamples)*q-margin && val <= float64(numSamples)*q+margin) 171 } 172 } 173 174 func testStreamWithDecreasingSamples(t *testing.T, opts Options) { 175 numSamples := 100000 176 s := NewStream(opts) 177 s.ResetSetData(testQuantiles) 178 for i := numSamples - 1; i >= 0; i-- { 179 s.Add(float64(i)) 180 } 181 s.Flush() 182 183 require.Equal(t, 0.0, s.Min()) 184 require.Equal(t, float64(numSamples-1), s.Max()) 185 margin := float64(numSamples) * opts.Eps() 186 for _, q := range testQuantiles { 187 val := s.Quantile(q) 188 require.True(t, val >= float64(numSamples)*q-margin && val <= float64(numSamples)*q+margin) 189 } 190 } 191 192 func testStreamWithRandomSamples(t *testing.T, opts Options) { 193 numSamples := 100000 194 maxInt64 := int64(math.MaxInt64) 195 s := NewStream(opts) 196 s.ResetSetData(testQuantiles) 197 min := math.MaxFloat64 198 max := -1.0 199 200 rand.Seed(100) 201 for i := 0; i < numSamples; i++ { 202 v := float64(rand.Int63n(maxInt64)) 203 min = math.Min(min, v) 204 max = math.Max(max, v) 205 s.Add(v) 206 207 } 208 s.Flush() 209 210 require.Equal(t, min, s.Min()) 211 require.Equal(t, max, s.Max()) 212 margin := float64(maxInt64) * opts.Eps() 213 for _, q := range testQuantiles { 214 val := s.Quantile(q) 215 require.True(t, val >= float64(maxInt64)*q-margin && val <= float64(maxInt64)*q+margin) 216 } 217 } 218 219 func testStreamWithSkewedDistribution(t *testing.T, opts Options) { 220 s := NewStream(opts) 221 s.ResetSetData(testQuantiles) 222 for i := 0; i < 10000; i++ { 223 s.Add(1.0) 224 } 225 226 // Add a huge sample value (10M). 227 s.Add(10000000.0) 228 s.Flush() 229 230 require.Equal(t, 1.0, s.Min()) 231 require.Equal(t, 10000000.0, s.Max()) 232 for _, q := range testQuantiles { 233 require.Equal(t, 1.0, s.Quantile(q)) 234 } 235 }