k3s/pkg/api/resource/quantity_test.go

440 lines
13 KiB
Go
Raw Normal View History

2014-09-04 23:50:57 +00:00
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
import (
//"reflect"
"encoding/json"
2014-09-04 23:50:57 +00:00
"testing"
fuzz "github.com/google/gofuzz"
2014-09-04 23:50:57 +00:00
"speter.net/go/exp/math/dec/inf"
)
2015-01-02 21:57:10 +00:00
var (
testQuantityFlag = QuantityFlag("quantityFlag", "1M", "dummy flag for testing the quantity flag mechanism")
)
2014-09-04 23:50:57 +00:00
func dec(i int64, exponent int) *inf.Dec {
// See the below test-- scale is the negative of an exponent.
return inf.NewDec(i, inf.Scale(-exponent))
}
func TestDec(t *testing.T) {
table := []struct {
got *inf.Dec
expect string
}{
{dec(1, 0), "1"},
{dec(1, 1), "10"},
{dec(5, 2), "500"},
{dec(8, 3), "8000"},
{dec(2, 0), "2"},
{dec(1, -1), "0.1"},
{dec(3, -2), "0.03"},
{dec(4, -3), "0.004"},
}
for _, item := range table {
if e, a := item.expect, item.got.String(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
}
func TestQuantityParse(t *testing.T) {
table := []struct {
input string
expect Quantity
}{
{"0", Quantity{dec(0, 0), DecimalSI}},
// Binary suffixes
{"9i", Quantity{dec(9, 0), BinarySI}},
{"8Ki", Quantity{dec(8*1024, 0), BinarySI}},
{"7Mi", Quantity{dec(7*1024*1024, 0), BinarySI}},
{"6Gi", Quantity{dec(6*1024*1024*1024, 0), BinarySI}},
{"5Ti", Quantity{dec(5*1024*1024*1024*1024, 0), BinarySI}},
{"4Pi", Quantity{dec(4*1024*1024*1024*1024*1024, 0), BinarySI}},
{"3Ei", Quantity{dec(3*1024*1024*1024*1024*1024*1024, 0), BinarySI}},
{"10Ti", Quantity{dec(10*1024*1024*1024*1024, 0), BinarySI}},
{"100Ti", Quantity{dec(100*1024*1024*1024*1024, 0), BinarySI}},
// Decimal suffixes
{"3m", Quantity{dec(3, -3), DecimalSI}},
{"9", Quantity{dec(9, 0), DecimalSI}},
{"8k", Quantity{dec(8, 3), DecimalSI}},
{"7M", Quantity{dec(7, 6), DecimalSI}},
{"6G", Quantity{dec(6, 9), DecimalSI}},
{"5T", Quantity{dec(5, 12), DecimalSI}},
{"40T", Quantity{dec(4, 13), DecimalSI}},
{"300T", Quantity{dec(3, 14), DecimalSI}},
{"2P", Quantity{dec(2, 15), DecimalSI}},
{"1E", Quantity{dec(1, 18), DecimalSI}},
// Decimal exponents
{"1E-3", Quantity{dec(1, -3), DecimalExponent}},
{"1e3", Quantity{dec(1, 3), DecimalExponent}},
{"1E6", Quantity{dec(1, 6), DecimalExponent}},
{"1e9", Quantity{dec(1, 9), DecimalExponent}},
{"1E12", Quantity{dec(1, 12), DecimalExponent}},
{"1e15", Quantity{dec(1, 15), DecimalExponent}},
{"1E18", Quantity{dec(1, 18), DecimalExponent}},
// Nonstandard but still parsable
{"1e14", Quantity{dec(1, 14), DecimalExponent}},
{"1e13", Quantity{dec(1, 13), DecimalExponent}},
{"1e3", Quantity{dec(1, 3), DecimalExponent}},
{"100.035k", Quantity{dec(100035, 0), DecimalSI}},
// Things that look like floating point
{"0.001", Quantity{dec(1, -3), DecimalSI}},
{"0.0005k", Quantity{dec(5, -1), DecimalSI}},
2014-09-04 23:50:57 +00:00
{"0.005", Quantity{dec(5, -3), DecimalSI}},
{"0.05", Quantity{dec(5, -2), DecimalSI}},
{"0.5", Quantity{dec(5, -1), DecimalSI}},
{"0.00050k", Quantity{dec(5, -1), DecimalSI}},
2014-09-04 23:50:57 +00:00
{"0.00500", Quantity{dec(5, -3), DecimalSI}},
{"0.05000", Quantity{dec(5, -2), DecimalSI}},
{"0.50000", Quantity{dec(5, -1), DecimalSI}},
{"0.5e0", Quantity{dec(5, -1), DecimalExponent}},
2014-09-04 23:50:57 +00:00
{"0.5e-1", Quantity{dec(5, -2), DecimalExponent}},
{"0.5e-2", Quantity{dec(5, -3), DecimalExponent}},
2014-09-04 23:50:57 +00:00
{"0.5e0", Quantity{dec(5, -1), DecimalExponent}},
{"10.035M", Quantity{dec(10035, 3), DecimalSI}},
{"1.2e3", Quantity{dec(12, 2), DecimalExponent}},
{"1.3E+6", Quantity{dec(13, 5), DecimalExponent}},
2014-09-04 23:50:57 +00:00
{"1.40e9", Quantity{dec(14, 8), DecimalExponent}},
{"1.53E12", Quantity{dec(153, 10), DecimalExponent}},
{"1.6e15", Quantity{dec(16, 14), DecimalExponent}},
{"1.7E18", Quantity{dec(17, 17), DecimalExponent}},
{"9.01", Quantity{dec(901, -2), DecimalSI}},
{"8.1k", Quantity{dec(81, 2), DecimalSI}},
{"7.123456M", Quantity{dec(7123456, 0), DecimalSI}},
{"6.987654321G", Quantity{dec(6987654321, 0), DecimalSI}},
{"5.444T", Quantity{dec(5444, 9), DecimalSI}},
{"40.1T", Quantity{dec(401, 11), DecimalSI}},
{"300.2T", Quantity{dec(3002, 11), DecimalSI}},
{"2.5P", Quantity{dec(25, 14), DecimalSI}},
{"1.01E", Quantity{dec(101, 16), DecimalSI}},
// Things that saturate/round
{"3.001m", Quantity{dec(4, -3), DecimalSI}},
{"1.1E-3", Quantity{dec(2, -3), DecimalExponent}},
{"0.0001", Quantity{dec(1, -3), DecimalSI}},
{"0.0005", Quantity{dec(1, -3), DecimalSI}},
{"0.00050", Quantity{dec(1, -3), DecimalSI}},
{"0.5e-3", Quantity{dec(1, -3), DecimalExponent}},
{"0.9m", Quantity{dec(1, -3), DecimalSI}},
{"0.12345", Quantity{dec(124, -3), DecimalSI}},
{"0.12354", Quantity{dec(124, -3), DecimalSI}},
{"9Ei", Quantity{maxAllowed, BinarySI}},
{"9223372036854775807Ki", Quantity{maxAllowed, BinarySI}},
{"12E", Quantity{maxAllowed, DecimalSI}},
// We'll accept fractional binary stuff, too.
{"100.035Ki", Quantity{dec(10243584, -2), BinarySI}},
{"0.5Mi", Quantity{dec(.5*1024*1024, 0), BinarySI}},
{"0.05Gi", Quantity{dec(536870912, -1), BinarySI}},
{"0.025Ti", Quantity{dec(274877906944, -1), BinarySI}},
// These get rounded though
{"0.0001i", Quantity{dec(1, -3), DecimalSI}},
{"0.005i", Quantity{dec(5, -3), DecimalSI}},
{"0.05i", Quantity{dec(50, -3), DecimalSI}},
// Also, if below you expect (512, -3), you're wrong in two ways:
// In the sequence [1024*1024*1024, 1024*1024, 1024, ?], the last term is "1" not 1/1024.
// Even if it were, 500 * 1/1024 = .48828125, NOT .512
// I cannot recommend using this feature, it is confusing.
{"0.5i", Quantity{dec(500, -3), DecimalSI}},
2014-09-04 23:50:57 +00:00
}
for _, item := range table {
got, err := ParseQuantity(item.input)
if err != nil {
t.Errorf("%v: unexpected error: %v", item.input, err)
continue
}
if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 {
t.Errorf("%v: expected %v, got %v", item.input, e, a)
}
if e, a := item.expect.Format, got.Format; e != a {
t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
}
}
// Try the negative version of everything
desired := &inf.Dec{}
for _, item := range table {
got, err := ParseQuantity("-" + item.input)
if err != nil {
t.Errorf("-%v: unexpected error: %v", item.input, err)
continue
}
desired.Neg(item.expect.Amount)
if e, a := desired, got.Amount; e.Cmp(a) != 0 {
t.Errorf("%v: expected %v, got %v", item.input, e, a)
}
if e, a := item.expect.Format, got.Format; e != a {
t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
}
}
// Try everything with an explicit +
for _, item := range table {
got, err := ParseQuantity("+" + item.input)
if err != nil {
t.Errorf("-%v: unexpected error: %v", item.input, err)
continue
}
if e, a := item.expect.Amount, got.Amount; e.Cmp(a) != 0 {
t.Errorf("%v: expected %v, got %v", item.input, e, a)
}
if e, a := item.expect.Format, got.Format; e != a {
t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
}
}
2014-09-04 23:50:57 +00:00
invalid := []string{
"1.1.M",
"1+1.0M",
"0.1mi",
"0.1am",
"aoeu",
}
for _, item := range invalid {
_, err := ParseQuantity(item)
if err == nil {
t.Errorf("%v parsed unexpectedly", item)
}
}
}
func TestQuantityString(t *testing.T) {
table := []struct {
in Quantity
expect string
}{
{Quantity{dec(1024*1024*1024, 0), BinarySI}, "1Gi"},
{Quantity{dec(300*1024*1024, 0), BinarySI}, "300Mi"},
{Quantity{dec(6*1024, 0), BinarySI}, "6Ki"},
{Quantity{dec(1001*1024*1024*1024, 0), BinarySI}, "1001Gi"},
{Quantity{dec(1024*1024*1024*1024, 0), BinarySI}, "1Ti"},
{Quantity{dec(5, 0), BinarySI}, "5i"},
{Quantity{dec(500, -3), BinarySI}, "500m"},
2014-09-04 23:50:57 +00:00
{Quantity{dec(1, 9), DecimalSI}, "1G"},
{Quantity{dec(1000, 6), DecimalSI}, "1G"},
{Quantity{dec(1000000, 3), DecimalSI}, "1G"},
{Quantity{dec(1000000000, 0), DecimalSI}, "1G"},
{Quantity{dec(1, -3), DecimalSI}, "1m"},
{Quantity{dec(80, -3), DecimalSI}, "80m"},
{Quantity{dec(1080, -3), DecimalSI}, "1080m"},
{Quantity{dec(108, -2), DecimalSI}, "1080m"},
{Quantity{dec(10800, -4), DecimalSI}, "1080m"},
2014-09-04 23:50:57 +00:00
{Quantity{dec(300, 6), DecimalSI}, "300M"},
{Quantity{dec(1, 12), DecimalSI}, "1T"},
{Quantity{dec(1234567, 6), DecimalSI}, "1234567M"},
{Quantity{dec(1234567, -3), BinarySI}, "1235i"},
2014-09-04 23:50:57 +00:00
{Quantity{dec(3, 3), DecimalSI}, "3k"},
{Quantity{dec(0, 0), DecimalSI}, "0"},
{Quantity{dec(0, 0), BinarySI}, "0i"},
{Quantity{dec(1, 9), DecimalExponent}, "1e9"},
{Quantity{dec(1, -3), DecimalExponent}, "1e-3"},
{Quantity{dec(80, -3), DecimalExponent}, "80e-3"},
{Quantity{dec(300, 6), DecimalExponent}, "300e6"},
{Quantity{dec(1, 12), DecimalExponent}, "1e12"},
{Quantity{dec(1, 3), DecimalExponent}, "1e3"},
{Quantity{dec(3, 3), DecimalExponent}, "3e3"},
{Quantity{dec(3, 3), DecimalSI}, "3k"},
{Quantity{dec(0, 0), DecimalExponent}, "0"},
}
for _, item := range table {
got := item.in.String()
if e, a := item.expect, got; e != a {
t.Errorf("%#v: expected %v, got %v", item.in, e, a)
}
}
desired := &inf.Dec{} // Avoid modifying the values in the table.
for _, item := range table {
if item.in.Amount.Cmp(decZero) == 0 {
// Don't expect it to print "-0" ever
continue
}
q := item.in
q.Amount = desired.Neg(q.Amount)
if e, a := "-"+item.expect, q.String(); e != a {
t.Errorf("%#v: expected %v, got %v", item.in, e, a)
}
}
2014-09-04 23:50:57 +00:00
}
var fuzzer = fuzz.New().Funcs(
func(q *Quantity, c fuzz.Continue) {
q.Amount = &inf.Dec{}
if c.RandBool() {
q.Format = BinarySI
if c.RandBool() {
q.Amount.SetScale(0)
q.Amount.SetUnscaled(c.Int63())
return
}
// Be sure to test cases like 1Mi
q.Amount.SetScale(0)
q.Amount.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5)))
return
}
if c.RandBool() {
q.Format = DecimalSI
} else {
q.Format = DecimalExponent
}
if c.RandBool() {
q.Amount.SetScale(inf.Scale(c.Intn(4)))
q.Amount.SetUnscaled(c.Int63())
return
}
// Be sure to test cases like 1M
q.Amount.SetScale(inf.Scale(3 - c.Intn(15)))
q.Amount.SetUnscaled(c.Int63n(1000))
},
)
func TestJSON(t *testing.T) {
for i := 0; i < 500; i++ {
q := &Quantity{}
fuzzer.Fuzz(q)
b, err := json.Marshal(q)
if err != nil {
t.Errorf("error encoding %v", q)
}
q2 := &Quantity{}
err = json.Unmarshal(b, q2)
if err != nil {
t.Errorf("%v: error decoding %v", q, string(b))
}
if q2.Amount.Cmp(q.Amount) != 0 {
t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b))
}
}
}
func TestMilliNewSet(t *testing.T) {
table := []struct {
value int64
format Format
expect string
exact bool
}{
{1, DecimalSI, "1m", true},
{1000, DecimalSI, "1", true},
{1234000, DecimalSI, "1234", true},
{1024, BinarySI, "2i", false}, // Rounded up
{1000000, "invalidFormatDefaultsToExponent", "1e3", true},
{1024 * 1024, BinarySI, "1049i", false},
}
for _, item := range table {
q := NewMilliQuantity(item.value, item.format)
if e, a := item.expect, q.String(); e != a {
t.Errorf("Expected %v, got %v; %#v", e, a, q)
}
if !item.exact {
continue
}
q2, err := ParseQuantity(q.String())
if err != nil {
t.Errorf("Round trip failed on %v", q)
}
if e, a := item.value, q2.MilliValue(); e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
for _, item := range table {
q := NewQuantity(0, item.format)
q.SetMilli(item.value)
if e, a := item.expect, q.String(); e != a {
t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
}
}
}
func TestNewSet(t *testing.T) {
table := []struct {
value int64
format Format
expect string
}{
{1, DecimalSI, "1"},
{1000, DecimalSI, "1k"},
{1234000, DecimalSI, "1234k"},
{1024, BinarySI, "1Ki"},
{1000000, "invalidFormatDefaultsToExponent", "1e6"},
{1024 * 1024, BinarySI, "1Mi"},
}
for _, item := range table {
q := NewQuantity(item.value, item.format)
if e, a := item.expect, q.String(); e != a {
t.Errorf("Expected %v, got %v; %#v", e, a, q)
}
q2, err := ParseQuantity(q.String())
if err != nil {
t.Errorf("Round trip failed on %v", q)
}
if e, a := item.value, q2.Value(); e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
for _, item := range table {
q := NewQuantity(0, item.format)
q.Set(item.value)
if e, a := item.expect, q.String(); e != a {
t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
}
}
}
func TestUninitializedNoCrash(t *testing.T) {
var q Quantity
q.Value()
q.MilliValue()
q.Copy()
q.String()
q.MarshalJSON()
}
func TestCopy(t *testing.T) {
q := NewQuantity(5, DecimalSI)
c := q.Copy()
c.Set(6)
if q.Value() == 6 {
t.Errorf("Copy didn't")
}
}
2015-01-02 21:57:10 +00:00
func TestQFlagSet(t *testing.T) {
qf := qFlag{&Quantity{}}
qf.Set("1Ki")
if e, a := "1Ki", qf.String(); e != a {
t.Errorf("Unexpected result %v != %v", e, a)
}
}