Skip to content

Commit

Permalink
Support empty lower and upper bounds in P2 VersionRange
Browse files Browse the repository at this point in the history
This enables support for version range specifications where the lower or
upper bound is not specified and the delimiting bracket is directly next
to the separating colon. For example '[1,)' or '(,1]' but also '(,)' can
then be parsed successfully.

The type of supported brackets for an empty or unspecified bound is
something that's not inherently obvious. From a mathematical perspective
an empty bound can be considered equivalent to infinity. And in math may
not say inclusive infinity. From that perspective only '[1,infinity)'
respectively '[1,)' would be permitted, but not '[1,]'.
But technically infinity has a specific value for P2 versions, i.e.
Version.MAX_VERSION. So if one really wants to cover all possible
values, the upper bound still has to be inclusive, i.e. ']'. For the
lower bound the situation is similar, where '0.0.0' or the
'empty'-version is the smallest one possible.
  • Loading branch information
HannesWell committed Oct 19, 2024
1 parent 3388118 commit 3af8373
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,11 @@ public VersionRange(String versionRange) {
} else {
fmt = VersionFormat.OSGI_FORMAT;
}
String minStr;
String maxStr;
final String minStr;
final String maxStr;
StringBuilder sb = new StringBuilder();
if (c == '[' || c == '(') {
includeMin = (c == '[');
boolean isClosedBound = c == '[';
pos = copyEscaped(versionRange, ++pos, ",)]", sb); //$NON-NLS-1$
if (pos >= top) {
throw new IllegalArgumentException(NLS.bind(Messages.premature_EOS_0, versionRange));
Expand All @@ -171,6 +171,7 @@ public VersionRange(String versionRange) {
throw new IllegalArgumentException(NLS.bind(Messages.missing_comma_in_range_0, versionRange));
}
minStr = sb.toString();
includeMin = isClosedBound || minStr.isEmpty();
sb.setLength(0);
pos = copyEscaped(versionRange, pos, ")]", sb); //$NON-NLS-1$
if (pos >= top) {
Expand All @@ -179,7 +180,7 @@ public VersionRange(String versionRange) {
maxStr = sb.toString();

c = versionRange.charAt(pos++);
includeMax = (c == ']');
includeMax = c == ']' || maxStr.isEmpty();
} else {
StringBuilder sbMin = new StringBuilder();
pos = copyEscaped(versionRange, pos, rawPrefix ? "/" : null, sbMin); //$NON-NLS-1$
Expand Down Expand Up @@ -248,9 +249,11 @@ public VersionRange(String versionRange) {
}
}
}
minVersion = VersionFormat.parseRaw(minStr, fmt, origMin);
minVersion = minStr.isEmpty() ? Version.emptyVersion : VersionFormat.parseRaw(minStr, fmt, origMin);
if (maxStr != null) {
if (maxStr.equals(minStr)) {
if (maxStr.isEmpty()) {
maxVersion = Version.MAX_VERSION;
} else if (maxStr.equals(minStr)) {
maxVersion = minVersion;
} else {
maxVersion = VersionFormat.parseRaw(maxStr, fmt, origMax);
Expand All @@ -262,9 +265,11 @@ public VersionRange(String versionRange) {
if (fmt == null) {
fmt = VersionFormat.OSGI_FORMAT;
}
minVersion = fmt.parse(minStr);
minVersion = minStr.isEmpty() ? Version.emptyVersion : fmt.parse(minStr);
if (maxStr != null) {
if (maxStr.equals(minStr)) {
if (maxStr.isEmpty()) {
maxVersion = Version.MAX_VERSION;
} else if (maxStr.equals(minStr)) {
maxVersion = minVersion;
} else {
maxVersion = fmt.parse(maxStr);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Cloudsmith Inc. and others.
* Copyright (c) 2009, 2024 Cloudsmith Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -27,6 +27,10 @@
* Tests ranges of versions specified with osgi (default) version format.
*/
public class OSGiRangeTest extends VersionTesting {

private static Version ONE = Version.parseVersion("1");
private static Version TWO = Version.parseVersion("2");

@Test
public void testSingleVersionRange() {
VersionRange range;
Expand Down Expand Up @@ -100,6 +104,35 @@ public void testRangeStrings() {
assertEquals("(1.0.0.abcdef,2.0.0.abcdef)", v.toString());
}

@Test
public void testEmptyRange() {
assertBounds("", true, Version.emptyVersion, Version.MAX_VERSION, true);
}

@Test
public void testExplicitLowerAndUpperBound() {
assertBounds("[1,2)", true, ONE, TWO, false);
assertBounds("[1,2]", true, ONE, TWO, true);
}

@Test
public void testNoLowerBound() {
assertBounds("(,1)", true, Version.emptyVersion, ONE, false);
assertBounds("[,1)", true, Version.emptyVersion, ONE, false);
}

@Test
public void testNoUpperBound() {
assertBounds("[1,)", true, ONE, Version.MAX_VERSION, true);
assertBounds("[1,]", true, ONE, Version.MAX_VERSION, true);
}

@Test
public void testNoLowerAndUpperBound() {
assertBounds("(,)", true, Version.emptyVersion, Version.MAX_VERSION, true);
assertBounds("[,]", true, Version.emptyVersion, Version.MAX_VERSION, true);
}

/**
* Tests that null values passed to the {@link VersionRange} constructor are not
* interpreted as MIN/MAX versions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
* Tests version ranges specified using raw.
*/
public class RawRangeTest extends VersionTesting {

private static Version ONE = Version.parseVersion("raw:1");
private static Version TWO = Version.parseVersion("raw:2");

@Test
public void testEmptyRange() {
VersionRange range = new VersionRange("raw:''");
Expand Down Expand Up @@ -112,6 +116,30 @@ public void testLowerThan() {
assertNotIncludedInRange("1.5", upperBound, "raw:2.1");
}

@Test
public void testExplicitLowerAndUpperBound() {
assertBounds("raw:[1,2)", true, ONE, TWO, false);
assertBounds("raw:[1,2]", true, ONE, TWO, true);
}

@Test
public void testNoLowerBound() {
assertBounds("raw:(,1)", true, Version.emptyVersion, ONE, false);
assertBounds("raw:[,1)", true, Version.emptyVersion, ONE, false);
}

@Test
public void testNoUpperBound() {
assertBounds("raw:[1,)", true, ONE, Version.MAX_VERSION, true);
assertBounds("raw:[1,]", true, ONE, Version.MAX_VERSION, true);
}

@Test
public void testNoLowerAndUpperBound() {
assertBounds("raw:(,)", true, Version.emptyVersion, Version.MAX_VERSION, true);
assertBounds("raw:[,]", true, Version.emptyVersion, Version.MAX_VERSION, true);
}

@Test
public void testSerialize() {
VersionRange v = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2017 Cloudsmith Inc. and others.
* Copyright (c) 2009, 2024 Cloudsmith Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -33,6 +33,7 @@
* Base class for version testing. Adds useful assert methods.
*/
public class VersionTesting {

/**
* Asserts that the versionString version is included in the range.
*/
Expand All @@ -47,6 +48,15 @@ public void assertNotIncludedInRange(String message, VersionRange range, String
assertFalse(message, range.isIncluded(Version.parseVersion(versionString)));
}

public void assertBounds(String rangeSpecification, boolean includeMin, Version lowerBound, Version upperBound,
boolean includeMax) {
VersionRange range = new VersionRange(rangeSpecification);
assertEquals(includeMin, range.getIncludeMinimum());
assertEquals(includeMax, range.getIncludeMaximum());
assertEquals(lowerBound, range.getMinimum());
assertEquals(upperBound, range.getMaximum());
}

/**
* A strict assertion of order. asserts that b > a, a < b, a !=b, b != a
*/
Expand Down

0 comments on commit 3af8373

Please sign in to comment.