[jsemver] 44/95: Create SemVer Expression parser

Alexandre Viau reazem-guest at moszumanska.debian.org
Mon Feb 16 14:58:29 UTC 2015


This is an automated email from the git hooks/post-receive script.

reazem-guest pushed a commit to branch master
in repository jsemver.

commit 388f94915f928b97fdefb2e6f8d4749bb4504585
Author: Zafar Khaja <zafarkhaja at gmail.com>
Date:   Sun Nov 10 23:47:51 2013 +0400

    Create SemVer Expression parser
---
 README.md                                          |  90 ++++++---
 .../java/com/github/zafarkhaja/semver/Version.java |  19 ++
 .../zafarkhaja/semver/expr/ExpressionParser.java   | 222 +++++++++++++++++++++
 .../com/github/zafarkhaja/semver/VersionTest.java  |   7 +
 .../semver/expr/ExpressionParserTest.java          | 205 +++++++++++++++++++
 5 files changed, 516 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md
index 38b9cbc..e5a1251 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,10 @@
 Java SemVer v0.7.0 (SemVer 2) [![Build Status](https://travis-ci.org/zafarkhaja/java-semver.png)](https://travis-ci.org/zafarkhaja/java-semver)
-==================
+=============================
 
 Java SemVer is a Java implementation of the Semantic Versioning Specification
 (http://semver.org/).
 
-
-Versioning
-----------
+### Versioning ###
 Java SemVer is versioned according to the SemVer Specification.
 
 **NOTE**: The current release of the Java SemVer library has a major version of
@@ -19,13 +17,13 @@ Usage
 Below are some common use cases for the Java SemVer library.
 
 ### Creating Versions ###
-Java SemVer library is composed of one small package which contains a few
-classes. All the classes but one are package-private and are not accessible
-outside the package. The only public class is `Version` which acts as a
-_facade_ for the client code. By design, the `Version` class is made immutable
-by making its constructor package-private, so that it can not be subclassed or
-directly instantiated. Instead of public constructor, the `Version` class
-provides a _static factory method_, `Version.valueOf(String value)`.
+The main class of the Java SemVer library is `Version` which implements the
+Facade design pattern. By design, the `Version` class is made immutable by
+making its constructors package-private, so that it can not be subclassed or
+directly instantiated. Instead of public constructors, the `Version` class
+provides few _static factory methods_.
+
+One of the methods is the `Version.valueOf` method.
 
 ```java
 import com.github.zafarkhaja.semver.Version;
@@ -43,6 +41,17 @@ String build      = v.getBuildMetadata();     // "build.1"
 String str = v.toString(); // "1.0.0-rc.1+build.1"
 ```
 
+The other static factory method is `Version.forIntegers` which is also
+overloaded to allow fewer arguments.
+
+```java
+import com.github.zafarkhaja.semver.Version;
+
+Version v1 = Version.forIntegers(1);
+Version v2 = Version.forIntegers(1, 2);
+Version v3 = Version.forIntegers(1, 2, 3);
+```
+
 Another way to create a `Version` is to use a _builder_ class `Version.Builder`.
 
 ```java
@@ -76,15 +85,15 @@ import com.github.zafarkhaja.semver.Version;
 
 Version v1 = Version.valueOf("1.2.3");
 
-// Incrementing major version
+// Incrementing the major version
 Version v2 = v1.incrementMajorVersion();        // "2.0.0"
 Version v2 = v1.incrementMajorVersion("alpha"); // "2.0.0-alpha"
 
-// Incrementing minor version
+// Incrementing the minor version
 Version v3 = v1.incrementMinorVersion();        // "1.3.0"
 Version v3 = v1.incrementMinorVersion("alpha"); // "1.3.0-alpha"
 
-// Incrementing patch version
+// Incrementing the patch version
 Version v4 = v1.incrementPatchVersion();        // "1.2.4"
 Version v4 = v1.incrementPatchVersion("alpha"); // "1.2.4-alpha"
 
@@ -92,30 +101,32 @@ Version v4 = v1.incrementPatchVersion("alpha"); // "1.2.4-alpha"
 String str = v1.toString(); // "1.2.3"
 ```
 
-There are also incrementor methods for pre-release version and build metadata.
+There are also incrementor methods for the pre-release version and the build
+metadata.
 
 ```java
 import com.github.zafarkhaja.semver.Version;
 
-// Incrementing pre-release version
+// Incrementing the pre-release version
 Version v1 = Version.valueOf("1.2.3-rc");        // considered as "rc.0"
 Version v2 = v1.incrementPreReleaseVersion();    // "1.2.3-rc.1"
 Version v3 = v2.incrementPreReleaseVersion();    // "1.2.3-rc.2"
 
-// Incrementing build metadata
+// Incrementing the build metadata
 Version v1 = Version.valueOf("1.2.3-rc+build");  // considered as "build.0"
 Version v2 = v1.incrementBuildMetadata();        // "1.2.3-rc+build.1"
 Version v3 = v2.incrementBuildMetadata();        // "1.2.3-rc+build.2"
 ```
 
-When incrementing normal or pre-release versions build metadata is always dropped.
+When incrementing the normal or pre-release versions the build metadata is
+always dropped.
 
 ```java
 import com.github.zafarkhaja.semver.Version;
 
 Version v1 = Version.valueOf("1.2.3-beta+build");
 
-// Incrementing normal version
+// Incrementing the normal version
 Version v2 = v1.incrementMajorVersion();        // "2.0.0"
 Version v2 = v1.incrementMajorVersion("alpha"); // "2.0.0-alpha"
 
@@ -125,7 +136,7 @@ Version v3 = v1.incrementMinorVersion("alpha"); // "1.3.0-alpha"
 Version v4 = v1.incrementPatchVersion();        // "1.2.4"
 Version v4 = v1.incrementPatchVersion("alpha"); // "1.2.4-alpha"
 
-// Incrementing pre-release version
+// Incrementing the pre-release version
 Version v2 = v1.incrementPreReleaseVersion();   // "1.2.3-beta.1"
 ```
 **NOTE**: The discussion page https://github.com/mojombo/semver/issues/60 might
@@ -134,8 +145,8 @@ incrementor methods.
 
 ### Comparing Versions ###
 Comparing versions with Java SemVer is easy. The `Version` class implements the
-`Comparable` interface, it also overrides the `Object.equals(Object obj)` method
-and provides some more methods for convenient comparing.
+`Comparable` interface, it also overrides the `Object.equals` method and provides
+some more methods for convenient comparing.
 
 ```java
 import com.github.zafarkhaja.semver.Version;
@@ -152,7 +163,7 @@ boolean result = v1.lessThan(v2);              // true
 boolean result = v1.lessThanOrEqualTo(v2);     // true
 ```
 
-When determining version precedence build metadata is ignored (SemVer p.10).
+When determining version precedence the build metadata is ignored (SemVer p.10).
 
 ```java
 import com.github.zafarkhaja.semver.Version;
@@ -164,9 +175,9 @@ int result = v1.compareTo(v2);  // = 0
 boolean result = v1.equals(v2); // true
 ```
 
-Sometimes, however, you might want to compare versions with build metadata in
-mind. For such cases Java SemVer provides a _comparator_ `Version.BUILD_AWARE_ORDER`
-and a convenience method `Version.compareWithBuildsTo(Version other)`.
+Sometimes, however, you might want to compare versions with the build metadata
+in mind. For such cases Java SemVer provides a _comparator_ `Version.BUILD_AWARE_ORDER`
+and a convenience method `Version.compareWithBuildsTo`.
 
 ```java
 import com.github.zafarkhaja.semver.Version;
@@ -181,9 +192,34 @@ boolean result = v1.equals(v2);           // false
 ```
 
 
+SemVer Expressions API (Ranges)
+----------------------
+Since version 0.7.0 Java SemVer supports the SemVer Expressions API which is
+implemented as an external DSL. The BNF grammar for the SemVer Expressions DSL
+can be found in the corresponding issue
+"[Implement the SemVer Expressions API](https://github.com/zafarkhaja/java-semver/issues/1)".
+
+The entry point for the API is the `Version.satisfies` method.
+
+```java
+import com.github.zafarkhaja.semver.Version;
+
+Version v = Version.valueOf("1.0.0-beta");
+boolean result = v.satisfies(">=1.0.0 & <2.0.0");  // false
+```
+
+Below are examples of some common use cases, as well as syntactic sugar and some
+other interesting capabilities of the SemVer Expressions DSL.
+* Wildcard - `1.*` which is equivalent to `>=1.0.0 & <2.0.0`
+* Tilde operator - `~1.5` which is equivalent to `>=1.5.0 & <2.0.0`
+* Range - `1.0-2.0` which is equivalent to `>=1.0.0 & <=2.0.0`
+* Negation operator - `!(1.*)` which is equivalent to `<1.0.0 & >=2.0.0`
+* Short notation - `1` which is equivalent to `=1.0.0`
+* Parenthesized expression - `~1.3 | (1.4.* & !=1.4.5) | ~2`
+
+
 TODO
 ----
-* [Implement ranges](https://github.com/zafarkhaja/java-semver/issues/1)
 * [Write doc comments for all API classes and methods](https://github.com/zafarkhaja/java-semver/issues/2)
 
 
diff --git a/src/main/java/com/github/zafarkhaja/semver/Version.java b/src/main/java/com/github/zafarkhaja/semver/Version.java
index 40e1839..30a2db1 100644
--- a/src/main/java/com/github/zafarkhaja/semver/Version.java
+++ b/src/main/java/com/github/zafarkhaja/semver/Version.java
@@ -23,6 +23,8 @@
  */
 package com.github.zafarkhaja.semver;
 
+import com.github.zafarkhaja.semver.expr.Expression;
+import com.github.zafarkhaja.semver.expr.ExpressionParser;
 import java.util.Comparator;
 
 /**
@@ -116,6 +118,23 @@ public class Version implements Comparable<Version> {
         return VersionParser.parseValidSemVer(version);
     }
 
+    public static Version forIntegers(int major) {
+        return new Version(new NormalVersion(major, 0, 0));
+    }
+
+    public static Version forIntegers(int major, int minor) {
+        return new Version(new NormalVersion(major, minor, 0));
+    }
+
+    public static Version forIntegers(int major, int minor, int patch) {
+        return new Version(new NormalVersion(major, minor, patch));
+    }
+
+    public boolean satisfies(String expr) {
+        Parser<Expression> parser = ExpressionParser.newInstance();
+        return parser.parse(expr).interpret(this);
+    }
+
     public Version incrementMajorVersion() {
         return new Version(normal.incrementMajor());
     }
diff --git a/src/main/java/com/github/zafarkhaja/semver/expr/ExpressionParser.java b/src/main/java/com/github/zafarkhaja/semver/expr/ExpressionParser.java
new file mode 100644
index 0000000..e403bb2
--- /dev/null
+++ b/src/main/java/com/github/zafarkhaja/semver/expr/ExpressionParser.java
@@ -0,0 +1,222 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.expr;
+
+import com.github.zafarkhaja.semver.Parser;
+import com.github.zafarkhaja.semver.Version;
+import com.github.zafarkhaja.semver.expr.Lexer.Token;
+import com.github.zafarkhaja.semver.util.Stream;
+import com.github.zafarkhaja.semver.util.Stream.ElementType;
+import java.util.EnumSet;
+import java.util.Iterator;
+import static com.github.zafarkhaja.semver.expr.Lexer.Token.Type.*;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+public class ExpressionParser implements Parser<Expression> {
+
+    private final Lexer lexer;
+    private Stream<Token> tokens;
+
+    ExpressionParser(Lexer lexer) {
+        this.lexer = lexer;
+    }
+
+    public static Parser<Expression> newInstance() {
+        return new ExpressionParser(new Lexer());
+    }
+
+    @Override
+    public Expression parse(String input) {
+        tokens = lexer.tokenize(input);
+        return parseSemVerExpression();
+    }
+
+    private Expression parseSemVerExpression() {
+        Expression expr;
+        if (tokens.positiveLookahead(NOT)) {
+            tokens.consume();
+            tokens.consume(LEFT_PAREN);
+            expr = new Not(parseSemVerExpression());
+            tokens.consume(RIGHT_PAREN);
+        } else if (tokens.positiveLookahead(LEFT_PAREN)) {
+            tokens.consume(LEFT_PAREN);
+            expr = parseSemVerExpression();
+            tokens.consume(RIGHT_PAREN);
+        } else {
+            expr = parseExpression();
+        }
+        return parseBooleanExpression(expr);
+    }
+
+    private Expression parseBooleanExpression(Expression expr) {
+        if (tokens.positiveLookahead(AND)) {
+            tokens.consume();
+            expr = new And(expr, parseSemVerExpression());
+        } else if (tokens.positiveLookahead(OR)) {
+            tokens.consume();
+            expr = new Or(expr, parseSemVerExpression());
+        }
+        return expr;
+    }
+
+    private Expression parseExpression() {
+        if (tokens.positiveLookahead(TILDE)) {
+            return parseTildeExpression();
+        } else if (isVersionExpression()) {
+            return parseVersionExpression();
+        } else if (isRangeExpression()) {
+            return parseRangeExpression();
+        }
+        return parseComparisonExpression();
+    }
+
+    private Expression parseComparisonExpression() {
+        Token token = tokens.lookahead();
+        Expression expr;
+        switch (token.type) {
+            case EQUAL:
+                tokens.consume();
+                expr = new Equal(parseVersion());
+                break;
+            case NOT_EQUAL:
+                tokens.consume();
+                expr = new NotEqual(parseVersion());
+                break;
+            case GREATER:
+                tokens.consume();
+                expr = new Greater(parseVersion());
+                break;
+            case GREATER_EQUAL:
+                tokens.consume();
+                expr = new GreaterOrEqual(parseVersion());
+                break;
+            case LESS:
+                tokens.consume();
+                expr = new Less(parseVersion());
+                break;
+            case LESS_EQUAL:
+                tokens.consume();
+                expr = new LessOrEqual(parseVersion());
+                break;
+            default:
+                expr = new Equal(parseVersion());
+        }
+        return expr;
+    }
+
+    private Expression parseTildeExpression() {
+        tokens.consume(TILDE);
+        int major = intOf(tokens.consume(NUMERIC).lexeme);
+        if (!tokens.positiveLookahead(DOT)) {
+            return new GreaterOrEqual(versionOf(major, 0, 0));
+        }
+        tokens.consume(DOT);
+        int minor = intOf(tokens.consume(NUMERIC).lexeme);
+        if (!tokens.positiveLookahead(DOT)) {
+            return new And(
+                new GreaterOrEqual(versionOf(major, minor, 0)),
+                new Less(versionOf(major + 1, 0, 0))
+            );
+        }
+        tokens.consume(DOT);
+        int patch = intOf(tokens.consume(NUMERIC).lexeme);
+        return new And(
+            new GreaterOrEqual(versionOf(major, minor, patch)),
+            new Less(versionOf(major, minor + 1, 0))
+        );
+    }
+
+    private boolean isVersionExpression() {
+        return isVersionFollowedBy(STAR);
+    }
+
+    private Expression parseVersionExpression() {
+        int major = intOf(tokens.consume(NUMERIC).lexeme);
+        tokens.consume(DOT);
+        if (tokens.positiveLookahead(STAR)) {
+            tokens.consume();
+            return new And(
+                new GreaterOrEqual(versionOf(major, 0, 0)),
+                new Less(versionOf(major + 1, 0, 0))
+            );
+        }
+        int minor = intOf(tokens.consume(NUMERIC).lexeme);
+        tokens.consume(DOT);
+        tokens.consume(STAR);
+        return new And(
+            new GreaterOrEqual(versionOf(major, minor, 0)),
+            new Less(versionOf(major, minor + 1, 0))
+        );
+    }
+
+    private boolean isRangeExpression() {
+        return isVersionFollowedBy(HYPHEN);
+    }
+
+    private Expression parseRangeExpression() {
+        Expression ge = new GreaterOrEqual(parseVersion());
+        tokens.consume(HYPHEN);
+        Expression le = new LessOrEqual(parseVersion());
+        return new And(ge, le);
+    }
+
+    private Version parseVersion() {
+        int major = intOf(tokens.consume(NUMERIC).lexeme);
+        int minor = 0;
+        if (tokens.positiveLookahead(DOT)) {
+            tokens.consume();
+            minor = intOf(tokens.consume(NUMERIC).lexeme);
+        }
+        int patch = 0;
+        if (tokens.positiveLookahead(DOT)) {
+            tokens.consume();
+            patch = intOf(tokens.consume(NUMERIC).lexeme);
+        }
+        return versionOf(major, minor, patch);
+    }
+
+    private boolean isVersionFollowedBy(ElementType<Token> type) {
+        EnumSet<Token.Type> expected = EnumSet.of(NUMERIC, DOT);
+        Iterator<Token> it = tokens.iterator();
+        Token lookahead = null;
+        while (it.hasNext()) {
+            lookahead = it.next();
+            if (!expected.contains(lookahead.type)) {
+                break;
+            }
+        }
+        return type.isMatchedBy(lookahead);
+    }
+
+    private Version versionOf(int major, int minor, int patch) {
+        return Version.forIntegers(major, minor, patch);
+    }
+
+    private int intOf(String value) {
+        return Integer.parseInt(value);
+    }
+}
diff --git a/src/test/java/com/github/zafarkhaja/semver/VersionTest.java b/src/test/java/com/github/zafarkhaja/semver/VersionTest.java
index af65488..e993fa9 100644
--- a/src/test/java/com/github/zafarkhaja/semver/VersionTest.java
+++ b/src/test/java/com/github/zafarkhaja/semver/VersionTest.java
@@ -305,6 +305,13 @@ public class VersionTest {
             assertTrue(0 == v1.compareTo(v2));
             assertTrue(0 > v1.compareWithBuildsTo(v2));
         }
+
+        @Test
+        public void shouldCheckIfVersionSatisfiesExpression() {
+            Version v = Version.valueOf("2.0.0-beta");
+            assertTrue(v.satisfies("~1.0"));
+            assertFalse(v.satisfies(">=2.0 & <3.0"));
+        }
     }
 
     public static class EqualsMethodTest {
diff --git a/src/test/java/com/github/zafarkhaja/semver/expr/ExpressionParserTest.java b/src/test/java/com/github/zafarkhaja/semver/expr/ExpressionParserTest.java
new file mode 100644
index 0000000..e8cdd65
--- /dev/null
+++ b/src/test/java/com/github/zafarkhaja/semver/expr/ExpressionParserTest.java
@@ -0,0 +1,205 @@
+/*
+ * The MIT License
+ *
+ * Copyright 2013 Zafar Khaja <zafarkhaja at gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package com.github.zafarkhaja.semver.expr;
+
+import com.github.zafarkhaja.semver.Version;
+import com.github.zafarkhaja.semver.util.UnexpectedElementTypeException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Zafar Khaja <zafarkhaja at gmail.com>
+ */
+public class ExpressionParserTest {
+
+    @Test
+    public void shouldParseEqualComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression eq = parser.parse("=1.0.0");
+        assertTrue(eq.interpret(Version.valueOf("1.0.0")));
+    }
+
+    @Test
+    public void shouldParseEqualComparisonExpressionIfOnlyVersionGiven() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression eq = parser.parse("1.0.0");
+        assertTrue(eq.interpret(Version.valueOf("1.0.0")));
+    }
+
+    @Test
+    public void shouldParseNotEqualComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression ne = parser.parse("!=1.0.0");
+        assertTrue(ne.interpret(Version.valueOf("1.2.3")));
+    }
+
+    @Test
+    public void shouldParseGreaterComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression gt = parser.parse(">1.0.0");
+        assertTrue(gt.interpret(Version.valueOf("1.2.3")));
+    }
+
+    @Test
+    public void shouldParseGreaterOrEqualComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression ge = parser.parse(">=1.0.0");
+        assertTrue(ge.interpret(Version.valueOf("1.0.0")));
+        assertTrue(ge.interpret(Version.valueOf("1.2.3")));
+    }
+
+    @Test
+    public void shouldParseLessComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression lt = parser.parse("<1.2.3");
+        assertTrue(lt.interpret(Version.valueOf("1.0.0")));
+    }
+
+    @Test
+    public void shouldParseLessOrEqualComparisonExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression le = parser.parse("<=1.2.3");
+        assertTrue(le.interpret(Version.valueOf("1.0.0")));
+        assertTrue(le.interpret(Version.valueOf("1.2.3")));
+    }
+
+    @Test
+    public void shouldParseTildeExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr1 = parser.parse("~1");
+        assertTrue(expr1.interpret(Version.valueOf("1.2.3")));
+        assertTrue(expr1.interpret(Version.valueOf("3.2.1")));
+        Expression expr2 = parser.parse("~1.2");
+        assertTrue(expr2.interpret(Version.valueOf("1.2.3")));
+        assertFalse(expr2.interpret(Version.valueOf("2.0.0")));
+        Expression expr3 = parser.parse("~1.2.3");
+        assertTrue(expr3.interpret(Version.valueOf("1.2.3")));
+        assertFalse(expr3.interpret(Version.valueOf("1.3.0")));
+    }
+
+    @Test
+    public void shouldParseShortFormOfVersion() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr1 = parser.parse("1");
+        assertTrue(expr1.interpret(Version.valueOf("1.0.0")));
+        Expression expr2 = parser.parse("2.0");
+        assertTrue(expr2.interpret(Version.valueOf("2.0.0")));
+    }
+
+    @Test
+    public void shouldParseVersionExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr1 = parser.parse("1.*");
+        assertTrue(expr1.interpret(Version.valueOf("1.2.3")));
+        assertFalse(expr1.interpret(Version.valueOf("3.2.1")));
+        Expression expr2 = parser.parse("1.2.*");
+        assertTrue(expr2.interpret(Version.valueOf("1.2.3")));
+        assertFalse(expr2.interpret(Version.valueOf("1.3.2")));
+    }
+
+    @Test
+    public void shouldParseRangeExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression range = parser.parse("1.0.0 - 2.0.0");
+        assertTrue(range.interpret(Version.valueOf("1.2.3")));
+        assertFalse(range.interpret(Version.valueOf("3.2.1")));
+    }
+
+    @Test
+    public void shouldParseAndBooleanExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression and = parser.parse(">=1.0.0 & <2.0.0");
+        assertTrue(and.interpret(Version.valueOf("1.2.3")));
+        assertFalse(and.interpret(Version.valueOf("3.2.1")));
+    }
+
+    @Test
+    public void shouldParseOrBooleanExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression or = parser.parse("1.* | =2.0.0");
+        assertTrue(or.interpret(Version.valueOf("1.2.3")));
+        assertFalse(or.interpret(Version.valueOf("2.1.0")));
+    }
+
+    @Test
+    public void shouldParseParenthesizedExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr = parser.parse("(1)");
+        assertTrue(expr.interpret(Version.valueOf("1.0.0")));
+        assertFalse(expr.interpret(Version.valueOf("2.0.0")));
+    }
+
+    @Test
+    public void shouldParseExpressionWithMultipleParentheses() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr = parser.parse("((1))");
+        assertTrue(expr.interpret(Version.valueOf("1.0.0")));
+        assertFalse(expr.interpret(Version.valueOf("2.0.0")));
+    }
+
+    @Test
+    public void shouldParseNotExpression() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression not1 = parser.parse("!(1)");
+        assertTrue(not1.interpret(Version.valueOf("2.0.0")));
+        assertFalse(not1.interpret(Version.valueOf("1.0.0")));
+        Expression not2 = parser.parse("0.* & !(>=1 & <2)");
+        assertTrue(not2.interpret(Version.valueOf("0.5.0")));
+        assertFalse(not2.interpret(Version.valueOf("1.0.1")));
+        Expression not3 = parser.parse("!(>=1 & <2) & >=2");
+        assertTrue(not3.interpret(Version.valueOf("2.0.0")));
+        assertFalse(not3.interpret(Version.valueOf("1.2.3")));
+    }
+
+    @Test
+    public void shouldRespectPrecedenceWhenUsedWithParentheses() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr1 = parser.parse("(~1.0 & <2.0) | >2.0");
+        assertTrue(expr1.interpret(Version.valueOf("2.5.0")));
+        Expression expr2 = parser.parse("~1.0 & (<2.0 | >2.0)");
+        assertFalse(expr2.interpret(Version.valueOf("2.5.0")));
+    }
+
+    @Test
+    public void shouldParseComplexExpressions() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        Expression expr = parser.parse(
+            "((>=1.0.1 & <2) | (>=3.0 & <4)) & ((1-1.5) & (~1.5))"
+        );
+        assertTrue(expr.interpret(Version.valueOf("1.5.0")));
+        assertFalse(expr.interpret(Version.valueOf("2.5.0")));
+    }
+
+    @Test
+    public void shouldRaiseErrorIfClosingParenthesisIsMissing() {
+        ExpressionParser parser = new ExpressionParser(new Lexer());
+        try {
+            parser.parse("((>=1.0.1 & < 2)");
+        } catch (UnexpectedElementTypeException e) {
+            return;
+        }
+        fail("Should raise error if closing parenthesis is missing");
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jsemver.git



More information about the pkg-java-commits mailing list