1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package version
18
19import (
20	"fmt"
21	"reflect"
22	"testing"
23)
24
25type testItem struct {
26	version    string
27	unparsed   string
28	equalsPrev bool
29}
30
31func testOne(v *Version, item, prev testItem) error {
32	str := v.String()
33	if item.unparsed == "" {
34		if str != item.version {
35			return fmt.Errorf("bad round-trip: %q -> %q", item.version, str)
36		}
37	} else {
38		if str != item.unparsed {
39			return fmt.Errorf("bad unparse: %q -> %q, expected %q", item.version, str, item.unparsed)
40		}
41	}
42
43	if prev.version != "" {
44		cmp, err := v.Compare(prev.version)
45		if err != nil {
46			return fmt.Errorf("unexpected parse error: %v", err)
47		}
48		rv, err := parse(prev.version, v.semver)
49		if err != nil {
50			return fmt.Errorf("unexpected parse error: %v", err)
51		}
52		rcmp, err := rv.Compare(item.version)
53		if err != nil {
54			return fmt.Errorf("unexpected parse error: %v", err)
55		}
56
57		switch {
58		case cmp == -1:
59			return fmt.Errorf("unexpected ordering %q < %q", item.version, prev.version)
60		case cmp == 0 && !item.equalsPrev:
61			return fmt.Errorf("unexpected comparison %q == %q", item.version, prev.version)
62		case cmp == 1 && item.equalsPrev:
63			return fmt.Errorf("unexpected comparison %q != %q", item.version, prev.version)
64		case cmp != -rcmp:
65			return fmt.Errorf("unexpected reverse comparison %q <=> %q %v %v %v %v", item.version, prev.version, cmp, rcmp, v.Components(), rv.Components())
66		}
67	}
68
69	return nil
70}
71
72func TestSemanticVersions(t *testing.T) {
73	tests := []testItem{
74		// This is every version string that appears in the 2.0 semver spec,
75		// sorted in strictly increasing order except as noted.
76		{version: "0.1.0"},
77		{version: "1.0.0-0.3.7"},
78		{version: "1.0.0-alpha"},
79		{version: "1.0.0-alpha+001", equalsPrev: true},
80		{version: "1.0.0-alpha.1"},
81		{version: "1.0.0-alpha.beta"},
82		{version: "1.0.0-beta"},
83		{version: "1.0.0-beta+exp.sha.5114f85", equalsPrev: true},
84		{version: "1.0.0-beta.2"},
85		{version: "1.0.0-beta.11"},
86		{version: "1.0.0-rc.1"},
87		{version: "1.0.0-x.7.z.92"},
88		{version: "1.0.0"},
89		{version: "1.0.0+20130313144700", equalsPrev: true},
90		{version: "1.8.0-alpha.3"},
91		{version: "1.8.0-alpha.3.673+73326ef01d2d7c"},
92		{version: "1.9.0"},
93		{version: "1.10.0"},
94		{version: "1.11.0"},
95		{version: "2.0.0"},
96		{version: "2.1.0"},
97		{version: "2.1.1"},
98		{version: "42.0.0"},
99
100		// We also allow whitespace and "v" prefix
101		{version: "   42.0.0", unparsed: "42.0.0", equalsPrev: true},
102		{version: "\t42.0.0  ", unparsed: "42.0.0", equalsPrev: true},
103		{version: "43.0.0-1", unparsed: "43.0.0-1"},
104		{version: "43.0.0-1  ", unparsed: "43.0.0-1", equalsPrev: true},
105		{version: "v43.0.0-1", unparsed: "43.0.0-1", equalsPrev: true},
106		{version: "  v43.0.0", unparsed: "43.0.0"},
107		{version: " 43.0.0 ", unparsed: "43.0.0", equalsPrev: true},
108	}
109
110	var prev testItem
111	for _, item := range tests {
112		v, err := ParseSemantic(item.version)
113		if err != nil {
114			t.Errorf("unexpected parse error: %v", err)
115			continue
116		}
117		err = testOne(v, item, prev)
118		if err != nil {
119			t.Errorf("%v", err)
120		}
121		prev = item
122	}
123}
124
125func TestBadSemanticVersions(t *testing.T) {
126	tests := []string{
127		// "MUST take the form X.Y.Z"
128		"1",
129		"1.2",
130		"1.2.3.4",
131		".2.3",
132		"1..3",
133		"1.2.",
134		"",
135		"..",
136		// "where X, Y, and Z are non-negative integers"
137		"-1.2.3",
138		"1.-2.3",
139		"1.2.-3",
140		"1a.2.3",
141		"1.2a.3",
142		"1.2.3a",
143		"a1.2.3",
144		"a.b.c",
145		"1 .2.3",
146		"1. 2.3",
147		// "and MUST NOT contain leading zeroes."
148		"01.2.3",
149		"1.02.3",
150		"1.2.03",
151		// "[pre-release] identifiers MUST comprise only ASCII alphanumerics and hyphen"
152		"1.2.3-/",
153		// "[pre-release] identifiers MUST NOT be empty"
154		"1.2.3-",
155		"1.2.3-.",
156		"1.2.3-foo.",
157		"1.2.3-.foo",
158		// "Numeric [pre-release] identifiers MUST NOT include leading zeroes"
159		"1.2.3-01",
160		// "[build metadata] identifiers MUST comprise only ASCII alphanumerics and hyphen"
161		"1.2.3+/",
162		// "[build metadata] identifiers MUST NOT be empty"
163		"1.2.3+",
164		"1.2.3+.",
165		"1.2.3+foo.",
166		"1.2.3+.foo",
167
168		// whitespace/"v"-prefix checks
169		"v 1.2.3",
170		"vv1.2.3",
171	}
172
173	for i := range tests {
174		_, err := ParseSemantic(tests[i])
175		if err == nil {
176			t.Errorf("unexpected success parsing invalid semver %q", tests[i])
177		}
178	}
179}
180
181func TestGenericVersions(t *testing.T) {
182	tests := []testItem{
183		// This is all of the strings from TestSemanticVersions, plus some strings
184		// from TestBadSemanticVersions that should parse as generic versions,
185		// plus some additional strings.
186		{version: "0.1.0", unparsed: "0.1.0"},
187		{version: "1.0.0-0.3.7", unparsed: "1.0.0"},
188		{version: "1.0.0-alpha", unparsed: "1.0.0", equalsPrev: true},
189		{version: "1.0.0-alpha+001", unparsed: "1.0.0", equalsPrev: true},
190		{version: "1.0.0-alpha.1", unparsed: "1.0.0", equalsPrev: true},
191		{version: "1.0.0-alpha.beta", unparsed: "1.0.0", equalsPrev: true},
192		{version: "1.0.0.beta", unparsed: "1.0.0", equalsPrev: true},
193		{version: "1.0.0-beta+exp.sha.5114f85", unparsed: "1.0.0", equalsPrev: true},
194		{version: "1.0.0.beta.2", unparsed: "1.0.0", equalsPrev: true},
195		{version: "1.0.0.beta.11", unparsed: "1.0.0", equalsPrev: true},
196		{version: "1.0.0.rc.1", unparsed: "1.0.0", equalsPrev: true},
197		{version: "1.0.0-x.7.z.92", unparsed: "1.0.0", equalsPrev: true},
198		{version: "1.0.0", unparsed: "1.0.0", equalsPrev: true},
199		{version: "1.0.0+20130313144700", unparsed: "1.0.0", equalsPrev: true},
200		{version: "1.2", unparsed: "1.2"},
201		{version: "1.2a.3", unparsed: "1.2", equalsPrev: true},
202		{version: "1.2.3", unparsed: "1.2.3"},
203		{version: "1.2.3.0", unparsed: "1.2.3.0", equalsPrev: true},
204		{version: "1.2.3a", unparsed: "1.2.3", equalsPrev: true},
205		{version: "1.2.3-foo.", unparsed: "1.2.3", equalsPrev: true},
206		{version: "1.2.3-.foo", unparsed: "1.2.3", equalsPrev: true},
207		{version: "1.2.3-01", unparsed: "1.2.3", equalsPrev: true},
208		{version: "1.2.3+", unparsed: "1.2.3", equalsPrev: true},
209		{version: "1.2.3+foo.", unparsed: "1.2.3", equalsPrev: true},
210		{version: "1.2.3+.foo", unparsed: "1.2.3", equalsPrev: true},
211		{version: "1.02.3", unparsed: "1.2.3", equalsPrev: true},
212		{version: "1.2.03", unparsed: "1.2.3", equalsPrev: true},
213		{version: "1.2.003", unparsed: "1.2.3", equalsPrev: true},
214		{version: "1.2.3.4", unparsed: "1.2.3.4"},
215		{version: "1.2.3.4b3", unparsed: "1.2.3.4", equalsPrev: true},
216		{version: "1.2.3.4.5", unparsed: "1.2.3.4.5"},
217		{version: "1.9.0", unparsed: "1.9.0"},
218		{version: "1.9.0.0.0.0.0.0", unparsed: "1.9.0.0.0.0.0.0", equalsPrev: true},
219		{version: "1.10.0", unparsed: "1.10.0"},
220		{version: "1.11.0", unparsed: "1.11.0"},
221		{version: "1.11.0.0.5", unparsed: "1.11.0.0.5"},
222		{version: "2.0.0", unparsed: "2.0.0"},
223		{version: "2.1.0", unparsed: "2.1.0"},
224		{version: "2.1.1", unparsed: "2.1.1"},
225		{version: "42.0.0", unparsed: "42.0.0"},
226		{version: "   42.0.0", unparsed: "42.0.0", equalsPrev: true},
227		{version: "\t42.0.0  ", unparsed: "42.0.0", equalsPrev: true},
228		{version: "42.0.0-1", unparsed: "42.0.0", equalsPrev: true},
229		{version: "42.0.0-1  ", unparsed: "42.0.0", equalsPrev: true},
230		{version: "v42.0.0-1", unparsed: "42.0.0", equalsPrev: true},
231		{version: "  v43.0.0", unparsed: "43.0.0"},
232		{version: " 43.0.0 ", unparsed: "43.0.0", equalsPrev: true},
233	}
234
235	var prev testItem
236	for _, item := range tests {
237		v, err := ParseGeneric(item.version)
238		if err != nil {
239			t.Errorf("unexpected parse error: %v", err)
240			continue
241		}
242		err = testOne(v, item, prev)
243		if err != nil {
244			t.Errorf("%v", err)
245		}
246		prev = item
247	}
248}
249
250func TestBadGenericVersions(t *testing.T) {
251	tests := []string{
252		"1",
253		"01.2.3",
254		"-1.2.3",
255		"1.-2.3",
256		".2.3",
257		"1..3",
258		"1a.2.3",
259		"a1.2.3",
260		"1 .2.3",
261		"1. 2.3",
262		"1.bob",
263		"bob",
264		"v 1.2.3",
265		"vv1.2.3",
266		"",
267		".",
268	}
269
270	for i := range tests {
271		_, err := ParseGeneric(tests[i])
272		if err == nil {
273			t.Errorf("unexpected success parsing invalid version %q", tests[i])
274		}
275	}
276}
277
278func TestComponents(t *testing.T) {
279
280	var tests = []struct {
281		version               string
282		semver                bool
283		expectedComponents    []uint
284		expectedMajor         uint
285		expectedMinor         uint
286		expectedPatch         uint
287		expectedPreRelease    string
288		expectedBuildMetadata string
289	}{
290		{
291			version:            "1.0.2",
292			semver:             true,
293			expectedComponents: []uint{1, 0, 2},
294			expectedMajor:      1,
295			expectedMinor:      0,
296			expectedPatch:      2,
297		},
298		{
299			version:               "1.0.2-alpha+001",
300			semver:                true,
301			expectedComponents:    []uint{1, 0, 2},
302			expectedMajor:         1,
303			expectedMinor:         0,
304			expectedPatch:         2,
305			expectedPreRelease:    "alpha",
306			expectedBuildMetadata: "001",
307		},
308		{
309			version:            "1.2",
310			semver:             false,
311			expectedComponents: []uint{1, 2},
312			expectedMajor:      1,
313			expectedMinor:      2,
314		},
315		{
316			version:               "1.0.2-beta+exp.sha.5114f85",
317			semver:                true,
318			expectedComponents:    []uint{1, 0, 2},
319			expectedMajor:         1,
320			expectedMinor:         0,
321			expectedPatch:         2,
322			expectedPreRelease:    "beta",
323			expectedBuildMetadata: "exp.sha.5114f85",
324		},
325	}
326
327	for _, test := range tests {
328		version, _ := parse(test.version, test.semver)
329		if !reflect.DeepEqual(test.expectedComponents, version.Components()) {
330			t.Error("parse returned un'expected components")
331		}
332		if test.expectedMajor != version.Major() {
333			t.Errorf("parse returned version.Major %d, expected %d", test.expectedMajor, version.Major())
334		}
335		if test.expectedMinor != version.Minor() {
336			t.Errorf("parse returned version.Minor %d, expected %d", test.expectedMinor, version.Minor())
337		}
338		if test.expectedPatch != version.Patch() {
339			t.Errorf("parse returned version.Patch %d, expected %d", test.expectedPatch, version.Patch())
340		}
341		if test.expectedPreRelease != version.PreRelease() {
342			t.Errorf("parse returned version.PreRelease %s, expected %s", test.expectedPreRelease, version.PreRelease())
343		}
344		if test.expectedBuildMetadata != version.BuildMetadata() {
345			t.Errorf("parse returned version.BuildMetadata %s, expected %s", test.expectedBuildMetadata, version.BuildMetadata())
346		}
347	}
348}
349