1 package semver
2
3 import (
4 "reflect"
5 "strings"
6 "testing"
7 )
8
9 type wildcardTypeTest struct {
10 input string
11 wildcardType wildcardType
12 }
13
14 type comparatorTest struct {
15 input string
16 comparator func(comparator) bool
17 }
18
19 func 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
47 var (
48 v1 = MustParse("1.2.2")
49 v2 = MustParse("1.2.3")
50 v3 = MustParse("1.2.4")
51 )
52
53 func testEQ(f comparator) bool {
54 return f(v1, v1) && !f(v1, v2)
55 }
56
57 func testNE(f comparator) bool {
58 return !f(v1, v1) && f(v1, v2)
59 }
60
61 func testGT(f comparator) bool {
62 return f(v2, v1) && f(v3, v2) && !f(v1, v2) && !f(v1, v1)
63 }
64
65 func testGE(f comparator) bool {
66 return f(v2, v1) && f(v3, v2) && !f(v1, v2)
67 }
68
69 func testLT(f comparator) bool {
70 return f(v1, v2) && f(v2, v3) && !f(v2, v1) && !f(v1, v1)
71 }
72
73 func testLE(f comparator) bool {
74 return f(v1, v2) && f(v2, v3) && !f(v2, v1)
75 }
76
77 func 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"}},
84 {" >= 1.2.3 <= 1.2.3 ", []string{">=1.2.3", "<=1.2.3"}},
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
97 func 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
127 func 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, ""},
144 {"=", "invalid", nil, ""},
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
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
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
172 func 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
200 func 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
216 func 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
233 func 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
251 func 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
269 func 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
297 func 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
308 func 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
330 func 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
355 func 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
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
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
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
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
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
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
506 func 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
514 func 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
523 func 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
532 func 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
541 func 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
550 func 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
561 func 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
572 func 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
View as plain text