File: teststat.go

package info (click to toggle)
golang-github-go-kit-kit 0.13.0-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,784 kB
  • sloc: sh: 22; makefile: 11
file content (134 lines) | stat: -rw-r--r-- 3,673 bytes parent folder | download | duplicates (2)
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
134
// Package teststat provides helpers for testing metrics backends.
package teststat

import (
	"errors"
	"fmt"
	"math"
	"math/rand"
	"reflect"
	"sort"
	"strings"

	"github.com/go-kit/kit/metrics"
)

// TestCounter puts some deltas through the counter, and then calls the value
// func to check that the counter has the correct final value.
func TestCounter(counter metrics.Counter, value func() float64) error {
	want := FillCounter(counter)
	if have := value(); want != have {
		return fmt.Errorf("want %f, have %f", want, have)
	}

	return nil
}

// FillCounter puts some deltas through the counter and returns the total value.
func FillCounter(counter metrics.Counter) float64 {
	a := rand.Perm(100)
	n := rand.Intn(len(a))

	var want float64
	for i := 0; i < n; i++ {
		f := float64(a[i])
		counter.Add(f)
		want += f
	}
	return want
}

// TestGauge puts some values through the gauge, and then calls the value func
// to check that the gauge has the correct final value.
func TestGauge(gauge metrics.Gauge, value func() []float64) error {
	a := rand.Perm(100)
	n := rand.Intn(len(a)) + 1

	var want []float64
	for i := 0; i < n; i++ {
		f := float64(a[i])
		gauge.Set(f)
		want = append(want, f)
	}

	for i := 0; i < n; i++ {
		f := float64(a[i])
		gauge.Add(f)
		want = append(want, want[len(want)-1]+f)
	}

	have := value()

	switch len(have) {
	case 0:
		return fmt.Errorf("got 0 values")
	case 1: // provider doesn't support multi value
		if have[0] != want[len(want)-1] {
			return fmt.Errorf("want %f, have %f", want, have)
		}
	default: // provider support multi value gauges
		sort.Float64s(want)
		sort.Float64s(have)
		if !reflect.DeepEqual(want, have) {
			return fmt.Errorf("want %f, have %f", want, have)
		}
	}

	return nil
}

// TestHistogram puts some observations through the histogram, and then calls
// the quantiles func to checks that the histogram has computed the correct
// quantiles within some tolerance
func TestHistogram(histogram metrics.Histogram, quantiles func() (p50, p90, p95, p99 float64), tolerance float64) error {
	PopulateNormalHistogram(histogram, rand.Int())

	want50, want90, want95, want99 := normalQuantiles()
	have50, have90, have95, have99 := quantiles()

	var errs []string
	if want, have := want50, have50; !cmp(want, have, tolerance) {
		errs = append(errs, fmt.Sprintf("p50: want %f, have %f", want, have))
	}
	if want, have := want90, have90; !cmp(want, have, tolerance) {
		errs = append(errs, fmt.Sprintf("p90: want %f, have %f", want, have))
	}
	if want, have := want95, have95; !cmp(want, have, tolerance) {
		errs = append(errs, fmt.Sprintf("p95: want %f, have %f", want, have))
	}
	if want, have := want99, have99; !cmp(want, have, tolerance) {
		errs = append(errs, fmt.Sprintf("p99: want %f, have %f", want, have))
	}
	if len(errs) > 0 {
		return errors.New(strings.Join(errs, "; "))
	}

	return nil
}

var (
	// Count is the number of observations.
	Count = 12345

	// Mean is the center of the normal distribution of observations.
	Mean = 500

	// Stdev of the normal distribution of observations.
	Stdev = 25
)

// ExpectedObservationsLessThan returns the number of observations that should
// have a value less than or equal to the given value, given a normal
// distribution of observations described by Count, Mean, and Stdev.
func ExpectedObservationsLessThan(bucket int64) int64 {
	// https://code.google.com/p/gostat/source/browse/stat/normal.go
	cdf := ((1.0 / 2.0) * (1 + math.Erf((float64(bucket)-float64(Mean))/(float64(Stdev)*math.Sqrt2))))
	return int64(cdf * float64(Count))
}

func cmp(want, have, tol float64) bool {
	if (math.Abs(want-have) / want) > tol {
		return false
	}
	return true
}