View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.artifact.versioning;
20  
21  import java.util.Locale;
22  
23  import org.junit.jupiter.api.Test;
24  
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  /**
29   * Test ComparableVersion.
30   *
31   */
32  @SuppressWarnings("unchecked")
33  class ComparableVersionTest {
34      private ComparableVersion newComparable(String version) {
35          ComparableVersion ret = new ComparableVersion(version);
36          String canonical = ret.getCanonical();
37          String parsedCanonical = new ComparableVersion(canonical).getCanonical();
38  
39          assertEquals(
40                  canonical,
41                  parsedCanonical,
42                  "canonical( " + version + " ) = " + canonical + " -> canonical: " + parsedCanonical);
43  
44          return ret;
45      }
46  
47      private static final String[] VERSIONS_QUALIFIER = {
48          "1-alpha2snapshot",
49          "1-alpha2",
50          "1-alpha-123",
51          "1-beta-2",
52          "1-beta123",
53          "1-m2",
54          "1-m11",
55          "1-rc",
56          "1-cr2",
57          "1-rc123",
58          "1-SNAPSHOT",
59          "1",
60          "1-sp",
61          "1-sp2",
62          "1-sp123",
63          "1-abc",
64          "1-def",
65          "1-pom-1",
66          "1-1-snapshot",
67          "1-1",
68          "1-2",
69          "1-123"
70      };
71  
72      private static final String[] VERSIONS_NUMBER = {
73          "2.0", "2.0.a", "2-1", "2.0.2", "2.0.123", "2.1.0", "2.1-a", "2.1b", "2.1-c", "2.1-1", "2.1.0.1", "2.2",
74          "2.123", "11.a2", "11.a11", "11.b2", "11.b11", "11.m2", "11.m11", "11", "11.a", "11b", "11c", "11m"
75      };
76  
77      private void checkVersionsOrder(String[] versions) {
78          Comparable[] c = new Comparable[versions.length];
79          for (int i = 0; i < versions.length; i++) {
80              c[i] = newComparable(versions[i]);
81          }
82  
83          for (int i = 1; i < versions.length; i++) {
84              Comparable low = c[i - 1];
85              for (int j = i; j < versions.length; j++) {
86                  Comparable high = c[j];
87                  assertTrue(low.compareTo(high) < 0, "expected " + low + " < " + high);
88                  assertTrue(high.compareTo(low) > 0, "expected " + high + " > " + low);
89              }
90          }
91      }
92  
93      private void checkVersionsEqual(String v1, String v2) {
94          Comparable c1 = newComparable(v1);
95          Comparable c2 = newComparable(v2);
96          assertEquals(0, c1.compareTo(c2), "expected " + v1 + " == " + v2);
97          assertEquals(0, c2.compareTo(c1), "expected " + v2 + " == " + v1);
98          assertEquals(c1.hashCode(), c2.hashCode(), "expected same hashcode for " + v1 + " and " + v2);
99          assertEquals(c1, c2, "expected " + v1 + ".equals( " + v2 + " )");
100         assertEquals(c2, c1, "expected " + v2 + ".equals( " + v1 + " )");
101     }
102 
103     private void checkVersionsHaveSameOrder(String v1, String v2) {
104         ComparableVersion c1 = new ComparableVersion(v1);
105         ComparableVersion c2 = new ComparableVersion(v2);
106         assertEquals(0, c1.compareTo(c2), "expected " + v1 + " == " + v2);
107         assertEquals(0, c2.compareTo(c1), "expected " + v2 + " == " + v1);
108     }
109 
110     private void checkVersionsArrayEqual(String[] array) {
111         // compare against each other (including itself)
112         for (int i = 0; i < array.length; ++i)
113             for (int j = i; j < array.length; ++j) checkVersionsEqual(array[i], array[j]);
114     }
115 
116     private void checkVersionsOrder(String v1, String v2) {
117         Comparable c1 = newComparable(v1);
118         Comparable c2 = newComparable(v2);
119         assertTrue(c1.compareTo(c2) < 0, "expected " + v1 + " < " + v2);
120         assertTrue(c2.compareTo(c1) > 0, "expected " + v2 + " > " + v1);
121     }
122 
123     @Test
124     void testVersionsQualifier() {
125         checkVersionsOrder(VERSIONS_QUALIFIER);
126     }
127 
128     @Test
129     void testVersionsNumber() {
130         checkVersionsOrder(VERSIONS_NUMBER);
131     }
132 
133     @Test
134     void testVersionsEqual() {
135         newComparable("1.0-alpha");
136         checkVersionsEqual("1", "1");
137         checkVersionsEqual("1", "1.0");
138         checkVersionsEqual("1", "1.0.0");
139         checkVersionsEqual("1.0", "1.0.0");
140         checkVersionsEqual("1", "1-0");
141         checkVersionsEqual("1", "1.0-0");
142         checkVersionsEqual("1.0", "1.0-0");
143         // no separator between number and character
144         checkVersionsEqual("1a", "1-a");
145         checkVersionsEqual("1a", "1.0-a");
146         checkVersionsEqual("1a", "1.0.0-a");
147         checkVersionsEqual("1.0a", "1-a");
148         checkVersionsEqual("1.0.0a", "1-a");
149         checkVersionsEqual("1x", "1-x");
150         checkVersionsEqual("1x", "1.0-x");
151         checkVersionsEqual("1x", "1.0.0-x");
152         checkVersionsEqual("1.0x", "1-x");
153         checkVersionsEqual("1.0.0x", "1-x");
154         checkVersionsEqual("1cr", "1rc");
155 
156         // special "aliases" a, b and m for alpha, beta and milestone
157         checkVersionsEqual("1a1", "1-alpha-1");
158         checkVersionsEqual("1b2", "1-beta-2");
159         checkVersionsEqual("1m3", "1-milestone-3");
160 
161         // case insensitive
162         checkVersionsEqual("1X", "1x");
163         checkVersionsEqual("1A", "1a");
164         checkVersionsEqual("1B", "1b");
165         checkVersionsEqual("1M", "1m");
166         checkVersionsEqual("1Cr", "1Rc");
167         checkVersionsEqual("1cR", "1rC");
168         checkVersionsEqual("1m3", "1Milestone3");
169         checkVersionsEqual("1m3", "1MileStone3");
170         checkVersionsEqual("1m3", "1MILESTONE3");
171     }
172 
173     @Test
174     void testVersionsHaveSameOrderButAreNotEqual() {
175         checkVersionsHaveSameOrder("1ga", "1");
176         checkVersionsHaveSameOrder("1release", "1");
177         checkVersionsHaveSameOrder("1final", "1");
178         checkVersionsHaveSameOrder("1Ga", "1");
179         checkVersionsHaveSameOrder("1GA", "1");
180         checkVersionsHaveSameOrder("1RELEASE", "1");
181         checkVersionsHaveSameOrder("1release", "1");
182         checkVersionsHaveSameOrder("1RELeaSE", "1");
183         checkVersionsHaveSameOrder("1Final", "1");
184         checkVersionsHaveSameOrder("1FinaL", "1");
185         checkVersionsHaveSameOrder("1FINAL", "1");
186     }
187 
188     @Test
189     void testVersionComparing() {
190         checkVersionsOrder("1", "2");
191         checkVersionsOrder("1.5", "2");
192         checkVersionsOrder("1", "2.5");
193         checkVersionsOrder("1.0", "1.1");
194         checkVersionsOrder("1.1", "1.2");
195         checkVersionsOrder("1.0.0", "1.1");
196         checkVersionsOrder("1.0.1", "1.1");
197         checkVersionsOrder("1.1", "1.2.0");
198 
199         checkVersionsOrder("1.0-alpha-1", "1.0");
200         checkVersionsOrder("1.0-alpha-1", "1.0-alpha-2");
201         checkVersionsOrder("1.0-alpha-1", "1.0-beta-1");
202 
203         checkVersionsOrder("1.0-beta-1", "1.0-SNAPSHOT");
204         checkVersionsOrder("1.0-SNAPSHOT", "1.0");
205         checkVersionsOrder("1.0-alpha-1-SNAPSHOT", "1.0-alpha-1");
206 
207         checkVersionsOrder("1.0", "1.0-1");
208         checkVersionsOrder("1.0-1", "1.0-2");
209         checkVersionsOrder("1.0.0", "1.0-1");
210 
211         checkVersionsOrder("2.0-1", "2.0.1");
212         checkVersionsOrder("2.0.1-klm", "2.0.1-lmn");
213         checkVersionsOrder("2.0.1", "2.0.1-xyz");
214 
215         checkVersionsOrder("2.0.1", "2.0.1-123");
216         checkVersionsOrder("2.0.1-xyz", "2.0.1-123");
217     }
218 
219     @Test
220     void testLeadingZeroes() {
221         checkVersionsOrder("0.7", "2");
222         checkVersionsOrder("0.2", "1.0.7");
223     }
224 
225     @Test
226     void testGetCanonical() {
227         // MNG-7700
228         newComparable("0.x");
229         newComparable("0-x");
230         newComparable("0.rc");
231         newComparable("0-1");
232 
233         ComparableVersion version = new ComparableVersion("0.x");
234         assertEquals("x", version.getCanonical());
235         ComparableVersion version2 = new ComparableVersion("0.2");
236         assertEquals("0.2", version2.getCanonical());
237     }
238 
239     /**
240      * Test <a href="https://issues.apache.org/jira/browse/MNG-5568">MNG-5568</a> edge case
241      * which was showing transitive inconsistency: since A &gt; B and B &gt; C then we should have A &gt; C
242      * otherwise sorting a list of ComparableVersions() will in some cases throw runtime exception;
243      * see Netbeans issues <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=240845">240845</a> and
244      * <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=226100">226100</a>
245      */
246     @Test
247     void testMng5568() {
248         String a = "6.1.0";
249         String b = "6.1.0rc3";
250         String c = "6.1H.5-beta"; // this is the unusual version string, with 'H' in the middle
251 
252         checkVersionsOrder(b, a); // classical
253         checkVersionsOrder(b, c); // now b < c, but before MNG-5568, we had b > c
254         checkVersionsOrder(a, c);
255     }
256 
257     /**
258      * Test <a href="https://jira.apache.org/jira/browse/MNG-6572">MNG-6572</a> optimization.
259      */
260     @Test
261     void testMng6572() {
262         String a = "20190126.230843"; // resembles a SNAPSHOT
263         String b = "1234567890.12345"; // 10 digit number
264         String c = "123456789012345.1H.5-beta"; // 15 digit number
265         String d = "12345678901234567890.1H.5-beta"; // 20 digit number
266 
267         checkVersionsOrder(a, b);
268         checkVersionsOrder(b, c);
269         checkVersionsOrder(a, c);
270         checkVersionsOrder(c, d);
271         checkVersionsOrder(b, d);
272         checkVersionsOrder(a, d);
273     }
274 
275     /**
276      * Test all versions are equal when starting with many leading zeroes regardless of string length
277      * (related to MNG-6572 optimization)
278      */
279     @Test
280     void testVersionEqualWithLeadingZeroes() {
281         // versions with string lengths from 1 to 19
282         String[] arr = new String[] {
283             "0000000000000000001",
284             "000000000000000001",
285             "00000000000000001",
286             "0000000000000001",
287             "000000000000001",
288             "00000000000001",
289             "0000000000001",
290             "000000000001",
291             "00000000001",
292             "0000000001",
293             "000000001",
294             "00000001",
295             "0000001",
296             "000001",
297             "00001",
298             "0001",
299             "001",
300             "01",
301             "1"
302         };
303 
304         checkVersionsArrayEqual(arr);
305     }
306 
307     /**
308      * Test all "0" versions are equal when starting with many leading zeroes regardless of string length
309      * (related to MNG-6572 optimization)
310      */
311     @Test
312     void testVersionZeroEqualWithLeadingZeroes() {
313         // versions with string lengths from 1 to 19
314         String[] arr = new String[] {
315             "0000000000000000000",
316             "000000000000000000",
317             "00000000000000000",
318             "0000000000000000",
319             "000000000000000",
320             "00000000000000",
321             "0000000000000",
322             "000000000000",
323             "00000000000",
324             "0000000000",
325             "000000000",
326             "00000000",
327             "0000000",
328             "000000",
329             "00000",
330             "0000",
331             "000",
332             "00",
333             "0"
334         };
335 
336         checkVersionsArrayEqual(arr);
337     }
338 
339     /**
340      * Test <a href="https://issues.apache.org/jira/browse/MNG-6964">MNG-6964</a> edge cases
341      * for qualifiers that start with "-0.", which was showing A == C and B == C but A &lt; B.
342      */
343     @Test
344     void testMng6964() {
345         String a = "1-0.alpha";
346         String b = "1-0.beta";
347         String c = "1";
348 
349         checkVersionsOrder(a, c); // Now a < c, but before MNG-6964 they were equal
350         checkVersionsOrder(b, c); // Now b < c, but before MNG-6964 they were equal
351         checkVersionsOrder(a, b); // Should still be true
352     }
353 
354     @Test
355     void testLocaleIndependent() {
356         Locale orig = Locale.getDefault();
357         Locale[] locales = {Locale.ENGLISH, new Locale("tr"), Locale.getDefault()};
358         try {
359             for (Locale locale : locales) {
360                 Locale.setDefault(locale);
361                 checkVersionsEqual("1-abcdefghijklmnopqrstuvwxyz", "1-ABCDEFGHIJKLMNOPQRSTUVWXYZ");
362             }
363         } finally {
364             Locale.setDefault(orig);
365         }
366     }
367 
368     @Test
369     void testReuse() {
370         ComparableVersion c1 = new ComparableVersion("1");
371         c1.parseVersion("2");
372 
373         Comparable<?> c2 = newComparable("2");
374 
375         assertEquals(c1, c2, "reused instance should be equivalent to new instance");
376     }
377 
378     /**
379      * Test <a href="https://issues.apache.org/jira/browse/MNG-7644">MNG-7644</a> edge cases
380      * 1.0.0.RC1 &lt; 1.0.0-RC2 and more generally:
381      * 1.0.0.X1 &lt; 1.0.0-X2 for any string X
382      */
383     @Test
384     void testMng7644() {
385         for (String x : new String[] {"abc", "alpha", "a", "beta", "b", "def", "milestone", "m", "RC"}) {
386             // 1.0.0.X1 < 1.0.0-X2 for any string x
387             checkVersionsOrder("1.0.0." + x + "1", "1.0.0-" + x + "2");
388             // 2.0.X == 2-X == 2.0.0.X for any string x
389             checkVersionsEqual("2-" + x, "2.0." + x); // previously ordered, now equals
390             checkVersionsEqual("2-" + x, "2.0.0." + x); // previously ordered, now equals
391             checkVersionsEqual("2.0." + x, "2.0.0." + x); // previously ordered, now equals
392         }
393     }
394 
395     @Test
396     public void testMng7714() {
397         ComparableVersion f = new ComparableVersion("1.0.final-redhat");
398         ComparableVersion sp1 = new ComparableVersion("1.0-sp1-redhat");
399         ComparableVersion sp2 = new ComparableVersion("1.0-sp-1-redhat");
400         ComparableVersion sp3 = new ComparableVersion("1.0-sp.1-redhat");
401         assertTrue(f.compareTo(sp1) < 0, "expected " + f + " < " + sp1);
402         assertTrue(f.compareTo(sp2) < 0, "expected " + f + " < " + sp2);
403         assertTrue(f.compareTo(sp3) < 0, "expected " + f + " < " + sp3);
404     }
405 }