Add tests for examplars
Signed-off-by: beorn7 <beorn@grafana.com>
This commit is contained in:
parent
57d41259e1
commit
c32ffd121f
|
@ -70,7 +70,7 @@ func NewCounter(opts CounterOpts) Counter {
|
||||||
nil,
|
nil,
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs}
|
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
|
||||||
result.init(result) // Init self-collection.
|
result.init(result) // Init self-collection.
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,8 @@ type counter struct {
|
||||||
|
|
||||||
labelPairs []*dto.LabelPair
|
labelPairs []*dto.LabelPair
|
||||||
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
||||||
|
|
||||||
|
now func() time.Time // To mock out time.Now() for testing.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *counter) Desc() *Desc {
|
func (c *counter) Desc() *Desc {
|
||||||
|
@ -140,7 +142,7 @@ func (c *counter) updateExemplar(v float64, l Labels) {
|
||||||
if l == nil {
|
if l == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e, err := newExemplar(v, time.Now(), l)
|
e, err := newExemplar(v, c.now(), l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
@ -210,3 +214,61 @@ func TestCounterAddSmall(t *testing.T) {
|
||||||
t.Errorf("expected %q, got %q", expected, got)
|
t.Errorf("expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCounterExemplar(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
counter := NewCounter(CounterOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "test help",
|
||||||
|
}).(*counter)
|
||||||
|
counter.now = func() time.Time { return now }
|
||||||
|
|
||||||
|
ts, err := ptypes.TimestampProto(now)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedExemplar := &dto.Exemplar{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
&dto.LabelPair{Name: proto.String("foo"), Value: proto.String("bar")},
|
||||||
|
},
|
||||||
|
Value: proto.Float64(42),
|
||||||
|
Timestamp: ts,
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.AddWithExemplar(42, Labels{"foo": "bar"})
|
||||||
|
if expected, got := expectedExemplar.String(), counter.exemplar.Load().(*dto.Exemplar).String(); expected != got {
|
||||||
|
t.Errorf("expected exemplar %s, got %s.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
addExemplarWithInvalidLabel := func() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = e.(error)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Should panic because of invalid label name.
|
||||||
|
counter.AddWithExemplar(42, Labels{":o)": "smile"})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if addExemplarWithInvalidLabel() == nil {
|
||||||
|
t.Error("adding exemplar with invalid label succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
addExemplarWithOversizedLabels := func() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = e.(error)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Should panic because of 65 runes.
|
||||||
|
counter.AddWithExemplar(42, Labels{
|
||||||
|
"abcdefghijklmnopqrstuvwxyz": "26+16 characters",
|
||||||
|
"x1234567": "8+15 characters",
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if addExemplarWithOversizedLabels() == nil {
|
||||||
|
t.Error("adding exemplar with oversized labels succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -198,6 +198,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||||
upperBounds: opts.Buckets,
|
upperBounds: opts.Buckets,
|
||||||
labelPairs: makeLabelPairs(desc, labelValues),
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
counts: [2]*histogramCounts{{}, {}},
|
counts: [2]*histogramCounts{{}, {}},
|
||||||
|
now: time.Now,
|
||||||
}
|
}
|
||||||
for i, upperBound := range h.upperBounds {
|
for i, upperBound := range h.upperBounds {
|
||||||
if i < len(h.upperBounds)-1 {
|
if i < len(h.upperBounds)-1 {
|
||||||
|
@ -266,6 +267,8 @@ type histogram struct {
|
||||||
upperBounds []float64
|
upperBounds []float64
|
||||||
labelPairs []*dto.LabelPair
|
labelPairs []*dto.LabelPair
|
||||||
exemplars []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
|
exemplars []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
|
||||||
|
|
||||||
|
now func() time.Time // To mock out time.Now() for testing.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *histogram) Desc() *Desc {
|
func (h *histogram) Desc() *Desc {
|
||||||
|
@ -397,7 +400,7 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
||||||
if l == nil {
|
if l == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e, err := newExemplar(v, time.Now(), l)
|
e, err := newExemplar(v, h.now(), l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
@ -182,7 +186,11 @@ func TestHistogramConcurrency(t *testing.T) {
|
||||||
go func(vals []float64) {
|
go func(vals []float64) {
|
||||||
start.Wait()
|
start.Wait()
|
||||||
for _, v := range vals {
|
for _, v := range vals {
|
||||||
|
if n%2 == 0 {
|
||||||
sum.Observe(v)
|
sum.Observe(v)
|
||||||
|
} else {
|
||||||
|
sum.ObserveWithExemplar(v, Labels{"foo": "bar"})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end.Done()
|
end.Done()
|
||||||
}(vals)
|
}(vals)
|
||||||
|
@ -201,9 +209,13 @@ func TestHistogramConcurrency(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wantCounts := getCumulativeCounts(allVars)
|
wantCounts := getCumulativeCounts(allVars)
|
||||||
|
wantBuckets := len(testBuckets)
|
||||||
|
if !math.IsInf(m.Histogram.Bucket[len(m.Histogram.Bucket)-1].GetUpperBound(), +1) {
|
||||||
|
wantBuckets--
|
||||||
|
}
|
||||||
|
|
||||||
if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
|
if got := len(m.Histogram.Bucket); got != wantBuckets {
|
||||||
t.Errorf("got %d buckets in protobuf, want %d", got, want)
|
t.Errorf("got %d buckets in protobuf, want %d", got, wantBuckets)
|
||||||
}
|
}
|
||||||
for i, wantBound := range testBuckets {
|
for i, wantBound := range testBuckets {
|
||||||
if i == len(testBuckets)-1 {
|
if i == len(testBuckets)-1 {
|
||||||
|
@ -384,3 +396,62 @@ func TestHistogramAtomicObserve(t *testing.T) {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHistogramExemplar(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
histogram := NewHistogram(HistogramOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "test help",
|
||||||
|
Buckets: []float64{1, 2, 3, 4},
|
||||||
|
}).(*histogram)
|
||||||
|
histogram.now = func() time.Time { return now }
|
||||||
|
|
||||||
|
ts, err := ptypes.TimestampProto(now)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedExemplars := []*dto.Exemplar{
|
||||||
|
nil,
|
||||||
|
&dto.Exemplar{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
&dto.LabelPair{Name: proto.String("id"), Value: proto.String("2")},
|
||||||
|
},
|
||||||
|
Value: proto.Float64(1.6),
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
&dto.Exemplar{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
&dto.LabelPair{Name: proto.String("id"), Value: proto.String("3")},
|
||||||
|
},
|
||||||
|
Value: proto.Float64(4),
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
&dto.Exemplar{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
&dto.LabelPair{Name: proto.String("id"), Value: proto.String("4")},
|
||||||
|
},
|
||||||
|
Value: proto.Float64(4.5),
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
histogram.ObserveWithExemplar(1.5, Labels{"id": "1"})
|
||||||
|
histogram.ObserveWithExemplar(1.6, Labels{"id": "2"}) // To replace exemplar in bucket 0.
|
||||||
|
histogram.ObserveWithExemplar(4, Labels{"id": "3"})
|
||||||
|
histogram.ObserveWithExemplar(4.5, Labels{"id": "4"}) // Should go to +Inf bucket.
|
||||||
|
|
||||||
|
for i, ex := range histogram.exemplars {
|
||||||
|
var got, expected string
|
||||||
|
if val := ex.Load(); val != nil {
|
||||||
|
got = val.(*dto.Exemplar).String()
|
||||||
|
}
|
||||||
|
if expectedExemplars[i] != nil {
|
||||||
|
expected = expectedExemplars[i].String()
|
||||||
|
}
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("expected exemplar %s, got %s.", expected, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue