ExpressionParserTest.java
/*
* The MIT License
*
* Copyright 2012-2024 Zafar Khaja <zafarkhaja@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 java.util.Arrays;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
*
* @author Zafar Khaja {@literal <zafarkhaja@gmail.com>}
*/
class ExpressionParserTest {
@Test
void shouldParseEqualComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression eq = parser.parse("=1.0.0");
assertTrue(eq.interpret(Version.of(1, 0, 0)));
}
@Test
void shouldParseEqualComparisonRangeIfOnlyFullVersionGiven() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression eq = parser.parse("1.0.0");
assertTrue(eq.interpret(Version.of(1, 0, 0)));
}
@Test
void shouldParseNotEqualComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression ne = parser.parse("!=1.0.0");
assertTrue(ne.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldParseGreaterComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression gt = parser.parse(">1.0.0");
assertTrue(gt.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldParseGreaterOrEqualComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression ge = parser.parse(">=1.0.0");
assertTrue(ge.interpret(Version.of(1, 0, 0)));
assertTrue(ge.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldParseLessComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression lt = parser.parse("<1.2.3");
assertTrue(lt.interpret(Version.of(1, 0, 0)));
}
@Test
void shouldParseLessOrEqualComparisonRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression le = parser.parse("<=1.2.3");
assertTrue(le.interpret(Version.of(1, 0, 0)));
assertTrue(le.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldSupportLongNumericIdentifiersInComparisonRanges() {
long l = Integer.MAX_VALUE + 1L;
String version = "=" + l + "." + l + "." + l;
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression eq = parser.parse(version);
assertTrue(eq.interpret(Version.of(l, l, l)));
}
@Test
void shouldParseTildeRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr1 = parser.parse("~1");
assertTrue(expr1.interpret(Version.of(1, 2, 3)));
assertFalse(expr1.interpret(Version.of(3, 2, 1)));
Expression expr2 = parser.parse("~1.2");
assertTrue(expr2.interpret(Version.of(1, 2, 3)));
assertFalse(expr2.interpret(Version.of(2, 0, 0)));
Expression expr3 = parser.parse("~1.2.3");
assertTrue(expr3.interpret(Version.of(1, 2, 3)));
assertFalse(expr3.interpret(Version.of(1, 3, 0)));
}
@Test
void shouldSupportLongNumericIdentifiersInTildeRanges() {
long l = Integer.MAX_VALUE + 1L;
String tildeRange = "~" + l + "." + l + "." + l;
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse(tildeRange);
assertTrue(expr.interpret(Version.of(l, l, l)));
}
@Test
void shouldRaiseErrorIfIncrementCausesOverflowInTildeRanges() {
long lmv = Long.MAX_VALUE;
ExpressionParser parser = new ExpressionParser(new Lexer());
for (String r : Arrays.asList(("~" + lmv), ("~1." + lmv), ("~1." + lmv + ".0"))) {
assertThrows(ArithmeticException.class, () -> parser.parse(r));
}
}
@Test
void shouldParseCaretRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr1 = parser.parse("^1");
assertTrue(expr1.interpret(Version.of(1, 2, 3)));
assertFalse(expr1.interpret(Version.of(3, 2, 1)));
Expression expr2 = parser.parse("^0.2");
assertTrue(expr2.interpret(Version.of(0, 2, 3)));
assertFalse(expr2.interpret(Version.of(0, 3, 0)));
Expression expr3 = parser.parse("^0.0.3");
assertTrue(expr3.interpret(Version.of(0, 0, 3)));
assertFalse(expr3.interpret(Version.of(0, 0, 4)));
}
@Test
void shouldSupportLongNumericIdentifiersInCaretRanges() {
long l = Integer.MAX_VALUE + 1L;
String caretRange = "^" + l + "." + l + "." + l;
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse(caretRange);
assertTrue(expr.interpret(Version.of(l, l, l)));
}
@Test
void shouldRaiseErrorIfIncrementCausesOverflowInCaretRanges() {
long lmv = Long.MAX_VALUE;
ExpressionParser parser = new ExpressionParser(new Lexer());
for (String r : Arrays.asList(("^" + lmv), ("^0." + lmv), ("^0.0." + lmv))) {
assertThrows(ArithmeticException.class, () -> parser.parse(r));
}
}
@Test
void shouldParsePartialVersionRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr1 = parser.parse("1");
assertTrue(expr1.interpret(Version.of(1, 2, 3)));
Expression expr2 = parser.parse("2.0");
assertTrue(expr2.interpret(Version.of(2, 0, 9)));
}
@Test
void shouldSupportLongNumericIdentifiersInPartialVersionRanges() {
long l = Integer.MAX_VALUE + 1L;
String partialVersion = l + "." + l;
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse(partialVersion);
assertTrue(expr.interpret(Version.of(l, l, l)));
}
@Test
void shouldRaiseErrorIfIncrementCausesOverflowInPartialVersionRanges() {
String lmv = String.valueOf(Long.MAX_VALUE);
ExpressionParser parser = new ExpressionParser(new Lexer());
for (String r : Arrays.asList((lmv), ("1." + lmv))) {
assertThrows(ArithmeticException.class, () -> parser.parse(r));
}
}
@Test
void shouldParseWildcardRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr1 = parser.parse("1.*");
assertTrue(expr1.interpret(Version.of(1, 2, 3)));
assertFalse(expr1.interpret(Version.of(3, 2, 1)));
Expression expr2 = parser.parse("1.2.x");
assertTrue(expr2.interpret(Version.of(1, 2, 3)));
assertFalse(expr2.interpret(Version.of(1, 3, 2)));
Expression expr3 = parser.parse("X");
assertTrue(expr3.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldSupportLongNumericIdentifiersInWildcardRanges() {
long l = Integer.MAX_VALUE + 1L;
String wildcardRange = l + "." + l + "." + "x";
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse(wildcardRange);
assertTrue(expr.interpret(Version.of(l, l, l)));
}
@Test
void shouldRaiseErrorIfIncrementCausesOverflowInWildcardRanges() {
long lmv = Long.MAX_VALUE;
ExpressionParser parser = new ExpressionParser(new Lexer());
for (String r : Arrays.asList((lmv + ".x"), ("1." + lmv + ".x"))) {
assertThrows(ArithmeticException.class, () -> parser.parse(r));
}
}
@Test
void shouldParseHyphenRange() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression range = parser.parse("1.0.0 - 2.0.0");
assertTrue(range.interpret(Version.of(1, 2, 3)));
assertFalse(range.interpret(Version.of(3, 2, 1)));
}
@Test
void shouldParseMultipleRangesJoinedWithAnd() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression and = parser.parse(">=1.0.0 && <2.0.0");
assertTrue(and.interpret(Version.of(1, 2, 3)));
assertFalse(and.interpret(Version.of(3, 2, 1)));
}
@Test
void shouldParseMultipleRangesJoinedWithOr() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression or = parser.parse("1.* || =2.0.0");
assertTrue(or.interpret(Version.of(1, 2, 3)));
assertFalse(or.interpret(Version.of(2, 1, 0)));
}
@Test
void shouldParseParenthesizedExpression() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse("(1)");
assertTrue(expr.interpret(Version.of(1, 2, 3)));
assertFalse(expr.interpret(Version.of(2, 0, 0)));
}
@Test
void shouldParseExpressionWithMultipleParentheses() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr = parser.parse("((1))");
assertTrue(expr.interpret(Version.of(1, 2, 3)));
assertFalse(expr.interpret(Version.of(2, 0, 0)));
}
@Test
void shouldParseNotExpression() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression not1 = parser.parse("!(1)");
assertTrue(not1.interpret(Version.of(2, 0, 0)));
assertFalse(not1.interpret(Version.of(1, 2, 3)));
Expression not2 = parser.parse("0.* & !(>=1 & <2)");
assertTrue(not2.interpret(Version.of(0, 5, 0)));
assertFalse(not2.interpret(Version.of(1, 0, 1)));
Expression not3 = parser.parse("!(>=1 & <2) & >=2");
assertTrue(not3.interpret(Version.of(2, 0, 0)));
assertFalse(not3.interpret(Version.of(1, 2, 3)));
}
@Test
void shouldRespectPrecedenceWhenUsedWithParentheses() {
ExpressionParser parser = new ExpressionParser(new Lexer());
Expression expr1 = parser.parse("(~1.0 & <2.0) | >2.0");
assertTrue(expr1.interpret(Version.of(2, 5, 0)));
Expression expr2 = parser.parse("~1.0 & (<2.0 | >2.0)");
assertFalse(expr2.interpret(Version.of(2, 5, 0)));
}
@Test
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.of(1, 5, 0)));
assertFalse(expr.interpret(Version.of(2, 5, 0)));
}
}