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 }