summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/prometheus/client_golang/prometheus/summary_test.go')
-rw-r--r--vendor/github.com/prometheus/client_golang/prometheus/summary_test.go347
1 files changed, 347 insertions, 0 deletions
diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go b/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
new file mode 100644
index 000000000..c4575ffbd
--- /dev/null
+++ b/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
@@ -0,0 +1,347 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "math"
+ "math/rand"
+ "sort"
+ "sync"
+ "testing"
+ "testing/quick"
+ "time"
+
+ dto "github.com/prometheus/client_model/go"
+)
+
+func benchmarkSummaryObserve(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewSummary(SummaryOpts{})
+
+ for i := 0; i < w; i++ {
+ go func() {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Observe(float64(i))
+ }
+
+ wg.Done()
+ }()
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkSummaryObserve1(b *testing.B) {
+ benchmarkSummaryObserve(1, b)
+}
+
+func BenchmarkSummaryObserve2(b *testing.B) {
+ benchmarkSummaryObserve(2, b)
+}
+
+func BenchmarkSummaryObserve4(b *testing.B) {
+ benchmarkSummaryObserve(4, b)
+}
+
+func BenchmarkSummaryObserve8(b *testing.B) {
+ benchmarkSummaryObserve(8, b)
+}
+
+func benchmarkSummaryWrite(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewSummary(SummaryOpts{})
+
+ for i := 0; i < 1000000; i++ {
+ s.Observe(float64(i))
+ }
+
+ for j := 0; j < w; j++ {
+ outs := make([]dto.Metric, b.N)
+
+ go func(o []dto.Metric) {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Write(&o[i])
+ }
+
+ wg.Done()
+ }(outs)
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkSummaryWrite1(b *testing.B) {
+ benchmarkSummaryWrite(1, b)
+}
+
+func BenchmarkSummaryWrite2(b *testing.B) {
+ benchmarkSummaryWrite(2, b)
+}
+
+func BenchmarkSummaryWrite4(b *testing.B) {
+ benchmarkSummaryWrite(4, b)
+}
+
+func BenchmarkSummaryWrite8(b *testing.B) {
+ benchmarkSummaryWrite(8, b)
+}
+
+func TestSummaryConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%5 + 1)
+ total := mutations * concLevel
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sum := NewSummary(SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ })
+
+ allVars := make([]float64, total)
+ var sampleSum float64
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ allVars[i*mutations+j] = v
+ sampleSum += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for _, v := range vals {
+ sum.Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ sort.Float64s(allVars)
+ start.Done()
+ end.Wait()
+
+ m := &dto.Metric{}
+ sum.Write(m)
+ if got, want := int(*m.Summary.SampleCount), total; got != want {
+ t.Errorf("got sample count %d, want %d", got, want)
+ }
+ if got, want := *m.Summary.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f, want %f", got, want)
+ }
+
+ objectives := make([]float64, 0, len(DefObjectives))
+ for qu := range DefObjectives {
+ objectives = append(objectives, qu)
+ }
+ sort.Float64s(objectives)
+
+ for i, wantQ := range objectives {
+ ε := DefObjectives[wantQ]
+ gotQ := *m.Summary.Quantile[i].Quantile
+ gotV := *m.Summary.Quantile[i].Value
+ min, max := getBounds(allVars, wantQ, ε)
+ if gotQ != wantQ {
+ t.Errorf("got quantile %f, want %f", gotQ, wantQ)
+ }
+ if gotV < min || gotV > max {
+ t.Errorf("got %f for quantile %f, want [%f,%f]", gotV, gotQ, min, max)
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSummaryVecConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ objectives := make([]float64, 0, len(DefObjectives))
+ for qu := range DefObjectives {
+
+ objectives = append(objectives, qu)
+ }
+ sort.Float64s(objectives)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%7 + 1)
+ vecLength := int(n%3 + 1)
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sum := NewSummaryVec(
+ SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ },
+ []string{"label"},
+ )
+
+ allVars := make([][]float64, vecLength)
+ sampleSums := make([]float64, vecLength)
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ picks := make([]int, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ pick := rand.Intn(vecLength)
+ picks[j] = pick
+ allVars[pick] = append(allVars[pick], v)
+ sampleSums[pick] += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for i, v := range vals {
+ sum.WithLabelValues(string('A' + picks[i])).Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ for _, vars := range allVars {
+ sort.Float64s(vars)
+ }
+ start.Done()
+ end.Wait()
+
+ for i := 0; i < vecLength; i++ {
+ m := &dto.Metric{}
+ s := sum.WithLabelValues(string('A' + i))
+ s.Write(m)
+ if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want {
+ t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want)
+ }
+ if got, want := *m.Summary.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f for label %c, want %f", got, 'A'+i, want)
+ }
+ for j, wantQ := range objectives {
+ ε := DefObjectives[wantQ]
+ gotQ := *m.Summary.Quantile[j].Quantile
+ gotV := *m.Summary.Quantile[j].Value
+ min, max := getBounds(allVars[i], wantQ, ε)
+ if gotQ != wantQ {
+ t.Errorf("got quantile %f for label %c, want %f", gotQ, 'A'+i, wantQ)
+ }
+ if gotV < min || gotV > max {
+ t.Errorf("got %f for quantile %f for label %c, want [%f,%f]", gotV, gotQ, 'A'+i, min, max)
+ }
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSummaryDecay(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ // More because it depends on timing than because it is particularly long...
+ }
+
+ sum := NewSummary(SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ MaxAge: 100 * time.Millisecond,
+ Objectives: map[float64]float64{0.1: 0.001},
+ AgeBuckets: 10,
+ })
+
+ m := &dto.Metric{}
+ i := 0
+ tick := time.NewTicker(time.Millisecond)
+ for _ = range tick.C {
+ i++
+ sum.Observe(float64(i))
+ if i%10 == 0 {
+ sum.Write(m)
+ if got, want := *m.Summary.Quantile[0].Value, math.Max(float64(i)/10, float64(i-90)); math.Abs(got-want) > 20 {
+ t.Errorf("%d. got %f, want %f", i, got, want)
+ }
+ m.Reset()
+ }
+ if i >= 1000 {
+ break
+ }
+ }
+ tick.Stop()
+ // Wait for MaxAge without observations and make sure quantiles are NaN.
+ time.Sleep(100 * time.Millisecond)
+ sum.Write(m)
+ if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) {
+ t.Errorf("got %f, want NaN after expiration", got)
+ }
+}
+
+func getBounds(vars []float64, q, ε float64) (min, max float64) {
+ // TODO(beorn7): This currently tolerates an error of up to 2*ε. The
+ // error must be at most ε, but for some reason, it's sometimes slightly
+ // higher. That's a bug.
+ n := float64(len(vars))
+ lower := int((q - 2*ε) * n)
+ upper := int(math.Ceil((q + 2*ε) * n))
+ min = vars[0]
+ if lower > 1 {
+ min = vars[lower-1]
+ }
+ max = vars[len(vars)-1]
+ if upper < len(vars) {
+ max = vars[upper-1]
+ }
+ return
+}