| /* |
| Copyright 2014 The Kubernetes Authors. |
| |
| 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 ( |
| "encoding/json" |
| "math/rand" |
| "strings" |
| "testing" |
| "unicode" |
| |
| fuzz "github.com/google/gofuzz" |
| |
| inf "gopkg.in/inf.v0" |
| ) |
| |
| func amount(i int64, exponent int) infDecAmount { |
| // See the below test-- scale is the negative of an exponent. |
| return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))} |
| } |
| |
| func dec(i int64, exponent int) infDecAmount { |
| // See the below test-- scale is the negative of an exponent. |
| return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))} |
| } |
| |
| func decQuantity(i int64, exponent int, format Format) Quantity { |
| return Quantity{d: dec(i, exponent), Format: format} |
| } |
| |
| func intQuantity(i int64, exponent Scale, format Format) Quantity { |
| return Quantity{i: int64Amount{value: i, scale: exponent}, Format: format} |
| } |
| |
| func TestDec(t *testing.T) { |
| table := []struct { |
| got infDecAmount |
| 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.Dec.String(); e != a { |
| t.Errorf("expected %v, got %v", e, a) |
| } |
| } |
| } |
| |
| // TestQuantityParseZero ensures that when a 0 quantity is passed, its string value is 0 |
| func TestQuantityParseZero(t *testing.T) { |
| zero := MustParse("0") |
| if expected, actual := "0", zero.String(); expected != actual { |
| t.Errorf("Expected %v, actual %v", expected, actual) |
| } |
| } |
| |
| // TestQuantityParseNonNumericPanic ensures that when a non-numeric string is parsed |
| // it panics |
| func TestQuantityParseNonNumericPanic(t *testing.T) { |
| defer func() { |
| if r := recover(); r == nil { |
| t.Errorf("MustParse did not panic") |
| } |
| }() |
| _ = MustParse("Non-Numeric") |
| } |
| |
| // TestQuantityAddZeroPreservesSuffix verifies that a suffix is preserved |
| // independent of the order of operations when adding a zero and non-zero val |
| func TestQuantityAddZeroPreservesSuffix(t *testing.T) { |
| testValues := []string{"100m", "1Gi"} |
| zero := MustParse("0") |
| for _, testValue := range testValues { |
| value := MustParse(testValue) |
| v1 := *value.Copy() |
| // ensure non-zero + zero = non-zero (suffix preserved) |
| v1.Add(zero) |
| // ensure zero + non-zero = non-zero (suffix preserved) |
| v2 := *zero.Copy() |
| v2.Add(value) |
| |
| if v1.String() != testValue { |
| t.Errorf("Expected %v, actual %v", testValue, v1.String()) |
| continue |
| } |
| if v2.String() != testValue { |
| t.Errorf("Expected %v, actual %v", testValue, v2.String()) |
| } |
| } |
| } |
| |
| // TestQuantitySubZeroPreservesSuffix verifies that a suffix is preserved |
| // independent of the order of operations when subtracting a zero and non-zero val |
| func TestQuantitySubZeroPreservesSuffix(t *testing.T) { |
| testValues := []string{"100m", "1Gi"} |
| zero := MustParse("0") |
| for _, testValue := range testValues { |
| value := MustParse(testValue) |
| v1 := *value.Copy() |
| // ensure non-zero - zero = non-zero (suffix preserved) |
| v1.Sub(zero) |
| // ensure we preserved the input value |
| if v1.String() != testValue { |
| t.Errorf("Expected %v, actual %v", testValue, v1.String()) |
| } |
| |
| // ensure zero - non-zero = -non-zero (suffix preserved) |
| v2 := *zero.Copy() |
| v2.Sub(value) |
| negVal := *value.Copy() |
| negVal.Neg() |
| if v2.String() != negVal.String() { |
| t.Errorf("Expected %v, actual %v", negVal.String(), v2.String()) |
| } |
| } |
| } |
| |
| // TestQuantityCanocicalizeZero verifies that you get 0 as canonical value if internal value is 0, and not 0<suffix> |
| func TestQuantityCanocicalizeZero(t *testing.T) { |
| val := MustParse("1000m") |
| val.i.Sub(int64Amount{value: 1}) |
| zero := Quantity{i: val.i, Format: DecimalSI} |
| if expected, actual := "0", zero.String(); expected != actual { |
| t.Errorf("Expected %v, actual %v", expected, actual) |
| } |
| } |
| |
| func TestQuantityCmp(t *testing.T) { |
| // Test when d is nil |
| table := []struct { |
| x string |
| y string |
| expect int |
| }{ |
| {"0", "0", 0}, |
| {"100m", "50m", 1}, |
| {"50m", "100m", -1}, |
| {"10000T", "100Gi", 1}, |
| } |
| for _, testCase := range table { |
| q1 := MustParse(testCase.x) |
| q2 := MustParse(testCase.y) |
| if result := q1.Cmp(q2); result != testCase.expect { |
| t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result) |
| } |
| } |
| // Test when i is {0,0} |
| table2 := []struct { |
| x *inf.Dec |
| y *inf.Dec |
| expect int |
| }{ |
| {dec(0, 0).Dec, dec(0, 0).Dec, 0}, |
| {nil, dec(0, 0).Dec, 0}, |
| {dec(0, 0).Dec, nil, 0}, |
| {nil, nil, 0}, |
| {nil, dec(10, 0).Dec, -1}, |
| {nil, dec(-10, 0).Dec, 1}, |
| {dec(10, 0).Dec, nil, 1}, |
| {dec(-10, 0).Dec, nil, -1}, |
| } |
| for _, testCase := range table2 { |
| q1 := Quantity{d: infDecAmount{testCase.x}, Format: DecimalSI} |
| q2 := Quantity{d: infDecAmount{testCase.y}, Format: DecimalSI} |
| if result := q1.Cmp(q2); result != testCase.expect { |
| t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result) |
| } |
| } |
| } |
| |
| func TestParseQuantityString(t *testing.T) { |
| table := []struct { |
| input string |
| positive bool |
| value string |
| num, denom, suffix string |
| }{ |
| {"0.025Ti", true, "0.025", "0", "025", "Ti"}, |
| {"1.025Ti", true, "1.025", "1", "025", "Ti"}, |
| {"-1.025Ti", false, "-1.025", "1", "025", "Ti"}, |
| {".", true, ".", "0", "", ""}, |
| {"-.", false, "-.", "0", "", ""}, |
| {"1E-3", true, "1", "1", "", "E-3"}, |
| } |
| for _, test := range table { |
| positive, value, num, denom, suffix, err := parseQuantityString(test.input) |
| if err != nil { |
| t.Errorf("%s: error: %v", test.input, err) |
| continue |
| } |
| if positive != test.positive || value != test.value || num != test.num || denom != test.denom || suffix != test.suffix { |
| t.Errorf("%s: unmatched: %t %q %q %q %q", test.input, positive, value, num, denom, suffix) |
| } |
| } |
| } |
| |
| func TestQuantityParse(t *testing.T) { |
| if _, err := ParseQuantity(""); err == nil { |
| t.Errorf("expected empty string to return error") |
| } |
| |
| table := []struct { |
| input string |
| expect Quantity |
| }{ |
| {"0", decQuantity(0, 0, DecimalSI)}, |
| {"0n", decQuantity(0, 0, DecimalSI)}, |
| {"0u", decQuantity(0, 0, DecimalSI)}, |
| {"0m", decQuantity(0, 0, DecimalSI)}, |
| {"0Ki", decQuantity(0, 0, BinarySI)}, |
| {"0k", decQuantity(0, 0, DecimalSI)}, |
| {"0Mi", decQuantity(0, 0, BinarySI)}, |
| {"0M", decQuantity(0, 0, DecimalSI)}, |
| {"0Gi", decQuantity(0, 0, BinarySI)}, |
| {"0G", decQuantity(0, 0, DecimalSI)}, |
| {"0Ti", decQuantity(0, 0, BinarySI)}, |
| {"0T", decQuantity(0, 0, DecimalSI)}, |
| |
| // Quantity less numbers are allowed |
| {"1", decQuantity(1, 0, DecimalSI)}, |
| |
| // Binary suffixes |
| {"1Ki", decQuantity(1024, 0, BinarySI)}, |
| {"8Ki", decQuantity(8*1024, 0, BinarySI)}, |
| {"7Mi", decQuantity(7*1024*1024, 0, BinarySI)}, |
| {"6Gi", decQuantity(6*1024*1024*1024, 0, BinarySI)}, |
| {"5Ti", decQuantity(5*1024*1024*1024*1024, 0, BinarySI)}, |
| {"4Pi", decQuantity(4*1024*1024*1024*1024*1024, 0, BinarySI)}, |
| {"3Ei", decQuantity(3*1024*1024*1024*1024*1024*1024, 0, BinarySI)}, |
| |
| {"10Ti", decQuantity(10*1024*1024*1024*1024, 0, BinarySI)}, |
| {"100Ti", decQuantity(100*1024*1024*1024*1024, 0, BinarySI)}, |
| |
| // Decimal suffixes |
| {"5n", decQuantity(5, -9, DecimalSI)}, |
| {"4u", decQuantity(4, -6, DecimalSI)}, |
| {"3m", decQuantity(3, -3, DecimalSI)}, |
| {"9", decQuantity(9, 0, DecimalSI)}, |
| {"8k", decQuantity(8, 3, DecimalSI)}, |
| {"50k", decQuantity(5, 4, DecimalSI)}, |
| {"7M", decQuantity(7, 6, DecimalSI)}, |
| {"6G", decQuantity(6, 9, DecimalSI)}, |
| {"5T", decQuantity(5, 12, DecimalSI)}, |
| {"40T", decQuantity(4, 13, DecimalSI)}, |
| {"300T", decQuantity(3, 14, DecimalSI)}, |
| {"2P", decQuantity(2, 15, DecimalSI)}, |
| {"1E", decQuantity(1, 18, DecimalSI)}, |
| |
| // Decimal exponents |
| {"1E-3", decQuantity(1, -3, DecimalExponent)}, |
| {"1e3", decQuantity(1, 3, DecimalExponent)}, |
| {"1E6", decQuantity(1, 6, DecimalExponent)}, |
| {"1e9", decQuantity(1, 9, DecimalExponent)}, |
| {"1E12", decQuantity(1, 12, DecimalExponent)}, |
| {"1e15", decQuantity(1, 15, DecimalExponent)}, |
| {"1E18", decQuantity(1, 18, DecimalExponent)}, |
| |
| // Nonstandard but still parsable |
| {"1e14", decQuantity(1, 14, DecimalExponent)}, |
| {"1e13", decQuantity(1, 13, DecimalExponent)}, |
| {"1e3", decQuantity(1, 3, DecimalExponent)}, |
| {"100.035k", decQuantity(100035, 0, DecimalSI)}, |
| |
| // Things that look like floating point |
| {"0.001", decQuantity(1, -3, DecimalSI)}, |
| {"0.0005k", decQuantity(5, -1, DecimalSI)}, |
| {"0.005", decQuantity(5, -3, DecimalSI)}, |
| {"0.05", decQuantity(5, -2, DecimalSI)}, |
| {"0.5", decQuantity(5, -1, DecimalSI)}, |
| {"0.00050k", decQuantity(5, -1, DecimalSI)}, |
| {"0.00500", decQuantity(5, -3, DecimalSI)}, |
| {"0.05000", decQuantity(5, -2, DecimalSI)}, |
| {"0.50000", decQuantity(5, -1, DecimalSI)}, |
| {"0.5e0", decQuantity(5, -1, DecimalExponent)}, |
| {"0.5e-1", decQuantity(5, -2, DecimalExponent)}, |
| {"0.5e-2", decQuantity(5, -3, DecimalExponent)}, |
| {"0.5e0", decQuantity(5, -1, DecimalExponent)}, |
| {"10.035M", decQuantity(10035, 3, DecimalSI)}, |
| |
| {"1.2e3", decQuantity(12, 2, DecimalExponent)}, |
| {"1.3E+6", decQuantity(13, 5, DecimalExponent)}, |
| {"1.40e9", decQuantity(14, 8, DecimalExponent)}, |
| {"1.53E12", decQuantity(153, 10, DecimalExponent)}, |
| {"1.6e15", decQuantity(16, 14, DecimalExponent)}, |
| {"1.7E18", decQuantity(17, 17, DecimalExponent)}, |
| |
| {"9.01", decQuantity(901, -2, DecimalSI)}, |
| {"8.1k", decQuantity(81, 2, DecimalSI)}, |
| {"7.123456M", decQuantity(7123456, 0, DecimalSI)}, |
| {"6.987654321G", decQuantity(6987654321, 0, DecimalSI)}, |
| {"5.444T", decQuantity(5444, 9, DecimalSI)}, |
| {"40.1T", decQuantity(401, 11, DecimalSI)}, |
| {"300.2T", decQuantity(3002, 11, DecimalSI)}, |
| {"2.5P", decQuantity(25, 14, DecimalSI)}, |
| {"1.01E", decQuantity(101, 16, DecimalSI)}, |
| |
| // Things that saturate/round |
| {"3.001n", decQuantity(4, -9, DecimalSI)}, |
| {"1.1E-9", decQuantity(2, -9, DecimalExponent)}, |
| {"0.0000000001", decQuantity(1, -9, DecimalSI)}, |
| {"0.0000000005", decQuantity(1, -9, DecimalSI)}, |
| {"0.00000000050", decQuantity(1, -9, DecimalSI)}, |
| {"0.5e-9", decQuantity(1, -9, DecimalExponent)}, |
| {"0.9n", decQuantity(1, -9, DecimalSI)}, |
| {"0.00000012345", decQuantity(124, -9, DecimalSI)}, |
| {"0.00000012354", decQuantity(124, -9, DecimalSI)}, |
| {"9Ei", Quantity{d: maxAllowed, Format: BinarySI}}, |
| {"9223372036854775807Ki", Quantity{d: maxAllowed, Format: BinarySI}}, |
| {"12E", decQuantity(12, 18, DecimalSI)}, |
| |
| // We'll accept fractional binary stuff, too. |
| {"100.035Ki", decQuantity(10243584, -2, BinarySI)}, |
| {"0.5Mi", decQuantity(.5*1024*1024, 0, BinarySI)}, |
| {"0.05Gi", decQuantity(536870912, -1, BinarySI)}, |
| {"0.025Ti", decQuantity(274877906944, -1, BinarySI)}, |
| |
| // Things written by trolls |
| {"0.000000000001Ki", decQuantity(2, -9, DecimalSI)}, // rounds up, changes format |
| {".001", decQuantity(1, -3, DecimalSI)}, |
| {".0001k", decQuantity(100, -3, DecimalSI)}, |
| {"1.", decQuantity(1, 0, DecimalSI)}, |
| {"1.G", decQuantity(1, 9, DecimalSI)}, |
| } |
| |
| for _, asDec := range []bool{false, true} { |
| for _, item := range table { |
| got, err := ParseQuantity(item.input) |
| if err != nil { |
| t.Errorf("%v: unexpected error: %v", item.input, err) |
| continue |
| } |
| if asDec { |
| got.AsDec() |
| } |
| |
| if e, a := item.expect, got; e.Cmp(a) != 0 { |
| t.Errorf("%v: expected %v, got %v", item.input, e.String(), a.String()) |
| } |
| if e, a := item.expect.Format, got.Format; e != a { |
| t.Errorf("%v: expected %#v, got %#v", item.input, e, a) |
| } |
| |
| if asDec { |
| if i, ok := got.AsInt64(); i != 0 || ok { |
| t.Errorf("%v: expected inf.Dec to return false for AsInt64: %d", item.input, i) |
| } |
| continue |
| } |
| i, ok := item.expect.AsInt64() |
| if !ok { |
| continue |
| } |
| j, ok := got.AsInt64() |
| if !ok { |
| if got.d.Dec == nil && got.i.scale >= 0 { |
| t.Errorf("%v: is an int64Amount, but can't return AsInt64: %v", item.input, got) |
| } |
| continue |
| } |
| if i != j { |
| t.Errorf("%v: expected equivalent representation as int64: %d %d", item.input, i, j) |
| } |
| } |
| |
| for _, item := range table { |
| got, err := ParseQuantity(item.input) |
| if err != nil { |
| t.Errorf("%v: unexpected error: %v", item.input, err) |
| continue |
| } |
| |
| if asDec { |
| got.AsDec() |
| } |
| |
| // verify that we can decompose the input and get the same result by building up from the base. |
| positive, _, num, denom, suffix, err := parseQuantityString(item.input) |
| if err != nil { |
| t.Errorf("%v: unexpected error: %v", item.input, err) |
| continue |
| } |
| if got.Sign() >= 0 && !positive || got.Sign() < 0 && positive { |
| t.Errorf("%v: positive was incorrect: %t", item.input, positive) |
| continue |
| } |
| var value string |
| if !positive { |
| value = "-" |
| } |
| value += num |
| if len(denom) > 0 { |
| value += "." + denom |
| } |
| value += suffix |
| if len(value) == 0 { |
| t.Errorf("%v: did not parse correctly, %q %q %q", item.input, num, denom, suffix) |
| } |
| expected, err := ParseQuantity(value) |
| if err != nil { |
| t.Errorf("%v: unexpected error for %s: %v", item.input, value, err) |
| continue |
| } |
| if expected.Cmp(got) != 0 { |
| t.Errorf("%v: not the same as %s", item.input, value) |
| continue |
| } |
| } |
| |
| // Try the negative version of everything |
| desired := &inf.Dec{} |
| expect := Quantity{d: infDecAmount{Dec: desired}} |
| for _, item := range table { |
| got, err := ParseQuantity("-" + strings.TrimLeftFunc(item.input, unicode.IsSpace)) |
| if err != nil { |
| t.Errorf("-%v: unexpected error: %v", item.input, err) |
| continue |
| } |
| if asDec { |
| got.AsDec() |
| } |
| |
| expected := item.expect |
| desired.Neg(expected.AsDec()) |
| |
| if e, a := expect, got; e.Cmp(a) != 0 { |
| t.Errorf("%v: expected %s, got %s", item.input, e.String(), a.String()) |
| } |
| if e, a := expected.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("+" + strings.TrimLeftFunc(item.input, unicode.IsSpace)) |
| if err != nil { |
| t.Errorf("-%v: unexpected error: %v", item.input, err) |
| continue |
| } |
| if asDec { |
| got.AsDec() |
| } |
| |
| if e, a := item.expect, got; e.Cmp(a) != 0 { |
| t.Errorf("%v(%t): expected %s, got %s", item.input, asDec, e.String(), a.String()) |
| } |
| if e, a := item.expect.Format, got.Format; e != a { |
| t.Errorf("%v: expected %#v, got %#v", item.input, e, a) |
| } |
| } |
| } |
| |
| invalid := []string{ |
| "1.1.M", |
| "1+1.0M", |
| "0.1mi", |
| "0.1am", |
| "aoeu", |
| ".5i", |
| "1i", |
| "-3.01i", |
| "-3.01e-", |
| |
| // trailing whitespace is forbidden |
| " 1", |
| "1 ", |
| } |
| for _, item := range invalid { |
| _, err := ParseQuantity(item) |
| if err == nil { |
| t.Errorf("%v parsed unexpectedly", item) |
| } |
| } |
| } |
| |
| func TestQuantityRoundUp(t *testing.T) { |
| table := []struct { |
| in string |
| scale Scale |
| expect Quantity |
| ok bool |
| }{ |
| {"9.01", -3, decQuantity(901, -2, DecimalSI), true}, |
| {"9.01", -2, decQuantity(901, -2, DecimalSI), true}, |
| {"9.01", -1, decQuantity(91, -1, DecimalSI), false}, |
| {"9.01", 0, decQuantity(10, 0, DecimalSI), false}, |
| {"9.01", 1, decQuantity(10, 0, DecimalSI), false}, |
| {"9.01", 2, decQuantity(100, 0, DecimalSI), false}, |
| |
| {"-9.01", -3, decQuantity(-901, -2, DecimalSI), true}, |
| {"-9.01", -2, decQuantity(-901, -2, DecimalSI), true}, |
| {"-9.01", -1, decQuantity(-91, -1, DecimalSI), false}, |
| {"-9.01", 0, decQuantity(-10, 0, DecimalSI), false}, |
| {"-9.01", 1, decQuantity(-10, 0, DecimalSI), false}, |
| {"-9.01", 2, decQuantity(-100, 0, DecimalSI), false}, |
| } |
| |
| for _, asDec := range []bool{false, true} { |
| for _, item := range table { |
| got, err := ParseQuantity(item.in) |
| if err != nil { |
| t.Fatalf("unexpected error: %v", err) |
| } |
| expect := *item.expect.Copy() |
| if asDec { |
| got.AsDec() |
| } |
| if ok := got.RoundUp(item.scale); ok != item.ok { |
| t.Errorf("%s(%d,%t): unexpected ok: %t", item.in, item.scale, asDec, ok) |
| } |
| if got.Cmp(expect) != 0 { |
| t.Errorf("%s(%d,%t): unexpected round: %s vs %s", item.in, item.scale, asDec, got.String(), expect.String()) |
| } |
| } |
| } |
| } |
| |
| func TestQuantityCmpInt64AndDec(t *testing.T) { |
| table := []struct { |
| a, b Quantity |
| cmp int |
| }{ |
| {intQuantity(901, -2, DecimalSI), intQuantity(901, -2, DecimalSI), 0}, |
| {intQuantity(90, -1, DecimalSI), intQuantity(901, -2, DecimalSI), -1}, |
| {intQuantity(901, -2, DecimalSI), intQuantity(900, -2, DecimalSI), 1}, |
| {intQuantity(0, 0, DecimalSI), intQuantity(0, 0, DecimalSI), 0}, |
| {intQuantity(0, 1, DecimalSI), intQuantity(0, -1, DecimalSI), 0}, |
| {intQuantity(0, -1, DecimalSI), intQuantity(0, 1, DecimalSI), 0}, |
| {intQuantity(800, -3, DecimalSI), intQuantity(1, 0, DecimalSI), -1}, |
| {intQuantity(800, -3, DecimalSI), intQuantity(79, -2, DecimalSI), 1}, |
| |
| {intQuantity(mostPositive, 0, DecimalSI), intQuantity(1, -1, DecimalSI), 1}, |
| {intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 0, DecimalSI), 1}, |
| {intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 1, DecimalSI), 1}, |
| {intQuantity(mostPositive, 1, DecimalSI), intQuantity(0, 1, DecimalSI), 1}, |
| {intQuantity(mostPositive, -16, DecimalSI), intQuantity(1, 3, DecimalSI), -1}, |
| |
| {intQuantity(mostNegative, 0, DecimalSI), intQuantity(0, 0, DecimalSI), -1}, |
| {intQuantity(mostNegative, -18, DecimalSI), intQuantity(-1, 0, DecimalSI), -1}, |
| {intQuantity(mostNegative, -19, DecimalSI), intQuantity(-1, 0, DecimalSI), 1}, |
| |
| {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 0}, |
| {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 1}, |
| {intQuantity(-1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 0}, |
| {intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 0, DecimalSI), 1}, |
| |
| {intQuantity(1*1000000*1000000*1000000+1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 1}, |
| {intQuantity(1*1000000*1000000*1000000-1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), -1}, |
| } |
| |
| for _, item := range table { |
| if cmp := item.a.Cmp(item.b); cmp != item.cmp { |
| t.Errorf("%#v: unexpected Cmp: %d", item, cmp) |
| } |
| if cmp := item.b.Cmp(item.a); cmp != -item.cmp { |
| t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) |
| } |
| } |
| |
| for _, item := range table { |
| a, b := *item.a.Copy(), *item.b.Copy() |
| a.AsDec() |
| if cmp := a.Cmp(b); cmp != item.cmp { |
| t.Errorf("%#v: unexpected Cmp: %d", item, cmp) |
| } |
| if cmp := b.Cmp(a); cmp != -item.cmp { |
| t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) |
| } |
| } |
| |
| for _, item := range table { |
| a, b := *item.a.Copy(), *item.b.Copy() |
| b.AsDec() |
| if cmp := a.Cmp(b); cmp != item.cmp { |
| t.Errorf("%#v: unexpected Cmp: %d", item, cmp) |
| } |
| if cmp := b.Cmp(a); cmp != -item.cmp { |
| t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) |
| } |
| } |
| |
| for _, item := range table { |
| a, b := *item.a.Copy(), *item.b.Copy() |
| a.AsDec() |
| b.AsDec() |
| if cmp := a.Cmp(b); cmp != item.cmp { |
| t.Errorf("%#v: unexpected Cmp: %d", item, cmp) |
| } |
| if cmp := b.Cmp(a); cmp != -item.cmp { |
| t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp) |
| } |
| } |
| } |
| |
| func TestQuantityNeg(t *testing.T) { |
| table := []struct { |
| a Quantity |
| out string |
| }{ |
| {intQuantity(901, -2, DecimalSI), "-9010m"}, |
| {decQuantity(901, -2, DecimalSI), "-9010m"}, |
| } |
| |
| for i, item := range table { |
| out := *item.a.Copy() |
| out.Neg() |
| if out.Cmp(item.a) == 0 { |
| t.Errorf("%d: negating an item should not mutate the source: %s", i, out.String()) |
| } |
| if out.String() != item.out { |
| t.Errorf("%d: negating did not equal exact value: %s", i, out.String()) |
| } |
| } |
| } |
| |
| func TestQuantityString(t *testing.T) { |
| table := []struct { |
| in Quantity |
| expect string |
| alternate string |
| }{ |
| {decQuantity(1024*1024*1024, 0, BinarySI), "1Gi", "1024Mi"}, |
| {decQuantity(300*1024*1024, 0, BinarySI), "300Mi", "307200Ki"}, |
| {decQuantity(6*1024, 0, BinarySI), "6Ki", ""}, |
| {decQuantity(1001*1024*1024*1024, 0, BinarySI), "1001Gi", "1025024Mi"}, |
| {decQuantity(1024*1024*1024*1024, 0, BinarySI), "1Ti", "1024Gi"}, |
| {decQuantity(5, 0, BinarySI), "5", "5000m"}, |
| {decQuantity(500, -3, BinarySI), "500m", "0.5"}, |
| {decQuantity(1, 9, DecimalSI), "1G", "1000M"}, |
| {decQuantity(1000, 6, DecimalSI), "1G", "0.001T"}, |
| {decQuantity(1000000, 3, DecimalSI), "1G", ""}, |
| {decQuantity(1000000000, 0, DecimalSI), "1G", ""}, |
| {decQuantity(1, -3, DecimalSI), "1m", "1000u"}, |
| {decQuantity(80, -3, DecimalSI), "80m", ""}, |
| {decQuantity(1080, -3, DecimalSI), "1080m", "1.08"}, |
| {decQuantity(108, -2, DecimalSI), "1080m", "1080000000n"}, |
| {decQuantity(10800, -4, DecimalSI), "1080m", ""}, |
| {decQuantity(300, 6, DecimalSI), "300M", ""}, |
| {decQuantity(1, 12, DecimalSI), "1T", ""}, |
| {decQuantity(1234567, 6, DecimalSI), "1234567M", ""}, |
| {decQuantity(1234567, -3, BinarySI), "1234567m", ""}, |
| {decQuantity(3, 3, DecimalSI), "3k", ""}, |
| {decQuantity(1025, 0, BinarySI), "1025", ""}, |
| {decQuantity(0, 0, DecimalSI), "0", ""}, |
| {decQuantity(0, 0, BinarySI), "0", ""}, |
| {decQuantity(1, 9, DecimalExponent), "1e9", ".001e12"}, |
| {decQuantity(1, -3, DecimalExponent), "1e-3", "0.001e0"}, |
| {decQuantity(1, -9, DecimalExponent), "1e-9", "1000e-12"}, |
| {decQuantity(80, -3, DecimalExponent), "80e-3", ""}, |
| {decQuantity(300, 6, DecimalExponent), "300e6", ""}, |
| {decQuantity(1, 12, DecimalExponent), "1e12", ""}, |
| {decQuantity(1, 3, DecimalExponent), "1e3", ""}, |
| {decQuantity(3, 3, DecimalExponent), "3e3", ""}, |
| {decQuantity(3, 3, DecimalSI), "3k", ""}, |
| {decQuantity(0, 0, DecimalExponent), "0", "00"}, |
| {decQuantity(1, -9, DecimalSI), "1n", ""}, |
| {decQuantity(80, -9, DecimalSI), "80n", ""}, |
| {decQuantity(1080, -9, DecimalSI), "1080n", ""}, |
| {decQuantity(108, -8, DecimalSI), "1080n", ""}, |
| {decQuantity(10800, -10, DecimalSI), "1080n", ""}, |
| {decQuantity(1, -6, DecimalSI), "1u", ""}, |
| {decQuantity(80, -6, DecimalSI), "80u", ""}, |
| {decQuantity(1080, -6, DecimalSI), "1080u", ""}, |
| } |
| 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) |
| } |
| q, err := ParseQuantity(item.expect) |
| if err != nil { |
| t.Errorf("%#v: unexpected error: %v", item.expect, err) |
| } |
| if len(q.s) == 0 || q.s != item.expect { |
| t.Errorf("%#v: did not copy canonical string on parse: %s", item.expect, q.s) |
| } |
| if len(item.alternate) == 0 { |
| continue |
| } |
| q, err = ParseQuantity(item.alternate) |
| if err != nil { |
| t.Errorf("%#v: unexpected error: %v", item.expect, err) |
| continue |
| } |
| if len(q.s) != 0 { |
| t.Errorf("%#v: unexpected nested string: %v", item.expect, q.s) |
| } |
| if q.String() != item.expect { |
| t.Errorf("%#v: unexpected alternate canonical: %v", item.expect, q.String()) |
| } |
| if len(q.s) == 0 || q.s != item.expect { |
| t.Errorf("%#v: did not set canonical string on ToString: %s", item.expect, q.s) |
| } |
| } |
| desired := &inf.Dec{} // Avoid modifying the values in the table. |
| for _, item := range table { |
| if item.in.Cmp(Quantity{}) == 0 { |
| // Don't expect it to print "-0" ever |
| continue |
| } |
| q := item.in |
| q.d = infDecAmount{desired.Neg(q.AsDec())} |
| if e, a := "-"+item.expect, q.String(); e != a { |
| t.Errorf("%#v: expected %v, got %v", item.in, e, a) |
| } |
| } |
| } |
| |
| func TestQuantityParseEmit(t *testing.T) { |
| table := []struct { |
| in string |
| expect string |
| }{ |
| {"1Ki", "1Ki"}, |
| {"1Mi", "1Mi"}, |
| {"1Gi", "1Gi"}, |
| {"1024Mi", "1Gi"}, |
| {"1000M", "1G"}, |
| {".001Ki", "1024m"}, |
| {".000001Ki", "1024u"}, |
| {".000000001Ki", "1024n"}, |
| {".000000000001Ki", "2n"}, |
| } |
| |
| for _, item := range table { |
| q, err := ParseQuantity(item.in) |
| if err != nil { |
| t.Errorf("Couldn't parse %v", item.in) |
| continue |
| } |
| if e, a := item.expect, q.String(); e != a { |
| t.Errorf("%#v: expected %v, got %v", item.in, e, a) |
| } |
| } |
| for _, item := range table { |
| q, err := ParseQuantity("-" + item.in) |
| if err != nil { |
| t.Errorf("Couldn't parse %v", item.in) |
| continue |
| } |
| if q.Cmp(Quantity{}) == 0 { |
| continue |
| } |
| if e, a := "-"+item.expect, q.String(); e != a { |
| t.Errorf("%#v: expected %v, got %v (%#v)", item.in, e, a, q.i) |
| } |
| } |
| } |
| |
| var fuzzer = fuzz.New().Funcs( |
| func(q *Quantity, c fuzz.Continue) { |
| q.i = Zero |
| if c.RandBool() { |
| q.Format = BinarySI |
| if c.RandBool() { |
| dec := &inf.Dec{} |
| q.d = infDecAmount{Dec: dec} |
| dec.SetScale(0) |
| dec.SetUnscaled(c.Int63()) |
| return |
| } |
| // Be sure to test cases like 1Mi |
| dec := &inf.Dec{} |
| q.d = infDecAmount{Dec: dec} |
| dec.SetScale(0) |
| dec.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5))) |
| return |
| } |
| if c.RandBool() { |
| q.Format = DecimalSI |
| } else { |
| q.Format = DecimalExponent |
| } |
| if c.RandBool() { |
| dec := &inf.Dec{} |
| q.d = infDecAmount{Dec: dec} |
| dec.SetScale(inf.Scale(c.Intn(4))) |
| dec.SetUnscaled(c.Int63()) |
| return |
| } |
| // Be sure to test cases like 1M |
| dec := &inf.Dec{} |
| q.d = infDecAmount{Dec: dec} |
| dec.SetScale(inf.Scale(3 - c.Intn(15))) |
| dec.SetUnscaled(c.Int63n(1000)) |
| }, |
| ) |
| |
| func TestQuantityDeepCopy(t *testing.T) { |
| // Test when d is nil |
| slice := []string{"0", "100m", "50m", "10000T"} |
| for _, testCase := range slice { |
| q := MustParse(testCase) |
| if result := q.DeepCopy(); result != q { |
| t.Errorf("Expected: %v, Actual: %v", q, result) |
| } |
| } |
| table := []*inf.Dec{ |
| dec(0, 0).Dec, |
| dec(10, 0).Dec, |
| dec(-10, 0).Dec, |
| } |
| // Test when i is {0,0} |
| for _, testCase := range table { |
| q := Quantity{d: infDecAmount{testCase}, Format: DecimalSI} |
| result := q.DeepCopy() |
| if q.d.Cmp(result.AsDec()) != 0 { |
| t.Errorf("Expected: %v, Actual: %v", q.String(), result.String()) |
| } |
| result = Quantity{d: infDecAmount{dec(2, 0).Dec}, Format: DecimalSI} |
| if q.d.Cmp(result.AsDec()) == 0 { |
| t.Errorf("Modifying result has affected q") |
| } |
| } |
| } |
| |
| 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: %v", q, err) |
| continue |
| } |
| q2 := &Quantity{} |
| err = json.Unmarshal(b, q2) |
| if err != nil { |
| t.Logf("%d: %s", i, string(b)) |
| t.Errorf("%v: error decoding %v: %v", q, string(b), err) |
| } |
| if q2.Cmp(*q) != 0 { |
| t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b)) |
| } |
| } |
| } |
| |
| func TestJSONWhitespace(t *testing.T) { |
| q := Quantity{} |
| testCases := []struct { |
| in string |
| expect string |
| }{ |
| {`" 1"`, "1"}, |
| {`"1 "`, "1"}, |
| {`1`, "1"}, |
| {` 1`, "1"}, |
| {`1 `, "1"}, |
| {`10`, "10"}, |
| {`-1`, "-1"}, |
| {` -1`, "-1"}, |
| } |
| for _, test := range testCases { |
| if err := json.Unmarshal([]byte(test.in), &q); err != nil { |
| t.Errorf("%q: %v", test.in, err) |
| } |
| if q.String() != test.expect { |
| t.Errorf("unexpected string: %q", q.String()) |
| } |
| } |
| } |
| |
| 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, "1024m", false}, // Format changes |
| {1000000, "invalidFormatDefaultsToExponent", "1e3", true}, |
| {1024 * 1024, BinarySI, "1048576m", false}, // Format changes |
| } |
| |
| 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 _, asDec := range []bool{false, true} { |
| for _, item := range table { |
| q := NewQuantity(item.value, item.format) |
| if asDec { |
| q.ToDec() |
| } |
| 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 asDec { |
| q.ToDec() |
| } |
| if e, a := item.expect, q.String(); e != a { |
| t.Errorf("Set: Expected %v, got %v; %#v", e, a, q) |
| } |
| } |
| } |
| } |
| |
| func TestNewScaledSet(t *testing.T) { |
| table := []struct { |
| value int64 |
| scale Scale |
| expect string |
| }{ |
| {1, Nano, "1n"}, |
| {1000, Nano, "1u"}, |
| {1, Micro, "1u"}, |
| {1000, Micro, "1m"}, |
| {1, Milli, "1m"}, |
| {1000, Milli, "1"}, |
| {1, 0, "1"}, |
| {0, Nano, "0"}, |
| {0, Micro, "0"}, |
| {0, Milli, "0"}, |
| {0, 0, "0"}, |
| } |
| |
| for _, item := range table { |
| q := NewScaledQuantity(item.value, item.scale) |
| 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.ScaledValue(item.scale); e != a { |
| t.Errorf("Expected %v, got %v", e, a) |
| } |
| q3 := NewQuantity(0, DecimalSI) |
| q3.SetScaled(item.value, item.scale) |
| if q.Cmp(*q3) != 0 { |
| t.Errorf("Expected %v and %v to be equal", q, q3) |
| } |
| } |
| } |
| |
| func TestScaledValue(t *testing.T) { |
| table := []struct { |
| fromScale Scale |
| toScale Scale |
| expected int64 |
| }{ |
| {Nano, Nano, 1}, |
| {Nano, Micro, 1}, |
| {Nano, Milli, 1}, |
| {Nano, 0, 1}, |
| {Micro, Nano, 1000}, |
| {Micro, Micro, 1}, |
| {Micro, Milli, 1}, |
| {Micro, 0, 1}, |
| {Milli, Nano, 1000 * 1000}, |
| {Milli, Micro, 1000}, |
| {Milli, Milli, 1}, |
| {Milli, 0, 1}, |
| {0, Nano, 1000 * 1000 * 1000}, |
| {0, Micro, 1000 * 1000}, |
| {0, Milli, 1000}, |
| {0, 0, 1}, |
| } |
| |
| for _, item := range table { |
| q := NewScaledQuantity(1, item.fromScale) |
| if e, a := item.expected, q.ScaledValue(item.toScale); e != a { |
| t.Errorf("%v to %v: Expected %v, got %v", item.fromScale, item.toScale, e, a) |
| } |
| } |
| } |
| |
| 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") |
| } |
| } |
| |
| func TestSub(t *testing.T) { |
| tests := []struct { |
| a Quantity |
| b Quantity |
| expected Quantity |
| }{ |
| {decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(0, 0, DecimalSI)}, |
| {decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(9, 0, DecimalSI)}, |
| {decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(9, 0, BinarySI)}, |
| {Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(-50, 0, DecimalSI)}, |
| {decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)}, |
| {Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)}, |
| } |
| |
| for i, test := range tests { |
| test.a.Sub(test.b) |
| if test.a.Cmp(test.expected) != 0 { |
| t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) |
| } |
| } |
| } |
| |
| func TestNeg(t *testing.T) { |
| tests := []struct { |
| a Quantity |
| b Quantity |
| expected Quantity |
| }{ |
| {a: intQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)}, |
| {a: Quantity{}, expected: Quantity{}}, |
| {a: intQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)}, |
| {a: intQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)}, |
| {a: decQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)}, |
| {a: decQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)}, |
| {a: decQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)}, |
| } |
| |
| for i, test := range tests { |
| a := test.a.Copy() |
| a.Neg() |
| // ensure value is same |
| if a.Cmp(test.expected) != 0 { |
| t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), a.String()) |
| } |
| } |
| } |
| |
| func TestAdd(t *testing.T) { |
| tests := []struct { |
| a Quantity |
| b Quantity |
| expected Quantity |
| }{ |
| {decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(20, 0, DecimalSI)}, |
| {decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(11, 0, DecimalSI)}, |
| {decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(11, 0, BinarySI)}, |
| {Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(50, 0, DecimalSI)}, |
| {decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)}, |
| {Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)}, |
| } |
| |
| for i, test := range tests { |
| test.a.Add(test.b) |
| if test.a.Cmp(test.expected) != 0 { |
| t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String()) |
| } |
| } |
| } |
| |
| func TestAddSubRoundTrip(t *testing.T) { |
| for k := -10; k <= 10; k++ { |
| q := Quantity{Format: DecimalSI} |
| var order []int64 |
| for i := 0; i < 100; i++ { |
| j := rand.Int63() |
| order = append(order, j) |
| q.Add(*NewScaledQuantity(j, Scale(k))) |
| } |
| for _, j := range order { |
| q.Sub(*NewScaledQuantity(j, Scale(k))) |
| } |
| if !q.IsZero() { |
| t.Errorf("addition and subtraction did not cancel: %s", &q) |
| } |
| } |
| } |
| |
| func TestAddSubRoundTripAcrossScales(t *testing.T) { |
| q := Quantity{Format: DecimalSI} |
| var order []int64 |
| for i := 0; i < 100; i++ { |
| j := rand.Int63() |
| order = append(order, j) |
| q.Add(*NewScaledQuantity(j, Scale(j%20-10))) |
| } |
| for _, j := range order { |
| q.Sub(*NewScaledQuantity(j, Scale(j%20-10))) |
| } |
| if !q.IsZero() { |
| t.Errorf("addition and subtraction did not cancel: %s", &q) |
| } |
| } |
| |
| func TestNegateRoundTrip(t *testing.T) { |
| for _, asDec := range []bool{false, true} { |
| for k := -10; k <= 10; k++ { |
| for i := 0; i < 100; i++ { |
| j := rand.Int63() |
| q := *NewScaledQuantity(j, Scale(k)) |
| if asDec { |
| q.AsDec() |
| } |
| |
| b := q.Copy() |
| b.Neg() |
| b.Neg() |
| if b.Cmp(q) != 0 { |
| t.Errorf("double negation did not cancel: %s", &q) |
| } |
| } |
| } |
| } |
| } |
| func benchmarkQuantities() []Quantity { |
| return []Quantity{ |
| intQuantity(1024*1024*1024, 0, BinarySI), |
| intQuantity(1024*1024*1024*1024, 0, BinarySI), |
| intQuantity(1000000, 3, DecimalSI), |
| intQuantity(1000000000, 0, DecimalSI), |
| intQuantity(1, -3, DecimalSI), |
| intQuantity(80, -3, DecimalSI), |
| intQuantity(1080, -3, DecimalSI), |
| intQuantity(0, 0, BinarySI), |
| intQuantity(1, 9, DecimalExponent), |
| intQuantity(1, -9, DecimalSI), |
| intQuantity(1000000, 10, DecimalSI), |
| } |
| } |
| |
| func BenchmarkQuantityString(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| var s string |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| q.s = "" |
| s = q.String() |
| } |
| b.StopTimer() |
| if len(s) == 0 { |
| b.Fatal(s) |
| } |
| } |
| |
| func BenchmarkQuantityStringPrecalc(b *testing.B) { |
| values := benchmarkQuantities() |
| for i := range values { |
| _ = values[i].String() |
| } |
| b.ResetTimer() |
| var s string |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| s = q.String() |
| } |
| b.StopTimer() |
| if len(s) == 0 { |
| b.Fatal(s) |
| } |
| } |
| |
| func BenchmarkQuantityStringBinarySI(b *testing.B) { |
| values := benchmarkQuantities() |
| for i := range values { |
| values[i].Format = BinarySI |
| } |
| b.ResetTimer() |
| var s string |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| q.s = "" |
| s = q.String() |
| } |
| b.StopTimer() |
| if len(s) == 0 { |
| b.Fatal(s) |
| } |
| } |
| |
| func BenchmarkQuantityMarshalJSON(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| q.s = "" |
| if _, err := q.MarshalJSON(); err != nil { |
| b.Fatal(err) |
| } |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkQuantityUnmarshalJSON(b *testing.B) { |
| values := benchmarkQuantities() |
| var json [][]byte |
| for _, v := range values { |
| data, _ := v.MarshalJSON() |
| json = append(json, data) |
| } |
| |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| var q Quantity |
| if err := q.UnmarshalJSON(json[i%len(values)]); err != nil { |
| b.Fatal(err) |
| } |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkParseQuantity(b *testing.B) { |
| values := benchmarkQuantities() |
| var strings []string |
| for _, v := range values { |
| strings = append(strings, v.String()) |
| } |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| if _, err := ParseQuantity(strings[i%len(values)]); err != nil { |
| b.Fatal(err) |
| } |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkCanonicalize(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| buffer := make([]byte, 0, 100) |
| for i := 0; i < b.N; i++ { |
| s, _ := values[i%len(values)].CanonicalizeBytes(buffer) |
| if len(s) == 0 { |
| b.Fatal(s) |
| } |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkQuantityRoundUp(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| copied := q |
| copied.RoundUp(-3) |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkQuantityCopy(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| values[i%len(values)].Copy() |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkQuantityAdd(b *testing.B) { |
| values := benchmarkQuantities() |
| base := &Quantity{} |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| base.d.Dec = nil |
| base.i = int64Amount{value: 100} |
| base.Add(q) |
| } |
| b.StopTimer() |
| } |
| |
| func BenchmarkQuantityCmp(b *testing.B) { |
| values := benchmarkQuantities() |
| b.ResetTimer() |
| for i := 0; i < b.N; i++ { |
| q := values[i%len(values)] |
| if q.Cmp(q) != 0 { |
| b.Fatal(q) |
| } |
| } |
| b.StopTimer() |
| } |