Package list golang-github-go-kit-kit / a352675
Fix metrics test structure Peter Bourgon 6 years ago
8 changed file(s) with 299 addition(s) and 196 deletion(s). Raw diff Collapse all Expand all
00 package expvar_test
11
22 import (
3 stdexpvar "expvar"
4 "fmt"
5 "math"
6 "math/rand"
7 "strconv"
83 "testing"
94
10 "github.com/peterbourgon/gokit/metrics"
115 "github.com/peterbourgon/gokit/metrics/expvar"
6 "github.com/peterbourgon/gokit/metrics/teststat"
127 )
138
149 func TestHistogramQuantiles(t *testing.T) {
1712 h := expvar.NewHistogram(metricName, 0, 100, 3, quantiles...)
1813
1914 const seed, mean, stdev int64 = 424242, 50, 10
20 populateNormalHistogram(t, h, seed, mean, stdev)
21 assertNormalHistogram(t, metricName, mean, stdev, quantiles)
15 teststat.PopulateNormalHistogram(t, h, seed, mean, stdev)
16 teststat.AssertExpvarNormalHistogram(t, metricName, mean, stdev, quantiles)
2217 }
23
24 func populateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) {
25 rand.Seed(seed)
26 for i := 0; i < 1234; i++ {
27 sample := int64(rand.NormFloat64()*float64(stdev) + float64(mean))
28 h.Observe(sample)
29 }
30 }
31
32 func assertNormalHistogram(t *testing.T, metricName string, mean, stdev int64, quantiles []int) {
33 const tolerance int = 2
34 for _, quantile := range quantiles {
35 want := normalValueAtQuantile(mean, stdev, quantile)
36 s := stdexpvar.Get(fmt.Sprintf("%s_p%02d", metricName, quantile)).String()
37 have, err := strconv.Atoi(s)
38 if err != nil {
39 t.Fatal(err)
40 }
41 if int(math.Abs(float64(want)-float64(have))) > tolerance {
42 t.Errorf("quantile %d: want %d, have %d", quantile, want, have)
43 }
44 }
45 }
46
47 // https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function
48 func normalValueAtQuantile(mean, stdev int64, quantile int) int64 {
49 return int64(float64(mean) + float64(stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1))
50 }
51
52 // https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function
53 func erfinv(y float64) float64 {
54 if y < -1.0 || y > 1.0 {
55 panic("invalid input")
56 }
57
58 var (
59 a = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}
60 b = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}
61 c = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}
62 d = [2]float64{3.543889200, 1.637067800}
63 )
64
65 const y0 = 0.7
66 var x, z float64
67
68 if math.Abs(y) == 1.0 {
69 x = -y * math.Log(0.0)
70 } else if y < -y0 {
71 z = math.Sqrt(-math.Log((1.0 + y) / 2.0))
72 x = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
73 } else {
74 if y < y0 {
75 z = y * y
76 x = y * (((a[3]*z+a[2])*z+a[1])*z + a[0]) / ((((b[3]*z+b[3])*z+b[1])*z+b[0])*z + 1.0)
77 } else {
78 z = math.Sqrt(-math.Log((1.0 - y) / 2.0))
79 x = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
80 }
81 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
82 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
83 }
84
85 return x
86 }
00 package metrics_test
11
22 import (
3 "expvar"
3 stdexpvar "expvar"
4 "fmt"
5 "io/ioutil"
6 "math"
7 "math/rand"
8 "net/http"
9 "net/http/httptest"
10 "regexp"
11 "strconv"
412 "strings"
513 "testing"
614
15 stdprometheus "github.com/prometheus/client_golang/prometheus"
16
717 "github.com/peterbourgon/gokit/metrics"
18 "github.com/peterbourgon/gokit/metrics/expvar"
19 "github.com/peterbourgon/gokit/metrics/prometheus"
820 )
921
1022 func TestMultiWith(t *testing.T) {
1123 c := metrics.NewMultiCounter(
12 metrics.NewExpvarCounter("foo"),
13 metrics.NewPrometheusCounter("test", "multi_with", "bar", "Bar counter.", []string{"a"}),
24 expvar.NewCounter("foo"),
25 prometheus.NewCounter("test", "multi_with", "bar", "Bar counter.", []string{"a"}),
1426 )
1527
1628 c.Add(1)
2941
3042 func TestMultiCounter(t *testing.T) {
3143 metrics.NewMultiCounter(
32 metrics.NewExpvarCounter("alpha"),
33 metrics.NewPrometheusCounter("test", "multi_counter", "beta", "Beta counter.", []string{}),
44 expvar.NewCounter("alpha"),
45 prometheus.NewCounter("test", "multi_counter", "beta", "Beta counter.", []string{}),
3446 ).Add(123)
3547
36 if want, have := "123", expvar.Get("alpha").String(); want != have {
48 if want, have := "123", stdexpvar.Get("alpha").String(); want != have {
3749 t.Errorf("expvar: want %q, have %q", want, have)
3850 }
3951
4860
4961 func TestMultiGauge(t *testing.T) {
5062 g := metrics.NewMultiGauge(
51 metrics.NewExpvarGauge("delta"),
52 metrics.NewPrometheusGauge("test", "multi_gauge", "kappa", "Kappa gauge.", []string{}),
63 expvar.NewGauge("delta"),
64 prometheus.NewGauge("test", "multi_gauge", "kappa", "Kappa gauge.", []string{}),
5365 )
5466
5567 g.Set(34)
5668
57 if want, have := "34", expvar.Get("delta").String(); want != have {
69 if want, have := "34", stdexpvar.Get("delta").String(); want != have {
5870 t.Errorf("expvar: want %q, have %q", want, have)
5971 }
6072 if want, have := strings.Join([]string{
6779
6880 g.Add(-40)
6981
70 if want, have := "-6", expvar.Get("delta").String(); want != have {
82 if want, have := "-6", stdexpvar.Get("delta").String(); want != have {
7183 t.Errorf("expvar: want %q, have %q", want, have)
7284 }
7385 if want, have := strings.Join([]string{
8294 func TestMultiHistogram(t *testing.T) {
8395 quantiles := []int{50, 90, 99}
8496 h := metrics.NewMultiHistogram(
85 metrics.NewExpvarHistogram("omicron", 0, 100, 3, quantiles...),
86 metrics.NewPrometheusHistogram("test", "multi_histogram", "nu", "Nu histogram.", []string{}),
97 expvar.NewHistogram("omicron", 0, 100, 3, quantiles...),
98 prometheus.NewHistogram("test", "multi_histogram", "nu", "Nu histogram.", []string{}),
8799 )
88100
89101 const seed, mean, stdev int64 = 123, 50, 10
91103 assertExpvarNormalHistogram(t, "omicron", mean, stdev, quantiles)
92104 assertPrometheusNormalHistogram(t, "test_multi_histogram_nu", mean, stdev)
93105 }
106
107 func populateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) {
108 rand.Seed(seed)
109 for i := 0; i < 1234; i++ {
110 sample := int64(rand.NormFloat64()*float64(stdev) + float64(mean))
111 h.Observe(sample)
112 }
113 }
114
115 func assertExpvarNormalHistogram(t *testing.T, metricName string, mean, stdev int64, quantiles []int) {
116 const tolerance int = 2
117 for _, quantile := range quantiles {
118 want := normalValueAtQuantile(mean, stdev, quantile)
119 s := stdexpvar.Get(fmt.Sprintf("%s_p%02d", metricName, quantile)).String()
120 have, err := strconv.Atoi(s)
121 if err != nil {
122 t.Fatal(err)
123 }
124 if int(math.Abs(float64(want)-float64(have))) > tolerance {
125 t.Errorf("quantile %d: want %d, have %d", quantile, want, have)
126 }
127 }
128 }
129
130 func assertPrometheusNormalHistogram(t *testing.T, metricName string, mean, stdev int64) {
131 scrape := scrapePrometheus(t)
132 const tolerance int = 5 // Prometheus approximates higher quantiles badly -_-;
133 for quantileInt, quantileStr := range map[int]string{50: "0.5", 90: "0.9", 99: "0.99"} {
134 want := normalValueAtQuantile(mean, stdev, quantileInt)
135 have := getPrometheusQuantile(t, scrape, metricName, quantileStr)
136 if int(math.Abs(float64(want)-float64(have))) > tolerance {
137 t.Errorf("%q: want %d, have %d", quantileStr, want, have)
138 }
139 }
140 }
141
142 // https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function
143 func normalValueAtQuantile(mean, stdev int64, quantile int) int64 {
144 return int64(float64(mean) + float64(stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1))
145 }
146
147 // https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function
148 func erfinv(y float64) float64 {
149 if y < -1.0 || y > 1.0 {
150 panic("invalid input")
151 }
152
153 var (
154 a = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}
155 b = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}
156 c = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}
157 d = [2]float64{3.543889200, 1.637067800}
158 )
159
160 const y0 = 0.7
161 var x, z float64
162
163 if math.Abs(y) == 1.0 {
164 x = -y * math.Log(0.0)
165 } else if y < -y0 {
166 z = math.Sqrt(-math.Log((1.0 + y) / 2.0))
167 x = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
168 } else {
169 if y < y0 {
170 z = y * y
171 x = y * (((a[3]*z+a[2])*z+a[1])*z + a[0]) / ((((b[3]*z+b[3])*z+b[1])*z+b[0])*z + 1.0)
172 } else {
173 z = math.Sqrt(-math.Log((1.0 - y) / 2.0))
174 x = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
175 }
176 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
177 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
178 }
179
180 return x
181 }
182
183 func scrapePrometheus(t *testing.T) string {
184 server := httptest.NewServer(stdprometheus.UninstrumentedHandler())
185 defer server.Close()
186
187 resp, err := http.Get(server.URL)
188 if err != nil {
189 t.Fatal(err)
190 }
191 defer resp.Body.Close()
192
193 buf, err := ioutil.ReadAll(resp.Body)
194 if err != nil {
195 t.Fatal(err)
196 }
197
198 return strings.TrimSpace(string(buf))
199 }
200
201 func getPrometheusQuantile(t *testing.T, scrape, name, quantileStr string) int {
202 matches := regexp.MustCompile(name+`{quantile="`+quantileStr+`"} ([0-9]+)`).FindAllStringSubmatch(scrape, -1)
203 if len(matches) < 1 {
204 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
205 }
206 if len(matches[0]) < 2 {
207 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
208 }
209 i, err := strconv.Atoi(matches[0][1])
210 if err != nil {
211 t.Fatal(err)
212 }
213 return i
214 }
00 package prometheus_test
11
22 import (
3 "io/ioutil"
4 "math"
5 "math/rand"
6 "net/http"
7 "net/http/httptest"
8 "regexp"
9 "strconv"
103 "strings"
114 "testing"
125
13 stdprometheus "github.com/prometheus/client_golang/prometheus"
14
156 "github.com/peterbourgon/gokit/metrics"
167 "github.com/peterbourgon/gokit/metrics/prometheus"
8 "github.com/peterbourgon/gokit/metrics/teststat"
179 )
1810
1911 func TestPrometheusLabelBehavior(t *testing.T) {
2618 `# TYPE test_prometheus_label_behavior_foobar counter`,
2719 `test_prometheus_label_behavior_foobar{unused_key="unknown",used_key="declared"} 1`,
2820 `test_prometheus_label_behavior_foobar{unused_key="unknown",used_key="unknown"} 1`,
29 }, "\n"), scrapePrometheus(t); !strings.Contains(have, want) {
21 }, "\n"), teststat.ScrapePrometheus(t); !strings.Contains(have, want) {
3022 t.Errorf("metric stanza not found or incorrect\n%s", have)
3123 }
3224 }
3931 `# HELP test_prometheus_counter_foobar Lorem ipsum.`,
4032 `# TYPE test_prometheus_counter_foobar counter`,
4133 `test_prometheus_counter_foobar 3`,
42 }, "\n"), scrapePrometheus(t); !strings.Contains(have, want) {
34 }, "\n"), teststat.ScrapePrometheus(t); !strings.Contains(have, want) {
4335 t.Errorf("metric stanza not found or incorrect\n%s", have)
4436 }
4537 c.Add(3)
4840 `# HELP test_prometheus_counter_foobar Lorem ipsum.`,
4941 `# TYPE test_prometheus_counter_foobar counter`,
5042 `test_prometheus_counter_foobar 10`,
51 }, "\n"), scrapePrometheus(t); !strings.Contains(have, want) {
43 }, "\n"), teststat.ScrapePrometheus(t); !strings.Contains(have, want) {
5244 t.Errorf("metric stanza not found or incorrect\n%s", have)
5345 }
5446 }
6052 `# HELP test_prometheus_gauge_foobar Dolor sit.`,
6153 `# TYPE test_prometheus_gauge_foobar gauge`,
6254 `test_prometheus_gauge_foobar 42`,
63 }, "\n"), scrapePrometheus(t); !strings.Contains(have, want) {
55 }, "\n"), teststat.ScrapePrometheus(t); !strings.Contains(have, want) {
6456 t.Errorf("metric stanza not found or incorrect\n%s", have)
6557 }
6658 c.Add(-43)
6860 `# HELP test_prometheus_gauge_foobar Dolor sit.`,
6961 `# TYPE test_prometheus_gauge_foobar gauge`,
7062 `test_prometheus_gauge_foobar -1`,
71 }, "\n"), scrapePrometheus(t); !strings.Contains(have, want) {
63 }, "\n"), teststat.ScrapePrometheus(t); !strings.Contains(have, want) {
7264 t.Errorf("metric stanza not found or incorrect\n%s", have)
7365 }
7466 }
7769 h := prometheus.NewHistogram("test", "prometheus_histogram", "foobar", "Qwerty asdf.", []string{})
7870
7971 const mean, stdev int64 = 50, 10
80 populateNormalHistogram(t, h, 34, mean, stdev)
81 assertNormalHistogram(t, "test_prometheus_histogram_foobar", mean, stdev)
72 teststat.PopulateNormalHistogram(t, h, 34, mean, stdev)
73 teststat.AssertPrometheusNormalHistogram(t, "test_prometheus_histogram_foobar", mean, stdev)
8274 }
83
84 func populateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) {
85 rand.Seed(seed)
86 for i := 0; i < 1234; i++ {
87 sample := int64(rand.NormFloat64()*float64(stdev) + float64(mean))
88 h.Observe(sample)
89 }
90 }
91
92 func assertNormalHistogram(t *testing.T, metricName string, mean, stdev int64) {
93 scrape := scrapePrometheus(t)
94 const tolerance int = 5 // Prometheus approximates higher quantiles badly -_-;
95 for quantileInt, quantileStr := range map[int]string{50: "0.5", 90: "0.9", 99: "0.99"} {
96 want := normalValueAtQuantile(mean, stdev, quantileInt)
97 have := getPrometheusQuantile(t, scrape, metricName, quantileStr)
98 if int(math.Abs(float64(want)-float64(have))) > tolerance {
99 t.Errorf("%q: want %d, have %d", quantileStr, want, have)
100 }
101 }
102 }
103
104 // https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function
105 func normalValueAtQuantile(mean, stdev int64, quantile int) int64 {
106 return int64(float64(mean) + float64(stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1))
107 }
108
109 // https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function
110 func erfinv(y float64) float64 {
111 if y < -1.0 || y > 1.0 {
112 panic("invalid input")
113 }
114
115 var (
116 a = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}
117 b = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}
118 c = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}
119 d = [2]float64{3.543889200, 1.637067800}
120 )
121
122 const y0 = 0.7
123 var x, z float64
124
125 if math.Abs(y) == 1.0 {
126 x = -y * math.Log(0.0)
127 } else if y < -y0 {
128 z = math.Sqrt(-math.Log((1.0 + y) / 2.0))
129 x = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
130 } else {
131 if y < y0 {
132 z = y * y
133 x = y * (((a[3]*z+a[2])*z+a[1])*z + a[0]) / ((((b[3]*z+b[3])*z+b[1])*z+b[0])*z + 1.0)
134 } else {
135 z = math.Sqrt(-math.Log((1.0 - y) / 2.0))
136 x = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
137 }
138 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
139 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
140 }
141
142 return x
143 }
144
145 func scrapePrometheus(t *testing.T) string {
146 server := httptest.NewServer(stdprometheus.UninstrumentedHandler())
147 defer server.Close()
148
149 resp, err := http.Get(server.URL)
150 if err != nil {
151 t.Fatal(err)
152 }
153 defer resp.Body.Close()
154
155 buf, err := ioutil.ReadAll(resp.Body)
156 if err != nil {
157 t.Fatal(err)
158 }
159
160 return strings.TrimSpace(string(buf))
161 }
162
163 func getPrometheusQuantile(t *testing.T, scrape, name, quantileStr string) int {
164 matches := regexp.MustCompile(name+`{quantile="`+quantileStr+`"} ([0-9]+)`).FindAllStringSubmatch(scrape, -1)
165 if len(matches) < 1 {
166 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
167 }
168 if len(matches[0]) < 2 {
169 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
170 }
171 i, err := strconv.Atoi(matches[0][1])
172 if err != nil {
173 t.Fatal(err)
174 }
175 return i
176 }
33 "testing"
44
55 "github.com/peterbourgon/gokit/metrics"
6 "github.com/peterbourgon/gokit/metrics/expvar"
67 )
78
89 func TestScaledHistogram(t *testing.T) {
1112 metricName := "test_scaled_histogram"
1213
1314 var h metrics.Histogram
14 h = metrics.NewExpvarHistogram(metricName, 0, 1000, 3, quantiles...)
15 h = expvar.NewHistogram(metricName, 0, 1000, 3, quantiles...)
1516 h = metrics.NewScaledHistogram(h, scale)
1617
1718 const seed, mean, stdev = 333, 500, 100 // input values
0 // Package teststat contains helper functions for statistical testing of
1 // metrics implementations.
2 package teststat
3
4 import (
5 "math"
6 "math/rand"
7 "testing"
8
9 "github.com/peterbourgon/gokit/metrics"
10 )
11
12 // PopulateNormalHistogram populates the Histogram with a normal distribution
13 // of observations.
14 func PopulateNormalHistogram(t *testing.T, h metrics.Histogram, seed int64, mean, stdev int64) {
15 rand.Seed(seed)
16 for i := 0; i < 1234; i++ {
17 sample := int64(rand.NormFloat64()*float64(stdev) + float64(mean))
18 h.Observe(sample)
19 }
20 }
21
22 // https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function
23 func normalValueAtQuantile(mean, stdev int64, quantile int) int64 {
24 return int64(float64(mean) + float64(stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1))
25 }
26
27 // https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function
28 func erfinv(y float64) float64 {
29 if y < -1.0 || y > 1.0 {
30 panic("invalid input")
31 }
32
33 var (
34 a = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}
35 b = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}
36 c = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}
37 d = [2]float64{3.543889200, 1.637067800}
38 )
39
40 const y0 = 0.7
41 var x, z float64
42
43 if math.Abs(y) == 1.0 {
44 x = -y * math.Log(0.0)
45 } else if y < -y0 {
46 z = math.Sqrt(-math.Log((1.0 + y) / 2.0))
47 x = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
48 } else {
49 if y < y0 {
50 z = y * y
51 x = y * (((a[3]*z+a[2])*z+a[1])*z + a[0]) / ((((b[3]*z+b[3])*z+b[1])*z+b[0])*z + 1.0)
52 } else {
53 z = math.Sqrt(-math.Log((1.0 - y) / 2.0))
54 x = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
55 }
56 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
57 x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
58 }
59
60 return x
61 }
0 package teststat
1
2 import (
3 "expvar"
4 "fmt"
5 "math"
6 "strconv"
7 "testing"
8 )
9
10 // AssertExpvarNormalHistogram ensures the expvar Histogram referenced by
11 // metricName abides a normal distribution.
12 func AssertExpvarNormalHistogram(t *testing.T, metricName string, mean, stdev int64, quantiles []int) {
13 const tolerance int = 2
14 for _, quantile := range quantiles {
15 want := normalValueAtQuantile(mean, stdev, quantile)
16 s := expvar.Get(fmt.Sprintf("%s_p%02d", metricName, quantile)).String()
17 have, err := strconv.Atoi(s)
18 if err != nil {
19 t.Fatal(err)
20 }
21 if int(math.Abs(float64(want)-float64(have))) > tolerance {
22 t.Errorf("quantile %d: want %d, have %d", quantile, want, have)
23 }
24 }
25 }
0 package teststat
1
2 import (
3 "io/ioutil"
4 "math"
5 "net/http"
6 "net/http/httptest"
7 "regexp"
8 "strconv"
9 "strings"
10 "testing"
11
12 "github.com/prometheus/client_golang/prometheus"
13 )
14
15 // ScrapePrometheus returns the text encoding of the current state of
16 // Prometheus.
17 func ScrapePrometheus(t *testing.T) string {
18 server := httptest.NewServer(prometheus.UninstrumentedHandler())
19 defer server.Close()
20
21 resp, err := http.Get(server.URL)
22 if err != nil {
23 t.Fatal(err)
24 }
25 defer resp.Body.Close()
26
27 buf, err := ioutil.ReadAll(resp.Body)
28 if err != nil {
29 t.Fatal(err)
30 }
31
32 return strings.TrimSpace(string(buf))
33 }
34
35 // AssertPrometheusNormalHistogram ensures the Prometheus Histogram referenced
36 // by metricName abides a normal distribution.
37 func AssertPrometheusNormalHistogram(t *testing.T, metricName string, mean, stdev int64) {
38 scrape := ScrapePrometheus(t)
39 const tolerance int = 5 // Prometheus approximates higher quantiles badly -_-;
40 for quantileInt, quantileStr := range map[int]string{50: "0.5", 90: "0.9", 99: "0.99"} {
41 want := normalValueAtQuantile(mean, stdev, quantileInt)
42 have := getPrometheusQuantile(t, scrape, metricName, quantileStr)
43 if int(math.Abs(float64(want)-float64(have))) > tolerance {
44 t.Errorf("%q: want %d, have %d", quantileStr, want, have)
45 }
46 }
47 }
48
49 func getPrometheusQuantile(t *testing.T, scrape, name, quantileStr string) int {
50 matches := regexp.MustCompile(name+`{quantile="`+quantileStr+`"} ([0-9]+)`).FindAllStringSubmatch(scrape, -1)
51 if len(matches) < 1 {
52 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
53 }
54 if len(matches[0]) < 2 {
55 t.Fatalf("%q: quantile %q not found in scrape", name, quantileStr)
56 }
57 i, err := strconv.Atoi(matches[0][1])
58 if err != nil {
59 t.Fatal(err)
60 }
61 return i
62 }
55 "time"
66
77 "github.com/peterbourgon/gokit/metrics"
8 "github.com/peterbourgon/gokit/metrics/expvar"
89 )
910
1011 func TestTimeHistogram(t *testing.T) {
1112 const metricName string = "test_time_histogram"
1213 quantiles := []int{50, 90, 99}
13 h0 := metrics.NewExpvarHistogram(metricName, 0, 200, 3, quantiles...)
14 h0 := expvar.NewHistogram(metricName, 0, 200, 3, quantiles...)
1415 h := metrics.NewTimeHistogram(h0, time.Millisecond)
1516 const seed, mean, stdev int64 = 321, 100, 20
1617