golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/trace/histogram_test.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package trace 6 7 import ( 8 "math" 9 "testing" 10 ) 11 12 type sumTest struct { 13 value int64 14 sum int64 15 sumOfSquares float64 16 total int64 17 } 18 19 var sumTests = []sumTest{ 20 {100, 100, 10000, 1}, 21 {50, 150, 12500, 2}, 22 {50, 200, 15000, 3}, 23 {50, 250, 17500, 4}, 24 } 25 26 type bucketingTest struct { 27 in int64 28 log int 29 bucket int 30 } 31 32 var bucketingTests = []bucketingTest{ 33 {0, 0, 0}, 34 {1, 1, 0}, 35 {2, 2, 1}, 36 {3, 2, 1}, 37 {4, 3, 2}, 38 {1000, 10, 9}, 39 {1023, 10, 9}, 40 {1024, 11, 10}, 41 {1000000, 20, 19}, 42 } 43 44 type multiplyTest struct { 45 in int64 46 ratio float64 47 expectedSum int64 48 expectedTotal int64 49 expectedSumOfSquares float64 50 } 51 52 var multiplyTests = []multiplyTest{ 53 {15, 2.5, 37, 2, 562.5}, 54 {128, 4.6, 758, 13, 77953.9}, 55 } 56 57 type percentileTest struct { 58 fraction float64 59 expected int64 60 } 61 62 var percentileTests = []percentileTest{ 63 {0.25, 48}, 64 {0.5, 96}, 65 {0.6, 109}, 66 {0.75, 128}, 67 {0.90, 205}, 68 {0.95, 230}, 69 {0.99, 256}, 70 } 71 72 func TestSum(t *testing.T) { 73 var h histogram 74 75 for _, test := range sumTests { 76 h.addMeasurement(test.value) 77 sum := h.sum 78 if sum != test.sum { 79 t.Errorf("h.Sum = %v WANT: %v", sum, test.sum) 80 } 81 82 sumOfSquares := h.sumOfSquares 83 if sumOfSquares != test.sumOfSquares { 84 t.Errorf("h.SumOfSquares = %v WANT: %v", sumOfSquares, test.sumOfSquares) 85 } 86 87 total := h.total() 88 if total != test.total { 89 t.Errorf("h.Total = %v WANT: %v", total, test.total) 90 } 91 } 92 } 93 94 func TestMultiply(t *testing.T) { 95 var h histogram 96 for i, test := range multiplyTests { 97 h.addMeasurement(test.in) 98 h.Multiply(test.ratio) 99 if h.sum != test.expectedSum { 100 t.Errorf("#%v: h.sum = %v WANT: %v", i, h.sum, test.expectedSum) 101 } 102 if h.total() != test.expectedTotal { 103 t.Errorf("#%v: h.total = %v WANT: %v", i, h.total(), test.expectedTotal) 104 } 105 if h.sumOfSquares != test.expectedSumOfSquares { 106 t.Errorf("#%v: h.SumOfSquares = %v WANT: %v", i, test.expectedSumOfSquares, h.sumOfSquares) 107 } 108 } 109 } 110 111 func TestBucketingFunctions(t *testing.T) { 112 for _, test := range bucketingTests { 113 log := log2(test.in) 114 if log != test.log { 115 t.Errorf("log2 = %v WANT: %v", log, test.log) 116 } 117 118 bucket := getBucket(test.in) 119 if bucket != test.bucket { 120 t.Errorf("getBucket = %v WANT: %v", bucket, test.bucket) 121 } 122 } 123 } 124 125 func TestAverage(t *testing.T) { 126 a := new(histogram) 127 average := a.average() 128 if average != 0 { 129 t.Errorf("Average of empty histogram was %v WANT: 0", average) 130 } 131 132 a.addMeasurement(1) 133 a.addMeasurement(1) 134 a.addMeasurement(3) 135 const expected = float64(5) / float64(3) 136 average = a.average() 137 138 if !isApproximate(average, expected) { 139 t.Errorf("Average = %g WANT: %v", average, expected) 140 } 141 } 142 143 func TestStandardDeviation(t *testing.T) { 144 a := new(histogram) 145 add(a, 10, 1<<4) 146 add(a, 10, 1<<5) 147 add(a, 10, 1<<6) 148 stdDev := a.standardDeviation() 149 const expected = 19.95 150 151 if !isApproximate(stdDev, expected) { 152 t.Errorf("StandardDeviation = %v WANT: %v", stdDev, expected) 153 } 154 155 // No values 156 a = new(histogram) 157 stdDev = a.standardDeviation() 158 159 if !isApproximate(stdDev, 0) { 160 t.Errorf("StandardDeviation = %v WANT: 0", stdDev) 161 } 162 163 add(a, 1, 1<<4) 164 if !isApproximate(stdDev, 0) { 165 t.Errorf("StandardDeviation = %v WANT: 0", stdDev) 166 } 167 168 add(a, 10, 1<<4) 169 if !isApproximate(stdDev, 0) { 170 t.Errorf("StandardDeviation = %v WANT: 0", stdDev) 171 } 172 } 173 174 func TestPercentileBoundary(t *testing.T) { 175 a := new(histogram) 176 add(a, 5, 1<<4) 177 add(a, 10, 1<<6) 178 add(a, 5, 1<<7) 179 180 for _, test := range percentileTests { 181 percentile := a.percentileBoundary(test.fraction) 182 if percentile != test.expected { 183 t.Errorf("h.PercentileBoundary (fraction=%v) = %v WANT: %v", test.fraction, percentile, test.expected) 184 } 185 } 186 } 187 188 func TestCopyFrom(t *testing.T) { 189 a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 190 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1} 191 b := histogram{6, 36, []int64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 192 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, 5, -1} 193 194 a.CopyFrom(&b) 195 196 if a.String() != b.String() { 197 t.Errorf("a.String = %s WANT: %s", a.String(), b.String()) 198 } 199 } 200 201 func TestClear(t *testing.T) { 202 a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 203 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1} 204 205 a.Clear() 206 207 expected := "0, 0.000000, 0, 0, []" 208 if a.String() != expected { 209 t.Errorf("a.String = %s WANT %s", a.String(), expected) 210 } 211 } 212 213 func TestNew(t *testing.T) { 214 a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 215 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1} 216 b := a.New() 217 218 expected := "0, 0.000000, 0, 0, []" 219 if b.(*histogram).String() != expected { 220 t.Errorf("b.(*histogram).String = %s WANT: %s", b.(*histogram).String(), expected) 221 } 222 } 223 224 func TestAdd(t *testing.T) { 225 // The tests here depend on the associativity of addMeasurement and Add. 226 // Add empty observation 227 a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 228 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1} 229 b := a.New() 230 231 expected := a.String() 232 a.Add(b) 233 if a.String() != expected { 234 t.Errorf("a.String = %s WANT: %s", a.String(), expected) 235 } 236 237 // Add same bucketed value, no new buckets 238 c := new(histogram) 239 d := new(histogram) 240 e := new(histogram) 241 c.addMeasurement(12) 242 d.addMeasurement(11) 243 e.addMeasurement(12) 244 e.addMeasurement(11) 245 c.Add(d) 246 if c.String() != e.String() { 247 t.Errorf("c.String = %s WANT: %s", c.String(), e.String()) 248 } 249 250 // Add bucketed values 251 f := new(histogram) 252 g := new(histogram) 253 h := new(histogram) 254 f.addMeasurement(4) 255 f.addMeasurement(12) 256 f.addMeasurement(100) 257 g.addMeasurement(18) 258 g.addMeasurement(36) 259 g.addMeasurement(255) 260 h.addMeasurement(4) 261 h.addMeasurement(12) 262 h.addMeasurement(100) 263 h.addMeasurement(18) 264 h.addMeasurement(36) 265 h.addMeasurement(255) 266 f.Add(g) 267 if f.String() != h.String() { 268 t.Errorf("f.String = %q WANT: %q", f.String(), h.String()) 269 } 270 271 // add buckets to no buckets 272 i := new(histogram) 273 j := new(histogram) 274 k := new(histogram) 275 j.addMeasurement(18) 276 j.addMeasurement(36) 277 j.addMeasurement(255) 278 k.addMeasurement(18) 279 k.addMeasurement(36) 280 k.addMeasurement(255) 281 i.Add(j) 282 if i.String() != k.String() { 283 t.Errorf("i.String = %q WANT: %q", i.String(), k.String()) 284 } 285 286 // add buckets to single value (no overlap) 287 l := new(histogram) 288 m := new(histogram) 289 n := new(histogram) 290 l.addMeasurement(0) 291 m.addMeasurement(18) 292 m.addMeasurement(36) 293 m.addMeasurement(255) 294 n.addMeasurement(0) 295 n.addMeasurement(18) 296 n.addMeasurement(36) 297 n.addMeasurement(255) 298 l.Add(m) 299 if l.String() != n.String() { 300 t.Errorf("l.String = %q WANT: %q", l.String(), n.String()) 301 } 302 303 // mixed order 304 o := new(histogram) 305 p := new(histogram) 306 o.addMeasurement(0) 307 o.addMeasurement(2) 308 o.addMeasurement(0) 309 p.addMeasurement(0) 310 p.addMeasurement(0) 311 p.addMeasurement(2) 312 if o.String() != p.String() { 313 t.Errorf("o.String = %q WANT: %q", o.String(), p.String()) 314 } 315 } 316 317 func add(h *histogram, times int, val int64) { 318 for i := 0; i < times; i++ { 319 h.addMeasurement(val) 320 } 321 } 322 323 func isApproximate(x, y float64) bool { 324 return math.Abs(x-y) < 1e-2 325 }