github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/resource/quantity_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resource
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"math"
    23  	"math/rand"
    24  	"os"
    25  	"strings"
    26  	"testing"
    27  	"unicode"
    28  
    29  	fuzz "github.com/google/gofuzz"
    30  	"github.com/spf13/pflag"
    31  
    32  	inf "gopkg.in/inf.v0"
    33  )
    34  
    35  func dec(i int64, exponent int) infDecAmount {
    36  	// See the below test-- scale is the negative of an exponent.
    37  	return infDecAmount{inf.NewDec(i, inf.Scale(-exponent))}
    38  }
    39  
    40  func decQuantity(i int64, exponent int, format Format) Quantity {
    41  	return Quantity{d: dec(i, exponent), Format: format}
    42  }
    43  
    44  func intQuantity(i int64, exponent Scale, format Format) Quantity {
    45  	return Quantity{i: int64Amount{value: i, scale: exponent}, Format: format}
    46  }
    47  
    48  func TestDec(t *testing.T) {
    49  	table := []struct {
    50  		got    infDecAmount
    51  		expect string
    52  	}{
    53  		{dec(1, 0), "1"},
    54  		{dec(1, 1), "10"},
    55  		{dec(5, 2), "500"},
    56  		{dec(8, 3), "8000"},
    57  		{dec(2, 0), "2"},
    58  		{dec(1, -1), "0.1"},
    59  		{dec(3, -2), "0.03"},
    60  		{dec(4, -3), "0.004"},
    61  	}
    62  
    63  	for _, item := range table {
    64  		if e, a := item.expect, item.got.Dec.String(); e != a {
    65  			t.Errorf("expected %v, got %v", e, a)
    66  		}
    67  	}
    68  }
    69  
    70  // TestQuantityParseZero ensures that when a 0 quantity is passed, its string value is 0
    71  func TestQuantityParseZero(t *testing.T) {
    72  	zero := MustParse("0")
    73  	if expected, actual := "0", zero.String(); expected != actual {
    74  		t.Errorf("Expected %v, actual %v", expected, actual)
    75  	}
    76  }
    77  
    78  // TestQuantityParseNonNumericPanic ensures that when a non-numeric string is parsed
    79  // it panics
    80  func TestQuantityParseNonNumericPanic(t *testing.T) {
    81  	defer func() {
    82  		if r := recover(); r == nil {
    83  			t.Errorf("MustParse did not panic")
    84  		}
    85  	}()
    86  	_ = MustParse("Non-Numeric")
    87  }
    88  
    89  // TestQuantityAddZeroPreservesSuffix verifies that a suffix is preserved
    90  // independent of the order of operations when adding a zero and non-zero val
    91  func TestQuantityAddZeroPreservesSuffix(t *testing.T) {
    92  	testValues := []string{"100m", "1Gi"}
    93  	zero := MustParse("0")
    94  	for _, testValue := range testValues {
    95  		value := MustParse(testValue)
    96  		v1 := value.DeepCopy()
    97  		// ensure non-zero + zero = non-zero (suffix preserved)
    98  		v1.Add(zero)
    99  		// ensure zero + non-zero = non-zero (suffix preserved)
   100  		v2 := zero.DeepCopy()
   101  		v2.Add(value)
   102  
   103  		if v1.String() != testValue {
   104  			t.Errorf("Expected %v, actual %v", testValue, v1.String())
   105  			continue
   106  		}
   107  		if v2.String() != testValue {
   108  			t.Errorf("Expected %v, actual %v", testValue, v2.String())
   109  		}
   110  	}
   111  }
   112  
   113  // TestQuantitySubZeroPreservesSuffix verifies that a suffix is preserved
   114  // independent of the order of operations when subtracting a zero and non-zero val
   115  func TestQuantitySubZeroPreservesSuffix(t *testing.T) {
   116  	testValues := []string{"100m", "1Gi"}
   117  	zero := MustParse("0")
   118  	for _, testValue := range testValues {
   119  		value := MustParse(testValue)
   120  		v1 := value.DeepCopy()
   121  		// ensure non-zero - zero = non-zero (suffix preserved)
   122  		v1.Sub(zero)
   123  		// ensure we preserved the input value
   124  		if v1.String() != testValue {
   125  			t.Errorf("Expected %v, actual %v", testValue, v1.String())
   126  		}
   127  
   128  		// ensure zero - non-zero = -non-zero (suffix preserved)
   129  		v2 := zero.DeepCopy()
   130  		v2.Sub(value)
   131  		negVal := value.DeepCopy()
   132  		negVal.Neg()
   133  		if v2.String() != negVal.String() {
   134  			t.Errorf("Expected %v, actual %v", negVal.String(), v2.String())
   135  		}
   136  	}
   137  }
   138  
   139  // TestQuantityCanocicalizeZero verifies that you get 0 as canonical value if internal value is 0, and not 0<suffix>
   140  func TestQuantityCanocicalizeZero(t *testing.T) {
   141  	val := MustParse("1000m")
   142  	val.i.Sub(int64Amount{value: 1})
   143  	zero := Quantity{i: val.i, Format: DecimalSI}
   144  	if expected, actual := "0", zero.String(); expected != actual {
   145  		t.Errorf("Expected %v, actual %v", expected, actual)
   146  	}
   147  }
   148  
   149  func TestQuantityCmp(t *testing.T) {
   150  	// Test when d is nil
   151  	table := []struct {
   152  		x      string
   153  		y      string
   154  		expect int
   155  	}{
   156  		{"0", "0", 0},
   157  		{"100m", "50m", 1},
   158  		{"50m", "100m", -1},
   159  		{"10000T", "100Gi", 1},
   160  	}
   161  	for _, testCase := range table {
   162  		q1 := MustParse(testCase.x)
   163  		q2 := MustParse(testCase.y)
   164  		if result := q1.Cmp(q2); result != testCase.expect {
   165  			t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result)
   166  		}
   167  	}
   168  	// Test when i is {0,0}
   169  	table2 := []struct {
   170  		x      *inf.Dec
   171  		y      *inf.Dec
   172  		expect int
   173  	}{
   174  		{dec(0, 0).Dec, dec(0, 0).Dec, 0},
   175  		{nil, dec(0, 0).Dec, 0},
   176  		{dec(0, 0).Dec, nil, 0},
   177  		{nil, nil, 0},
   178  		{nil, dec(10, 0).Dec, -1},
   179  		{nil, dec(-10, 0).Dec, 1},
   180  		{dec(10, 0).Dec, nil, 1},
   181  		{dec(-10, 0).Dec, nil, -1},
   182  	}
   183  	for _, testCase := range table2 {
   184  		q1 := Quantity{d: infDecAmount{testCase.x}, Format: DecimalSI}
   185  		q2 := Quantity{d: infDecAmount{testCase.y}, Format: DecimalSI}
   186  		if result := q1.Cmp(q2); result != testCase.expect {
   187  			t.Errorf("X: %v, Y: %v, Expected: %v, Actual: %v", testCase.x, testCase.y, testCase.expect, result)
   188  		}
   189  	}
   190  }
   191  
   192  func TestParseQuantityString(t *testing.T) {
   193  	table := []struct {
   194  		input              string
   195  		positive           bool
   196  		value              string
   197  		num, denom, suffix string
   198  	}{
   199  		{"0.025Ti", true, "0.025", "0", "025", "Ti"},
   200  		{"1.025Ti", true, "1.025", "1", "025", "Ti"},
   201  		{"-1.025Ti", false, "-1.025", "1", "025", "Ti"},
   202  		{".", true, ".", "0", "", ""},
   203  		{"-.", false, "-.", "0", "", ""},
   204  		{"1E-3", true, "1", "1", "", "E-3"},
   205  	}
   206  	for _, test := range table {
   207  		positive, value, num, denom, suffix, err := parseQuantityString(test.input)
   208  		if err != nil {
   209  			t.Errorf("%s: error: %v", test.input, err)
   210  			continue
   211  		}
   212  		if positive != test.positive || value != test.value || num != test.num || denom != test.denom || suffix != test.suffix {
   213  			t.Errorf("%s: unmatched: %t %q %q %q %q", test.input, positive, value, num, denom, suffix)
   214  		}
   215  	}
   216  }
   217  
   218  func TestQuantityParse(t *testing.T) {
   219  	if _, err := ParseQuantity(""); err == nil {
   220  		t.Errorf("expected empty string to return error")
   221  	}
   222  
   223  	table := []struct {
   224  		input  string
   225  		expect Quantity
   226  	}{
   227  		{"0", decQuantity(0, 0, DecimalSI)},
   228  		{"0n", decQuantity(0, 0, DecimalSI)},
   229  		{"0u", decQuantity(0, 0, DecimalSI)},
   230  		{"0m", decQuantity(0, 0, DecimalSI)},
   231  		{"0Ki", decQuantity(0, 0, BinarySI)},
   232  		{"0k", decQuantity(0, 0, DecimalSI)},
   233  		{"0Mi", decQuantity(0, 0, BinarySI)},
   234  		{"0M", decQuantity(0, 0, DecimalSI)},
   235  		{"0Gi", decQuantity(0, 0, BinarySI)},
   236  		{"0G", decQuantity(0, 0, DecimalSI)},
   237  		{"0Ti", decQuantity(0, 0, BinarySI)},
   238  		{"0T", decQuantity(0, 0, DecimalSI)},
   239  
   240  		// Quantity less numbers are allowed
   241  		{"1", decQuantity(1, 0, DecimalSI)},
   242  
   243  		// Binary suffixes
   244  		{"1Ki", decQuantity(1024, 0, BinarySI)},
   245  		{"8Ki", decQuantity(8*1024, 0, BinarySI)},
   246  		{"7Mi", decQuantity(7*1024*1024, 0, BinarySI)},
   247  		{"6Gi", decQuantity(6*1024*1024*1024, 0, BinarySI)},
   248  		{"5Ti", decQuantity(5*1024*1024*1024*1024, 0, BinarySI)},
   249  		{"4Pi", decQuantity(4*1024*1024*1024*1024*1024, 0, BinarySI)},
   250  		{"3Ei", decQuantity(3*1024*1024*1024*1024*1024*1024, 0, BinarySI)},
   251  
   252  		{"10Ti", decQuantity(10*1024*1024*1024*1024, 0, BinarySI)},
   253  		{"100Ti", decQuantity(100*1024*1024*1024*1024, 0, BinarySI)},
   254  
   255  		// Decimal suffixes
   256  		{"5n", decQuantity(5, -9, DecimalSI)},
   257  		{"4u", decQuantity(4, -6, DecimalSI)},
   258  		{"3m", decQuantity(3, -3, DecimalSI)},
   259  		{"9", decQuantity(9, 0, DecimalSI)},
   260  		{"8k", decQuantity(8, 3, DecimalSI)},
   261  		{"50k", decQuantity(5, 4, DecimalSI)},
   262  		{"7M", decQuantity(7, 6, DecimalSI)},
   263  		{"6G", decQuantity(6, 9, DecimalSI)},
   264  		{"5T", decQuantity(5, 12, DecimalSI)},
   265  		{"40T", decQuantity(4, 13, DecimalSI)},
   266  		{"300T", decQuantity(3, 14, DecimalSI)},
   267  		{"2P", decQuantity(2, 15, DecimalSI)},
   268  		{"1E", decQuantity(1, 18, DecimalSI)},
   269  
   270  		// Decimal exponents
   271  		{"1E-3", decQuantity(1, -3, DecimalExponent)},
   272  		{"1e3", decQuantity(1, 3, DecimalExponent)},
   273  		{"1E6", decQuantity(1, 6, DecimalExponent)},
   274  		{"1e9", decQuantity(1, 9, DecimalExponent)},
   275  		{"1E12", decQuantity(1, 12, DecimalExponent)},
   276  		{"1e15", decQuantity(1, 15, DecimalExponent)},
   277  		{"1E18", decQuantity(1, 18, DecimalExponent)},
   278  
   279  		// Nonstandard but still parsable
   280  		{"1e14", decQuantity(1, 14, DecimalExponent)},
   281  		{"1e13", decQuantity(1, 13, DecimalExponent)},
   282  		{"1e3", decQuantity(1, 3, DecimalExponent)},
   283  		{"100.035k", decQuantity(100035, 0, DecimalSI)},
   284  
   285  		// Things that look like floating point
   286  		{"0.001", decQuantity(1, -3, DecimalSI)},
   287  		{"0.0005k", decQuantity(5, -1, DecimalSI)},
   288  		{"0.005", decQuantity(5, -3, DecimalSI)},
   289  		{"0.05", decQuantity(5, -2, DecimalSI)},
   290  		{"0.5", decQuantity(5, -1, DecimalSI)},
   291  		{"0.00050k", decQuantity(5, -1, DecimalSI)},
   292  		{"0.00500", decQuantity(5, -3, DecimalSI)},
   293  		{"0.05000", decQuantity(5, -2, DecimalSI)},
   294  		{"0.50000", decQuantity(5, -1, DecimalSI)},
   295  		{"0.5e0", decQuantity(5, -1, DecimalExponent)},
   296  		{"0.5e-1", decQuantity(5, -2, DecimalExponent)},
   297  		{"0.5e-2", decQuantity(5, -3, DecimalExponent)},
   298  		{"0.5e0", decQuantity(5, -1, DecimalExponent)},
   299  		{"10.035M", decQuantity(10035, 3, DecimalSI)},
   300  
   301  		{"1.2e3", decQuantity(12, 2, DecimalExponent)},
   302  		{"1.3E+6", decQuantity(13, 5, DecimalExponent)},
   303  		{"1.40e9", decQuantity(14, 8, DecimalExponent)},
   304  		{"1.53E12", decQuantity(153, 10, DecimalExponent)},
   305  		{"1.6e15", decQuantity(16, 14, DecimalExponent)},
   306  		{"1.7E18", decQuantity(17, 17, DecimalExponent)},
   307  
   308  		{"9.01", decQuantity(901, -2, DecimalSI)},
   309  		{"8.1k", decQuantity(81, 2, DecimalSI)},
   310  		{"7.123456M", decQuantity(7123456, 0, DecimalSI)},
   311  		{"6.987654321G", decQuantity(6987654321, 0, DecimalSI)},
   312  		{"5.444T", decQuantity(5444, 9, DecimalSI)},
   313  		{"40.1T", decQuantity(401, 11, DecimalSI)},
   314  		{"300.2T", decQuantity(3002, 11, DecimalSI)},
   315  		{"2.5P", decQuantity(25, 14, DecimalSI)},
   316  		{"1.01E", decQuantity(101, 16, DecimalSI)},
   317  
   318  		// Things that saturate/round
   319  		{"3.001n", decQuantity(4, -9, DecimalSI)},
   320  		{"1.1E-9", decQuantity(2, -9, DecimalExponent)},
   321  		{"0.0000000001", decQuantity(1, -9, DecimalSI)},
   322  		{"0.0000000005", decQuantity(1, -9, DecimalSI)},
   323  		{"0.00000000050", decQuantity(1, -9, DecimalSI)},
   324  		{"0.5e-9", decQuantity(1, -9, DecimalExponent)},
   325  		{"0.9n", decQuantity(1, -9, DecimalSI)},
   326  		{"0.00000012345", decQuantity(124, -9, DecimalSI)},
   327  		{"0.00000012354", decQuantity(124, -9, DecimalSI)},
   328  		{"9Ei", Quantity{d: maxAllowed, Format: BinarySI}},
   329  		{"9223372036854775807Ki", Quantity{d: maxAllowed, Format: BinarySI}},
   330  		{"12E", decQuantity(12, 18, DecimalSI)},
   331  
   332  		// We'll accept fractional binary stuff, too.
   333  		{"100.035Ki", decQuantity(10243584, -2, BinarySI)},
   334  		{"0.5Mi", decQuantity(.5*1024*1024, 0, BinarySI)},
   335  		{"0.05Gi", decQuantity(536870912, -1, BinarySI)},
   336  		{"0.025Ti", decQuantity(274877906944, -1, BinarySI)},
   337  
   338  		// Things written by trolls
   339  		{"0.000000000001Ki", decQuantity(2, -9, DecimalSI)}, // rounds up, changes format
   340  		{".001", decQuantity(1, -3, DecimalSI)},
   341  		{".0001k", decQuantity(100, -3, DecimalSI)},
   342  		{"1.", decQuantity(1, 0, DecimalSI)},
   343  		{"1.G", decQuantity(1, 9, DecimalSI)},
   344  	}
   345  
   346  	for _, asDec := range []bool{false, true} {
   347  		for _, item := range table {
   348  			got, err := ParseQuantity(item.input)
   349  			if err != nil {
   350  				t.Errorf("%v: unexpected error: %v", item.input, err)
   351  				continue
   352  			}
   353  			if asDec {
   354  				got.AsDec()
   355  			}
   356  
   357  			if e, a := item.expect, got; e.Cmp(a) != 0 {
   358  				t.Errorf("%v: expected %v, got %v", item.input, e.String(), a.String())
   359  			}
   360  			if e, a := item.expect.Format, got.Format; e != a {
   361  				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
   362  			}
   363  
   364  			if asDec {
   365  				if i, ok := got.AsInt64(); i != 0 || ok {
   366  					t.Errorf("%v: expected inf.Dec to return false for AsInt64: %d", item.input, i)
   367  				}
   368  				continue
   369  			}
   370  			i, ok := item.expect.AsInt64()
   371  			if !ok {
   372  				continue
   373  			}
   374  			j, ok := got.AsInt64()
   375  			if !ok {
   376  				if got.d.Dec == nil && got.i.scale >= 0 {
   377  					t.Errorf("%v: is an int64Amount, but can't return AsInt64: %v", item.input, got)
   378  				}
   379  				continue
   380  			}
   381  			if i != j {
   382  				t.Errorf("%v: expected equivalent representation as int64: %d %d", item.input, i, j)
   383  			}
   384  		}
   385  
   386  		for _, item := range table {
   387  			got, err := ParseQuantity(item.input)
   388  			if err != nil {
   389  				t.Errorf("%v: unexpected error: %v", item.input, err)
   390  				continue
   391  			}
   392  
   393  			if asDec {
   394  				got.AsDec()
   395  			}
   396  
   397  			for _, format := range []Format{DecimalSI, BinarySI, DecimalExponent} {
   398  				// ensure we are not simply checking pointer equality by creating a new inf.Dec
   399  				var copied inf.Dec
   400  				copied.Add(inf.NewDec(0, inf.Scale(0)), got.AsDec())
   401  				q := NewDecimalQuantity(copied, format)
   402  				if c := q.Cmp(got); c != 0 {
   403  					t.Errorf("%v: round trip from decimal back to quantity is not comparable: %d: %#v vs %#v", item.input, c, got, q)
   404  				}
   405  			}
   406  
   407  			// verify that we can decompose the input and get the same result by building up from the base.
   408  			positive, _, num, denom, suffix, err := parseQuantityString(item.input)
   409  			if err != nil {
   410  				t.Errorf("%v: unexpected error: %v", item.input, err)
   411  				continue
   412  			}
   413  			if got.Sign() >= 0 && !positive || got.Sign() < 0 && positive {
   414  				t.Errorf("%v: positive was incorrect: %t", item.input, positive)
   415  				continue
   416  			}
   417  			var value string
   418  			if !positive {
   419  				value = "-"
   420  			}
   421  			value += num
   422  			if len(denom) > 0 {
   423  				value += "." + denom
   424  			}
   425  			value += suffix
   426  			if len(value) == 0 {
   427  				t.Errorf("%v: did not parse correctly, %q %q %q", item.input, num, denom, suffix)
   428  			}
   429  			expected, err := ParseQuantity(value)
   430  			if err != nil {
   431  				t.Errorf("%v: unexpected error for %s: %v", item.input, value, err)
   432  				continue
   433  			}
   434  			if expected.Cmp(got) != 0 {
   435  				t.Errorf("%v: not the same as %s", item.input, value)
   436  				continue
   437  			}
   438  		}
   439  
   440  		// Try the negative version of everything
   441  		desired := &inf.Dec{}
   442  		expect := Quantity{d: infDecAmount{Dec: desired}}
   443  		for _, item := range table {
   444  			got, err := ParseQuantity("-" + strings.TrimLeftFunc(item.input, unicode.IsSpace))
   445  			if err != nil {
   446  				t.Errorf("-%v: unexpected error: %v", item.input, err)
   447  				continue
   448  			}
   449  			if asDec {
   450  				got.AsDec()
   451  			}
   452  
   453  			expected := item.expect
   454  			desired.Neg(expected.AsDec())
   455  
   456  			if e, a := expect, got; e.Cmp(a) != 0 {
   457  				t.Errorf("%v: expected %s, got %s", item.input, e.String(), a.String())
   458  			}
   459  			if e, a := expected.Format, got.Format; e != a {
   460  				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
   461  			}
   462  		}
   463  
   464  		// Try everything with an explicit +
   465  		for _, item := range table {
   466  			got, err := ParseQuantity("+" + strings.TrimLeftFunc(item.input, unicode.IsSpace))
   467  			if err != nil {
   468  				t.Errorf("-%v: unexpected error: %v", item.input, err)
   469  				continue
   470  			}
   471  			if asDec {
   472  				got.AsDec()
   473  			}
   474  
   475  			if e, a := item.expect, got; e.Cmp(a) != 0 {
   476  				t.Errorf("%v(%t): expected %s, got %s", item.input, asDec, e.String(), a.String())
   477  			}
   478  			if e, a := item.expect.Format, got.Format; e != a {
   479  				t.Errorf("%v: expected %#v, got %#v", item.input, e, a)
   480  			}
   481  		}
   482  	}
   483  
   484  	invalid := []string{
   485  		"1.1.M",
   486  		"1+1.0M",
   487  		"0.1mi",
   488  		"0.1am",
   489  		"aoeu",
   490  		".5i",
   491  		"1i",
   492  		"-3.01i",
   493  		"-3.01e-",
   494  
   495  		// trailing whitespace is forbidden
   496  		" 1",
   497  		"1 ",
   498  	}
   499  	for _, item := range invalid {
   500  		_, err := ParseQuantity(item)
   501  		if err == nil {
   502  			t.Errorf("%v parsed unexpectedly", item)
   503  		}
   504  	}
   505  }
   506  
   507  func TestQuantityRoundUp(t *testing.T) {
   508  	table := []struct {
   509  		in     string
   510  		scale  Scale
   511  		expect Quantity
   512  		ok     bool
   513  	}{
   514  		{"9.01", -3, decQuantity(901, -2, DecimalSI), true},
   515  		{"9.01", -2, decQuantity(901, -2, DecimalSI), true},
   516  		{"9.01", -1, decQuantity(91, -1, DecimalSI), false},
   517  		{"9.01", 0, decQuantity(10, 0, DecimalSI), false},
   518  		{"9.01", 1, decQuantity(10, 0, DecimalSI), false},
   519  		{"9.01", 2, decQuantity(100, 0, DecimalSI), false},
   520  
   521  		{"-9.01", -3, decQuantity(-901, -2, DecimalSI), true},
   522  		{"-9.01", -2, decQuantity(-901, -2, DecimalSI), true},
   523  		{"-9.01", -1, decQuantity(-91, -1, DecimalSI), false},
   524  		{"-9.01", 0, decQuantity(-10, 0, DecimalSI), false},
   525  		{"-9.01", 1, decQuantity(-10, 0, DecimalSI), false},
   526  		{"-9.01", 2, decQuantity(-100, 0, DecimalSI), false},
   527  	}
   528  
   529  	for _, asDec := range []bool{false, true} {
   530  		for _, item := range table {
   531  			got, err := ParseQuantity(item.in)
   532  			if err != nil {
   533  				t.Fatalf("unexpected error: %v", err)
   534  			}
   535  			expect := item.expect.DeepCopy()
   536  			if asDec {
   537  				got.AsDec()
   538  			}
   539  			if ok := got.RoundUp(item.scale); ok != item.ok {
   540  				t.Errorf("%s(%d,%t): unexpected ok: %t", item.in, item.scale, asDec, ok)
   541  			}
   542  			if got.Cmp(expect) != 0 {
   543  				t.Errorf("%s(%d,%t): unexpected round: %s vs %s", item.in, item.scale, asDec, got.String(), expect.String())
   544  			}
   545  		}
   546  	}
   547  }
   548  
   549  func TestQuantityCmpInt64AndDec(t *testing.T) {
   550  	table := []struct {
   551  		a, b Quantity
   552  		cmp  int
   553  	}{
   554  		{intQuantity(901, -2, DecimalSI), intQuantity(901, -2, DecimalSI), 0},
   555  		{intQuantity(90, -1, DecimalSI), intQuantity(901, -2, DecimalSI), -1},
   556  		{intQuantity(901, -2, DecimalSI), intQuantity(900, -2, DecimalSI), 1},
   557  		{intQuantity(0, 0, DecimalSI), intQuantity(0, 0, DecimalSI), 0},
   558  		{intQuantity(0, 1, DecimalSI), intQuantity(0, -1, DecimalSI), 0},
   559  		{intQuantity(0, -1, DecimalSI), intQuantity(0, 1, DecimalSI), 0},
   560  		{intQuantity(800, -3, DecimalSI), intQuantity(1, 0, DecimalSI), -1},
   561  		{intQuantity(800, -3, DecimalSI), intQuantity(79, -2, DecimalSI), 1},
   562  
   563  		{intQuantity(mostPositive, 0, DecimalSI), intQuantity(1, -1, DecimalSI), 1},
   564  		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 0, DecimalSI), 1},
   565  		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(1, 1, DecimalSI), 1},
   566  		{intQuantity(mostPositive, 1, DecimalSI), intQuantity(0, 1, DecimalSI), 1},
   567  		{intQuantity(mostPositive, -16, DecimalSI), intQuantity(1, 3, DecimalSI), -1},
   568  
   569  		{intQuantity(mostNegative, 0, DecimalSI), intQuantity(0, 0, DecimalSI), -1},
   570  		{intQuantity(mostNegative, -18, DecimalSI), intQuantity(-1, 0, DecimalSI), -1},
   571  		{intQuantity(mostNegative, -19, DecimalSI), intQuantity(-1, 0, DecimalSI), 1},
   572  
   573  		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 0},
   574  		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 1},
   575  		{intQuantity(-1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(-10, 0, DecimalSI), 0},
   576  		{intQuantity(1*1000000*1000000*1000000, -17, DecimalSI), intQuantity(1, 0, DecimalSI), 1},
   577  
   578  		{intQuantity(1*1000000*1000000*1000000+1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), 1},
   579  		{intQuantity(1*1000000*1000000*1000000-1, -17, DecimalSI), intQuantity(1, 1, DecimalSI), -1},
   580  	}
   581  
   582  	for _, item := range table {
   583  		if cmp := item.a.Cmp(item.b); cmp != item.cmp {
   584  			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
   585  		}
   586  		if cmp := item.b.Cmp(item.a); cmp != -item.cmp {
   587  			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
   588  		}
   589  	}
   590  
   591  	for _, item := range table {
   592  		a, b := item.a.DeepCopy(), item.b.DeepCopy()
   593  		a.AsDec()
   594  		if cmp := a.Cmp(b); cmp != item.cmp {
   595  			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
   596  		}
   597  		if cmp := b.Cmp(a); cmp != -item.cmp {
   598  			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
   599  		}
   600  	}
   601  
   602  	for _, item := range table {
   603  		a, b := item.a.DeepCopy(), item.b.DeepCopy()
   604  		b.AsDec()
   605  		if cmp := a.Cmp(b); cmp != item.cmp {
   606  			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
   607  		}
   608  		if cmp := b.Cmp(a); cmp != -item.cmp {
   609  			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
   610  		}
   611  	}
   612  
   613  	for _, item := range table {
   614  		a, b := item.a.DeepCopy(), item.b.DeepCopy()
   615  		a.AsDec()
   616  		b.AsDec()
   617  		if cmp := a.Cmp(b); cmp != item.cmp {
   618  			t.Errorf("%#v: unexpected Cmp: %d", item, cmp)
   619  		}
   620  		if cmp := b.Cmp(a); cmp != -item.cmp {
   621  			t.Errorf("%#v: unexpected inverted Cmp: %d", item, cmp)
   622  		}
   623  	}
   624  }
   625  
   626  func TestQuantityNeg(t *testing.T) {
   627  	table := []struct {
   628  		a   Quantity
   629  		out string
   630  	}{
   631  		{intQuantity(901, -2, DecimalSI), "-9010m"},
   632  		{decQuantity(901, -2, DecimalSI), "-9010m"},
   633  	}
   634  
   635  	for i, item := range table {
   636  		out := item.a.DeepCopy()
   637  		out.Neg()
   638  		if out.Cmp(item.a) == 0 {
   639  			t.Errorf("%d: negating an item should not mutate the source: %s", i, out.String())
   640  		}
   641  		if out.String() != item.out {
   642  			t.Errorf("%d: negating did not equal exact value: %s", i, out.String())
   643  		}
   644  	}
   645  }
   646  
   647  func TestQuantityString(t *testing.T) {
   648  	table := []struct {
   649  		in        Quantity
   650  		expect    string
   651  		alternate string
   652  	}{
   653  		{decQuantity(1024*1024*1024, 0, BinarySI), "1Gi", "1024Mi"},
   654  		{decQuantity(300*1024*1024, 0, BinarySI), "300Mi", "307200Ki"},
   655  		{decQuantity(6*1024, 0, BinarySI), "6Ki", ""},
   656  		{decQuantity(1001*1024*1024*1024, 0, BinarySI), "1001Gi", "1025024Mi"},
   657  		{decQuantity(1024*1024*1024*1024, 0, BinarySI), "1Ti", "1024Gi"},
   658  		{decQuantity(5, 0, BinarySI), "5", "5000m"},
   659  		{decQuantity(500, -3, BinarySI), "500m", "0.5"},
   660  		{decQuantity(1, 9, DecimalSI), "1G", "1000M"},
   661  		{decQuantity(1000, 6, DecimalSI), "1G", "0.001T"},
   662  		{decQuantity(1000000, 3, DecimalSI), "1G", ""},
   663  		{decQuantity(1000000000, 0, DecimalSI), "1G", ""},
   664  		{decQuantity(1, -3, DecimalSI), "1m", "1000u"},
   665  		{decQuantity(80, -3, DecimalSI), "80m", ""},
   666  		{decQuantity(1080, -3, DecimalSI), "1080m", "1.08"},
   667  		{decQuantity(108, -2, DecimalSI), "1080m", "1080000000n"},
   668  		{decQuantity(10800, -4, DecimalSI), "1080m", ""},
   669  		{decQuantity(300, 6, DecimalSI), "300M", ""},
   670  		{decQuantity(1, 12, DecimalSI), "1T", ""},
   671  		{decQuantity(1234567, 6, DecimalSI), "1234567M", ""},
   672  		{decQuantity(1234567, -3, BinarySI), "1234567m", ""},
   673  		{decQuantity(3, 3, DecimalSI), "3k", ""},
   674  		{decQuantity(1025, 0, BinarySI), "1025", ""},
   675  		{decQuantity(0, 0, DecimalSI), "0", ""},
   676  		{decQuantity(0, 0, BinarySI), "0", ""},
   677  		{decQuantity(1, 9, DecimalExponent), "1e9", ".001e12"},
   678  		{decQuantity(1, -3, DecimalExponent), "1e-3", "0.001e0"},
   679  		{decQuantity(1, -9, DecimalExponent), "1e-9", "1000e-12"},
   680  		{decQuantity(80, -3, DecimalExponent), "80e-3", ""},
   681  		{decQuantity(300, 6, DecimalExponent), "300e6", ""},
   682  		{decQuantity(1, 12, DecimalExponent), "1e12", ""},
   683  		{decQuantity(1, 3, DecimalExponent), "1e3", ""},
   684  		{decQuantity(3, 3, DecimalExponent), "3e3", ""},
   685  		{decQuantity(3, 3, DecimalSI), "3k", ""},
   686  		{decQuantity(0, 0, DecimalExponent), "0", "00"},
   687  		{decQuantity(1, -9, DecimalSI), "1n", ""},
   688  		{decQuantity(80, -9, DecimalSI), "80n", ""},
   689  		{decQuantity(1080, -9, DecimalSI), "1080n", ""},
   690  		{decQuantity(108, -8, DecimalSI), "1080n", ""},
   691  		{decQuantity(10800, -10, DecimalSI), "1080n", ""},
   692  		{decQuantity(1, -6, DecimalSI), "1u", ""},
   693  		{decQuantity(80, -6, DecimalSI), "80u", ""},
   694  		{decQuantity(1080, -6, DecimalSI), "1080u", ""},
   695  	}
   696  	for _, item := range table {
   697  		got := item.in.String()
   698  		if e, a := item.expect, got; e != a {
   699  			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
   700  		}
   701  		q, err := ParseQuantity(item.expect)
   702  		if err != nil {
   703  			t.Errorf("%#v: unexpected error: %v", item.expect, err)
   704  		}
   705  		if len(q.s) == 0 || q.s != item.expect {
   706  			t.Errorf("%#v: did not copy canonical string on parse: %s", item.expect, q.s)
   707  		}
   708  		if len(item.alternate) == 0 {
   709  			continue
   710  		}
   711  		q, err = ParseQuantity(item.alternate)
   712  		if err != nil {
   713  			t.Errorf("%#v: unexpected error: %v", item.expect, err)
   714  			continue
   715  		}
   716  		if len(q.s) != 0 {
   717  			t.Errorf("%#v: unexpected nested string: %v", item.expect, q.s)
   718  		}
   719  		if q.String() != item.expect {
   720  			t.Errorf("%#v: unexpected alternate canonical: %v", item.expect, q.String())
   721  		}
   722  		if len(q.s) == 0 || q.s != item.expect {
   723  			t.Errorf("%#v: did not set canonical string on ToString: %s", item.expect, q.s)
   724  		}
   725  	}
   726  	desired := &inf.Dec{} // Avoid modifying the values in the table.
   727  	for _, item := range table {
   728  		if item.in.Cmp(Quantity{}) == 0 {
   729  			// Don't expect it to print "-0" ever
   730  			continue
   731  		}
   732  		q := item.in
   733  		q.d = infDecAmount{desired.Neg(q.AsDec())}
   734  		if e, a := "-"+item.expect, q.String(); e != a {
   735  			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
   736  		}
   737  	}
   738  }
   739  
   740  func TestQuantityParseEmit(t *testing.T) {
   741  	table := []struct {
   742  		in     string
   743  		expect string
   744  	}{
   745  		{"1Ki", "1Ki"},
   746  		{"1Mi", "1Mi"},
   747  		{"1Gi", "1Gi"},
   748  		{"1024Mi", "1Gi"},
   749  		{"1000M", "1G"},
   750  		{".001Ki", "1024m"},
   751  		{".000001Ki", "1024u"},
   752  		{".000000001Ki", "1024n"},
   753  		{".000000000001Ki", "2n"},
   754  	}
   755  
   756  	for _, item := range table {
   757  		q, err := ParseQuantity(item.in)
   758  		if err != nil {
   759  			t.Errorf("Couldn't parse %v", item.in)
   760  			continue
   761  		}
   762  		if e, a := item.expect, q.String(); e != a {
   763  			t.Errorf("%#v: expected %v, got %v", item.in, e, a)
   764  		}
   765  	}
   766  	for _, item := range table {
   767  		q, err := ParseQuantity("-" + item.in)
   768  		if err != nil {
   769  			t.Errorf("Couldn't parse %v", item.in)
   770  			continue
   771  		}
   772  		if q.Cmp(Quantity{}) == 0 {
   773  			continue
   774  		}
   775  		if e, a := "-"+item.expect, q.String(); e != a {
   776  			t.Errorf("%#v: expected %v, got %v (%#v)", item.in, e, a, q.i)
   777  		}
   778  	}
   779  }
   780  
   781  var fuzzer = fuzz.New().Funcs(
   782  	func(q *Quantity, c fuzz.Continue) {
   783  		q.i = Zero
   784  		if c.RandBool() {
   785  			q.Format = BinarySI
   786  			if c.RandBool() {
   787  				dec := &inf.Dec{}
   788  				q.d = infDecAmount{Dec: dec}
   789  				dec.SetScale(0)
   790  				dec.SetUnscaled(c.Int63())
   791  				return
   792  			}
   793  			// Be sure to test cases like 1Mi
   794  			dec := &inf.Dec{}
   795  			q.d = infDecAmount{Dec: dec}
   796  			dec.SetScale(0)
   797  			dec.SetUnscaled(c.Int63n(1024) << uint(10*c.Intn(5)))
   798  			return
   799  		}
   800  		if c.RandBool() {
   801  			q.Format = DecimalSI
   802  		} else {
   803  			q.Format = DecimalExponent
   804  		}
   805  		if c.RandBool() {
   806  			dec := &inf.Dec{}
   807  			q.d = infDecAmount{Dec: dec}
   808  			dec.SetScale(inf.Scale(c.Intn(4)))
   809  			dec.SetUnscaled(c.Int63())
   810  			return
   811  		}
   812  		// Be sure to test cases like 1M
   813  		dec := &inf.Dec{}
   814  		q.d = infDecAmount{Dec: dec}
   815  		dec.SetScale(inf.Scale(3 - c.Intn(15)))
   816  		dec.SetUnscaled(c.Int63n(1000))
   817  	},
   818  )
   819  
   820  func TestQuantityDeepCopy(t *testing.T) {
   821  	// Test when d is nil
   822  	slice := []string{"0", "100m", "50m", "10000T"}
   823  	for _, testCase := range slice {
   824  		q := MustParse(testCase)
   825  		if result := q.DeepCopy(); result != q {
   826  			t.Errorf("Expected: %v, Actual: %v", q, result)
   827  		}
   828  	}
   829  	table := []*inf.Dec{
   830  		dec(0, 0).Dec,
   831  		dec(10, 0).Dec,
   832  		dec(-10, 0).Dec,
   833  	}
   834  	// Test when i is {0,0}
   835  	for _, testCase := range table {
   836  		q := Quantity{d: infDecAmount{testCase}, Format: DecimalSI}
   837  		result := q.DeepCopy()
   838  		if q.d.Cmp(result.AsDec()) != 0 {
   839  			t.Errorf("Expected: %v, Actual: %v", q.String(), result.String())
   840  		}
   841  		result = Quantity{d: infDecAmount{dec(2, 0).Dec}, Format: DecimalSI}
   842  		if q.d.Cmp(result.AsDec()) == 0 {
   843  			t.Errorf("Modifying result has affected q")
   844  		}
   845  	}
   846  }
   847  
   848  func TestJSON(t *testing.T) {
   849  	for i := 0; i < 500; i++ {
   850  		q := &Quantity{}
   851  		fuzzer.Fuzz(q)
   852  		b, err := json.Marshal(q)
   853  		if err != nil {
   854  			t.Errorf("error encoding %v: %v", q, err)
   855  			continue
   856  		}
   857  		q2 := &Quantity{}
   858  		err = json.Unmarshal(b, q2)
   859  		if err != nil {
   860  			t.Logf("%d: %s", i, string(b))
   861  			t.Errorf("%v: error decoding %v: %v", q, string(b), err)
   862  		}
   863  		if q2.Cmp(*q) != 0 {
   864  			t.Errorf("Expected equal: %v, %v (json was '%v')", q, q2, string(b))
   865  		}
   866  	}
   867  }
   868  
   869  func TestJSONWhitespace(t *testing.T) {
   870  	q := Quantity{}
   871  	testCases := []struct {
   872  		in     string
   873  		expect string
   874  	}{
   875  		{`" 1"`, "1"},
   876  		{`"1 "`, "1"},
   877  		{`1`, "1"},
   878  		{` 1`, "1"},
   879  		{`1 `, "1"},
   880  		{`10`, "10"},
   881  		{`-1`, "-1"},
   882  		{` -1`, "-1"},
   883  	}
   884  	for _, test := range testCases {
   885  		if err := json.Unmarshal([]byte(test.in), &q); err != nil {
   886  			t.Errorf("%q: %v", test.in, err)
   887  		}
   888  		if q.String() != test.expect {
   889  			t.Errorf("unexpected string: %q", q.String())
   890  		}
   891  	}
   892  }
   893  
   894  func TestMilliNewSet(t *testing.T) {
   895  	table := []struct {
   896  		value  int64
   897  		format Format
   898  		expect string
   899  		exact  bool
   900  	}{
   901  		{1, DecimalSI, "1m", true},
   902  		{1000, DecimalSI, "1", true},
   903  		{1234000, DecimalSI, "1234", true},
   904  		{1024, BinarySI, "1024m", false}, // Format changes
   905  		{1000000, "invalidFormatDefaultsToExponent", "1e3", true},
   906  		{1024 * 1024, BinarySI, "1048576m", false}, // Format changes
   907  	}
   908  
   909  	for _, item := range table {
   910  		q := NewMilliQuantity(item.value, item.format)
   911  		if e, a := item.expect, q.String(); e != a {
   912  			t.Errorf("Expected %v, got %v; %#v", e, a, q)
   913  		}
   914  		if !item.exact {
   915  			continue
   916  		}
   917  		q2, err := ParseQuantity(q.String())
   918  		if err != nil {
   919  			t.Errorf("Round trip failed on %v", q)
   920  		}
   921  		if e, a := item.value, q2.MilliValue(); e != a {
   922  			t.Errorf("Expected %v, got %v", e, a)
   923  		}
   924  	}
   925  
   926  	for _, item := range table {
   927  		q := NewQuantity(0, item.format)
   928  		q.SetMilli(item.value)
   929  		if e, a := item.expect, q.String(); e != a {
   930  			t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
   931  		}
   932  	}
   933  }
   934  
   935  func TestNewSet(t *testing.T) {
   936  	table := []struct {
   937  		value  int64
   938  		format Format
   939  		expect string
   940  	}{
   941  		{1, DecimalSI, "1"},
   942  		{1000, DecimalSI, "1k"},
   943  		{1234000, DecimalSI, "1234k"},
   944  		{1024, BinarySI, "1Ki"},
   945  		{1000000, "invalidFormatDefaultsToExponent", "1e6"},
   946  		{1024 * 1024, BinarySI, "1Mi"},
   947  	}
   948  
   949  	for _, asDec := range []bool{false, true} {
   950  		for _, item := range table {
   951  			q := NewQuantity(item.value, item.format)
   952  			if asDec {
   953  				q.ToDec()
   954  			}
   955  			if e, a := item.expect, q.String(); e != a {
   956  				t.Errorf("Expected %v, got %v; %#v", e, a, q)
   957  			}
   958  			q2, err := ParseQuantity(q.String())
   959  			if err != nil {
   960  				t.Errorf("Round trip failed on %v", q)
   961  			}
   962  			if e, a := item.value, q2.Value(); e != a {
   963  				t.Errorf("Expected %v, got %v", e, a)
   964  			}
   965  		}
   966  
   967  		for _, item := range table {
   968  			q := NewQuantity(0, item.format)
   969  			q.Set(item.value)
   970  			if asDec {
   971  				q.ToDec()
   972  			}
   973  			if e, a := item.expect, q.String(); e != a {
   974  				t.Errorf("Set: Expected %v, got %v; %#v", e, a, q)
   975  			}
   976  		}
   977  	}
   978  }
   979  
   980  func TestNewScaledSet(t *testing.T) {
   981  	table := []struct {
   982  		value  int64
   983  		scale  Scale
   984  		expect string
   985  	}{
   986  		{1, Nano, "1n"},
   987  		{1000, Nano, "1u"},
   988  		{1, Micro, "1u"},
   989  		{1000, Micro, "1m"},
   990  		{1, Milli, "1m"},
   991  		{1000, Milli, "1"},
   992  		{1, 0, "1"},
   993  		{0, Nano, "0"},
   994  		{0, Micro, "0"},
   995  		{0, Milli, "0"},
   996  		{0, 0, "0"},
   997  	}
   998  
   999  	for _, item := range table {
  1000  		q := NewScaledQuantity(item.value, item.scale)
  1001  		if e, a := item.expect, q.String(); e != a {
  1002  			t.Errorf("Expected %v, got %v; %#v", e, a, q)
  1003  		}
  1004  		q2, err := ParseQuantity(q.String())
  1005  		if err != nil {
  1006  			t.Errorf("Round trip failed on %v", q)
  1007  		}
  1008  		if e, a := item.value, q2.ScaledValue(item.scale); e != a {
  1009  			t.Errorf("Expected %v, got %v", e, a)
  1010  		}
  1011  		q3 := NewQuantity(0, DecimalSI)
  1012  		q3.SetScaled(item.value, item.scale)
  1013  		if q.Cmp(*q3) != 0 {
  1014  			t.Errorf("Expected %v and %v to be equal", q, q3)
  1015  		}
  1016  	}
  1017  }
  1018  
  1019  func TestScaledValue(t *testing.T) {
  1020  	table := []struct {
  1021  		fromScale Scale
  1022  		toScale   Scale
  1023  		expected  int64
  1024  	}{
  1025  		{Nano, Nano, 1},
  1026  		{Nano, Micro, 1},
  1027  		{Nano, Milli, 1},
  1028  		{Nano, 0, 1},
  1029  		{Micro, Nano, 1000},
  1030  		{Micro, Micro, 1},
  1031  		{Micro, Milli, 1},
  1032  		{Micro, 0, 1},
  1033  		{Milli, Nano, 1000 * 1000},
  1034  		{Milli, Micro, 1000},
  1035  		{Milli, Milli, 1},
  1036  		{Milli, 0, 1},
  1037  		{0, Nano, 1000 * 1000 * 1000},
  1038  		{0, Micro, 1000 * 1000},
  1039  		{0, Milli, 1000},
  1040  		{0, 0, 1},
  1041  		{2, -2, 100 * 100},
  1042  	}
  1043  
  1044  	for _, item := range table {
  1045  		q := NewScaledQuantity(1, item.fromScale)
  1046  		if e, a := item.expected, q.ScaledValue(item.toScale); e != a {
  1047  			t.Errorf("%v to %v: Expected %v, got %v", item.fromScale, item.toScale, e, a)
  1048  		}
  1049  	}
  1050  }
  1051  
  1052  func TestUninitializedNoCrash(t *testing.T) {
  1053  	var q Quantity
  1054  
  1055  	q.Value()
  1056  	q.MilliValue()
  1057  	q.DeepCopy()
  1058  	_ = q.String()
  1059  	q.MarshalJSON()
  1060  }
  1061  
  1062  func TestDeepCopy(t *testing.T) {
  1063  	q := NewQuantity(5, DecimalSI)
  1064  	c := q.DeepCopy()
  1065  	c.Set(6)
  1066  	if q.Value() == 6 {
  1067  		t.Errorf("Copy didn't")
  1068  	}
  1069  }
  1070  
  1071  func TestSub(t *testing.T) {
  1072  	tests := []struct {
  1073  		a        Quantity
  1074  		b        Quantity
  1075  		expected Quantity
  1076  	}{
  1077  		{decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(0, 0, DecimalSI)},
  1078  		{decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(9, 0, DecimalSI)},
  1079  		{decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(9, 0, BinarySI)},
  1080  		{Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(-50, 0, DecimalSI)},
  1081  		{decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)},
  1082  		{Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
  1083  	}
  1084  
  1085  	for i, test := range tests {
  1086  		test.a.Sub(test.b)
  1087  		if test.a.Cmp(test.expected) != 0 {
  1088  			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String())
  1089  		}
  1090  	}
  1091  }
  1092  
  1093  func TestNeg(t *testing.T) {
  1094  	tests := []struct {
  1095  		a        Quantity
  1096  		b        Quantity
  1097  		expected Quantity
  1098  	}{
  1099  		{a: intQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)},
  1100  		{a: Quantity{}, expected: Quantity{}},
  1101  		{a: intQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)},
  1102  		{a: intQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)},
  1103  		{a: decQuantity(0, 0, DecimalSI), expected: intQuantity(0, 0, DecimalSI)},
  1104  		{a: decQuantity(10, 0, BinarySI), expected: intQuantity(-10, 0, BinarySI)},
  1105  		{a: decQuantity(-10, 0, BinarySI), expected: intQuantity(10, 0, BinarySI)},
  1106  	}
  1107  
  1108  	for i, test := range tests {
  1109  		a := test.a.DeepCopy()
  1110  		a.Neg()
  1111  		// ensure value is same
  1112  		if a.Cmp(test.expected) != 0 {
  1113  			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), a.String())
  1114  		}
  1115  	}
  1116  }
  1117  
  1118  func TestAdd(t *testing.T) {
  1119  	tests := []struct {
  1120  		a        Quantity
  1121  		b        Quantity
  1122  		expected Quantity
  1123  	}{
  1124  		{decQuantity(10, 0, DecimalSI), decQuantity(1, 1, DecimalSI), decQuantity(20, 0, DecimalSI)},
  1125  		{decQuantity(10, 0, DecimalSI), decQuantity(1, 0, BinarySI), decQuantity(11, 0, DecimalSI)},
  1126  		{decQuantity(10, 0, BinarySI), decQuantity(1, 0, DecimalSI), decQuantity(11, 0, BinarySI)},
  1127  		{Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI), decQuantity(50, 0, DecimalSI)},
  1128  		{decQuantity(50, 0, DecimalSI), Quantity{Format: DecimalSI}, decQuantity(50, 0, DecimalSI)},
  1129  		{Quantity{Format: DecimalSI}, Quantity{Format: DecimalSI}, decQuantity(0, 0, DecimalSI)},
  1130  	}
  1131  
  1132  	for i, test := range tests {
  1133  		test.a.Add(test.b)
  1134  		if test.a.Cmp(test.expected) != 0 {
  1135  			t.Errorf("[%d] Expected %q, got %q", i, test.expected.String(), test.a.String())
  1136  		}
  1137  	}
  1138  }
  1139  
  1140  func TestAddSubRoundTrip(t *testing.T) {
  1141  	for k := -10; k <= 10; k++ {
  1142  		q := Quantity{Format: DecimalSI}
  1143  		var order []int64
  1144  		for i := 0; i < 100; i++ {
  1145  			j := rand.Int63()
  1146  			order = append(order, j)
  1147  			q.Add(*NewScaledQuantity(j, Scale(k)))
  1148  		}
  1149  		for _, j := range order {
  1150  			q.Sub(*NewScaledQuantity(j, Scale(k)))
  1151  		}
  1152  		if !q.IsZero() {
  1153  			t.Errorf("addition and subtraction did not cancel: %s", &q)
  1154  		}
  1155  	}
  1156  }
  1157  
  1158  func TestAddSubRoundTripAcrossScales(t *testing.T) {
  1159  	q := Quantity{Format: DecimalSI}
  1160  	var order []int64
  1161  	for i := 0; i < 100; i++ {
  1162  		j := rand.Int63()
  1163  		order = append(order, j)
  1164  		q.Add(*NewScaledQuantity(j, Scale(j%20-10)))
  1165  	}
  1166  	for _, j := range order {
  1167  		q.Sub(*NewScaledQuantity(j, Scale(j%20-10)))
  1168  	}
  1169  	if !q.IsZero() {
  1170  		t.Errorf("addition and subtraction did not cancel: %s", &q)
  1171  	}
  1172  }
  1173  
  1174  func TestNegateRoundTrip(t *testing.T) {
  1175  	for _, asDec := range []bool{false, true} {
  1176  		for k := -10; k <= 10; k++ {
  1177  			for i := 0; i < 100; i++ {
  1178  				j := rand.Int63()
  1179  				q := *NewScaledQuantity(j, Scale(k))
  1180  				if asDec {
  1181  					q.AsDec()
  1182  				}
  1183  
  1184  				b := q.DeepCopy()
  1185  				b.Neg()
  1186  				b.Neg()
  1187  				if b.Cmp(q) != 0 {
  1188  					t.Errorf("double negation did not cancel: %s", &q)
  1189  				}
  1190  			}
  1191  		}
  1192  	}
  1193  }
  1194  
  1195  func TestQuantityAsApproximateFloat64(t *testing.T) {
  1196  	table := []struct {
  1197  		in  Quantity
  1198  		out float64
  1199  	}{
  1200  		{decQuantity(0, 0, DecimalSI), 0.0},
  1201  		{decQuantity(0, 0, DecimalExponent), 0.0},
  1202  		{decQuantity(0, 0, BinarySI), 0.0},
  1203  
  1204  		{decQuantity(1, 0, DecimalSI), 1},
  1205  		{decQuantity(1, 0, DecimalExponent), 1},
  1206  		{decQuantity(1, 0, BinarySI), 1},
  1207  
  1208  		// Binary suffixes
  1209  		{decQuantity(1024, 0, BinarySI), 1024},
  1210  		{decQuantity(8*1024, 0, BinarySI), 8 * 1024},
  1211  		{decQuantity(7*1024*1024, 0, BinarySI), 7 * 1024 * 1024},
  1212  		{decQuantity(7*1024*1024, 1, BinarySI), (7 * 1024 * 1024) * 10},
  1213  		{decQuantity(7*1024*1024, 4, BinarySI), (7 * 1024 * 1024) * 10000},
  1214  		{decQuantity(7*1024*1024, 8, BinarySI), (7 * 1024 * 1024) * 100000000},
  1215  		{decQuantity(7*1024*1024, -1, BinarySI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
  1216  		{decQuantity(7*1024*1024, -8, BinarySI), (7 * 1024 * 1024) / float64(100000000)},
  1217  
  1218  		{decQuantity(1024, 0, DecimalSI), 1024},
  1219  		{decQuantity(8*1024, 0, DecimalSI), 8 * 1024},
  1220  		{decQuantity(7*1024*1024, 0, DecimalSI), 7 * 1024 * 1024},
  1221  		{decQuantity(7*1024*1024, 1, DecimalSI), (7 * 1024 * 1024) * 10},
  1222  		{decQuantity(7*1024*1024, 4, DecimalSI), (7 * 1024 * 1024) * 10000},
  1223  		{decQuantity(7*1024*1024, 8, DecimalSI), (7 * 1024 * 1024) * 100000000},
  1224  		{decQuantity(7*1024*1024, -1, DecimalSI), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
  1225  		{decQuantity(7*1024*1024, -8, DecimalSI), (7 * 1024 * 1024) / float64(100000000)},
  1226  
  1227  		{decQuantity(1024, 0, DecimalExponent), 1024},
  1228  		{decQuantity(8*1024, 0, DecimalExponent), 8 * 1024},
  1229  		{decQuantity(7*1024*1024, 0, DecimalExponent), 7 * 1024 * 1024},
  1230  		{decQuantity(7*1024*1024, 1, DecimalExponent), (7 * 1024 * 1024) * 10},
  1231  		{decQuantity(7*1024*1024, 4, DecimalExponent), (7 * 1024 * 1024) * 10000},
  1232  		{decQuantity(7*1024*1024, 8, DecimalExponent), (7 * 1024 * 1024) * 100000000},
  1233  		{decQuantity(7*1024*1024, -1, DecimalExponent), (7 * 1024 * 1024) * math.Pow10(-1)}, // '* Pow10' and '/ float(10)' do not round the same way
  1234  		{decQuantity(7*1024*1024, -8, DecimalExponent), (7 * 1024 * 1024) / float64(100000000)},
  1235  
  1236  		// very large numbers
  1237  		{Quantity{d: maxAllowed, Format: DecimalSI}, math.MaxInt64},
  1238  		{Quantity{d: maxAllowed, Format: BinarySI}, math.MaxInt64},
  1239  		{decQuantity(12, 18, DecimalSI), 1.2e19},
  1240  
  1241  		// infinities caused due to float64 overflow
  1242  		{decQuantity(12, 500, DecimalSI), math.Inf(0)},
  1243  		{decQuantity(-12, 500, DecimalSI), math.Inf(-1)},
  1244  	}
  1245  
  1246  	for _, item := range table {
  1247  		t.Run(fmt.Sprintf("%s %s", item.in.Format, item.in.String()), func(t *testing.T) {
  1248  			out := item.in.AsApproximateFloat64()
  1249  			if out != item.out {
  1250  				t.Fatalf("expected %v, got %v", item.out, out)
  1251  			}
  1252  			if item.in.d.Dec != nil {
  1253  				if i, ok := item.in.AsInt64(); ok {
  1254  					q := intQuantity(i, 0, item.in.Format)
  1255  					out := q.AsApproximateFloat64()
  1256  					if out != item.out {
  1257  						t.Fatalf("as int quantity: expected %v, got %v", item.out, out)
  1258  					}
  1259  				}
  1260  			}
  1261  		})
  1262  	}
  1263  }
  1264  
  1265  func TestStringQuantityAsApproximateFloat64(t *testing.T) {
  1266  	table := []struct {
  1267  		in  string
  1268  		out float64
  1269  	}{
  1270  		{"2Ki", 2048},
  1271  		{"1.1Ki", 1126.4e+0},
  1272  		{"1Mi", 1.048576e+06},
  1273  		{"2Gi", 2.147483648e+09},
  1274  	}
  1275  
  1276  	for _, item := range table {
  1277  		t.Run(item.in, func(t *testing.T) {
  1278  			in, err := ParseQuantity(item.in)
  1279  			if err != nil {
  1280  				t.Fatal(err)
  1281  			}
  1282  			out := in.AsApproximateFloat64()
  1283  			if out != item.out {
  1284  				t.Fatalf("expected %v, got %v", item.out, out)
  1285  			}
  1286  			if in.d.Dec != nil {
  1287  				if i, ok := in.AsInt64(); ok {
  1288  					q := intQuantity(i, 0, in.Format)
  1289  					out := q.AsApproximateFloat64()
  1290  					if out != item.out {
  1291  						t.Fatalf("as int quantity: expected %v, got %v", item.out, out)
  1292  					}
  1293  				}
  1294  			}
  1295  		})
  1296  	}
  1297  }
  1298  
  1299  func benchmarkQuantities() []Quantity {
  1300  	return []Quantity{
  1301  		intQuantity(1024*1024*1024, 0, BinarySI),
  1302  		intQuantity(1024*1024*1024*1024, 0, BinarySI),
  1303  		intQuantity(1000000, 3, DecimalSI),
  1304  		intQuantity(1000000000, 0, DecimalSI),
  1305  		intQuantity(1, -3, DecimalSI),
  1306  		intQuantity(80, -3, DecimalSI),
  1307  		intQuantity(1080, -3, DecimalSI),
  1308  		intQuantity(0, 0, BinarySI),
  1309  		intQuantity(1, 9, DecimalExponent),
  1310  		intQuantity(1, -9, DecimalSI),
  1311  		intQuantity(1000000, 10, DecimalSI),
  1312  	}
  1313  }
  1314  
  1315  func BenchmarkQuantityString(b *testing.B) {
  1316  	values := benchmarkQuantities()
  1317  	b.ResetTimer()
  1318  	var s string
  1319  	for i := 0; i < b.N; i++ {
  1320  		q := values[i%len(values)]
  1321  		q.s = ""
  1322  		s = q.String()
  1323  	}
  1324  	b.StopTimer()
  1325  	if len(s) == 0 {
  1326  		b.Fatal(s)
  1327  	}
  1328  }
  1329  
  1330  func BenchmarkQuantityStringPrecalc(b *testing.B) {
  1331  	values := benchmarkQuantities()
  1332  	for i := range values {
  1333  		_ = values[i].String()
  1334  	}
  1335  	b.ResetTimer()
  1336  	var s string
  1337  	for i := 0; i < b.N; i++ {
  1338  		q := values[i%len(values)]
  1339  		s = q.String()
  1340  	}
  1341  	b.StopTimer()
  1342  	if len(s) == 0 {
  1343  		b.Fatal(s)
  1344  	}
  1345  }
  1346  
  1347  func BenchmarkQuantityStringBinarySI(b *testing.B) {
  1348  	values := benchmarkQuantities()
  1349  	for i := range values {
  1350  		values[i].Format = BinarySI
  1351  	}
  1352  	b.ResetTimer()
  1353  	var s string
  1354  	for i := 0; i < b.N; i++ {
  1355  		q := values[i%len(values)]
  1356  		q.s = ""
  1357  		s = q.String()
  1358  	}
  1359  	b.StopTimer()
  1360  	if len(s) == 0 {
  1361  		b.Fatal(s)
  1362  	}
  1363  }
  1364  
  1365  func BenchmarkQuantityMarshalJSON(b *testing.B) {
  1366  	values := benchmarkQuantities()
  1367  	b.ResetTimer()
  1368  	for i := 0; i < b.N; i++ {
  1369  		q := values[i%len(values)]
  1370  		q.s = ""
  1371  		if _, err := q.MarshalJSON(); err != nil {
  1372  			b.Fatal(err)
  1373  		}
  1374  	}
  1375  	b.StopTimer()
  1376  }
  1377  
  1378  func BenchmarkQuantityUnmarshalJSON(b *testing.B) {
  1379  	values := benchmarkQuantities()
  1380  	var json [][]byte
  1381  	for _, v := range values {
  1382  		data, _ := v.MarshalJSON()
  1383  		json = append(json, data)
  1384  	}
  1385  
  1386  	b.ResetTimer()
  1387  	for i := 0; i < b.N; i++ {
  1388  		var q Quantity
  1389  		if err := q.UnmarshalJSON(json[i%len(values)]); err != nil {
  1390  			b.Fatal(err)
  1391  		}
  1392  	}
  1393  	b.StopTimer()
  1394  }
  1395  
  1396  func BenchmarkParseQuantity(b *testing.B) {
  1397  	values := benchmarkQuantities()
  1398  	var strings []string
  1399  	for _, v := range values {
  1400  		strings = append(strings, v.String())
  1401  	}
  1402  	b.ResetTimer()
  1403  	for i := 0; i < b.N; i++ {
  1404  		if _, err := ParseQuantity(strings[i%len(values)]); err != nil {
  1405  			b.Fatal(err)
  1406  		}
  1407  	}
  1408  	b.StopTimer()
  1409  }
  1410  
  1411  func BenchmarkCanonicalize(b *testing.B) {
  1412  	values := benchmarkQuantities()
  1413  	b.ResetTimer()
  1414  	buffer := make([]byte, 0, 100)
  1415  	for i := 0; i < b.N; i++ {
  1416  		s, _ := values[i%len(values)].CanonicalizeBytes(buffer)
  1417  		if len(s) == 0 {
  1418  			b.Fatal(s)
  1419  		}
  1420  	}
  1421  	b.StopTimer()
  1422  }
  1423  
  1424  func BenchmarkQuantityRoundUp(b *testing.B) {
  1425  	values := benchmarkQuantities()
  1426  	b.ResetTimer()
  1427  	for i := 0; i < b.N; i++ {
  1428  		q := values[i%len(values)]
  1429  		copied := q
  1430  		copied.RoundUp(-3)
  1431  	}
  1432  	b.StopTimer()
  1433  }
  1434  
  1435  func BenchmarkQuantityCopy(b *testing.B) {
  1436  	values := benchmarkQuantities()
  1437  	b.ResetTimer()
  1438  	for i := 0; i < b.N; i++ {
  1439  		values[i%len(values)].DeepCopy()
  1440  	}
  1441  	b.StopTimer()
  1442  }
  1443  
  1444  func BenchmarkQuantityAdd(b *testing.B) {
  1445  	values := benchmarkQuantities()
  1446  	base := &Quantity{}
  1447  	b.ResetTimer()
  1448  	for i := 0; i < b.N; i++ {
  1449  		q := values[i%len(values)]
  1450  		base.d.Dec = nil
  1451  		base.i = int64Amount{value: 100}
  1452  		base.Add(q)
  1453  	}
  1454  	b.StopTimer()
  1455  }
  1456  
  1457  func BenchmarkQuantityCmp(b *testing.B) {
  1458  	values := benchmarkQuantities()
  1459  	b.ResetTimer()
  1460  	for i := 0; i < b.N; i++ {
  1461  		q := values[i%len(values)]
  1462  		if q.Cmp(q) != 0 {
  1463  			b.Fatal(q)
  1464  		}
  1465  	}
  1466  	b.StopTimer()
  1467  }
  1468  
  1469  func BenchmarkQuantityAsApproximateFloat64(b *testing.B) {
  1470  	values := benchmarkQuantities()
  1471  	b.ResetTimer()
  1472  	for i := 0; i < b.N; i++ {
  1473  		q := values[i%len(values)]
  1474  		if q.AsApproximateFloat64() == -1 {
  1475  			b.Fatal(q)
  1476  		}
  1477  	}
  1478  	b.StopTimer()
  1479  }
  1480  
  1481  var _ pflag.Value = &QuantityValue{}
  1482  
  1483  func TestQuantityValueSet(t *testing.T) {
  1484  	q := QuantityValue{}
  1485  
  1486  	if err := q.Set("invalid"); err == nil {
  1487  
  1488  		t.Error("'invalid' did not trigger a parse error")
  1489  	}
  1490  
  1491  	if err := q.Set("1Mi"); err != nil {
  1492  		t.Errorf("parsing 1Mi should have worked, got: %v", err)
  1493  	}
  1494  	if q.Value() != 1024*1024 {
  1495  		t.Errorf("quantity should have been set to 1Mi, got: %v", q)
  1496  	}
  1497  
  1498  	data, err := json.Marshal(q)
  1499  	if err != nil {
  1500  		t.Errorf("unexpected encoding error: %v", err)
  1501  	}
  1502  	expected := `"1Mi"`
  1503  	if string(data) != expected {
  1504  		t.Errorf("expected 1Mi value to be encoded as %q, got: %q", expected, string(data))
  1505  	}
  1506  }
  1507  
  1508  func ExampleQuantityValue() {
  1509  	q := QuantityValue{
  1510  		Quantity: MustParse("1Mi"),
  1511  	}
  1512  	fs := pflag.FlagSet{}
  1513  	fs.SetOutput(os.Stdout)
  1514  	fs.Var(&q, "mem", "sets amount of memory")
  1515  	fs.PrintDefaults()
  1516  	// Output:
  1517  	// --mem quantity   sets amount of memory (default 1Mi)
  1518  }