1package semver 2 3import ( 4 "reflect" 5 "strings" 6 "testing" 7) 8 9type wildcardTypeTest struct { 10 input string 11 wildcardType wildcardType 12} 13 14type comparatorTest struct { 15 input string 16 comparator func(comparator) bool 17} 18 19func TestParseComparator(t *testing.T) { 20 compatorTests := []comparatorTest{ 21 {">", testGT}, 22 {">=", testGE}, 23 {"<", testLT}, 24 {"<=", testLE}, 25 {"", testEQ}, 26 {"=", testEQ}, 27 {"==", testEQ}, 28 {"!=", testNE}, 29 {"!", testNE}, 30 {"-", nil}, 31 {"<==", nil}, 32 {"<<", nil}, 33 {">>", nil}, 34 } 35 36 for _, tc := range compatorTests { 37 if c := parseComparator(tc.input); c == nil { 38 if tc.comparator != nil { 39 t.Errorf("Comparator nil for case %q\n", tc.input) 40 } 41 } else if !tc.comparator(c) { 42 t.Errorf("Invalid comparator for case %q\n", tc.input) 43 } 44 } 45} 46 47var ( 48 v1 = MustParse("1.2.2") 49 v2 = MustParse("1.2.3") 50 v3 = MustParse("1.2.4") 51) 52 53func testEQ(f comparator) bool { 54 return f(v1, v1) && !f(v1, v2) 55} 56 57func testNE(f comparator) bool { 58 return !f(v1, v1) && f(v1, v2) 59} 60 61func testGT(f comparator) bool { 62 return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1) 63} 64 65func testGE(f comparator) bool { 66 return f(v2, v1) && f(v3, v2) && !f(v1, v2) 67} 68 69func testLT(f comparator) bool { 70 return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1) 71} 72 73func testLE(f comparator) bool { 74 return f(v1, v2) && f(v2, v3) && !f(v2, v1) 75} 76 77func TestSplitAndTrim(t *testing.T) { 78 tests := []struct { 79 i string 80 s []string 81 }{ 82 {"1.2.3 1.2.3", []string{"1.2.3", "1.2.3"}}, 83 {" 1.2.3 1.2.3 ", []string{"1.2.3", "1.2.3"}}, // Spaces 84 {" >= 1.2.3 <= 1.2.3 ", []string{">=1.2.3", "<=1.2.3"}}, // Spaces between operator and version 85 {"1.2.3 || >=1.2.3 <1.2.3", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}}, 86 {" 1.2.3 || >=1.2.3 <1.2.3 ", []string{"1.2.3", "||", ">=1.2.3", "<1.2.3"}}, 87 } 88 89 for _, tc := range tests { 90 p := splitAndTrim(tc.i) 91 if !reflect.DeepEqual(p, tc.s) { 92 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) 93 } 94 } 95} 96 97func TestSplitComparatorVersion(t *testing.T) { 98 tests := []struct { 99 i string 100 p []string 101 }{ 102 {">1.2.3", []string{">", "1.2.3"}}, 103 {">=1.2.3", []string{">=", "1.2.3"}}, 104 {"<1.2.3", []string{"<", "1.2.3"}}, 105 {"<=1.2.3", []string{"<=", "1.2.3"}}, 106 {"1.2.3", []string{"", "1.2.3"}}, 107 {"=1.2.3", []string{"=", "1.2.3"}}, 108 {"==1.2.3", []string{"==", "1.2.3"}}, 109 {"!=1.2.3", []string{"!=", "1.2.3"}}, 110 {"!1.2.3", []string{"!", "1.2.3"}}, 111 {"error", nil}, 112 } 113 for _, tc := range tests { 114 if op, v, err := splitComparatorVersion(tc.i); err != nil { 115 if tc.p != nil { 116 t.Errorf("Invalid for case %q: Expected %q, got error %q", tc.i, tc.p, err) 117 } 118 } else if op != tc.p[0] { 119 t.Errorf("Invalid operator for case %q: Expected %q, got: %q", tc.i, tc.p[0], op) 120 } else if v != tc.p[1] { 121 t.Errorf("Invalid version for case %q: Expected %q, got: %q", tc.i, tc.p[1], v) 122 } 123 124 } 125} 126 127func TestBuildVersionRange(t *testing.T) { 128 tests := []struct { 129 opStr string 130 vStr string 131 c func(comparator) bool 132 v string 133 }{ 134 {">", "1.2.3", testGT, "1.2.3"}, 135 {">=", "1.2.3", testGE, "1.2.3"}, 136 {"<", "1.2.3", testLT, "1.2.3"}, 137 {"<=", "1.2.3", testLE, "1.2.3"}, 138 {"", "1.2.3", testEQ, "1.2.3"}, 139 {"=", "1.2.3", testEQ, "1.2.3"}, 140 {"==", "1.2.3", testEQ, "1.2.3"}, 141 {"!=", "1.2.3", testNE, "1.2.3"}, 142 {"!", "1.2.3", testNE, "1.2.3"}, 143 {">>", "1.2.3", nil, ""}, // Invalid comparator 144 {"=", "invalid", nil, ""}, // Invalid version 145 } 146 147 for _, tc := range tests { 148 if r, err := buildVersionRange(tc.opStr, tc.vStr); err != nil { 149 if tc.c != nil { 150 t.Errorf("Invalid for case %q: Expected %q, got error %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tc.v, err) 151 } 152 } else if r == nil { 153 t.Errorf("Invalid for case %q: got nil", strings.Join([]string{tc.opStr, tc.vStr}, "")) 154 } else { 155 // test version 156 if tv := MustParse(tc.v); !r.v.EQ(tv) { 157 t.Errorf("Invalid for case %q: Expected version %q, got: %q", strings.Join([]string{tc.opStr, tc.vStr}, ""), tv, r.v) 158 } 159 // test comparator 160 if r.c == nil { 161 t.Errorf("Invalid for case %q: got nil comparator", strings.Join([]string{tc.opStr, tc.vStr}, "")) 162 continue 163 } 164 if !tc.c(r.c) { 165 t.Errorf("Invalid comparator for case %q\n", strings.Join([]string{tc.opStr, tc.vStr}, "")) 166 } 167 } 168 } 169 170} 171 172func TestSplitORParts(t *testing.T) { 173 tests := []struct { 174 i []string 175 o [][]string 176 }{ 177 {[]string{">1.2.3", "||", "<1.2.3", "||", "=1.2.3"}, [][]string{ 178 {">1.2.3"}, 179 {"<1.2.3"}, 180 {"=1.2.3"}, 181 }}, 182 {[]string{">1.2.3", "<1.2.3", "||", "=1.2.3"}, [][]string{ 183 {">1.2.3", "<1.2.3"}, 184 {"=1.2.3"}, 185 }}, 186 {[]string{">1.2.3", "||"}, nil}, 187 {[]string{"||", ">1.2.3"}, nil}, 188 } 189 for _, tc := range tests { 190 o, err := splitORParts(tc.i) 191 if err != nil && tc.o != nil { 192 t.Errorf("Unexpected error for case %q: %s", tc.i, err) 193 } 194 if !reflect.DeepEqual(tc.o, o) { 195 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o) 196 } 197 } 198} 199 200func TestGetWildcardType(t *testing.T) { 201 wildcardTypeTests := []wildcardTypeTest{ 202 {"x", majorWildcard}, 203 {"1.x", minorWildcard}, 204 {"1.2.x", patchWildcard}, 205 {"fo.o.b.ar", noneWildcard}, 206 } 207 208 for _, tc := range wildcardTypeTests { 209 o := getWildcardType(tc.input) 210 if o != tc.wildcardType { 211 t.Errorf("Invalid for case: %q: Expected %q, got: %q", tc.input, tc.wildcardType, o) 212 } 213 } 214} 215 216func TestCreateVersionFromWildcard(t *testing.T) { 217 tests := []struct { 218 i string 219 s string 220 }{ 221 {"1.2.x", "1.2.0"}, 222 {"1.x", "1.0.0"}, 223 } 224 225 for _, tc := range tests { 226 p := createVersionFromWildcard(tc.i) 227 if p != tc.s { 228 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) 229 } 230 } 231} 232 233func TestIncrementMajorVersion(t *testing.T) { 234 tests := []struct { 235 i string 236 s string 237 }{ 238 {"1.2.3", "2.2.3"}, 239 {"1.2", "2.2"}, 240 {"foo.bar", ""}, 241 } 242 243 for _, tc := range tests { 244 p, _ := incrementMajorVersion(tc.i) 245 if p != tc.s { 246 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) 247 } 248 } 249} 250 251func TestIncrementMinorVersion(t *testing.T) { 252 tests := []struct { 253 i string 254 s string 255 }{ 256 {"1.2.3", "1.3.3"}, 257 {"1.2", "1.3"}, 258 {"foo.bar", ""}, 259 } 260 261 for _, tc := range tests { 262 p, _ := incrementMinorVersion(tc.i) 263 if p != tc.s { 264 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.s, p) 265 } 266 } 267} 268 269func TestExpandWildcardVersion(t *testing.T) { 270 tests := []struct { 271 i [][]string 272 o [][]string 273 }{ 274 {[][]string{{"foox"}}, nil}, 275 {[][]string{{">=1.2.x"}}, [][]string{{">=1.2.0"}}}, 276 {[][]string{{"<=1.2.x"}}, [][]string{{"<1.3.0"}}}, 277 {[][]string{{">1.2.x"}}, [][]string{{">=1.3.0"}}}, 278 {[][]string{{"<1.2.x"}}, [][]string{{"<1.2.0"}}}, 279 {[][]string{{"!=1.2.x"}}, [][]string{{"<1.2.0", ">=1.3.0"}}}, 280 {[][]string{{">=1.x"}}, [][]string{{">=1.0.0"}}}, 281 {[][]string{{"<=1.x"}}, [][]string{{"<2.0.0"}}}, 282 {[][]string{{">1.x"}}, [][]string{{">=2.0.0"}}}, 283 {[][]string{{"<1.x"}}, [][]string{{"<1.0.0"}}}, 284 {[][]string{{"!=1.x"}}, [][]string{{"<1.0.0", ">=2.0.0"}}}, 285 {[][]string{{"1.2.x"}}, [][]string{{">=1.2.0", "<1.3.0"}}}, 286 {[][]string{{"1.x"}}, [][]string{{">=1.0.0", "<2.0.0"}}}, 287 } 288 289 for _, tc := range tests { 290 o, _ := expandWildcardVersion(tc.i) 291 if !reflect.DeepEqual(tc.o, o) { 292 t.Errorf("Invalid for case %q: Expected %q, got: %q", tc.i, tc.o, o) 293 } 294 } 295} 296 297func TestVersionRangeToRange(t *testing.T) { 298 vr := versionRange{ 299 v: MustParse("1.2.3"), 300 c: compLT, 301 } 302 rf := vr.rangeFunc() 303 if !rf(MustParse("1.2.2")) || rf(MustParse("1.2.3")) { 304 t.Errorf("Invalid conversion to range func") 305 } 306} 307 308func TestRangeAND(t *testing.T) { 309 v := MustParse("1.2.2") 310 v1 := MustParse("1.2.1") 311 v2 := MustParse("1.2.3") 312 rf1 := Range(func(v Version) bool { 313 return v.GT(v1) 314 }) 315 rf2 := Range(func(v Version) bool { 316 return v.LT(v2) 317 }) 318 rf := rf1.AND(rf2) 319 if rf(v1) { 320 t.Errorf("Invalid rangefunc, accepted: %s", v1) 321 } 322 if rf(v2) { 323 t.Errorf("Invalid rangefunc, accepted: %s", v2) 324 } 325 if !rf(v) { 326 t.Errorf("Invalid rangefunc, did not accept: %s", v) 327 } 328} 329 330func TestRangeOR(t *testing.T) { 331 tests := []struct { 332 v Version 333 b bool 334 }{ 335 {MustParse("1.2.0"), true}, 336 {MustParse("1.2.2"), false}, 337 {MustParse("1.2.4"), true}, 338 } 339 v1 := MustParse("1.2.1") 340 v2 := MustParse("1.2.3") 341 rf1 := Range(func(v Version) bool { 342 return v.LT(v1) 343 }) 344 rf2 := Range(func(v Version) bool { 345 return v.GT(v2) 346 }) 347 rf := rf1.OR(rf2) 348 for _, tc := range tests { 349 if r := rf(tc.v); r != tc.b { 350 t.Errorf("Invalid for case %q: Expected %t, got %t", tc.v, tc.b, r) 351 } 352 } 353} 354 355func TestParseRange(t *testing.T) { 356 type tv struct { 357 v string 358 b bool 359 } 360 tests := []struct { 361 i string 362 t []tv 363 }{ 364 // Simple expressions 365 {">1.2.3", []tv{ 366 {"1.2.2", false}, 367 {"1.2.3", false}, 368 {"1.2.4", true}, 369 }}, 370 {">=1.2.3", []tv{ 371 {"1.2.3", true}, 372 {"1.2.4", true}, 373 {"1.2.2", false}, 374 }}, 375 {"<1.2.3", []tv{ 376 {"1.2.2", true}, 377 {"1.2.3", false}, 378 {"1.2.4", false}, 379 }}, 380 {"<=1.2.3", []tv{ 381 {"1.2.2", true}, 382 {"1.2.3", true}, 383 {"1.2.4", false}, 384 }}, 385 {"1.2.3", []tv{ 386 {"1.2.2", false}, 387 {"1.2.3", true}, 388 {"1.2.4", false}, 389 }}, 390 {"=1.2.3", []tv{ 391 {"1.2.2", false}, 392 {"1.2.3", true}, 393 {"1.2.4", false}, 394 }}, 395 {"==1.2.3", []tv{ 396 {"1.2.2", false}, 397 {"1.2.3", true}, 398 {"1.2.4", false}, 399 }}, 400 {"!=1.2.3", []tv{ 401 {"1.2.2", true}, 402 {"1.2.3", false}, 403 {"1.2.4", true}, 404 }}, 405 {"!1.2.3", []tv{ 406 {"1.2.2", true}, 407 {"1.2.3", false}, 408 {"1.2.4", true}, 409 }}, 410 // Simple Expression errors 411 {">>1.2.3", nil}, 412 {"!1.2.3", nil}, 413 {"1.0", nil}, 414 {"string", nil}, 415 {"", nil}, 416 {"fo.ob.ar.x", nil}, 417 // AND Expressions 418 {">1.2.2 <1.2.4", []tv{ 419 {"1.2.2", false}, 420 {"1.2.3", true}, 421 {"1.2.4", false}, 422 }}, 423 {"<1.2.2 <1.2.4", []tv{ 424 {"1.2.1", true}, 425 {"1.2.2", false}, 426 {"1.2.3", false}, 427 {"1.2.4", false}, 428 }}, 429 {">1.2.2 <1.2.5 !=1.2.4", []tv{ 430 {"1.2.2", false}, 431 {"1.2.3", true}, 432 {"1.2.4", false}, 433 {"1.2.5", false}, 434 }}, 435 {">1.2.2 <1.2.5 !1.2.4", []tv{ 436 {"1.2.2", false}, 437 {"1.2.3", true}, 438 {"1.2.4", false}, 439 {"1.2.5", false}, 440 }}, 441 // OR Expressions 442 {">1.2.2 || <1.2.4", []tv{ 443 {"1.2.2", true}, 444 {"1.2.3", true}, 445 {"1.2.4", true}, 446 }}, 447 {"<1.2.2 || >1.2.4", []tv{ 448 {"1.2.2", false}, 449 {"1.2.3", false}, 450 {"1.2.4", false}, 451 }}, 452 // Wildcard expressions 453 {">1.x", []tv{ 454 {"0.1.9", false}, 455 {"1.2.6", false}, 456 {"1.9.0", false}, 457 {"2.0.0", true}, 458 }}, 459 {">1.2.x", []tv{ 460 {"1.1.9", false}, 461 {"1.2.6", false}, 462 {"1.3.0", true}, 463 }}, 464 // Combined Expressions 465 {">1.2.2 <1.2.4 || >=2.0.0", []tv{ 466 {"1.2.2", false}, 467 {"1.2.3", true}, 468 {"1.2.4", false}, 469 {"2.0.0", true}, 470 {"2.0.1", true}, 471 }}, 472 {"1.x || >=2.0.x <2.2.x", []tv{ 473 {"0.9.2", false}, 474 {"1.2.2", true}, 475 {"2.0.0", true}, 476 {"2.1.8", true}, 477 {"2.2.0", false}, 478 }}, 479 {">1.2.2 <1.2.4 || >=2.0.0 <3.0.0", []tv{ 480 {"1.2.2", false}, 481 {"1.2.3", true}, 482 {"1.2.4", false}, 483 {"2.0.0", true}, 484 {"2.0.1", true}, 485 {"2.9.9", true}, 486 {"3.0.0", false}, 487 }}, 488 } 489 490 for _, tc := range tests { 491 r, err := ParseRange(tc.i) 492 if err != nil && tc.t != nil { 493 t.Errorf("Error parsing range %q: %s", tc.i, err) 494 continue 495 } 496 for _, tvc := range tc.t { 497 v := MustParse(tvc.v) 498 if res := r(v); res != tvc.b { 499 t.Errorf("Invalid for case %q matching %q: Expected %t, got: %t", tc.i, tvc.v, tvc.b, res) 500 } 501 } 502 503 } 504} 505 506func TestMustParseRange(t *testing.T) { 507 testCase := ">1.2.2 <1.2.4 || >=2.0.0 <3.0.0" 508 r := MustParseRange(testCase) 509 if !r(MustParse("1.2.3")) { 510 t.Errorf("Unexpected range behavior on MustParseRange") 511 } 512} 513 514func TestMustParseRange_panic(t *testing.T) { 515 defer func() { 516 if recover() == nil { 517 t.Errorf("Should have panicked") 518 } 519 }() 520 _ = MustParseRange("invalid version") 521} 522 523func BenchmarkRangeParseSimple(b *testing.B) { 524 const VERSION = ">1.0.0" 525 b.ReportAllocs() 526 b.ResetTimer() 527 for n := 0; n < b.N; n++ { 528 _, _ = ParseRange(VERSION) 529 } 530} 531 532func BenchmarkRangeParseAverage(b *testing.B) { 533 const VERSION = ">=1.0.0 <2.0.0" 534 b.ReportAllocs() 535 b.ResetTimer() 536 for n := 0; n < b.N; n++ { 537 _, _ = ParseRange(VERSION) 538 } 539} 540 541func BenchmarkRangeParseComplex(b *testing.B) { 542 const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0" 543 b.ReportAllocs() 544 b.ResetTimer() 545 for n := 0; n < b.N; n++ { 546 _, _ = ParseRange(VERSION) 547 } 548} 549 550func BenchmarkRangeMatchSimple(b *testing.B) { 551 const VERSION = ">1.0.0" 552 r, _ := ParseRange(VERSION) 553 v := MustParse("2.0.0") 554 b.ReportAllocs() 555 b.ResetTimer() 556 for n := 0; n < b.N; n++ { 557 r(v) 558 } 559} 560 561func BenchmarkRangeMatchAverage(b *testing.B) { 562 const VERSION = ">=1.0.0 <2.0.0" 563 r, _ := ParseRange(VERSION) 564 v := MustParse("1.2.3") 565 b.ReportAllocs() 566 b.ResetTimer() 567 for n := 0; n < b.N; n++ { 568 r(v) 569 } 570} 571 572func BenchmarkRangeMatchComplex(b *testing.B) { 573 const VERSION = ">=1.0.0 <2.0.0 || >=3.0.1 <4.0.0 !=3.0.3 || >=5.0.0" 574 r, _ := ParseRange(VERSION) 575 v := MustParse("5.0.1") 576 b.ReportAllocs() 577 b.ResetTimer() 578 for n := 0; n < b.N; n++ { 579 r(v) 580 } 581} 582