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  }