1package circonusllhist 2 3import ( 4 "bytes" 5 "fmt" 6 "math" 7 "math/rand" 8 "testing" 9 "time" 10) 11 12func TestCreate(t *testing.T) { 13 h := New() 14 /* 15 for j := 0; j < 100000; j++ { 16 h.RecordIntScale(rand.Intn(1000), 0) 17 } 18 */ 19 h.RecordIntScales(99, 0, int64(rand.Intn(2))+1) 20 buf := bytes.NewBuffer([]byte{}) 21 if err := h.Serialize(buf); err != nil { 22 t.Error(err) 23 } 24 h2, err := Deserialize(buf) 25 if err != nil { 26 t.Error(err) 27 } 28 for j := uint16(0); j < h2.used; j++ { 29 if h2.bvs[j].exp < 1 && (h2.bvs[j].val%10) != 0 { 30 t.Error(fmt.Errorf("bad bin[%v] %ve%v", j, float64(h2.bvs[j].val)/10.0, h2.bvs[j].exp)) 31 } 32 } 33} 34 35func TestSerialize(t *testing.T) { 36 h, err := NewFromStrings([]string{ 37 "H[0.0e+00]=1", 38 "H[1.0e+01]=1", 39 "H[2.0e+02]=1", 40 }, false) 41 if err != nil { 42 t.Error("could not read from strings for test") 43 } 44 45 buf := bytes.NewBuffer([]byte{}) 46 if err := h.Serialize(buf); err != nil { 47 t.Error(err) 48 } 49 50 h2, err := Deserialize(buf) 51 if err != nil { 52 t.Error(h2, err) 53 } 54 if !h.Equals(h2) { 55 t.Log(h.DecStrings()) 56 t.Log(h2.DecStrings()) 57 t.Error("histograms do not match") 58 } 59} 60 61func helpTestBin(t *testing.T, v float64, val, exp int8) { 62 b := newBinFromFloat64(v) 63 if b.val != val || b.exp != exp { 64 t.Errorf("%v -> [%v,%v] expected, but got [%v,%v]", v, val, exp, b.val, b.exp) 65 } 66} 67 68func fuzzy_equals(expected, actual float64) bool { 69 delta := math.Abs(expected / 100000.0) 70 if actual >= expected-delta && actual <= expected+delta { 71 return true 72 } 73 return false 74} 75 76func TestBins(t *testing.T) { 77 helpTestBin(t, 0.0, 0, 0) 78 helpTestBin(t, 100, 10, 2) 79 helpTestBin(t, 9.9999e-129, 0, 0) 80 helpTestBin(t, 1e-128, 10, -128) 81 helpTestBin(t, 1.00001e-128, 10, -128) 82 helpTestBin(t, 1.09999e-128, 10, -128) 83 helpTestBin(t, 1.1e-128, 11, -128) 84 helpTestBin(t, 1e127, 10, 127) 85 helpTestBin(t, 9.999e127, 99, 127) 86 helpTestBin(t, 1e128, -1, 0) 87 helpTestBin(t, -9.9999e-129, 0, 0) 88 helpTestBin(t, -1e-128, -10, -128) 89 helpTestBin(t, -1.00001e-128, -10, -128) 90 helpTestBin(t, -1.09999e-128, -10, -128) 91 helpTestBin(t, -1.1e-128, -11, -128) 92 helpTestBin(t, -1e127, -10, 127) 93 helpTestBin(t, -9.999e127, -99, 127) 94 helpTestBin(t, -1e128, -1, 0) 95 helpTestBin(t, 9.999e127, 99, 127) 96 97 h := New() 98 h.RecordIntScale(100, 0) 99 if h.bvs[0].val != 10 || h.bvs[0].exp != 2 { 100 t.Errorf("100 not added correctly") 101 } 102 103 h = New() 104 h.RecordValue(100.0) 105 if h.bvs[0].val != 10 || h.bvs[0].exp != 2 { 106 t.Errorf("100.0 not added correctly") 107 } 108} 109 110func TestRecordDuration(t *testing.T) { 111 tests := []struct { 112 input []time.Duration 113 inputUnit time.Duration 114 approxSum time.Duration 115 approxMean time.Duration 116 tolerance time.Duration 117 }{ 118 { 119 input: []time.Duration{time.Nanosecond}, 120 approxSum: time.Nanosecond, 121 approxMean: time.Nanosecond, 122 }, 123 { 124 input: []time.Duration{3 * time.Nanosecond}, 125 approxSum: 3 * time.Nanosecond, 126 approxMean: 3 * time.Nanosecond, 127 }, 128 { 129 input: []time.Duration{1000 * time.Second}, 130 approxSum: 1000 * time.Second, 131 approxMean: 1000 * time.Second, 132 }, 133 { 134 input: []time.Duration{ 135 4 * time.Second, 136 8 * time.Second, 137 }, 138 approxSum: 12.0 * time.Second, 139 approxMean: 6.0 * time.Second, 140 }, 141 } 142 143 fuzzyEquals := func(expected, actual time.Duration) bool { 144 diff := math.Abs(float64(expected) - float64(actual)) 145 if (diff / math.Max(float64(expected), float64(actual))) > 0.05 { 146 return false 147 } 148 return true 149 } 150 151 for n, test := range tests { 152 test := test 153 t.Run(fmt.Sprintf("%d", n), func(t *testing.T) { 154 h := New() 155 for _, dur := range test.input { 156 h.RecordDuration(dur) 157 } 158 159 if v := time.Duration(1000000000.0 * h.ApproxSum()); !fuzzyEquals(v, test.approxSum) { 160 t.Fatalf("%v approx sum bad: have=%v want=%v", test.input, h.ApproxSum(), test.approxSum) 161 } 162 163 if v := time.Duration(1000000000.0 * h.ApproxMean()); !fuzzyEquals(v, test.approxMean) { 164 t.Fatalf("%v approx mean bad: have=%v want=%v", test.input, v, test.approxMean) 165 } 166 }) 167 } 168} 169 170func helpTestVB(t *testing.T, v, b, w float64) { 171 bin := newBinFromFloat64(v) 172 out := bin.value() 173 interval := bin.binWidth() 174 if out < 0 { 175 interval *= -1.0 176 } 177 if !fuzzy_equals(b, out) { 178 t.Errorf("%v -> %v != %v\n", v, out, b) 179 } 180 if !fuzzy_equals(w, interval) { 181 t.Errorf("%v -> [%v] != [%v]\n", v, interval, w) 182 } 183} 184 185func TestBinSizes(t *testing.T) { 186 helpTestVB(t, 43.3, 43.0, 1.0) 187 helpTestVB(t, 99.9, 99.0, 1.0) 188 helpTestVB(t, 10.0, 10.0, 1.0) 189 helpTestVB(t, 1.0, 1.0, 0.1) 190 helpTestVB(t, 0.0002, 0.0002, 0.00001) 191 helpTestVB(t, 0.003, 0.003, 0.0001) 192 helpTestVB(t, 0.3201, 0.32, 0.01) 193 helpTestVB(t, 0.0035, 0.0035, 0.0001) 194 helpTestVB(t, -1.0, -1.0, -0.1) 195 helpTestVB(t, -0.00123, -0.0012, -0.0001) 196 helpTestVB(t, -987324, -980000, -10000) 197} 198