github.com/openconfig/goyang@v1.4.5/pkg/yang/types_builtin_test.go (about)

     1  // Copyright 2015 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package yang
    16  
    17  import (
    18  	"encoding/json"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/openconfig/gnmi/errdiff"
    23  )
    24  
    25  const (
    26  	maxUint64 uint64 = 18446744073709551615
    27  	maxUint32        = 0xFFFFFFFF
    28  	maxUint16        = 0xFFFF
    29  	maxUint8         = 0xFF
    30  	maxInt32         = 1<<31 - 1
    31  	minInt32         = -1 << 31
    32  	maxInt16         = 1<<15 - 1
    33  	minInt16         = -1 << 15
    34  	maxInt8          = 1<<7 - 1
    35  	minInt8          = -1 << 7
    36  )
    37  
    38  // R is a test helper for creating an int-based YRange.
    39  func R(a, b int64) YRange {
    40  	return YRange{FromInt(a), FromInt(b)}
    41  }
    42  
    43  // Rf is a test helper for creating a float-based YRange.
    44  func Rf(a, b int64, fracDig uint8) YRange {
    45  	n1 := Number{Value: uint64(a), FractionDigits: fracDig}
    46  	n2 := Number{Value: uint64(b), FractionDigits: fracDig}
    47  	if a < 0 {
    48  		n1.Value = uint64(-a)
    49  		n1.Negative = true
    50  	}
    51  	if b < 0 {
    52  		n2.Value = uint64(-b)
    53  		n2.Negative = true
    54  	}
    55  	return YRange{n1, n2}
    56  }
    57  
    58  func TestNumberInt(t *testing.T) {
    59  	tests := []struct {
    60  		desc    string
    61  		in      Number
    62  		want    int64
    63  		wantErr bool
    64  	}{{
    65  		desc: "zero",
    66  		in:   FromInt(0),
    67  		want: 0,
    68  	}, {
    69  		desc: "positive",
    70  		in:   FromInt(42),
    71  		want: 42,
    72  	}, {
    73  		desc: "negative",
    74  		in:   FromInt(-42),
    75  		want: -42,
    76  	}, {
    77  		desc:    "decimal",
    78  		in:      FromFloat(42),
    79  		wantErr: true,
    80  	}, {
    81  		desc:    "overflow",
    82  		in:      FromUint(maxUint64),
    83  		wantErr: true,
    84  	}}
    85  
    86  	for _, tt := range tests {
    87  		t.Run(tt.desc, func(t *testing.T) {
    88  			got, err := tt.in.Int()
    89  			if got != tt.want {
    90  				t.Errorf("got: %v, want: %v", got, tt.want)
    91  			}
    92  			if (err != nil) != tt.wantErr {
    93  				t.Errorf("gotErr: %v, wantErr: %v", err, tt.wantErr)
    94  			}
    95  		})
    96  	}
    97  }
    98  
    99  func TestRangeEqual(t *testing.T) {
   100  	tests := []struct {
   101  		desc        string
   102  		inBaseRange YangRange
   103  		inTestRange YangRange
   104  		want        bool
   105  	}{{
   106  		desc: "empty range equals empty range",
   107  		want: true,
   108  	}, {
   109  		desc:        "test range is default",
   110  		inBaseRange: YangRange{R(1, 2)}, want: false,
   111  	}, {
   112  		desc:        "base range is default",
   113  		inTestRange: YangRange{R(1, 2)}, want: false,
   114  	}, {
   115  		desc:        "equal ranges",
   116  		inBaseRange: YangRange{R(1, 2)},
   117  		inTestRange: YangRange{R(1, 2)},
   118  		want:        true,
   119  	}, {
   120  		desc:        "wider base range",
   121  		inBaseRange: YangRange{R(1, 3)},
   122  		inTestRange: YangRange{R(1, 2)},
   123  		want:        false,
   124  	}, {
   125  		desc:        "equal ranges with multiple subranges",
   126  		inBaseRange: YangRange{R(1, 2), R(4, 5)},
   127  		inTestRange: YangRange{R(1, 2), R(4, 5)},
   128  		want:        true,
   129  	}, {
   130  		desc:        "multiple subranges with one unequal",
   131  		inBaseRange: YangRange{R(1, 2), R(4, 6)},
   132  		inTestRange: YangRange{R(1, 2), R(4, 5)},
   133  		want:        false,
   134  	}, {
   135  		desc:        "extra subrange in base range",
   136  		inBaseRange: YangRange{R(1, 2)},
   137  		inTestRange: YangRange{R(1, 2), R(4, 5)},
   138  		want:        false,
   139  	}, {
   140  		desc:        "extra subrange in test range",
   141  		inBaseRange: YangRange{R(1, 2), R(4, 5)},
   142  		inTestRange: YangRange{R(1, 2)},
   143  		want:        false,
   144  	}}
   145  
   146  	for _, tt := range tests {
   147  		t.Run(tt.desc, func(t *testing.T) {
   148  			if want := tt.inBaseRange.Equal(tt.inTestRange); want != tt.want {
   149  				t.Errorf("got %v, want %v", want, tt.want)
   150  			}
   151  		})
   152  	}
   153  }
   154  
   155  func TestRangeContains(t *testing.T) {
   156  	tests := []struct {
   157  		desc        string
   158  		inBaseRange YangRange
   159  		inTestRange YangRange
   160  		want        bool
   161  	}{{
   162  		desc: "empty range contained in empty range",
   163  		want: true,
   164  	}, {
   165  		desc:        "empty range contained in non-empty range",
   166  		inBaseRange: YangRange{R(1, 2)},
   167  		want:        true,
   168  	}, {
   169  		desc:        "non-empty range contained in empty range",
   170  		inTestRange: YangRange{R(1, 2)},
   171  		want:        true,
   172  	}, {
   173  		desc:        "equal ranges contain",
   174  		inBaseRange: YangRange{R(1, 2)},
   175  		inTestRange: YangRange{R(1, 2)},
   176  		want:        true,
   177  	}, {
   178  		desc:        "superset contains",
   179  		inBaseRange: YangRange{R(1, 5)},
   180  		inTestRange: YangRange{R(2, 3)},
   181  		want:        true,
   182  	}, {
   183  		desc:        "subset doesn't contain",
   184  		inBaseRange: YangRange{R(2, 3)},
   185  		inTestRange: YangRange{R(1, 5)},
   186  		want:        false,
   187  	}, {
   188  		desc:        "contain subranges",
   189  		inBaseRange: YangRange{R(1, 10)},
   190  		inTestRange: YangRange{R(1, 2), R(4, 5), R(7, 10)},
   191  		want:        true,
   192  	}, {
   193  		desc:        "subranges leaks out",
   194  		inBaseRange: YangRange{R(1, 10)},
   195  		inTestRange: YangRange{R(1, 2), R(7, 11)},
   196  		want:        false,
   197  	}, {
   198  		desc:        "subranges containing a subset",
   199  		inBaseRange: YangRange{R(1, 9), R(11, 19), R(21, 29)},
   200  		inTestRange: YangRange{R(23, 25)},
   201  		want:        true,
   202  	}, {
   203  		desc:        "subranges containing a single valued range",
   204  		inBaseRange: YangRange{R(1, 9), R(11, 19), R(21, 29)},
   205  		inTestRange: YangRange{R(23, 23)},
   206  		want:        true,
   207  	}, {
   208  		desc:        "subranges doesn't contain a single outside value",
   209  		inBaseRange: YangRange{R(1, 9), R(11, 19), R(21, 29)},
   210  		inTestRange: YangRange{R(20, 20)},
   211  		want:        false,
   212  	}, {
   213  		desc:        "smaller range doesn't contain min..max",
   214  		inBaseRange: YangRange{R(1, 10)},
   215  		inTestRange: YangRange{R(MinInt64, MaxInt64)},
   216  		want:        false,
   217  	}, {
   218  		desc:        "full range contains any",
   219  		inBaseRange: YangRange{R(MinInt64, MaxInt64)},
   220  		inTestRange: YangRange{R(1, 10)},
   221  		want:        true,
   222  	}, {
   223  		desc:        "smaller range doesn't contain min..a|b..max",
   224  		inBaseRange: YangRange{R(1024, 65535)},
   225  		inTestRange: YangRange{R(MinInt64, 4096), R(5120, MaxInt64)},
   226  		want:        false,
   227  	}, {
   228  		desc:        "ranges don't overlap with max word used",
   229  		inBaseRange: YangRange{R(1024, 65535)},
   230  		inTestRange: YangRange{R(-999999, 4096), R(5120, MaxInt64)},
   231  		want:        false,
   232  	}, {
   233  		desc:        "ranges don't overlap with min word used",
   234  		inBaseRange: YangRange{R(1024, 65535)},
   235  		inTestRange: YangRange{R(MinInt64, 4096), R(5120, 999999)},
   236  		want:        false,
   237  	}}
   238  
   239  	for _, tt := range tests {
   240  		t.Run(tt.desc, func(t *testing.T) {
   241  			if got := tt.inBaseRange.Contains(tt.inTestRange); got != tt.want {
   242  				t.Errorf("got %v, want %v", got, tt.want)
   243  			}
   244  		})
   245  	}
   246  }
   247  
   248  func TestParseRangesInt(t *testing.T) {
   249  	tests := []struct {
   250  		desc             string
   251  		inParentRange    YangRange
   252  		in               string
   253  		want             YangRange
   254  		wantErrSubstring string
   255  	}{{
   256  		desc: "small numbers, coalescing",
   257  		in:   "0|2..3|4..5",
   258  		want: YangRange{R(0, 0), R(2, 5)},
   259  	}, {
   260  		desc: "small numbers, out of order, coalescing",
   261  		in:   "4..5|0|2..3",
   262  		want: YangRange{R(0, 0), R(2, 5)},
   263  	}, {
   264  		desc:             "invalid input: too many ..s",
   265  		in:               "0|2..3|4..5..6",
   266  		wantErrSubstring: "too many '..' in 4..5..6",
   267  	}, {
   268  		desc:             "invalid input: range boundaries out of order",
   269  		in:               "0|2..3|5..4",
   270  		wantErrSubstring: "range boundaries out of order",
   271  	}, {
   272  		desc:          "range with min",
   273  		inParentRange: Int64Range,
   274  		in:            "min..0|2..3|4..5",
   275  		want:          YangRange{R(MinInt64, 0), R(2, 5)},
   276  	}, {
   277  		desc:             "range with min but without parent range",
   278  		in:               "min..0|2..3|4..5",
   279  		wantErrSubstring: "empty YangRange parent object",
   280  	}, {
   281  		desc:          "range with max",
   282  		inParentRange: Int32Range,
   283  		in:            "min..0|2..3|4..5|7..max",
   284  		want:          YangRange{R(minInt32, 0), R(2, 5), R(7, maxInt32)},
   285  	}, {
   286  		desc:          "coalescing from min to max for uint64",
   287  		inParentRange: Uint64Range,
   288  		in:            "min..0|1..max",
   289  		want:          YangRange{YRange{FromInt(0), FromUint(maxUint64)}},
   290  	}, {
   291  		desc:          "coalescing from min to max for uint32",
   292  		inParentRange: Uint32Range,
   293  		in:            "min..0|1..max",
   294  		want:          YangRange{R(0, maxUint32)},
   295  	}, {
   296  		desc:          "coalescing from min to max for uint16",
   297  		inParentRange: Uint16Range,
   298  		in:            "min..0|1..max",
   299  		want:          YangRange{R(0, maxUint16)},
   300  	}, {
   301  		desc:          "coalescing from min to max for uint8",
   302  		inParentRange: Uint8Range,
   303  		in:            "min..0|1..max",
   304  		want:          YangRange{R(0, maxUint8)},
   305  	}, {
   306  		desc:          "coalescing from min to max for int64",
   307  		inParentRange: Int64Range,
   308  		in:            "min..0|1..max",
   309  		want:          YangRange{R(MinInt64, MaxInt64)},
   310  	}, {
   311  		desc:          "coalescing from min to max for int32",
   312  		inParentRange: Int32Range,
   313  		in:            "min..0|1..max",
   314  		want:          YangRange{R(minInt32, maxInt32)},
   315  	}, {
   316  		desc:          "coalescing from min to max for int16",
   317  		inParentRange: Int16Range,
   318  		in:            "min..0|1..max",
   319  		want:          YangRange{R(minInt16, maxInt16)},
   320  	}, {
   321  		desc:          "coalescing from min to max for int8",
   322  		inParentRange: Int8Range,
   323  		in:            "min..0|1..max",
   324  		want:          YangRange{R(minInt8, maxInt8)},
   325  	}, {
   326  		desc:             "spelling error",
   327  		inParentRange:    Int64Range,
   328  		in:               "mean..0|1..max",
   329  		wantErrSubstring: "invalid syntax",
   330  	}, {
   331  		desc: "big numbers, coalescing",
   332  		in:   "0..69|4294967294|4294967295",
   333  		want: YangRange{R(0, 69), R(4294967294, 4294967295)},
   334  	}, {
   335  		desc: "no ranges",
   336  		in:   "250|500|1000",
   337  		want: YangRange{R(250, 250), R(500, 500), R(1000, 1000)},
   338  	}, {
   339  		desc: "no ranges unsorted",
   340  		in:   "1000|500|250",
   341  		want: YangRange{R(250, 250), R(500, 500), R(1000, 1000)},
   342  	}, {
   343  		desc: "negative numbers",
   344  		in:   "-31..-1|1..31",
   345  		want: YangRange{R(-31, -1), R(1, 31)},
   346  	}, {
   347  		desc: "spaces",
   348  		in:   "-22 | -15 | -7 | 0",
   349  		want: YangRange{R(-22, -22), R(-15, -15), R(-7, -7), R(0, 0)},
   350  	}}
   351  
   352  	for _, tt := range tests {
   353  		t.Run(tt.desc, func(t *testing.T) {
   354  			got, err := tt.inParentRange.parseChildRanges(tt.in, false, 0)
   355  			if err != nil {
   356  				if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" {
   357  					t.Fatalf("did not get expected error, %s", diff)
   358  				}
   359  				return
   360  			}
   361  
   362  			if diff := cmp.Diff(tt.want, got); diff != "" {
   363  				t.Errorf("parseChildRanges (-want, +got):\n%s", diff)
   364  			}
   365  
   366  			if tt.inParentRange == nil {
   367  				if got, err = ParseRangesInt(tt.in); err != nil {
   368  					t.Fatalf("ParseRangesInt: unexpected error: %v", err)
   369  				}
   370  				if diff := cmp.Diff(tt.want, got); diff != "" {
   371  					t.Errorf("ParseRangesInt (-want, +got):\n%s", diff)
   372  				}
   373  			}
   374  		})
   375  	}
   376  }
   377  
   378  func TestCoalesce(t *testing.T) {
   379  	for x, tt := range []struct {
   380  		in, out YangRange
   381  	}{
   382  		{},
   383  		{YangRange{R(1, 4)}, YangRange{R(1, 4)}},
   384  		{YangRange{R(1, 2), R(3, 4)}, YangRange{R(1, 4)}},
   385  		{YangRange{Rf(10, 25, 1), Rf(30, 40, 1)}, YangRange{Rf(10, 25, 1), Rf(30, 40, 1)}},
   386  		{YangRange{Rf(10, 29, 1), Rf(30, 40, 1)}, YangRange{Rf(10, 40, 1)}},
   387  		{YangRange{R(1, 2), R(2, 4)}, YangRange{R(1, 4)}},
   388  		{YangRange{R(1, 2), R(4, 5)}, YangRange{R(1, 2), R(4, 5)}},
   389  		{YangRange{R(1, 3), R(2, 5)}, YangRange{R(1, 5)}},
   390  		{YangRange{R(1, 10), R(2, 5)}, YangRange{R(1, 10)}},
   391  		{YangRange{R(1, 10), R(1, 2), R(4, 5), R(7, 8)}, YangRange{R(1, 10)}},
   392  		{YangRange{Rf(1, 10, 3), Rf(1, 2, 3), Rf(4, 5, 3), Rf(7, 8, 3)}, YangRange{Rf(1, 10, 3)}},
   393  	} {
   394  		out := coalesce(tt.in)
   395  		if !out.Equal(tt.out) {
   396  			t.Errorf("#%d: got %v, want %v", x, out, tt.out)
   397  		}
   398  	}
   399  }
   400  
   401  func TestYangRangeSort(t *testing.T) {
   402  	for x, tt := range []struct {
   403  		in, out YangRange
   404  	}{
   405  		{YangRange{}, YangRange{}},
   406  		{YangRange{R(1, 4), R(6, 10)}, YangRange{R(1, 4), R(6, 10)}},
   407  		{YangRange{R(6, 10), R(1, 4)}, YangRange{R(1, 4), R(6, 10)}},
   408  		{YangRange{Rf(10, 25, 1), Rf(30, 40, 1)}, YangRange{Rf(10, 25, 1), Rf(30, 40, 1)}},
   409  		{YangRange{Rf(30, 40, 1), Rf(10, 25, 1)}, YangRange{Rf(10, 25, 1), Rf(30, 40, 1)}},
   410  		{YangRange{R(1, 2)}, YangRange{R(1, 2)}},
   411  		{YangRange{R(1, 2), R(4, 5)}, YangRange{R(1, 2), R(4, 5)}},
   412  		{YangRange{R(1, 3), R(2, 5)}, YangRange{R(1, 3), R(2, 5)}},
   413  		{YangRange{R(1, 10), R(2, 5)}, YangRange{R(1, 10), R(2, 5)}},
   414  		{YangRange{R(1, 10), R(1, 2), R(4, 5), R(7, 8)}, YangRange{R(1, 2), R(1, 10), R(4, 5), R(7, 8)}},
   415  	} {
   416  		tt.in.Sort()
   417  		if !tt.in.Equal(tt.out) {
   418  			t.Errorf("#%d: got %v, want %v", x, tt.in, tt.out)
   419  		}
   420  	}
   421  }
   422  
   423  func TestParseRangesDecimal(t *testing.T) {
   424  	rangeMax := mustParseRangesDecimal("-922337203685477580.8..922337203685477580.7", 1)
   425  	rangeRestricted := mustParseRangesDecimal("-42..42|100", 5)
   426  
   427  	tests := []struct {
   428  		desc             string
   429  		inParentRange    YangRange
   430  		in               string
   431  		inFracDig        uint8
   432  		want             YangRange
   433  		wantErrSubstring string
   434  	}{{
   435  		desc:          "min..max fraction-digits 1",
   436  		inParentRange: rangeMax,
   437  		in:            "min..max",
   438  		inFracDig:     1,
   439  		want:          YangRange{Rf(MinInt64, MaxInt64, 1)},
   440  	}, {
   441  		desc:          "min..max fraction-digits 2",
   442  		inParentRange: rangeMax,
   443  		in:            "min..max",
   444  		inFracDig:     2,
   445  		want:          YangRange{Rf(MinInt64, MaxInt64, 2)},
   446  	}, {
   447  		desc:             "min..max no parent range",
   448  		in:               "min..max",
   449  		inFracDig:        2,
   450  		want:             YangRange{Rf(MinInt64, MaxInt64, 2)},
   451  		wantErrSubstring: "empty YangRange parent object",
   452  	}, {
   453  		desc:             "min..max on fragmented range",
   454  		inParentRange:    rangeRestricted,
   455  		in:               "min..max",
   456  		inFracDig:        5,
   457  		wantErrSubstring: "not within",
   458  	}, {
   459  		desc:          "small decimals",
   460  		inParentRange: rangeMax,
   461  		in:            "0.0|2.0..30.0|1.34..1.99",
   462  		inFracDig:     2,
   463  		want:          YangRange{Rf(0, 0, 2), Rf(134, 3000, 2)},
   464  	}, {
   465  		desc:          "small decimals on restricted range",
   466  		inParentRange: rangeRestricted,
   467  		in:            "0.0|2.0..30.0|1.34..1.99999",
   468  		inFracDig:     5,
   469  		want:          YangRange{Rf(0, 0, 5), Rf(134000, 3000000, 5)},
   470  	}, {
   471  		desc:          "small decimals with coalescing",
   472  		inParentRange: rangeMax,
   473  		in:            "0.0|2.0..30.0",
   474  		inFracDig:     1,
   475  		want:          YangRange{Rf(0, 0, 1), Rf(20, 300, 1)},
   476  	}, {
   477  		desc:             "fractional digit cannot be too high",
   478  		in:               "0.0|2.0..30.0",
   479  		inFracDig:        19,
   480  		wantErrSubstring: "invalid number of fraction digits",
   481  	}, {
   482  		desc:             "fractional digit cannot be 0",
   483  		in:               "0.0|2.0..30.0",
   484  		inFracDig:        0,
   485  		wantErrSubstring: "invalid number of fraction digits",
   486  	}, {
   487  		desc:      "big decimals",
   488  		in:        "0.0..69|4294967294.1234|4294967295.1234",
   489  		inFracDig: 4,
   490  		want:      YangRange{Rf(0, 690000, 4), Rf(42949672941234, 42949672941234, 4), Rf(42949672951234, 42949672951234, 4)},
   491  	}, {
   492  		desc:      "small decimals, out of order",
   493  		in:        "4.0..5.55|0|2.32..3.23",
   494  		inFracDig: 3,
   495  		want:      YangRange{Rf(0, 0, 3), Rf(2320, 3230, 3), Rf(4000, 5550, 3)},
   496  	}, {
   497  		desc:             "invalid input: too many ..s",
   498  		in:               "4.0..5.55..6.66|0|2.32..3.23",
   499  		inFracDig:        3,
   500  		wantErrSubstring: "too many '..'",
   501  	}, {
   502  		desc:             "invalid input: range boundaries out of order",
   503  		in:               "5..4.0|0|2.32..3.23",
   504  		inFracDig:        3,
   505  		wantErrSubstring: "range boundaries out of order",
   506  	}, {
   507  		desc:          "range with min",
   508  		inParentRange: rangeMax,
   509  		in:            "4.0..5.55|min..0|2.32..3.23",
   510  		inFracDig:     3,
   511  		want:          YangRange{Rf(MinInt64, 0, 3), Rf(2320, 3230, 3), Rf(4000, 5550, 3)},
   512  	}, {
   513  		desc:          "range with max",
   514  		inParentRange: rangeMax,
   515  		in:            "4.0..max|min..0|2.32..3.23",
   516  		inFracDig:     3,
   517  		want:          YangRange{Rf(MinInt64, 0, 3), Rf(2320, 3230, 3), Rf(4000, MaxInt64, 3)},
   518  	}, {
   519  		desc:          "coalescing from min to max",
   520  		inParentRange: rangeMax,
   521  		in:            "min..0.9|1..max",
   522  		inFracDig:     1,
   523  		want:          YangRange{Rf(MinInt64, MaxInt64, 1)},
   524  	}, {
   525  		desc:             "spelling error",
   526  		inParentRange:    rangeMax,
   527  		in:               "min..0.9|1..masks",
   528  		inFracDig:        1,
   529  		wantErrSubstring: "invalid syntax",
   530  	}, {
   531  		desc:      "no ranges",
   532  		in:        "250.55|500.0|1000",
   533  		inFracDig: 2,
   534  		want:      YangRange{Rf(25055, 25055, 2), Rf(50000, 50000, 2), Rf(100000, 100000, 2)},
   535  	}, {
   536  		desc:      "no ranges unsorted",
   537  		in:        "1000|500.0|250.55",
   538  		inFracDig: 2,
   539  		want:      YangRange{Rf(25055, 25055, 2), Rf(50000, 50000, 2), Rf(100000, 100000, 2)},
   540  	}, {
   541  		desc:      "negative decimals",
   542  		in:        "-31.2..-1.5|1.5..31.2",
   543  		inFracDig: 1,
   544  		want:      YangRange{Rf(-312, -15, 1), Rf(15, 312, 1)},
   545  	}, {
   546  		desc:      "spaces",
   547  		in:        "-22.5 | -15 | -7.5 | 0",
   548  		inFracDig: 1,
   549  		want:      YangRange{Rf(-225, -225, 1), Rf(-150, -150, 1), Rf(-75, -75, 1), Rf(0, 0, 1)},
   550  	}}
   551  
   552  	for _, tt := range tests {
   553  		t.Run(tt.desc, func(t *testing.T) {
   554  			got, err := tt.inParentRange.parseChildRanges(tt.in, true, tt.inFracDig)
   555  			if err != nil {
   556  				if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" {
   557  					t.Fatalf("did not get expected error, %s", diff)
   558  				}
   559  				return
   560  			}
   561  
   562  			if diff := cmp.Diff(tt.want, got); diff != "" {
   563  				t.Errorf("(-want, +got):\n%s", diff)
   564  			}
   565  
   566  			if tt.inParentRange == nil {
   567  				if got, err = ParseRangesDecimal(tt.in, tt.inFracDig); err != nil {
   568  					t.Fatalf("ParseRangesDecimal: unexpected error: %v", err)
   569  				}
   570  				if diff := cmp.Diff(tt.want, got); diff != "" {
   571  					t.Errorf("ParseRangesDecimal (-want, +got):\n%s", diff)
   572  				}
   573  			}
   574  		})
   575  	}
   576  }
   577  
   578  func TestAdd(t *testing.T) {
   579  	tests := []struct {
   580  		desc  string
   581  		inVal Number
   582  		inAdd uint64
   583  		want  Number
   584  	}{{
   585  		desc:  "add one to integer",
   586  		inVal: FromInt(1),
   587  		inAdd: 1,
   588  		want:  FromInt(2),
   589  	}, {
   590  		desc:  "add one to decimal64",
   591  		inVal: FromFloat(1.0),
   592  		inAdd: 1,
   593  		want:  FromFloat(1.1),
   594  	}, {
   595  		desc:  "negative int becomes positive",
   596  		inVal: FromInt(-2),
   597  		inAdd: 3,
   598  		want:  FromInt(1),
   599  	}, {
   600  		desc:  "negative int stays negative",
   601  		inVal: FromInt(-3),
   602  		inAdd: 1,
   603  		want:  FromInt(-2),
   604  	}, {
   605  		desc:  "negative decimal becomes positive",
   606  		inVal: FromFloat(-2),
   607  		inAdd: 35,
   608  		want:  FromFloat(1.5),
   609  	}, {
   610  		desc:  "negative decimal stays negative",
   611  		inVal: FromFloat(-42.22),
   612  		inAdd: 4122,
   613  		want:  FromFloat(-1.0),
   614  	}, {
   615  		desc:  "explicitly set fraction digits",
   616  		inVal: Number{Value: 10000, FractionDigits: 5},
   617  		inAdd: 1,
   618  		want:  Number{Value: 10001, FractionDigits: 5},
   619  	}, {
   620  		desc:  "explicitly set fraction digits - negative",
   621  		inVal: Number{Value: 0, FractionDigits: 3},
   622  		inAdd: 42,
   623  		want:  FromFloat(0.042),
   624  	}}
   625  
   626  	for _, tt := range tests {
   627  		t.Run(tt.desc, func(t *testing.T) {
   628  			got := tt.inVal.addQuantum(tt.inAdd)
   629  			if !cmp.Equal(got, tt.want) {
   630  				t.Fatalf("did get expected result, got: %s, want: %s", got.String(), tt.want.String())
   631  			}
   632  		})
   633  	}
   634  }
   635  
   636  func TestParseInt(t *testing.T) {
   637  	tests := []struct {
   638  		desc             string
   639  		inStr            string
   640  		want             Number
   641  		wantErrSubstring string
   642  	}{{
   643  		desc:             "invalid string supplied",
   644  		inStr:            "fish",
   645  		wantErrSubstring: "valid syntax",
   646  	}, {
   647  		desc:  "negative int",
   648  		inStr: "-42",
   649  		want:  FromInt(-42),
   650  	}, {
   651  		desc:  "positive int",
   652  		inStr: "42",
   653  		want:  FromInt(42),
   654  	}, {
   655  		desc:  "positive int with plus sign",
   656  		inStr: "+42",
   657  		want:  FromInt(42),
   658  	}, {
   659  		desc:  "zero",
   660  		inStr: "0",
   661  		want:  FromInt(0),
   662  	}, {
   663  		desc:             "min",
   664  		inStr:            "min",
   665  		wantErrSubstring: "invalid syntax",
   666  	}, {
   667  		desc:             "max",
   668  		inStr:            "max",
   669  		wantErrSubstring: "invalid syntax",
   670  	}, {
   671  		desc:             "just a sign",
   672  		inStr:            "-",
   673  		wantErrSubstring: "sign with no value",
   674  	}}
   675  
   676  	for _, tt := range tests {
   677  		t.Run(tt.desc, func(t *testing.T) {
   678  			got, err := ParseInt(tt.inStr)
   679  			if err != nil {
   680  				if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" {
   681  					t.Fatalf("did not get expected error, %s", diff)
   682  				}
   683  				return
   684  			}
   685  
   686  			if !cmp.Equal(got, tt.want) {
   687  				t.Errorf("did not get expected Number, got: %s, want: %s", got, tt.want)
   688  			}
   689  
   690  			if got.IsDecimal() {
   691  				t.Errorf("Got decimal value instead of int: %v", got)
   692  			}
   693  		})
   694  	}
   695  }
   696  
   697  func TestParseDecimal(t *testing.T) {
   698  	tests := []struct {
   699  		desc                    string
   700  		inStr                   string
   701  		inFracDig               uint8
   702  		skipFractionDigitsCheck bool
   703  		want                    Number
   704  		wantErrSubstring        string
   705  	}{{
   706  		desc:             "too few fractional digits",
   707  		inStr:            "1.000",
   708  		inFracDig:        0,
   709  		wantErrSubstring: "invalid number of fraction digits",
   710  	}, {
   711  		desc:             "too many fraction digits",
   712  		inStr:            "1.000",
   713  		inFracDig:        24,
   714  		wantErrSubstring: "invalid number of fraction digits",
   715  	}, {
   716  		desc:             "more digits supplied",
   717  		inStr:            "1.14242",
   718  		inFracDig:        2,
   719  		wantErrSubstring: "has too much precision",
   720  	}, {
   721  		desc:      "single digit precision",
   722  		inStr:     "1.1",
   723  		inFracDig: 1,
   724  		want:      Number{Value: 11, FractionDigits: 1},
   725  	}, {
   726  		desc:                    "max precision",
   727  		inStr:                   "0.100000000000000000",
   728  		inFracDig:               18,
   729  		skipFractionDigitsCheck: true,
   730  		want:                    FromFloat(0.1),
   731  	}, {
   732  		desc:                    "max precision but not supplied",
   733  		inStr:                   "0.1",
   734  		inFracDig:               4,
   735  		skipFractionDigitsCheck: true,
   736  		want:                    FromFloat(0.1),
   737  	}, {
   738  		desc:             "invalid string supplied",
   739  		inStr:            "fish",
   740  		inFracDig:        17,
   741  		wantErrSubstring: "not a valid decimal number",
   742  	}, {
   743  		desc:      "negative number",
   744  		inStr:     "-42.0",
   745  		inFracDig: 1,
   746  		want:      FromFloat(-42),
   747  	}}
   748  
   749  	for _, tt := range tests {
   750  		t.Run(tt.desc, func(t *testing.T) {
   751  			got, err := ParseDecimal(tt.inStr, tt.inFracDig)
   752  			if err != nil {
   753  				if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" {
   754  					t.Fatalf("did not get expected error, %s", diff)
   755  				}
   756  				return
   757  			}
   758  
   759  			if !cmp.Equal(got, tt.want) {
   760  				t.Errorf("did not get expected Number, got: %s, want: %s", got, tt.want)
   761  			}
   762  
   763  			if !tt.skipFractionDigitsCheck {
   764  				if got, want := got.FractionDigits, tt.want.FractionDigits; got != want {
   765  					t.Errorf("fractional digits not equal, got: %d, want: %d", got, want)
   766  				}
   767  			}
   768  
   769  			if !got.IsDecimal() {
   770  				t.Errorf("Got non-decimal value: %v", got)
   771  			}
   772  		})
   773  	}
   774  }
   775  
   776  func TestNumberString(t *testing.T) {
   777  	tests := []struct {
   778  		desc string
   779  		in   Number
   780  		want string
   781  	}{{
   782  		desc: "min",
   783  		in:   FromInt(MinInt64),
   784  		want: "-9223372036854775808",
   785  	}, {
   786  		desc: "max",
   787  		in:   FromInt(MaxInt64),
   788  		want: "9223372036854775807",
   789  	}, {
   790  		desc: "integer",
   791  		in:   Number{Value: 1},
   792  		want: "1",
   793  	}, {
   794  		desc: "negative integer",
   795  		in:   Number{Value: 1, Negative: true},
   796  		want: "-1",
   797  	}, {
   798  		desc: "decimal, fractional digits = 1",
   799  		in:   Number{Value: 1, FractionDigits: 1},
   800  		want: "0.1",
   801  	}, {
   802  		desc: "decimal, fractional digits = 18",
   803  		in:   Number{Value: 123456789012345678, FractionDigits: 18},
   804  		want: "0.123456789012345678",
   805  	}, {
   806  		desc: "negative decimal",
   807  		in:   Number{Value: 100, FractionDigits: 2, Negative: true},
   808  		want: "-1.00",
   809  	}}
   810  
   811  	for _, tt := range tests {
   812  		t.Run(tt.desc, func(t *testing.T) {
   813  			if got := tt.in.String(); got != tt.want {
   814  				t.Fatalf("did not get expected number, got: %s, want: %s", got, tt.want)
   815  			}
   816  		})
   817  	}
   818  }
   819  
   820  func TestEnumToJson(t *testing.T) {
   821  	tests := []struct {
   822  		desc    string
   823  		in      *EnumType
   824  		want    string
   825  		wantErr bool
   826  	}{{
   827  		"empty enum to JSON",
   828  		&EnumType{
   829  			last:     -1, // +1 will start at 0
   830  			min:      0,
   831  			max:      MaxBitfieldSize - 1,
   832  			ToString: map[int64]string{},
   833  			ToInt:    map[string]int64{},
   834  		},
   835  		`{}`,
   836  		false,
   837  	}, {
   838  		"2 value enum to JSON",
   839  		&EnumType{
   840  			last: 2,
   841  			min:  0,
   842  			max:  MaxBitfieldSize - 1,
   843  			ToString: map[int64]string{
   844  				1: "value1",
   845  				2: "value2",
   846  			},
   847  			ToInt: map[string]int64{
   848  				"value1": 1,
   849  				"value2": 2,
   850  			},
   851  		},
   852  		`{"ToString":{"1":"value1","2":"value2"},"ToInt":{"value1":1,"value2":2}}`,
   853  		false,
   854  	}}
   855  
   856  	for _, tt := range tests {
   857  		t.Run(tt.desc, func(t *testing.T) {
   858  			got, err := json.Marshal(tt.in)
   859  			if string(got) != tt.want {
   860  				t.Errorf("got: %v, want: %v", string(got), tt.want)
   861  			}
   862  			if (err != nil) != tt.wantErr {
   863  				t.Errorf("gotErr: %v, wantErr: %v", err, tt.wantErr)
   864  			}
   865  		})
   866  	}
   867  }