blob: 54c9c55d32047e1e54cec57f833881e9404b3334 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file
package catapult_test
import (
"math"
"reflect"
"testing"
"fuchsia.googlesource.com/infra/infra/catapult"
)
// Expects NewLinearBoundaries to return an error when called with the given
// arguments.
func expectCreationFailure(t *testing.T, centralBinCount uint, min float64, max float64) {
if _, err := catapult.NewLinearBinBoundaries(centralBinCount, min, max); err == nil {
t.Errorf("expected an error, given (centralBinCount,min,max)=(%v,%v,%v). "+
"Error was nil.", centralBinCount, min, max)
}
}
// Expects GetBinIndex to return the given index for the given value.
func expectBinIndex(t *testing.T, bb *catapult.BinBoundaries, index uint, value float64) {
if actualBin := bb.GetBinIndex(value); actualBin != index {
t.Errorf("expected bin %v for %v with boundaries: %v Got bin %v",
index, value, bb.GetBoundaries(), actualBin)
}
}
// Expects BinBoundaries created from the given centralBinCount, min, and max to have
// the given boundaries.
func expectBoundaries(t *testing.T, centralBinCount uint, min float64, max float64, boundaries []float64) {
bb, err := catapult.NewLinearBinBoundaries(centralBinCount, min, max)
if err != nil {
t.Errorf("failed to create BinBoundaries %v", err)
}
actualBoundaries := bb.GetBoundaries()
if !reflect.DeepEqual(actualBoundaries, boundaries) {
t.Errorf("expected boundaries %v. Got %v", boundaries, actualBoundaries)
}
}
func TestNewLinearBinBoundaries(t *testing.T) {
t.Run("should err when given centralBinCount < 1", func(t *testing.T) {
expectCreationFailure(t, 0, math.Inf(1), math.Inf(-1))
})
t.Run("should err when min >= max", func(t *testing.T) {
centralBinCount := uint(10)
expectCreationFailure(t, centralBinCount, 4, 4)
expectCreationFailure(t, centralBinCount, 5, 4)
})
t.Run("should return BinBoundaries", func(t *testing.T) {
bb, err := catapult.NewLinearBinBoundaries(10, 20, 30)
if err != nil || bb == nil {
t.Errorf("expected BinBoundaries. Got %v and error: %v",
bb, err)
}
})
}
func TestBinBoundaries_GetBin_Linear(t *testing.T) {
t.Run("centralBinCount=1", func(t *testing.T) {
// We're using 1 central bin with a min and max of 0 and 10. Our bins
// should consist of an underflow bin, a central bin and an overflow bin
// with the following shape:
//
// 0 10
// | |
// | |
// -Inf <-------|---------|--------> Inf
// : underflow : central : overflow :
// : bin : bin : bin :
bb, err := catapult.NewLinearBinBoundaries(1, 0, 10)
if err != nil {
t.Errorf("failed to create BinBoundaries %v", err)
}
// Constant bin indices for our boundaries.
const underflow, central, overflow uint = 0, 1, 2
// Values <0 belong in underflow bin
t.Run("underflow bin", func(t *testing.T) {
expectBinIndex(t, bb, underflow, -100)
expectBinIndex(t, bb, underflow, -1)
expectBinIndex(t, bb, underflow, -1e-10)
})
// Values >=0 and <10 belong in central bin
t.Run("central bin", func(t *testing.T) {
expectBinIndex(t, bb, central, 0)
expectBinIndex(t, bb, central, 7)
expectBinIndex(t, bb, central, 9.9999)
})
// Values >=10 belong in overflow bin
t.Run("overflow bin", func(t *testing.T) {
expectBinIndex(t, bb, overflow, 10)
expectBinIndex(t, bb, overflow, 10.000000001)
expectBinIndex(t, bb, overflow, 100)
})
})
t.Run("centralBinCount>1", func(t *testing.T) {
// We're using 2 central bins with a min and max of 7 and 14. Our bins
// should consist of an underflow bin, a central bin and an overflow bin
// bin with the following shape:
//
// 7 10.5 14
// | | |
// | | |
// -Inf <-------|---------|--------|--------> Inf
// : underflow : central :central : overflow :
// : bin : bin0 : bin1 : bin :
bb, err := catapult.NewLinearBinBoundaries(2, 7, 14)
if err != nil {
t.Errorf("failed to create BinBoundaries %v", err)
}
// Constant bin indices for our boundaries.
const underflow, central0, central1, overflow uint = 0, 1, 2, 3
// Values <7 belong in underflow bin
t.Run("underflow bin", func(t *testing.T) {
expectBinIndex(t, bb, underflow, -99999)
expectBinIndex(t, bb, underflow, 0)
expectBinIndex(t, bb, underflow, 4)
expectBinIndex(t, bb, underflow, 6.999999999)
})
// Values >=7 and <10.5 belong in first central bin
t.Run("first central bin", func(t *testing.T) {
expectBinIndex(t, bb, central0, 7)
expectBinIndex(t, bb, central0, 7.0000000001)
expectBinIndex(t, bb, central0, 9)
expectBinIndex(t, bb, central0, 10.499999999999)
})
// Values >=10.5 and < 14 belong in second central bin
t.Run("second central bin", func(t *testing.T) {
expectBinIndex(t, bb, central1, 10.5)
expectBinIndex(t, bb, central1, 10.500000000001)
expectBinIndex(t, bb, central1, 12)
expectBinIndex(t, bb, central1, 13.99999999999)
})
// Values >=14 belong in overflow bin
t.Run("third central bin", func(t *testing.T) {
expectBinIndex(t, bb, overflow, 14)
expectBinIndex(t, bb, overflow, 14.000000001)
expectBinIndex(t, bb, overflow, 10000)
})
})
}
func TestBinBoundaries_GetBoundaries(t *testing.T) {
t.Run("centralBinCount==1", func(t *testing.T) {
expectBoundaries(t, 1, -3, 3, []float64{-3, 3})
expectBoundaries(t, 1, 1e-5, 1e-4, []float64{1e-5, 1e-4})
})
t.Run("centralBinCount>1", func(t *testing.T) {
expectBoundaries(t, 2, -100, 10, []float64{-100, -45, 10})
expectBoundaries(t, 3, 0, 20, []float64{0, 20.0 / 3, 40.0 / 3, 20})
})
}
func TestBinBoundaries_BinCount(t *testing.T) {
centralBinCount := uint(123)
bb, err := catapult.NewLinearBinBoundaries(centralBinCount, 0, 1)
if err != nil {
t.Errorf("failed to create BinBoundaries %v", err)
}
actualCount := bb.BinCount()
if actualCount != centralBinCount+2 {
t.Errorf("expect bin count %v. Got %v", centralBinCount, actualCount)
}
}