runtimehistogram_test.go 3.27 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
package metrics

import (
	"fmt"
	"math"
	"reflect"
	"runtime/metrics"
	"testing"
)

var _ Histogram = (*runtimeHistogram)(nil)

type runtimeHistogramTest struct {
	h metrics.Float64Histogram

	Count       int64
	Min         int64
	Max         int64
	Sum         int64
	Mean        float64
	Variance    float64
	StdDev      float64
	Percentiles []float64 // .5 .8 .9 .99 .995
}

// This test checks the results of statistical functions implemented
// by runtimeHistogramSnapshot.
func TestRuntimeHistogramStats(t *testing.T) {
	tests := []runtimeHistogramTest{
		0: {
			h: metrics.Float64Histogram{
				Counts:  []uint64{},
				Buckets: []float64{},
			},
			Count:       0,
			Max:         0,
			Min:         0,
			Sum:         0,
			Mean:        0,
			Variance:    0,
			StdDev:      0,
			Percentiles: []float64{0, 0, 0, 0, 0},
		},
		1: {
			// This checks the case where the highest bucket is +Inf.
			h: metrics.Float64Histogram{
				Counts:  []uint64{0, 1, 2},
				Buckets: []float64{0, 0.5, 1, math.Inf(1)},
			},
			Count:       3,
			Max:         1,
			Min:         0,
			Sum:         3,
			Mean:        0.9166666,
			Percentiles: []float64{1, 1, 1, 1, 1},
			Variance:    0.020833,
			StdDev:      0.144433,
		},
		2: {
			h: metrics.Float64Histogram{
				Counts:  []uint64{8, 6, 3, 1},
				Buckets: []float64{12, 16, 18, 24, 25},
			},
			Count:       18,
			Max:         25,
			Min:         12,
			Sum:         270,
			Mean:        16.75,
			Variance:    10.3015,
			StdDev:      3.2096,
			Percentiles: []float64{16, 18, 18, 24, 24},
		},
	}

	for i, test := range tests {
		t.Run(fmt.Sprint(i), func(t *testing.T) {
			s := runtimeHistogramSnapshot(test.h)

			if v := s.Count(); v != test.Count {
				t.Errorf("Count() = %v, want %v", v, test.Count)
			}
			if v := s.Min(); v != test.Min {
				t.Errorf("Min() = %v, want %v", v, test.Min)
			}
			if v := s.Max(); v != test.Max {
				t.Errorf("Max() = %v, want %v", v, test.Max)
			}
			if v := s.Sum(); v != test.Sum {
				t.Errorf("Sum() = %v, want %v", v, test.Sum)
			}
			if v := s.Mean(); !approxEqual(v, test.Mean, 0.0001) {
				t.Errorf("Mean() = %v, want %v", v, test.Mean)
			}
			if v := s.Variance(); !approxEqual(v, test.Variance, 0.0001) {
				t.Errorf("Variance() = %v, want %v", v, test.Variance)
			}
			if v := s.StdDev(); !approxEqual(v, test.StdDev, 0.0001) {
				t.Errorf("StdDev() = %v, want %v", v, test.StdDev)
			}
			ps := []float64{.5, .8, .9, .99, .995}
			if v := s.Percentiles(ps); !reflect.DeepEqual(v, test.Percentiles) {
				t.Errorf("Percentiles(%v) = %v, want %v", ps, v, test.Percentiles)
			}
		})
	}
}

func approxEqual(x, y, ε float64) bool {
	if math.IsInf(x, -1) && math.IsInf(y, -1) {
		return true
	}
	if math.IsInf(x, 1) && math.IsInf(y, 1) {
		return true
	}
	if math.IsNaN(x) && math.IsNaN(y) {
		return true
	}
	return math.Abs(x-y) < ε
}

// This test verifies that requesting Percentiles in unsorted order
// returns them in the requested order.
func TestRuntimeHistogramStatsPercentileOrder(t *testing.T) {
	p := runtimeHistogramSnapshot{
		Counts:  []uint64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
		Buckets: []float64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
	}
	result := p.Percentiles([]float64{1, 0.2, 0.5, 0.1, 0.2})
	expected := []float64{10, 2, 5, 1, 2}
	if !reflect.DeepEqual(result, expected) {
		t.Fatal("wrong result:", result)
	}
}