Moved to google.golang.org/genproto/googleapis/api/annotations

Fixes #52
This commit is contained in:
Valerio Gheri
2017-03-31 18:01:58 +02:00
parent 024c5a4e4e
commit c40779224f
2037 changed files with 831329 additions and 1854 deletions

View File

@@ -0,0 +1,65 @@
package teststat
import (
"bufio"
"bytes"
"io"
"regexp"
"strconv"
"github.com/go-kit/kit/metrics/generic"
)
// SumLines expects a regex whose first capture group can be parsed as a
// float64. It will dump the WriterTo and parse each line, expecting to find a
// match. It returns the sum of all captured floats.
func SumLines(w io.WriterTo, regex string) func() float64 {
return func() float64 {
sum, _ := stats(w, regex, nil)
return sum
}
}
// LastLine expects a regex whose first capture group can be parsed as a
// float64. It will dump the WriterTo and parse each line, expecting to find a
// match. It returns the final captured float.
func LastLine(w io.WriterTo, regex string) func() float64 {
return func() float64 {
_, final := stats(w, regex, nil)
return final
}
}
// Quantiles expects a regex whose first capture group can be parsed as a
// float64. It will dump the WriterTo and parse each line, expecting to find a
// match. It observes all captured floats into a generic.Histogram with the
// given number of buckets, and returns the 50th, 90th, 95th, and 99th quantiles
// from that histogram.
func Quantiles(w io.WriterTo, regex string, buckets int) func() (float64, float64, float64, float64) {
return func() (float64, float64, float64, float64) {
h := generic.NewHistogram("quantile-test", buckets)
stats(w, regex, h)
return h.Quantile(0.50), h.Quantile(0.90), h.Quantile(0.95), h.Quantile(0.99)
}
}
func stats(w io.WriterTo, regex string, h *generic.Histogram) (sum, final float64) {
re := regexp.MustCompile(regex)
buf := &bytes.Buffer{}
w.WriteTo(buf)
//fmt.Fprintf(os.Stderr, "%s\n", buf.String())
s := bufio.NewScanner(buf)
for s.Scan() {
match := re.FindStringSubmatch(s.Text())
f, err := strconv.ParseFloat(match[1], 64)
if err != nil {
panic(err)
}
sum += f
final = f
if h != nil {
h.Observe(f)
}
}
return sum, final
}

View File

@@ -0,0 +1,72 @@
package teststat
import (
"math"
"math/rand"
"github.com/go-kit/kit/metrics"
)
// PopulateNormalHistogram makes a series of normal random observations into the
// histogram. The number of observations is determined by Count. The randomness
// is determined by Mean, Stdev, and the seed parameter.
//
// This is a low-level function, exported only for metrics that don't perform
// dynamic quantile computation, like a Prometheus Histogram (c.f. Summary). In
// most cases, you don't need to use this function, and can use TestHistogram
// instead.
func PopulateNormalHistogram(h metrics.Histogram, seed int) {
r := rand.New(rand.NewSource(int64(seed)))
for i := 0; i < Count; i++ {
sample := r.NormFloat64()*float64(Stdev) + float64(Mean)
if sample < 0 {
sample = 0
}
h.Observe(sample)
}
}
func normalQuantiles() (p50, p90, p95, p99 float64) {
return nvq(50), nvq(90), nvq(95), nvq(99)
}
func nvq(quantile int) float64 {
// https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function
return float64(Mean) + float64(Stdev)*math.Sqrt2*erfinv(2*(float64(quantile)/100)-1)
}
func erfinv(y float64) float64 {
// https://stackoverflow.com/questions/5971830/need-code-for-inverse-error-function
if y < -1.0 || y > 1.0 {
panic("invalid input")
}
var (
a = [4]float64{0.886226899, -1.645349621, 0.914624893, -0.140543331}
b = [4]float64{-2.118377725, 1.442710462, -0.329097515, 0.012229801}
c = [4]float64{-1.970840454, -1.624906493, 3.429567803, 1.641345311}
d = [2]float64{3.543889200, 1.637067800}
)
const y0 = 0.7
var x, z float64
if math.Abs(y) == 1.0 {
x = -y * math.Log(0.0)
} else if y < -y0 {
z = math.Sqrt(-math.Log((1.0 + y) / 2.0))
x = -(((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
} else {
if y < y0 {
z = y * y
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)
} else {
z = math.Sqrt(-math.Log((1.0 - y) / 2.0))
x = (((c[3]*z+c[2])*z+c[1])*z + c[0]) / ((d[1]*z+d[0])*z + 1.0)
}
x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
x = x - (math.Erf(x)-y)/(2.0/math.SqrtPi*math.Exp(-x*x))
}
return x
}

View File

@@ -0,0 +1,114 @@
// Package teststat provides helpers for testing metrics backends.
package teststat
import (
"errors"
"fmt"
"math"
"math/rand"
"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 {
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
}
if have := value(); want != have {
return fmt.Errorf("want %f, have %f", want, have)
}
return nil
}
// 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))
var want float64
for i := 0; i < n; i++ {
f := float64(a[i])
gauge.Set(f)
want = f
}
for i := 0; i < n; i++ {
f := float64(a[i])
gauge.Add(f)
want += f
}
if have := value(); 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
}