diff --git a/README.md b/README.md index 767d9f7..3f4b137 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Semver sem1 = new Semver("1.2.3-beta.4+sha899d8g79f87"); // Defaults to STRICT m Semver sem2 = new Semver("1.2.3-beta.4+sha899d8g79f87", SemverType.NPM); // Specify the mode ``` -If the version is invalid, a `SemverException` will be thrown. +If the version is invalid, a `SemverException` will be thrown. You can access the different parts of the version using `getMajor()`, `getMinor()`, `getPatch()`, `getSuffixTokens()` or `getBuild()`. | Type | Mandatory | Optional | @@ -130,6 +130,7 @@ sem.diff("1.2.3-beta.4+sha32iddfu987"); // BUILD If you want to check if a version satisfies a requirement, use the `satisfies` method. +- A `SemverException` is thrown if the version is not fully-qualified (containing major, minor, and patch versions). - In `STRICT` and `LOOSE` modes, the requirement can only be another version. - In `NPM` mode, the requirement follows [NPM versioning rules](https://github.com/npm/node-semver). @@ -180,5 +181,5 @@ You can also use built-in versioning methods such as: ## Contributing -Any pull request or bug report is welcome! +Any pull request or bug report is welcome! If you have any suggestion about new features, you can open an issue. diff --git a/pom.xml b/pom.xml index b39fd49..bbb73b3 100644 --- a/pom.xml +++ b/pom.xml @@ -2,19 +2,19 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.vdurmont + org.thshsh semver4j - 3.1.0 + 3.1.1 jar semver4j - https://github.com/vdurmont/semver4j + https://github.com/theshoeshiner/semver4j Semantic versioning for Java apps. - scm:git:git@github.com:vdurmont/semver4j.git - scm:git:git@github.com:vdurmont/semver4j.git - git@github.com:vdurmont/semver4j.git + scm:git:git@github.com:theshoeshiner/semver4j.git + scm:git:git@github.com:theshoeshiner/semver4j.git + git@github.com:theshoeshiner/semver4j.git @@ -23,6 +23,10 @@ vdurmont@gmail.com http://www.vdurmont.com + + The Shoe Shiner + theshoeshiner@thshsh.org + @@ -129,6 +133,11 @@ sign + + + *.asc + + diff --git a/src/main/java/com/vdurmont/semver4j/Requirement.java b/src/main/java/com/vdurmont/semver4j/Requirement.java index 5a813c4..77f1dd8 100644 --- a/src/main/java/com/vdurmont/semver4j/Requirement.java +++ b/src/main/java/com/vdurmont/semver4j/Requirement.java @@ -322,7 +322,8 @@ private static Requirement evaluateReversePolishNotation(Iterator 0; + } + /** * @see #isGreaterThan(Semver) * @@ -329,8 +339,22 @@ public boolean isEquivalentTo(String version) { */ public boolean isEquivalentTo(Semver version) { // Get versions without build - Semver sem1 = this.getBuild() == null ? this : new Semver(this.getValue().replace("+" + this.getBuild(), "")); - Semver sem2 = version.getBuild() == null ? version : new Semver(version.getValue().replace("+" + version.getBuild(), "")); + Semver sem1 = this.getBuild() == null ? this : new Semver(this.getValue().replace("+" + this.getBuild(), ""), this.getType()); + Semver sem2 = version.getBuild() == null ? version : new Semver(version.getValue().replace("+" + version.getBuild(), ""), version.getType()); + + // Ignore minor and/or patch when versions are not equally qualified + if (sem1.getType() != SemverType.STRICT) { + if (!sem1.areSameSuffixes(sem2.getSuffixTokens())) return false; + + if (!Objects.equals(sem1.getMajor(), sem2.getMajor())) return false; + + if (sem2.getMinor() == null) return true; + if (!Objects.equals(sem1.getMinor(), sem2.getMinor())) return false; + + if (sem2.getPatch() == null) return true; + if (!Objects.equals(sem1.getPatch(), sem2.getPatch())) return false; + } + // Compare those new versions return sem1.isEqualTo(sem2); } @@ -354,12 +378,6 @@ public boolean isEqualTo(String version) { * @return true if the current version equals the provided version */ public boolean isEqualTo(Semver version) { - if (this.type == SemverType.NPM) { - if (this.getMajor() != version.getMajor()) return false; - if (version.getMinor() == null) return true; - if (version.getPatch() == null) return true; - } - return this.equals(version); } @@ -442,6 +460,24 @@ public Semver withIncPatch() { public Semver withIncPatch(int increment) { return this.withInc(0, 0, increment); } + + public Semver withIncSuffix(int index) { + return withIncSuffix(index, 1); + } + + public Semver withIncSuffix(int index, int increment) { + if(suffixTokens.length <= index) throw new SemverException("No suffix at index "+index); + try { + Integer token = Integer.valueOf(suffixTokens[index]); + token+=increment; + String[] newSuffix = Arrays.copyOf(suffixTokens,suffixTokens.length); + newSuffix[index] = token.toString(); + return with(major,minor,patch,newSuffix,build); + } + catch(NumberFormatException nfe) { + throw new SemverException("Suffix was not integer at index "+index); + } + } private Semver withInc(int majorInc, int minorInc, int patchInc) { Integer minor = this.minor; @@ -542,7 +578,7 @@ private static Semver create(SemverType type, int major, Integer minor, Integer @Override public int compareTo(Semver version) { if (this.isGreaterThan(version)) return 1; - else if(this.isLowerThan(version)) return -1; + else if (this.isLowerThan(version)) return -1; return 0; } diff --git a/src/test/java/com/vdurmont/semver4j/NpmSemverTest.java b/src/test/java/com/vdurmont/semver4j/NpmSemverTest.java index 16ad6ad..a284eef 100644 --- a/src/test/java/com/vdurmont/semver4j/NpmSemverTest.java +++ b/src/test/java/com/vdurmont/semver4j/NpmSemverTest.java @@ -30,9 +30,12 @@ public static Iterable getParameters() { // Fully-qualified versions: { "1.2.3", "1.2.3", true, }, { "1.2.4", "1.2.3", false, }, + { "1.3.3", "1.2.3", false, }, + { "2.2.3", "1.2.3", false, }, // Minor versions: { "1.2.3", "1.2", true, }, + { "1.3.3", "1.2", false, }, { "1.2.4", "1.3", false, }, // Major versions: @@ -47,7 +50,7 @@ public static Iterable getParameters() { { "2.3.0-alpha", "1.2.3 - 2.3.0-beta", true, }, { "2.3.4", "1.2.3 - 2.3", true, }, { "2.3.4", "1.2.3 - 2", true, }, - { "4.4", "3.X - 4.X", true, }, + { "4.4.0", "3.X - 4.X", true, }, { "1.0.0", "1.2.3 - 2.3.4", false, }, { "3.0.0", "1.2.3 - 2.3.4", false, }, { "2.4.3", "1.2.3 - 2.3", false, }, @@ -58,7 +61,7 @@ public static Iterable getParameters() { { "3.1.5", "", true, }, { "3.1.5", "*", true, }, { "0.0.0", "*", true, }, - { "1.0.0-beta", "*", true, }, + { "1.0.0-beta", "*", false, }, { "3.1.5-beta", "3.1.x", false, }, { "3.1.5-beta+exp.sha.5114f85", "3.1.x", false, }, { "3.1.5+exp.sha.5114f85", "3.1.x", true, }, @@ -121,6 +124,8 @@ public static Iterable getParameters() { { "2.0.1", "=2.0", true, }, { "2.0.0", "=2", true, }, { "2.0.1", "=2", true, }, + { "3.0.0", "=2.0", false, }, + { "2.1.0", "=2.0", false, }, { "2.0.1", "=2.0.0", false, }, { "1.9.9", "=2.0.0", false, }, { "1.9.9", "=2.0", false, }, @@ -176,6 +181,56 @@ public static Iterable getParameters() { { "3.0.0", "<=2.0", false, }, { "3.0.0", "<=2", false, }, + // Prerelease versions: + { "2.0.0-alpha", "=2.0.0-beta", false, }, + { "2.0.0-rc.2", "=2.0.0-rc.2", true, }, + { "2.0.0-rc.2", "=2.0.0-rc.3", false, }, + { "2.0.0-rc.2", "=2.0.0-rc.2.3", false, }, + { "2.0.0-rc.2", "=2.0.0-rc.3.2", false, }, + { "2.0.0-rc.2", "=2.0.0", false, }, + { "2.0.0-rc.2", "=2.0", false, }, + { "2.0.0-rc.2", "=2", false, }, + + { "2.0.0-alpha", ">2.0.0-beta", false, }, + { "2.0.0-rc.2", ">2.0.0-rc.1", true, }, + { "2.0.0-rc.2", ">2.0.0-rc.2", false, }, + { "2.0.0-rc.2", ">2.0.0-rc.3", false, }, + { "2.0.0-rc.2", ">2.0.0-rc.2.3", false, }, + { "2.0.0-rc.2", ">2.0.0-rc.3.2", false, }, + { "2.0.0-rc.2", ">2.0.0", false, }, + { "2.0.0-rc.2", ">2.0", false, }, + { "2.0.0-rc.2", ">2", false, }, + + { "2.0.0-alpha", "<2.0.0-beta", true, }, + { "2.0.0-rc.2", "<2.0.0-rc.3", true, }, + { "2.0.0-rc.2", "<2.0.0-rc.2.3", true, }, + { "2.0.0-rc.2", "<2.0.0-rc.3.2", true, }, + { "2.0.0-rc.2", "<2.0.0-rc.1", false, }, + { "2.0.0-rc.2", "<2.0.0-rc.2", false, }, + { "2.0.0-rc.2", "<2.0.0", false, }, + { "2.0.0-rc.2", "<2.0", false, }, + { "2.0.0-rc.2", "<2", false, }, + + { "2.0.0-alpha", ">=2.0.0-beta", false, }, + { "2.0.0-rc.2", ">=2.0.0-rc.1", true, }, + { "2.0.0-rc.2", ">=2.0.0-rc.2", true, }, + { "2.0.0-rc.2", ">=2.0.0-rc.3", false, }, + { "2.0.0-rc.2", ">=2.0.0-rc.2.3", false, }, + { "2.0.0-rc.2", ">=2.0.0-rc.3.2", false, }, + { "2.0.0-rc.2", ">=2.0.0", false, }, + { "2.0.0-rc.2", ">=2.0", false, }, + { "2.0.0-rc.2", ">=2", false, }, + + { "2.0.0-alpha", "<=2.0.0-beta", true, }, + { "2.0.0-rc.2", "<=2.0.0-rc.2", true, }, + { "2.0.0-rc.2", "<=2.0.0-rc.3", true, }, + { "2.0.0-rc.2", "<=2.0.0-rc.2.3", true, }, + { "2.0.0-rc.2", "<=2.0.0-rc.3.2", true, }, + { "2.0.0-rc.2", "<=2.0.0-rc.1", false, }, + { "2.0.0-rc.2", "<=2.0.0", false, }, + { "2.0.0-rc.2", "<=2.0", false, }, + { "2.0.0-rc.2", "<=2", false, }, + // AND ranges: { "2.0.1", ">2.0.0 <3.0.0", true, }, { "2.0.1", ">2.0 <3.0", false, }, @@ -222,12 +277,22 @@ public static Iterable getParameters() { { "1.1.0", "1.2 <1.2.8 || >2.0.0", false, }, { "1.2.9", "1.2 <1.2.8 || >2.0.0", false, }, { "2.0.0", "1.2 <1.2.8 || >2.0.0", false, }, + + // Big number equality: + { "128.0.0", "=128.0.0", true, }, + { "127.127.127", "=127.128", false, }, + { "0.128.0", "=0.128.0", true, }, + { "0.0.128", "=0.0.128", true, }, + { "127.127.127", "=127.127.127", true, }, + { "128.128.128", "=128.128.128", true, }, + { "999.999.999", "=999.999.999", true, }, + { "9999.9999.9999", "=9999.9999.9999", true, }, }); } @Test public void test() { - assertEquals(this.version + " , " + this.rangeExpression ,this.expected, new Semver(this.version, SemverType.NPM).satisfies(this.rangeExpression)); + assertEquals(this.version + " , " + this.rangeExpression, this.expected, new Semver(this.version, SemverType.NPM).satisfies(this.rangeExpression)); } } diff --git a/src/test/java/com/vdurmont/semver4j/RequirementTest.java b/src/test/java/com/vdurmont/semver4j/RequirementTest.java index ba9b7e6..55b4ed2 100644 --- a/src/test/java/com/vdurmont/semver4j/RequirementTest.java +++ b/src/test/java/com/vdurmont/semver4j/RequirementTest.java @@ -10,6 +10,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -324,9 +325,9 @@ public class RequirementTest { @Test public void isSatisfiedBy_with_a_loose_type() { Requirement req = Requirement.buildLoose("1.3.2"); - assertFalse(req.isSatisfiedBy("0.27")); + assertFalse(req.isSatisfiedBy("0.27.0")); assertTrue(req.isSatisfiedBy("1.3.2")); - assertFalse(req.isSatisfiedBy("1.5")); + assertFalse(req.isSatisfiedBy("1.5.0")); } @Test public void isSatisfiedBy_with_a_complex_example() { @@ -422,6 +423,14 @@ public class RequirementTest { assertTrue(req.isSatisfiedBy("0.2.3")); } + @Test public void isSatisfiedBy_unqualified_exception() { + Requirement req = Requirement.buildNPM("1.0"); + try { + req.isSatisfiedBy("1.0"); + fail("isSatisfiedBy() did not throw as expected."); + } catch (SemverException e) {} + } + @Test public void tildeRequirement_cocoapods() { // '~> 0.1.2' Version 0.1.2 and the versions up to 0.2, not including 0.2 and higher tildeTest("0.1.2", "0.1.2", "0.2.0", Semver.SemverType.COCOAPODS); diff --git a/src/test/java/com/vdurmont/semver4j/SemverTest.java b/src/test/java/com/vdurmont/semver4j/SemverTest.java index d0c0111..044d366 100644 --- a/src/test/java/com/vdurmont/semver4j/SemverTest.java +++ b/src/test/java/com/vdurmont/semver4j/SemverTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -119,6 +120,15 @@ private static void assertIsSemver(Semver semver, String value, Integer major, I assertEquals(build, semver.getBuild()); } + @Test public void satisfies_unqualified_exception() { + String version = "1.0"; + Semver semver = new Semver(version, Semver.SemverType.LOOSE); + try { + semver.satisfies("1.0"); + fail("satisfies() did not throw as expected."); + } catch (SemverException e) {} + } + @Test public void statisfies_works_will_all_the_types() { // Used to prevent bugs when we add a new type for (Semver.SemverType type : Semver.SemverType.values()) { @@ -187,6 +197,27 @@ private static void assertIsSemver(Semver semver, String value, Integer major, I assertTrue(semver.isEquivalentTo(version2)); } + @Test public void isEquivalentTo_isEqualTo_without_patch() { + Semver semver = new Semver("1.2.3", Semver.SemverType.NPM); + String version2 = "1.2"; + assertFalse(semver.isEqualTo(version2)); + assertTrue(semver.isEquivalentTo(version2)); + } + + @Test public void isEqualTo_symmetric() { + Semver semver1 = new Semver("1.2.3", Semver.SemverType.NPM); + Semver semver2 = new Semver("1.2", Semver.SemverType.NPM); + assertFalse(semver1.isEqualTo(semver2)); + assertFalse(semver2.isEqualTo(semver1)); + } + + @Test public void isEquivalentTo_asymmetric() { + Semver semver1 = new Semver("1.2.3", Semver.SemverType.NPM); + Semver semver2 = new Semver("1.2", Semver.SemverType.NPM); + assertTrue(semver1.isEquivalentTo(semver2)); + assertFalse(semver2.isEquivalentTo(semver1)); + } + @Test public void statisfies_calls_the_requirement() { Requirement req = mock(Requirement.class); Semver semver = new Semver("1.2.2"); @@ -275,6 +306,40 @@ private static void assertIsSemver(Semver semver, String value, Integer major, I Semver semver = new Semver("1.2.3-beta.4+sha123456789"); semver.nextPatch().isEqualTo("1.2.4"); } + + @Test public void withIncSuffix_test() { + Semver semver = new Semver("1.2.3-Draft.1"); + Semver result = semver.withIncSuffix(1); + assertEquals("1.2.3-Draft.2", result.toString()); + } + + @Test public void withIncSuffix_test_reverse() { + Semver semver = new Semver("1.2.3-1.draft.2"); + Semver result = semver.withIncSuffix(0); + assertEquals("1.2.3-2.draft.2", result.toString()); + } + + @Test public void withIncSuffix_test_multiple() { + Semver semver = new Semver("1.2.3-1.2"); + Semver result = semver.withIncSuffix(1).withIncSuffix(0); + assertEquals("1.2.3-2.3", result.toString()); + } + + @Test public void withIncSuffix_test_increment() { + Semver semver = new Semver("1.2.3-draft.1"); + Semver result = semver.withIncSuffix(1,3); + assertEquals("1.2.3-draft.4", result.toString()); + } + + @Test(expected = SemverException.class) public void withIncSuffix_test_indexerror() { + Semver semver = new Semver("1.2.3-Draft.1"); + semver.withIncSuffix(2); + } + + @Test(expected = SemverException.class) public void withIncSuffix_test_formaterror() { + Semver semver = new Semver("1.2.3-Draft.B"); + semver.withIncSuffix(1); + } @Test public void toStrict_test() { String[][] versionGroups = new String[][]{