1package semver
2
3import (
4	"testing"
5)
6
7func prstr(s string) PRVersion {
8	return PRVersion{s, 0, false}
9}
10
11func prnum(i uint64) PRVersion {
12	return PRVersion{"", i, true}
13}
14
15type formatTest struct {
16	v      Version
17	result string
18}
19
20var formatTests = []formatTest{
21	{Version{1, 2, 3, nil, nil}, "1.2.3"},
22	{Version{0, 0, 1, nil, nil}, "0.0.1"},
23	{Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1-alpha.preview+123.456"},
24	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3-alpha.1+123.456"},
25	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3-alpha.1"},
26	{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3+123.456"},
27	// Prereleases and build metadata hyphens
28	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3-alpha.b-eta+123.b-uild"},
29	{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3+123.b-uild"},
30	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3-alpha.b-eta"},
31}
32
33var tolerantFormatTests = []formatTest{
34	{Version{1, 2, 3, nil, nil}, "v1.2.3"},
35	{Version{1, 2, 0, []PRVersion{prstr("alpha")}, nil}, "1.2.0-alpha"},
36	{Version{1, 2, 0, nil, nil}, "1.2.00"},
37	{Version{1, 2, 3, nil, nil}, "	1.2.3 "},
38	{Version{1, 2, 3, nil, nil}, "01.02.03"},
39	{Version{0, 0, 3, nil, nil}, "00.0.03"},
40	{Version{0, 0, 3, nil, nil}, "000.0.03"},
41	{Version{1, 2, 0, nil, nil}, "1.2"},
42	{Version{1, 0, 0, nil, nil}, "1"},
43}
44
45func TestStringer(t *testing.T) {
46	for _, test := range formatTests {
47		if res := test.v.String(); res != test.result {
48			t.Errorf("Stringer, expected %q but got %q", test.result, res)
49		}
50	}
51}
52
53func TestParse(t *testing.T) {
54	for _, test := range formatTests {
55		if v, err := Parse(test.result); err != nil {
56			t.Errorf("Error parsing %q: %q", test.result, err)
57		} else if comp := v.Compare(test.v); comp != 0 {
58			t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
59		} else if err := v.Validate(); err != nil {
60			t.Errorf("Error validating parsed version %q: %q", test.v, err)
61		}
62	}
63}
64
65func TestParseTolerant(t *testing.T) {
66	for _, test := range tolerantFormatTests {
67		if v, err := ParseTolerant(test.result); err != nil {
68			t.Errorf("Error parsing %q: %q", test.result, err)
69		} else if comp := v.Compare(test.v); comp != 0 {
70			t.Errorf("Parsing, expected %q but got %q, comp: %d ", test.v, v, comp)
71		} else if err := v.Validate(); err != nil {
72			t.Errorf("Error validating parsed version %q: %q", test.v, err)
73		}
74	}
75}
76
77func TestMustParse(t *testing.T) {
78	_ = MustParse("32.2.1-alpha")
79}
80
81func TestMustParse_panic(t *testing.T) {
82	defer func() {
83		if recover() == nil {
84			t.Errorf("Should have panicked")
85		}
86	}()
87	_ = MustParse("invalid version")
88}
89
90func TestValidate(t *testing.T) {
91	for _, test := range formatTests {
92		if err := test.v.Validate(); err != nil {
93			t.Errorf("Error validating %q: %q", test.v, err)
94		}
95	}
96}
97
98var finalizeVersionMethod = []formatTest{
99	{Version{1, 2, 3, nil, nil}, "1.2.3"},
100	{Version{0, 0, 1, nil, nil}, "0.0.1"},
101	{Version{0, 0, 1, []PRVersion{prstr("alpha"), prstr("preview")}, []string{"123", "456"}}, "0.0.1"},
102	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, []string{"123", "456"}}, "1.2.3"},
103	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prnum(1)}, nil}, "1.2.3"},
104	{Version{1, 2, 3, nil, []string{"123", "456"}}, "1.2.3"},
105	// Prereleases and build metadata hyphens
106	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, []string{"123", "b-uild"}}, "1.2.3"},
107	{Version{1, 2, 3, nil, []string{"123", "b-uild"}}, "1.2.3"},
108	{Version{1, 2, 3, []PRVersion{prstr("alpha"), prstr("b-eta")}, nil}, "1.2.3"},
109}
110
111func TestFinalizeVersionMethod(t *testing.T) {
112	for _, test := range finalizeVersionMethod {
113		out := test.v.FinalizeVersion()
114		if out != test.result {
115			t.Errorf("Finalized version error, expected %q but got %q", test.result, out)
116		}
117	}
118}
119
120type compareTest struct {
121	v1     Version
122	v2     Version
123	result int
124}
125
126var compareTests = []compareTest{
127	{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 0},
128	{Version{2, 0, 0, nil, nil}, Version{1, 0, 0, nil, nil}, 1},
129	{Version{0, 1, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 0},
130	{Version{0, 2, 0, nil, nil}, Version{0, 1, 0, nil, nil}, 1},
131	{Version{0, 0, 1, nil, nil}, Version{0, 0, 1, nil, nil}, 0},
132	{Version{0, 0, 2, nil, nil}, Version{0, 0, 1, nil, nil}, 1},
133	{Version{1, 2, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 0},
134	{Version{2, 2, 4, nil, nil}, Version{1, 2, 4, nil, nil}, 1},
135	{Version{1, 3, 3, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
136	{Version{1, 2, 4, nil, nil}, Version{1, 2, 3, nil, nil}, 1},
137
138	// Spec Examples #11
139	{Version{1, 0, 0, nil, nil}, Version{2, 0, 0, nil, nil}, -1},
140	{Version{2, 0, 0, nil, nil}, Version{2, 1, 0, nil, nil}, -1},
141	{Version{2, 1, 0, nil, nil}, Version{2, 1, 1, nil, nil}, -1},
142
143	// Spec Examples #9
144	{Version{1, 0, 0, nil, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, 1},
145	{Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, -1},
146	{Version{1, 0, 0, []PRVersion{prstr("alpha"), prnum(1)}, nil}, Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, -1},
147	{Version{1, 0, 0, []PRVersion{prstr("alpha"), prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, -1},
148	{Version{1, 0, 0, []PRVersion{prstr("beta")}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, -1},
149	{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(2)}, nil}, Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, -1},
150	{Version{1, 0, 0, []PRVersion{prstr("beta"), prnum(11)}, nil}, Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, -1},
151	{Version{1, 0, 0, []PRVersion{prstr("rc"), prnum(1)}, nil}, Version{1, 0, 0, nil, nil}, -1},
152
153	// Ignore Build metadata
154	{Version{1, 0, 0, nil, []string{"1", "2", "3"}}, Version{1, 0, 0, nil, nil}, 0},
155}
156
157func TestCompare(t *testing.T) {
158	for _, test := range compareTests {
159		if res := test.v1.Compare(test.v2); res != test.result {
160			t.Errorf("Comparing %q : %q, expected %d but got %d", test.v1, test.v2, test.result, res)
161		}
162		// Test counterpart
163		if res := test.v2.Compare(test.v1); res != -test.result {
164			t.Errorf("Comparing %q : %q, expected %d but got %d", test.v2, test.v1, -test.result, res)
165		}
166	}
167}
168
169type wrongformatTest struct {
170	v   *Version
171	str string
172}
173
174var wrongformatTests = []wrongformatTest{
175	{nil, ""},
176	{nil, "."},
177	{nil, "1."},
178	{nil, ".1"},
179	{nil, "a.b.c"},
180	{nil, "1.a.b"},
181	{nil, "1.1.a"},
182	{nil, "1.a.1"},
183	{nil, "a.1.1"},
184	{nil, ".."},
185	{nil, "1.."},
186	{nil, "1.1."},
187	{nil, "1..1"},
188	{nil, "1.1.+123"},
189	{nil, "1.1.-beta"},
190	{nil, "-1.1.1"},
191	{nil, "1.-1.1"},
192	{nil, "1.1.-1"},
193	// giant numbers
194	{nil, "20000000000000000000.1.1"},
195	{nil, "1.20000000000000000000.1"},
196	{nil, "1.1.20000000000000000000"},
197	{nil, "1.1.1-20000000000000000000"},
198	// Leading zeroes
199	{nil, "01.1.1"},
200	{nil, "001.1.1"},
201	{nil, "1.01.1"},
202	{nil, "1.001.1"},
203	{nil, "1.1.01"},
204	{nil, "1.1.001"},
205	{nil, "1.1.1-01"},
206	{nil, "1.1.1-001"},
207	{nil, "1.1.1-beta.01"},
208	{nil, "1.1.1-beta.001"},
209	{&Version{0, 0, 0, []PRVersion{prstr("!")}, nil}, "0.0.0-!"},
210	{&Version{0, 0, 0, nil, []string{"!"}}, "0.0.0+!"},
211	// empty prversion
212	{&Version{0, 0, 0, []PRVersion{prstr(""), prstr("alpha")}, nil}, "0.0.0-.alpha"},
213	// empty build meta data
214	{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{""}}, "0.0.0-alpha+"},
215	{&Version{0, 0, 0, []PRVersion{prstr("alpha")}, []string{"test", ""}}, "0.0.0-alpha+test."},
216}
217
218func TestWrongFormat(t *testing.T) {
219	for _, test := range wrongformatTests {
220
221		if res, err := Parse(test.str); err == nil {
222			t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
223		}
224
225		if test.v != nil {
226			if err := test.v.Validate(); err == nil {
227				t.Errorf("Validating wrong format version %q (%q), expected error", test.v, test.str)
228			}
229		}
230	}
231}
232
233var wrongTolerantFormatTests = []wrongformatTest{
234	{nil, "1.0+abc"},
235	{nil, "1.0-rc.1"},
236}
237
238func TestWrongTolerantFormat(t *testing.T) {
239	for _, test := range wrongTolerantFormatTests {
240		if res, err := ParseTolerant(test.str); err == nil {
241			t.Errorf("Parsing wrong format version %q, expected error but got %q", test.str, res)
242		}
243	}
244}
245
246func TestCompareHelper(t *testing.T) {
247	v := Version{1, 0, 0, []PRVersion{prstr("alpha")}, nil}
248	v1 := Version{1, 0, 0, nil, nil}
249	if !v.EQ(v) {
250		t.Errorf("%q should be equal to %q", v, v)
251	}
252	if !v.Equals(v) {
253		t.Errorf("%q should be equal to %q", v, v)
254	}
255	if !v1.NE(v) {
256		t.Errorf("%q should not be equal to %q", v1, v)
257	}
258	if !v.GTE(v) {
259		t.Errorf("%q should be greater than or equal to %q", v, v)
260	}
261	if !v.LTE(v) {
262		t.Errorf("%q should be less than or equal to %q", v, v)
263	}
264	if !v.LT(v1) {
265		t.Errorf("%q should be less than %q", v, v1)
266	}
267	if !v.LTE(v1) {
268		t.Errorf("%q should be less than or equal %q", v, v1)
269	}
270	if !v.LE(v1) {
271		t.Errorf("%q should be less than or equal %q", v, v1)
272	}
273	if !v1.GT(v) {
274		t.Errorf("%q should be greater than %q", v1, v)
275	}
276	if !v1.GTE(v) {
277		t.Errorf("%q should be greater than or equal %q", v1, v)
278	}
279	if !v1.GE(v) {
280		t.Errorf("%q should be greater than or equal %q", v1, v)
281	}
282}
283
284const (
285	MAJOR = iota
286	MINOR
287	PATCH
288)
289
290type incrementTest struct {
291	version         Version
292	incrementType   int
293	expectingError  bool
294	expectedVersion Version
295}
296
297var incrementTests = []incrementTest{
298	{Version{1, 2, 3, nil, nil}, PATCH, false, Version{1, 2, 4, nil, nil}},
299	{Version{1, 2, 3, nil, nil}, MINOR, false, Version{1, 3, 0, nil, nil}},
300	{Version{1, 2, 3, nil, nil}, MAJOR, false, Version{2, 0, 0, nil, nil}},
301	{Version{0, 1, 2, nil, nil}, PATCH, false, Version{0, 1, 3, nil, nil}},
302	{Version{0, 1, 2, nil, nil}, MINOR, false, Version{0, 2, 0, nil, nil}},
303	{Version{0, 1, 2, nil, nil}, MAJOR, false, Version{1, 0, 0, nil, nil}},
304}
305
306func TestIncrements(t *testing.T) {
307	for _, test := range incrementTests {
308		var originalVersion = Version{
309			test.version.Major,
310			test.version.Minor,
311			test.version.Patch,
312			test.version.Pre,
313			test.version.Build,
314		}
315		var err error
316		switch test.incrementType {
317		case PATCH:
318			err = test.version.IncrementPatch()
319		case MINOR:
320			err = test.version.IncrementMinor()
321		case MAJOR:
322			err = test.version.IncrementMajor()
323		}
324		if test.expectingError {
325			if err != nil {
326				t.Errorf("Increment version, expecting %q, got error %q", test.expectedVersion, err)
327			}
328			if test.version.EQ(originalVersion) {
329				t.Errorf("Increment version, expecting %q, got %q", test.expectedVersion, test.version)
330			}
331		} else {
332			if (err != nil) && !test.expectingError {
333				t.Errorf("Increment version %q, not expecting error, got %q", test.version, err)
334			}
335			if test.version.NE(test.expectedVersion) {
336				t.Errorf("Increment version, expecting %q, got %q", test.expectedVersion, test.version)
337			}
338		}
339	}
340}
341
342func TestPreReleaseVersions(t *testing.T) {
343	p1, err := NewPRVersion("123")
344	if !p1.IsNumeric() {
345		t.Errorf("Expected numeric prversion, got %q", p1)
346	}
347	if p1.VersionNum != 123 {
348		t.Error("Wrong prversion number")
349	}
350	if err != nil {
351		t.Errorf("Not expected error %q", err)
352	}
353	p2, err := NewPRVersion("alpha")
354	if p2.IsNumeric() {
355		t.Errorf("Expected non-numeric prversion, got %q", p2)
356	}
357	if p2.VersionStr != "alpha" {
358		t.Error("Wrong prversion string")
359	}
360	if err != nil {
361		t.Errorf("Not expected error %q", err)
362	}
363}
364
365func TestBuildMetaDataVersions(t *testing.T) {
366	_, err := NewBuildVersion("123")
367	if err != nil {
368		t.Errorf("Unexpected error %q", err)
369	}
370
371	_, err = NewBuildVersion("build")
372	if err != nil {
373		t.Errorf("Unexpected error %q", err)
374	}
375
376	_, err = NewBuildVersion("test?")
377	if err == nil {
378		t.Error("Expected error, got none")
379	}
380
381	_, err = NewBuildVersion("")
382	if err == nil {
383		t.Error("Expected error, got none")
384	}
385}
386
387func TestNewHelper(t *testing.T) {
388	v, err := New("1.2.3")
389	if err != nil {
390		t.Fatalf("Unexpected error %q", err)
391	}
392
393	// New returns pointer
394	if v == nil {
395		t.Fatal("Version is nil")
396	}
397	if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
398		t.Fatal("Unexpected comparison problem")
399	}
400}
401
402func TestMakeHelper(t *testing.T) {
403	v, err := Make("1.2.3")
404	if err != nil {
405		t.Fatalf("Unexpected error %q", err)
406	}
407	if v.Compare(Version{1, 2, 3, nil, nil}) != 0 {
408		t.Fatal("Unexpected comparison problem")
409	}
410}
411
412type finalizeTest struct {
413	input  string
414	output string
415}
416
417var finalizeTests = []finalizeTest{
418	{"", ""},
419	{"1.2.3", "1.2.3"},
420	{"0.0.1", "0.0.1"},
421	{"0.0.1-alpha.preview+123.456", "0.0.1"},
422	{"1.2.3-alpha.1+123.456", "1.2.3"},
423	{"1.2.3-alpha.1", "1.2.3"},
424	{"1.2.3+123.456", "1.2.3"},
425	{"1.2.3-alpha.b-eta+123.b-uild", "1.2.3"},
426	{"1.2.3+123.b-uild", "1.2.3"},
427	{"1.2.3-alpha.b-eta", "1.2.3"},
428	{"1.2-alpha", ""},
429}
430
431func TestFinalizeVersion(t *testing.T) {
432	for _, test := range finalizeTests {
433		finalVer, err := FinalizeVersion(test.input)
434		if finalVer == "" {
435			if err == nil {
436				t.Errorf("Finalize Version error, expected error but got nil")
437			}
438		} else if finalVer != test.output && err != nil {
439			t.Errorf("Finalize Version error expected %q but got %q", test.output, finalVer)
440		}
441	}
442}
443
444func BenchmarkParseSimple(b *testing.B) {
445	const VERSION = "0.0.1"
446	b.ReportAllocs()
447	b.ResetTimer()
448	for n := 0; n < b.N; n++ {
449		_, _ = Parse(VERSION)
450	}
451}
452
453func BenchmarkParseComplex(b *testing.B) {
454	const VERSION = "0.0.1-alpha.preview+123.456"
455	b.ReportAllocs()
456	b.ResetTimer()
457	for n := 0; n < b.N; n++ {
458		_, _ = Parse(VERSION)
459	}
460}
461
462func BenchmarkParseAverage(b *testing.B) {
463	l := len(formatTests)
464	b.ReportAllocs()
465	b.ResetTimer()
466	for n := 0; n < b.N; n++ {
467		_, _ = Parse(formatTests[n%l].result)
468	}
469}
470
471func BenchmarkParseTolerantAverage(b *testing.B) {
472	l := len(tolerantFormatTests)
473	b.ReportAllocs()
474	b.ResetTimer()
475	for n := 0; n < b.N; n++ {
476		_, _ = ParseTolerant(tolerantFormatTests[n%l].result)
477	}
478}
479
480func BenchmarkStringSimple(b *testing.B) {
481	const VERSION = "0.0.1"
482	v, _ := Parse(VERSION)
483	b.ReportAllocs()
484	b.ResetTimer()
485	for n := 0; n < b.N; n++ {
486		_ = v.String()
487	}
488}
489
490func BenchmarkStringLarger(b *testing.B) {
491	const VERSION = "11.15.2012"
492	v, _ := Parse(VERSION)
493	b.ReportAllocs()
494	b.ResetTimer()
495	for n := 0; n < b.N; n++ {
496		_ = v.String()
497	}
498}
499
500func BenchmarkStringComplex(b *testing.B) {
501	const VERSION = "0.0.1-alpha.preview+123.456"
502	v, _ := Parse(VERSION)
503	b.ReportAllocs()
504	b.ResetTimer()
505	for n := 0; n < b.N; n++ {
506		_ = v.String()
507	}
508}
509
510func BenchmarkStringAverage(b *testing.B) {
511	l := len(formatTests)
512	b.ReportAllocs()
513	b.ResetTimer()
514	for n := 0; n < b.N; n++ {
515		_ = formatTests[n%l].v.String()
516	}
517}
518
519func BenchmarkValidateSimple(b *testing.B) {
520	const VERSION = "0.0.1"
521	v, _ := Parse(VERSION)
522	b.ReportAllocs()
523	b.ResetTimer()
524	for n := 0; n < b.N; n++ {
525		_ = v.Validate()
526	}
527}
528
529func BenchmarkValidateComplex(b *testing.B) {
530	const VERSION = "0.0.1-alpha.preview+123.456"
531	v, _ := Parse(VERSION)
532	b.ReportAllocs()
533	b.ResetTimer()
534	for n := 0; n < b.N; n++ {
535		_ = v.Validate()
536	}
537}
538
539func BenchmarkValidateAverage(b *testing.B) {
540	l := len(formatTests)
541	b.ReportAllocs()
542	b.ResetTimer()
543	for n := 0; n < b.N; n++ {
544		_ = formatTests[n%l].v.Validate()
545	}
546}
547
548func BenchmarkCompareSimple(b *testing.B) {
549	const VERSION = "0.0.1"
550	v, _ := Parse(VERSION)
551	b.ReportAllocs()
552	b.ResetTimer()
553	for n := 0; n < b.N; n++ {
554		v.Compare(v)
555	}
556}
557
558func BenchmarkCompareComplex(b *testing.B) {
559	const VERSION = "0.0.1-alpha.preview+123.456"
560	v, _ := Parse(VERSION)
561	b.ReportAllocs()
562	b.ResetTimer()
563	for n := 0; n < b.N; n++ {
564		v.Compare(v)
565	}
566}
567
568func BenchmarkCompareAverage(b *testing.B) {
569	l := len(compareTests)
570	b.ReportAllocs()
571	b.ResetTimer()
572	for n := 0; n < b.N; n++ {
573		compareTests[n%l].v1.Compare((compareTests[n%l].v2))
574	}
575}
576