test: add test cases for overflow and pricision lost
This commit is contained in:
parent
2c1dac4e8f
commit
3c3df7b2b7
|
@ -128,7 +128,7 @@ type counter struct {
|
||||||
//
|
//
|
||||||
// note that if a part of addend is added on base but the left causes a rounding error,
|
// note that if a part of addend is added on base but the left causes a rounding error,
|
||||||
// we don't respect this case
|
// we don't respect this case
|
||||||
func addWithRoundingErrorChecking(base float64, addend float64) (float64, bool) {
|
func addWithRoundingErrorChecking(base, addend float64) (float64, bool) {
|
||||||
if addend == 0 {
|
if addend == 0 {
|
||||||
return base, false
|
return base, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,114 @@ func TestCounterAdd(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCounterOverflowAdd(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
counter := NewCounter(CounterOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "test help",
|
||||||
|
ConstLabels: Labels{"a": "1", "b": "2"},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
}).(*counter)
|
||||||
|
counter.change = math.MaxUint64
|
||||||
|
counter.Inc()
|
||||||
|
|
||||||
|
// this is expected due to the overflow
|
||||||
|
if expected, got := uint64(0), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &dto.Metric{}
|
||||||
|
counter.Write(m)
|
||||||
|
|
||||||
|
expected := &dto.Metric{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
{Name: proto.String("a"), Value: proto.String("1")},
|
||||||
|
{Name: proto.String("b"), Value: proto.String("2")},
|
||||||
|
},
|
||||||
|
Counter: &dto.Counter{
|
||||||
|
Value: proto.Float64(0),
|
||||||
|
CreatedTimestamp: timestamppb.New(now),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !proto.Equal(expected, m) {
|
||||||
|
t.Errorf("expected %q, got %q", expected, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterPrecisionLosingAdd(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
counter := NewCounter(CounterOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "test help",
|
||||||
|
ConstLabels: Labels{"a": "1", "b": "2"},
|
||||||
|
now: func() time.Time { return now },
|
||||||
|
}).(*counter)
|
||||||
|
|
||||||
|
counter.Add(1 << 53)
|
||||||
|
if expected, got := float64(1<<53), math.Float64frombits(counter.valBits); expected != got {
|
||||||
|
t.Errorf("Expected %f, got %f.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.Inc()
|
||||||
|
if expected, got := uint64(1), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// along with the value 1 inside change, the sum could be added into the float without losing precision
|
||||||
|
counter.Add(1)
|
||||||
|
if expected, got := float64(1<<53+2), math.Float64frombits(counter.valBits); expected != got {
|
||||||
|
t.Errorf("Expected %f, got %f.", expected, got)
|
||||||
|
}
|
||||||
|
// the change is reset to 0
|
||||||
|
if expected, got := uint64(0), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the value will flip with 2
|
||||||
|
counter.Add(1)
|
||||||
|
if expected, got := float64(1<<53+4), math.Float64frombits(counter.valBits); expected != got {
|
||||||
|
t.Errorf("Expected %f, got %f.", expected, got)
|
||||||
|
}
|
||||||
|
if expected, got := uint64(0), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.Add(1)
|
||||||
|
if expected, got := float64(1<<53+4), math.Float64frombits(counter.valBits); expected != got {
|
||||||
|
t.Errorf("Expected %f, got %f.", expected, got)
|
||||||
|
}
|
||||||
|
if expected, got := uint64(1), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
counter.Add(1)
|
||||||
|
if expected, got := float64(1<<53+6), math.Float64frombits(counter.valBits); expected != got {
|
||||||
|
t.Errorf("Expected %f, got %f.", expected, got)
|
||||||
|
}
|
||||||
|
if expected, got := uint64(0), counter.change; expected != got {
|
||||||
|
t.Errorf("Expected %d, got %d.", expected, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &dto.Metric{}
|
||||||
|
counter.Write(m)
|
||||||
|
|
||||||
|
expected := &dto.Metric{
|
||||||
|
Label: []*dto.LabelPair{
|
||||||
|
{Name: proto.String("a"), Value: proto.String("1")},
|
||||||
|
{Name: proto.String("b"), Value: proto.String("2")},
|
||||||
|
},
|
||||||
|
Counter: &dto.Counter{
|
||||||
|
Value: proto.Float64(1<<53 + 6),
|
||||||
|
CreatedTimestamp: timestamppb.New(now),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !proto.Equal(expected, m) {
|
||||||
|
t.Errorf("expected %q, got %q", expected, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func decreaseCounter(c *counter) (err error) {
|
func decreaseCounter(c *counter) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
|
@ -418,6 +526,21 @@ func Test_hasRoundingError(t *testing.T) {
|
||||||
wantRoundingError: true,
|
wantRoundingError: true,
|
||||||
wantNumber: 1 << 53,
|
wantNumber: 1 << 53,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "no rounding error",
|
||||||
|
base: 1 << 53,
|
||||||
|
delta: 2,
|
||||||
|
wantRoundingError: true,
|
||||||
|
wantNumber: 1<<53 + 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no rounding error",
|
||||||
|
base: 1<<53 + 2,
|
||||||
|
delta: 1,
|
||||||
|
wantRoundingError: false,
|
||||||
|
// there is a precision losing
|
||||||
|
wantNumber: 1<<53 + 4,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "rounding error",
|
name: "rounding error",
|
||||||
base: 1 << 54,
|
base: 1 << 54,
|
||||||
|
|
Loading…
Reference in New Issue