209 lines
6.8 KiB
Go
209 lines
6.8 KiB
Go
package metrics
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestGetBucketIdx(t *testing.T) {
|
|
f := func(v float64, idxExpected uint) {
|
|
t.Helper()
|
|
idx := getBucketIdx(v)
|
|
if idx != idxExpected {
|
|
t.Fatalf("unexpected getBucketIdx(%g); got %d; want %d", v, idx, idxExpected)
|
|
}
|
|
}
|
|
f(0, 0)
|
|
f(math.Pow10(e10Min-10), 1)
|
|
f(math.Pow10(e10Min-1), 1)
|
|
f(1.5*math.Pow10(e10Min-1), 1)
|
|
f(2*math.Pow10(e10Min-1), 1)
|
|
f(3*math.Pow10(e10Min-1), 1)
|
|
f(9*math.Pow10(e10Min-1), 1)
|
|
f(9.999*math.Pow10(e10Min-1), 1)
|
|
f(math.Pow10(e10Min), 1)
|
|
f(1.00001*math.Pow10(e10Min), 2)
|
|
f(1.5*math.Pow10(e10Min), 2)
|
|
f(1.999999*math.Pow10(e10Min), 2)
|
|
f(2*math.Pow10(e10Min), 2)
|
|
f(2.0000001*math.Pow10(e10Min), 3)
|
|
f(2.999*math.Pow10(e10Min), 3)
|
|
f(8.999*math.Pow10(e10Min), 9)
|
|
f(9*math.Pow10(e10Min), 9)
|
|
f(9.01*math.Pow10(e10Min), 10)
|
|
f(9.99999*math.Pow10(e10Min), 10)
|
|
f(math.Pow10(e10Min+1), 10)
|
|
f(1.9*math.Pow10(e10Min+1), 11)
|
|
f(9.9*math.Pow10(e10Min+1), 19)
|
|
f(math.Pow10(e10Min+2), 19)
|
|
f(math.Pow10(e10Min+3), 28)
|
|
f(5*math.Pow10(e10Min+3), 32)
|
|
f(0.1, 1-9*(e10Min+1))
|
|
f(0.11, 2-9*(e10Min+1))
|
|
f(0.95, 1-9*e10Min)
|
|
f(1, 1-9*e10Min)
|
|
f(2, 2-9*e10Min)
|
|
f(math.Pow10(e10Max), 1+9*(e10Max-e10Min))
|
|
f(2*math.Pow10(e10Max), 2+9*(e10Max-e10Min))
|
|
f(9.999*math.Pow10(e10Max), 10+9*(e10Max-e10Min))
|
|
f(math.Pow10(e10Max+1), 10+9*(e10Max-e10Min))
|
|
f(2*math.Pow10(e10Max+1), 11+9*(e10Max-e10Min))
|
|
f(9*math.Pow10(e10Max+1), 11+9*(e10Max-e10Min))
|
|
f(math.Pow10(e10Max+5), 11+9*(e10Max-e10Min))
|
|
f(12.34*math.Pow10(e10Max+7), 11+9*(e10Max-e10Min))
|
|
f(math.Inf(1), 11+9*(e10Max-e10Min))
|
|
}
|
|
|
|
func TestGetRangeEndFromBucketIdx(t *testing.T) {
|
|
f := func(idx uint, endExpected string) {
|
|
t.Helper()
|
|
end := getRangeEndFromBucketIdx(idx)
|
|
if end != endExpected {
|
|
t.Fatalf("unexpected getRangeEndFromBucketIdx(%d); got %s; want %s", idx, end, endExpected)
|
|
}
|
|
}
|
|
f(0, "0")
|
|
f(1, fmt.Sprintf("1e%d", e10Min))
|
|
f(2, fmt.Sprintf("2e%d", e10Min))
|
|
f(3, fmt.Sprintf("3e%d", e10Min))
|
|
f(9, fmt.Sprintf("9e%d", e10Min))
|
|
f(10, fmt.Sprintf("1e%d", e10Min+1))
|
|
f(11, fmt.Sprintf("2e%d", e10Min+1))
|
|
f(16, fmt.Sprintf("7e%d", e10Min+1))
|
|
f(17, fmt.Sprintf("8e%d", e10Min+1))
|
|
f(18, fmt.Sprintf("9e%d", e10Min+1))
|
|
f(19, fmt.Sprintf("1e%d", e10Min+2))
|
|
f(20, fmt.Sprintf("2e%d", e10Min+2))
|
|
f(21, fmt.Sprintf("3e%d", e10Min+2))
|
|
f(bucketsCount-21, fmt.Sprintf("9e%d", e10Max-2))
|
|
f(bucketsCount-20, fmt.Sprintf("1e%d", e10Max-1))
|
|
f(bucketsCount-16, fmt.Sprintf("5e%d", e10Max-1))
|
|
f(bucketsCount-13, fmt.Sprintf("8e%d", e10Max-1))
|
|
f(bucketsCount-12, fmt.Sprintf("9e%d", e10Max-1))
|
|
f(bucketsCount-11, fmt.Sprintf("1e%d", e10Max))
|
|
f(bucketsCount-10, fmt.Sprintf("2e%d", e10Max))
|
|
f(bucketsCount-4, fmt.Sprintf("8e%d", e10Max))
|
|
f(bucketsCount-3, fmt.Sprintf("9e%d", e10Max))
|
|
f(bucketsCount-2, fmt.Sprintf("1e%d", e10Max+1))
|
|
f(bucketsCount-1, "+Inf")
|
|
}
|
|
|
|
func TestHistogramSerial(t *testing.T) {
|
|
name := `TestHistogramSerial`
|
|
h := NewHistogram(name)
|
|
|
|
// Verify that the histogram is invisible in the output of WritePrometheus when it has no data.
|
|
var bb bytes.Buffer
|
|
WritePrometheus(&bb, false)
|
|
result := bb.String()
|
|
if strings.Contains(result, name) {
|
|
t.Fatalf("histogram %s shouldn't be visible in the WritePrometheus output; got\n%s", name, result)
|
|
}
|
|
|
|
// Write data to histogram
|
|
for i := 84; i < 324; i++ {
|
|
h.Update(float64(i))
|
|
}
|
|
|
|
// Make sure the histogram prints <prefix>_xbucket on marshalTo call
|
|
testMarshalTo(t, h, "prefix", "prefix_bucket{vmrange=\"8e1...9e1\"} 7\nprefix_bucket{vmrange=\"9e1...1e2\"} 10\nprefix_bucket{vmrange=\"1e2...2e2\"} 100\nprefix_bucket{vmrange=\"2e2...3e2\"} 100\nprefix_bucket{vmrange=\"3e2...4e2\"} 23\nprefix_sum 48840\nprefix_count 240\n")
|
|
testMarshalTo(t, h, ` m{foo="bar"}`, "\t m_bucket{foo=\"bar\",vmrange=\"8e1...9e1\"} 7\n\t m_bucket{foo=\"bar\",vmrange=\"9e1...1e2\"} 10\n\t m_bucket{foo=\"bar\",vmrange=\"1e2...2e2\"} 100\n\t m_bucket{foo=\"bar\",vmrange=\"2e2...3e2\"} 100\n\t m_bucket{foo=\"bar\",vmrange=\"3e2...4e2\"} 23\n\t m_sum{foo=\"bar\"} 48840\n\t m_count{foo=\"bar\"} 240\n")
|
|
|
|
// Verify supported ranges
|
|
for i := -100; i < 100; i++ {
|
|
h.Update(1.23 * math.Pow10(i))
|
|
}
|
|
h.UpdateDuration(time.Now().Add(-time.Minute))
|
|
|
|
// Verify edge cases
|
|
h.Update(math.Inf(1))
|
|
h.Update(math.NaN())
|
|
h.Update(-123)
|
|
|
|
// Make sure the histogram becomes visible in the output of WritePrometheus,
|
|
// since now it contains values.
|
|
bb.Reset()
|
|
WritePrometheus(&bb, false)
|
|
result = bb.String()
|
|
if !strings.Contains(result, name) {
|
|
t.Fatalf("missing histogram %s in the WritePrometheus output; got\n%s", name, result)
|
|
}
|
|
}
|
|
|
|
func TestHistogramConcurrent(t *testing.T) {
|
|
name := "HistogramConcurrent"
|
|
h := NewHistogram(name)
|
|
err := testConcurrent(func() error {
|
|
for i := 0; i < 10; i++ {
|
|
h.Update(float64(i))
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testMarshalTo(t, h, "prefix", "prefix_bucket{vmrange=\"0...0\"} 5\nprefix_bucket{vmrange=\"9e-1...1\"} 5\nprefix_bucket{vmrange=\"1...2\"} 5\nprefix_bucket{vmrange=\"2...3\"} 5\nprefix_bucket{vmrange=\"3...4\"} 5\nprefix_bucket{vmrange=\"4...5\"} 5\nprefix_bucket{vmrange=\"5...6\"} 5\nprefix_bucket{vmrange=\"6...7\"} 5\nprefix_bucket{vmrange=\"7...8\"} 5\nprefix_bucket{vmrange=\"8...9\"} 5\nprefix_sum 225\nprefix_count 50\n")
|
|
|
|
var labels []string
|
|
var values []uint64
|
|
h.VisitNonZeroBuckets(func(label string, value uint64) {
|
|
labels = append(labels, label)
|
|
values = append(values, value)
|
|
})
|
|
labelsExpected := []string{"0...0", "9e-1...1", "1...2", "2...3", "3...4", "4...5", "5...6", "6...7", "7...8", "8...9"}
|
|
if !reflect.DeepEqual(labels, labelsExpected) {
|
|
t.Fatalf("unexpected labels; got %v; want %v", labels, labelsExpected)
|
|
}
|
|
valuesExpected := []uint64{5, 5, 5, 5, 5, 5, 5, 5, 5, 5}
|
|
if !reflect.DeepEqual(values, valuesExpected) {
|
|
t.Fatalf("unexpected values; got %v; want %v", values, valuesExpected)
|
|
}
|
|
}
|
|
|
|
func TestHistogramWithTags(t *testing.T) {
|
|
name := `TestHistogram{tag="foo"}`
|
|
h := NewHistogram(name)
|
|
h.Update(123)
|
|
|
|
var bb bytes.Buffer
|
|
WritePrometheus(&bb, false)
|
|
result := bb.String()
|
|
namePrefixWithTag := `TestHistogram_bucket{tag="foo",vmrange="1e2...2e2"} 1` + "\n"
|
|
if !strings.Contains(result, namePrefixWithTag) {
|
|
t.Fatalf("missing histogram %s in the WritePrometheus output; got\n%s", namePrefixWithTag, result)
|
|
}
|
|
}
|
|
|
|
func TestGetOrCreateHistogramSerial(t *testing.T) {
|
|
name := "GetOrCreateHistogramSerial"
|
|
if err := testGetOrCreateHistogram(name); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestGetOrCreateHistogramConcurrent(t *testing.T) {
|
|
name := "GetOrCreateHistogramConcurrent"
|
|
err := testConcurrent(func() error {
|
|
return testGetOrCreateHistogram(name)
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func testGetOrCreateHistogram(name string) error {
|
|
h1 := GetOrCreateHistogram(name)
|
|
for i := 0; i < 10; i++ {
|
|
h2 := GetOrCreateHistogram(name)
|
|
if h1 != h2 {
|
|
return fmt.Errorf("unexpected histogram returned; got %p; want %p", h2, h1)
|
|
}
|
|
}
|
|
return nil
|
|
}
|