SemVer: support for prerelease segment - post review fixes and additional tests

GitOrigin-RevId: 076ccaae4af5f6fbb1d8c4dbb6111579ccc54be5
This commit is contained in:
Piotr Tomiak
2019-06-11 18:11:11 +03:00
committed by intellij-monorepo-bot
parent fb74085e49
commit f2b1839a7d
4 changed files with 88 additions and 49 deletions
@@ -2850,18 +2850,13 @@ public class StringUtil extends StringUtilRt {
int length1 = s1.length();
int length2 = s2.length();
int i = 0;
int j = 0;
for (; i < length1 && j < length2; i++, j++) {
int diff = compare(s1.charAt(i), s2.charAt(j), ignoreCase);
for (; i < length1 && i < length2; i++) {
int diff = compare(s1.charAt(i), s2.charAt(i), ignoreCase);
if (diff != 0) {
return diff;
}
}
if (i < length1) return +1;
if (j < length2) return -1;
if (length1 != length2) return length1 - length2;
return ignoreCase ? compare(s1, s2, false) : 0;
return length1 - length2;
}
@Contract(pure = true)
@@ -21,6 +21,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import static com.intellij.openapi.util.text.StringUtil.isNotNegativeNumber;
/**
* Holds <a href="http://semver.org">Semantic Version</a>.
*/
@@ -151,13 +153,15 @@ public final class SemVer implements Comparable<SemVer> {
CharSequence segment1 = new CharSequenceSubSequence(pre1, start1, end1);
CharSequence segment2 = new CharSequenceSubSequence(pre2, start2, end2);
if (isNumeric(segment1)) {
if (!isNumeric(segment2)) {
if (isNotNegativeNumber(segment1)) {
if (!isNotNegativeNumber(segment2)) {
// According to SemVer specification numeric segments has lower precedence
// than non-numeric segments
return -1;
}
diff = compareNumeric(segment1, segment2);
}
else if (isNumeric(segment2)) {
else if (isNotNegativeNumber(segment2)) {
return 1;
}
else {
@@ -184,23 +188,12 @@ public final class SemVer implements Comparable<SemVer> {
int length1 = segment1.length();
int length2 = segment2.length();
int diff = Integer.compare(length1, length2);
for (int i = 0; i <= length1 && diff == 0; i++) {
for (int i = 0; i < length1 && diff == 0; i++) {
diff = segment1.charAt(i) - segment2.charAt(i);
}
return diff;
}
private static boolean isNumeric(CharSequence segment) {
int length = segment.length();
for (int i = 0; i < length; i++) {
int ch = segment.charAt(i);
if (ch < '0' || ch > '9') {
return false;
}
}
return true;
}
@Nullable
public static SemVer parseFromText(@Nullable String text) {
if (text != null) {
@@ -15,6 +15,7 @@
*/
package com.intellij.util.text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import org.junit.Test;
@@ -50,27 +51,37 @@ public class SemVerTest {
assertThat(parse("11.123.0-a.b.c-1")).isEqualByComparingTo(parse("11.123.0-a.b.c-1"));
assertPrecedence("0.10.0", "1.0.0");
assertPrecedence("1.0.0","2.10.0");
assertPrecedence("1.0.0", "2.10.0");
assertPrecedence("0.5.1000", "0.30.0");
assertPrecedence("0.30.10","0.100.0");
assertPrecedence("0.30.10", "0.100.0");
assertPrecedence("2.9.100", "2.9.123-test");
assertPrecedence("2.9.123-test","2.9.124");
assertPrecedence("2.9.123","2.9.124-test");
assertPrecedence("2.9.123-test", "2.9.124");
assertPrecedence("2.9.123", "2.9.124-test");
assertPrecedence("1.2.3-a","1.2.3");
assertPrecedence("1.2.3-a", "1.2.3");
assertPrecedence("1.2.3-12","1.2.3-a");
assertPrecedence("1.2.3-22","1.2.3-100");
assertPrecedence("1.2.3-22","1.2.3-31");
assertPrecedence("1.2.3-12", "1.2.3-a");
assertPrecedence("1.2.3-22", "1.2.3-100");
assertPrecedence("1.2.3-22", "1.2.3-31");
assertPrecedence("1.2.3-22", "1.2.3-222");
assertPrecedence("1.2.3-a.b.c","1.2.3-a.b.d");
assertPrecedence("1.2.3-a.b.c","1.2.3-a.b.c.a");
assertPrecedence("1.2.3-a.b.c", "1.2.3-a.b.d");
assertPrecedence("1.2.3-a.b.c", "1.2.3-a.b.c.a");
assertPrecedence("1.2.3-a.b.1","1.2.3-a.b.c");
assertPrecedence("1.2.3-a.b.1","1.2.3-a.c.1");
assertPrecedence("1.2.3-a.cbc.100","1.2.3-a.cca.1");
assertPrecedence("1.2.3-a.cb.1","1.2.3-a.cba.1");
assertPrecedence("1.2.3-a.b.1", "1.2.3-a.b.c");
assertPrecedence("1.2.3-a.b.1", "1.2.3-a.c.1");
assertPrecedence("1.2.3-a.cbc.100", "1.2.3-a.cca.1");
assertPrecedence("1.2.3-a.cb.1", "1.2.3-a.cba.1");
// Example from SemVer documentation https://semver.org/#spec-item-11
assertPrecedence("1.0.0-alpha", "1.0.0-alpha.1");
assertPrecedence("1.0.0-alpha.1", "1.0.0-alpha.beta");
assertPrecedence("1.0.0-alpha.beta", "1.0.0-beta");
assertPrecedence("1.0.0-beta", "1.0.0-beta.2");
assertPrecedence("1.0.0-beta.2", "1.0.0-beta.11");
assertPrecedence("1.0.0-beta.11", "1.0.0-rc.1");
assertPrecedence("1.0.0-rc.1", "1.0.0");
Assert.assertTrue(parse("4.12.5").isGreaterOrEqualThan(4, 12, 5));
Assert.assertTrue(parse("4.12.5-a").isGreaterOrEqualThan(4, 12, 5));
@@ -95,7 +106,11 @@ public class SemVerTest {
assertThat(v1).isNotEqualTo(v2);
}
private static void assertParsed(String version, int expectedMajor, int expectedMinor, int expectedPatch, @Nullable String expectedPreRelease) {
private static void assertParsed(@NotNull String version,
int expectedMajor,
int expectedMinor,
int expectedPatch,
@Nullable String expectedPreRelease) {
assertThat(parse(version)).isEqualTo(new SemVer(version, expectedMajor, expectedMinor, expectedPatch, expectedPreRelease));
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.text.LineColumn;
import com.intellij.openapi.util.text.NaturalComparator;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.LineSeparator;
import com.intellij.util.TripleFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xml.util.XmlStringUtil;
import org.jdom.Verifier;
@@ -199,16 +200,16 @@ public class StringUtilTest {
@Test
public void testNaturalCompare() {
final List<String> numbers = Arrays.asList("1a000001", "000001a1", "001a0001", "0001A001" , "00001a01", "01a00001");
final List<String> numbers = Arrays.asList("1a000001", "000001a1", "001a0001", "0001A001", "00001a01", "01a00001");
numbers.sort(NaturalComparator.INSTANCE);
assertEquals(Arrays.asList("1a000001", "01a00001", "001a0001", "0001A001" , "00001a01", "000001a1"), numbers);
assertEquals(Arrays.asList("1a000001", "01a00001", "001a0001", "0001A001", "00001a01", "000001a1"), numbers);
final List<String> test = Arrays.asList("test011", "test10", "test10a", "test010");
test.sort(NaturalComparator.INSTANCE);
assertEquals(Arrays.asList("test10", "test10a", "test010", "test011"), test);
final List<String> strings = Arrays.asList("Test99", "tes0", "test0", "testing", "test", "test99", "test011", "test1",
"test 3", "test2", "test10a", "test10", "1.2.10.5", "1.2.9.1");
"test 3", "test2", "test10a", "test10", "1.2.10.5", "1.2.9.1");
strings.sort(NaturalComparator.INSTANCE);
assertEquals(Arrays.asList("1.2.9.1", "1.2.10.5", "tes0", "test", "test0", "test1", "test2", "test 3", "test10", "test10a",
"test011", "Test99", "test99", "testing"), strings);
@@ -216,13 +217,14 @@ public class StringUtilTest {
final List<String> strings2 = Arrays.asList("t1", "t001", "T2", "T002", "T1", "t2");
strings2.sort(NaturalComparator.INSTANCE);
assertEquals(Arrays.asList("T1", "t1", "t001", "T2", "t2", "T002"), strings2);
assertEquals(1 ,StringUtil.naturalCompare("7403515080361171695", "07403515080361171694"));
assertEquals(1, StringUtil.naturalCompare("7403515080361171695", "07403515080361171694"));
assertEquals(-14, StringUtil.naturalCompare("_firstField", "myField1"));
//idea-80853
final List<String> strings3 =
Arrays.asList("C148A_InsomniaCure", "C148B_Escape", "C148C_TersePrincess", "C148D_BagOfMice", "C148E_Porcelain");
strings3.sort(NaturalComparator.INSTANCE);
assertEquals(Arrays.asList("C148A_InsomniaCure", "C148B_Escape", "C148C_TersePrincess", "C148D_BagOfMice", "C148E_Porcelain"), strings3);
assertEquals(Arrays.asList("C148A_InsomniaCure", "C148B_Escape", "C148C_TersePrincess", "C148D_BagOfMice", "C148E_Porcelain"),
strings3);
final List<String> l = Arrays.asList("a0002", "a0 2", "a001");
l.sort(NaturalComparator.INSTANCE);
@@ -374,7 +376,8 @@ public class StringUtilTest {
@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
public void testReplaceReturnReplacementIfTextEqualsToReplacedText() {
String str = "/tmp";
assertSame(str, StringUtil.replace("$PROJECT_FILE$", "$PROJECT_FILE$".toLowerCase().toUpperCase() /* ensure new String instance */, str));
assertSame(str,
StringUtil.replace("$PROJECT_FILE$", "$PROJECT_FILE$".toLowerCase().toUpperCase() /* ensure new String instance */, str));
}
@Test
@@ -453,6 +456,36 @@ public class StringUtilTest {
assertTrue(StringUtil.contains("12", "2"));
}
@Test
public void testCompareCharSequence() {
TripleFunction<CharSequence, CharSequence, Boolean, Boolean> assertPrecedence =
(lesser, greater, ignoreCase) -> {
assertTrue(StringUtil.compare(lesser, greater, ignoreCase) < 0);
assertTrue(StringUtil.compare(greater, lesser, ignoreCase) > 0);
return true;
};
TripleFunction<CharSequence, CharSequence, Boolean, Boolean> assertEquality =
(lesser, greater, ignoreCase) -> {
assertEquals(0, StringUtil.compare(lesser, greater, ignoreCase));
assertEquals(0, StringUtil.compare(greater, lesser, ignoreCase));
return true;
};
assertPrecedence.fun("A","b", true);
assertPrecedence.fun("a","aa", true);
assertPrecedence.fun("abb","abC", true);
assertPrecedence.fun("A","a", false);
assertPrecedence.fun("Aa","a", false);
assertPrecedence.fun("a","aa", false);
assertPrecedence.fun("-","A", false);
assertEquality.fun("a","A",true);
assertEquality.fun("aa12b","Aa12B",true);
assertEquality.fun("aa12b","aa12b",false);
}
@Test
public void testDetectSeparators() {
assertNull(StringUtil.detectSeparators(""));
@@ -549,7 +582,7 @@ public class StringUtilTest {
assertEquals("2 m 3 s", StringUtil.formatDurationApproximate(123456));
assertEquals("1 h 1 m", StringUtil.formatDurationApproximate(3659009));
assertEquals("2 h", StringUtil.formatDurationApproximate(7199000));
assertEquals("1 d", StringUtil.formatDurationApproximate((23*60*60 + 59*60 + 59) * 1000L));
assertEquals("1 d", StringUtil.formatDurationApproximate((23 * 60 * 60 + 59 * 60 + 59) * 1000L));
assertEquals("1 yr 1 mo", StringUtil.formatDurationApproximate(33786061001L));
}
@@ -558,7 +591,8 @@ public class StringUtilTest {
assertEquals("<![CDATA[abc]]>", XmlStringUtil.wrapInCDATA("abc"));
assertEquals("<![CDATA[abc]]]><![CDATA[]>]]>", XmlStringUtil.wrapInCDATA("abc]]>"));
assertEquals("<![CDATA[abc]]]><![CDATA[]>def]]>", XmlStringUtil.wrapInCDATA("abc]]>def"));
assertEquals("<![CDATA[123<![CDATA[wow<&>]]]><![CDATA[]>]]]><![CDATA[]><![CDATA[123]]>", XmlStringUtil.wrapInCDATA("123<![CDATA[wow<&>]]>]]><![CDATA[123"));
assertEquals("<![CDATA[123<![CDATA[wow<&>]]]><![CDATA[]>]]]><![CDATA[]><![CDATA[123]]>",
XmlStringUtil.wrapInCDATA("123<![CDATA[wow<&>]]>]]><![CDATA[123"));
}
@Test
@@ -571,7 +605,7 @@ public class StringUtilTest {
@Test
public void testIndexOf_1() {
char[] chars = new char[]{'a','b','c','d','a','b','c','d','A','B','C','D'};
char[] chars = new char[]{'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'A', 'B', 'C', 'D'};
assertEquals(2, StringUtil.indexOf(chars, 'c', 0, 12, false));
assertEquals(2, StringUtil.indexOf(chars, 'C', 0, 12, false));
assertEquals(10, StringUtil.indexOf(chars, 'C', 0, 12, true));
@@ -776,10 +810,12 @@ public class StringUtilTest {
CharSequence s = ByteArrayCharSequence.convertToBytesIfPossible("test");
assertTrue(s instanceof ByteArrayCharSequence || SystemInfo.IS_AT_LEAST_JAVA9 && s.getClass() == String.class);
CharSequence first = StringUtil.first(s, 1, false);
assertTrue(String.valueOf(first.getClass()), first instanceof CharSequenceSubSequence || SystemInfo.IS_AT_LEAST_JAVA9 && s.getClass() == String.class);
assertTrue(String.valueOf(first.getClass()),
first instanceof CharSequenceSubSequence || SystemInfo.IS_AT_LEAST_JAVA9 && s.getClass() == String.class);
assertEquals("t", first.toString());
CharSequence last = StringUtil.last(s, 1, false);
assertTrue(String.valueOf(last.getClass()), last instanceof CharSequenceSubSequence|| SystemInfo.IS_AT_LEAST_JAVA9 && s.getClass() == String.class);
assertTrue(String.valueOf(last.getClass()),
last instanceof CharSequenceSubSequence || SystemInfo.IS_AT_LEAST_JAVA9 && s.getClass() == String.class);
assertEquals("t", last.toString());
}