github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/m3tsz/m3tsz_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 m3tsz
    22  
    23  import (
    24  	"math"
    25  	"math/rand"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/require"
    32  )
    33  
    34  func TestCountConversions(t *testing.T) {
    35  	testIntConversions(t, 1000, 0, 0)
    36  	testIntConversions(t, 1000, 1, 0)
    37  	testIntConversions(t, 1000, 2, 0)
    38  	testIntConversions(t, 1000, 10, 0)
    39  	testIntConversions(t, 1000, 18, 0)
    40  }
    41  
    42  func TestTimerConversions(t *testing.T) {
    43  	testIntConversions(t, 1000, 0, 6)
    44  	testIntConversions(t, 1000, 1, 6)
    45  	testIntConversions(t, 1000, 3, 6)
    46  	testIntConversions(t, 1000, 5, 6)
    47  	testIntConversions(t, 1000, 7, 6)
    48  }
    49  
    50  func TestSmallGaugeConversions(t *testing.T) {
    51  	testIntConversions(t, 1000, 0, 1)
    52  	testIntConversions(t, 1000, 0, 3)
    53  	testIntConversions(t, 1000, 1, 3)
    54  	testIntConversions(t, 1000, 3, 3)
    55  	testIntConversions(t, 1000, 5, 3)
    56  	testIntConversions(t, 1000, 7, 3)
    57  }
    58  
    59  func TestPreciseGaugeConversions(t *testing.T) {
    60  	testFloatConversions(t, 1000, 0, 16)
    61  	testFloatConversions(t, 1000, 1, 16)
    62  	testFloatConversions(t, 1000, 5, 16)
    63  }
    64  
    65  func TestLargeGaugeConversions(t *testing.T) {
    66  	testFloatConversions(t, 1000, 9, 2)
    67  	testFloatConversions(t, 1000, 10, 3)
    68  	testFloatConversions(t, 1000, 11, 3)
    69  }
    70  
    71  func TestNegativeGaugeConversions(t *testing.T) {
    72  	testNegativeIntConversions(t, 1000, 1, 0)
    73  	testNegativeIntConversions(t, 1000, 3, 0)
    74  	testNegativeIntConversions(t, 1000, 1, 2)
    75  	testNegativeIntConversions(t, 1000, 3, 2)
    76  }
    77  
    78  func TestConvertFromIntFloat(t *testing.T) {
    79  	validateConvertFromIntFloat(t, 1.0, 0, 1.0)
    80  	validateConvertFromIntFloat(t, 2.0, 0, 2.0)
    81  	validateConvertFromIntFloat(t, 10.0, 1, 1.0)
    82  	validateConvertFromIntFloat(t, 200.0, 2, 2.0)
    83  }
    84  
    85  func TestInfNan(t *testing.T) {
    86  	validateConvertToIntFloat(t, math.Inf(0), 0, math.Inf(0), 0, true)
    87  	validateConvertToIntFloat(t, math.Inf(0), 3, math.Inf(0), 0, true)
    88  	validateConvertToIntFloat(t, math.Inf(0), 6, math.Inf(0), 0, true)
    89  	validateConvertToIntFloat(t, math.Inf(-1), 0, math.Inf(-1), 0, true)
    90  	validateConvertToIntFloat(t, math.Inf(-1), 3, math.Inf(-1), 0, true)
    91  	validateConvertToIntFloat(t, math.Inf(-1), 6, math.Inf(-1), 0, true)
    92  	validateConvertToIntFloat(t, math.NaN(), 0, math.NaN(), 0, true)
    93  	validateConvertToIntFloat(t, math.NaN(), 3, math.NaN(), 0, true)
    94  	validateConvertToIntFloat(t, math.NaN(), 6, math.NaN(), 0, true)
    95  }
    96  
    97  func TestInvalidMult(t *testing.T) {
    98  	_, _, _, err := convertToIntFloat(1.0, maxMult+1)
    99  	require.Error(t, err)
   100  }
   101  
   102  func testIntConversions(t *testing.T, numConv, numDig, numDec int) {
   103  	validateIntConversions(t, numConv, numDig, numDec, false)
   104  }
   105  
   106  func testNegativeIntConversions(t *testing.T, numConv, numDig, numDec int) {
   107  	validateIntConversions(t, numConv, numDig, numDec, true)
   108  }
   109  
   110  func validateIntConversions(t *testing.T, numConv, numDig, numDec int, neg bool) {
   111  	rand.Seed(time.Now().UnixNano())
   112  	digMod := int(math.Pow10(numDig))
   113  	decMod := int(math.Pow10(numDec))
   114  	sign := 1.0
   115  	if neg {
   116  		sign = -1.0
   117  	}
   118  	for i := 0; i < numConv; i++ {
   119  		var val float64
   120  		dig := rand.Int() % digMod
   121  		dec := rand.Int() % decMod
   122  		if numDec == 0 {
   123  			val = sign * float64(dig)
   124  			validateConvertToIntFloat(t, val, numDec, val, 0, false)
   125  		} else {
   126  			val, _ = strconv.ParseFloat(strconv.Itoa(dig)+"."+strconv.Itoa(dec), 64)
   127  			expDecStr := strconv.Itoa(dec)
   128  			if len(expDecStr) < numDec {
   129  				expDecStr += strings.Repeat("0", numDec-len(expDecStr))
   130  			}
   131  			expected, _ := strconv.ParseInt(strconv.Itoa(dig)+expDecStr, 10, 64)
   132  			validateConvertToIntFloat(t, sign*val, numDec, sign*float64(expected), numDec, false)
   133  		}
   134  	}
   135  }
   136  
   137  func testFloatConversions(t *testing.T, numConv, numDig, numDec int) {
   138  	rand.Seed(time.Now().UnixNano())
   139  	digMod := int(math.Pow10(numDig))
   140  	decMod := int(math.Pow10(numDec))
   141  
   142  	for i := 0; i < numConv; i++ {
   143  		dig := rand.Int() % digMod
   144  		dec := rand.Int() % decMod
   145  
   146  		expDecStr := strconv.Itoa(dec)
   147  		if len(expDecStr) < numDec {
   148  			expDecStr += strings.Repeat("0", numDec-len(expDecStr))
   149  		}
   150  
   151  		val, _ := strconv.ParseFloat(strconv.Itoa(dig)+"."+expDecStr, 64)
   152  		validateConvertFloat(t, val, 0)
   153  	}
   154  }
   155  
   156  func validateConvertToIntFloat(t *testing.T, val float64, curDec int, expectedVal float64, maxExpectedDec int, expectedFloat bool) {
   157  	iv, dec, isFloat, err := convertToIntFloat(val, uint8(curDec))
   158  	require.NoError(t, err)
   159  	if math.IsNaN(val) {
   160  		require.True(t, math.IsNaN(iv))
   161  	} else {
   162  		require.Equal(t, expectedVal, iv)
   163  	}
   164  
   165  	require.True(t, uint8(maxExpectedDec) >= dec)
   166  	require.Equal(t, expectedFloat, isFloat)
   167  }
   168  
   169  func validateConvertFloat(t *testing.T, val float64, curDec int) {
   170  	v, dec, isFloat, err := convertToIntFloat(val, uint8(curDec))
   171  	require.NoError(t, err)
   172  	if isFloat {
   173  		require.Equal(t, val, v)
   174  		require.Equal(t, uint8(0), dec)
   175  		require.Equal(t, true, isFloat)
   176  		return
   177  	}
   178  
   179  	// In the case where the randomly generated float can be converted to an int due to lack
   180  	// of decimal places, confirm that the returned val is as expected with an error factor
   181  	// that is less than the dec returned
   182  	require.True(t, math.Abs(val-v/math.Pow10(int(dec))) < 1.0/math.Pow10(int(dec)))
   183  }
   184  
   185  func validateConvertFromIntFloat(t *testing.T, val float64, mult int, expected float64) {
   186  	v := convertFromIntFloat(val, uint8(mult))
   187  	require.Equal(t, expected, v)
   188  }