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 }