Skip to content

Commit

Permalink
? instead of 0 for unsignificant version disance parts
Browse files Browse the repository at this point in the history
Signed-off-by: Walter de Boer <walterdeboer@dbso.nl>
  • Loading branch information
Walter de Boer committed Mar 1, 2023
1 parent 3a7516f commit ce81753
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 59 deletions.
66 changes: 44 additions & 22 deletions src/main/java/org/dependencytrack/util/VersionDistance.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.io.Serializable;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.dependencytrack.exception.PolicyException;

/**
* A version distance consists of four parts for each of the difference in epoch,
Expand Down Expand Up @@ -62,7 +61,7 @@ public class VersionDistance implements Comparable<VersionDistance>, Serializab
private static final Pattern DISTANCE_PATTERN = Pattern.compile(
// Optional epoch part: numbers before the first : sign.
"^(?:(?<"+GROUP_EPOCH+">\\d+):)?" +
"(?<"+GROUP_MAJOR+">\\d+)(?:\\.(?<"+GROUP_MINOR+">\\d+))?(?:\\.(?<"+GROUP_PATCH+">\\d+))?$"
"(?<"+GROUP_MAJOR+">\\?|\\d+)(?:\\.(?<"+GROUP_MINOR+">\\?|\\d+))?(?:\\.(?<"+GROUP_PATCH+">\\?|\\d+))?$"
);

// Semver-like version:any numbers parts without characters appended, with optial leading v.
Expand All @@ -89,32 +88,52 @@ public VersionDistance() {
this.patch = 0;
}

public VersionDistance(int major, int minor, int patch) {
this.epoch = 0;
this.major = major;
this.minor = minor;
this.patch = patch;
private void validate() throws IllegalArgumentException {
if ((epoch != 0) && ((major >= 0) || (minor >= 0) || (patch >= 0))) {
throw new IllegalArgumentException("Only the most significant number can be greater than 0, more significant parts cannot be ?");
}
if ((major != 0) && ((minor >= 0) || (patch >= 0))) {
throw new IllegalArgumentException("Only the most significant number can be greater than 0, more significant parts cannot be ?");
}
if ((minor != 0) && (patch >= 0)) {
throw new IllegalArgumentException("Only the most significant number can be greater than 0, more significant parts cannot be ?");
}
}

public VersionDistance(int epoch, int major, int minor, int patch) {
public VersionDistance(int major, int minor, int patch) throws IllegalArgumentException {
this (0, major, minor, patch);
}

public VersionDistance(int epoch, int major, int minor, int patch) throws IllegalArgumentException {
this.epoch = epoch;
this.major = major;
this.minor = minor;
this.patch = patch;
validate ();
}

public VersionDistance(String distance) throws NumberFormatException {
this.epoch = 0;
this.major = 0;
this.minor = 0;
this.patch = 0;
public VersionDistance(String distance) throws NumberFormatException, IllegalArgumentException {
epoch = 0;
major = 0;
minor = 0;
patch = 0;
if (!StringUtils.isEmpty(distance)) {
final var distanceMatcher = DISTANCE_PATTERN.matcher(distance);
if (distanceMatcher.matches()) {
epoch = parseVersion(distanceMatcher.group(GROUP_EPOCH));
if (epoch == -1) {
epoch = 0;
}
major = parseVersion(distanceMatcher.group(GROUP_MAJOR));
minor = parseVersion(distanceMatcher.group(GROUP_MINOR));
if ((major != 0) && (distanceMatcher.group(GROUP_MINOR) == null)) {
minor = -1;
}
patch = parseVersion(distanceMatcher.group(GROUP_PATCH));
if ((minor != 0) && (distanceMatcher.group(GROUP_PATCH) == null)) {
patch = -1;
}
validate();
} else {
throw new NumberFormatException("Invallid version distance: " + distance);
}
Expand All @@ -125,6 +144,9 @@ private static int parseVersion(String version) throws NumberFormatException {
if (StringUtils.isEmpty(version)) {
return 0;
}
if ("?".equals(version)) {
return -1;
}
return Integer.parseInt(version);
}

Expand Down Expand Up @@ -231,7 +253,7 @@ public int compareTo(VersionDistance other) {

@Override
public String toString() {
return epoch + ":" + major + "." + minor + "." + patch;
return epoch + ":" + (major < 0 ? "?" : major) + "." + (minor < 0 ? "?" : minor) + "." + (patch < 0 ? "?" : patch);
}

/**
Expand All @@ -242,10 +264,10 @@ public String toString() {
*
* Only the first difference number will be set, all others will be 0. So the
* distance will look like <epoch>:<major>.<minor>.<patch>:
* 1:0.0.0
* 0:1.0.0
* 1:0.0.0
* 0:0.3.0
* 1:?.?.?
* 0:1.?.?
* 1:?.?.?
* 0:0.3.?
* 0:0.0.1
*
* @param version1 the first version
Expand Down Expand Up @@ -279,20 +301,20 @@ public static VersionDistance getVersionDistance(String version1, String version
final var minorDistance = Math.abs(minor2 - minor1);
final var patchDistance = Math.abs(patch2 - patch1);
if (epochDistance != 0) {
return new VersionDistance(epochDistance, 0, 0, 0);
return new VersionDistance(epochDistance, -1, -1, -1);
}
if (majorDistance != 0) {
return new VersionDistance(0, majorDistance, 0, 0);
return new VersionDistance(0, majorDistance, -1, -1);
}
if (minorDistance != 0) {
return new VersionDistance(0, 0, minorDistance, 0);
return new VersionDistance(0, 0, minorDistance, -1);
}
if (patchDistance != 0) {
return new VersionDistance(0, 0, 0, patchDistance);
}
return new VersionDistance(0, 0, 0, 0);
}
throw new PolicyException("Incompatible versions: " + version1 + ", " + version2);
throw new NumberFormatException("Incompatible versions: " + version1 + ", " + version2);
}

}
83 changes: 46 additions & 37 deletions src/test/java/org/dependencytrack/util/VersionDistanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,90 @@ public class VersionDistanceTest {

@Test
public void testVersionDistance() {
Assert.assertEquals("0:1.?.?", new VersionDistance("1").toString());
Assert.assertEquals("1:?.?.?", new VersionDistance("1:?").toString());
Assert.assertEquals("0:0.0.0", new VersionDistance().toString());
Assert.assertEquals("0:0.0.0", new VersionDistance(null).toString());
Assert.assertEquals("0:0.0.0", new VersionDistance(0,0,0).toString());
Assert.assertEquals("0:1.2.3", new VersionDistance(1,2,3).toString());
Assert.assertEquals("0:1.2.0", new VersionDistance(1, 2, 0).toString());
Assert.assertEquals("0:1.2.3", new VersionDistance("1.2.3").toString());
Assert.assertEquals("0:1.0.0", new VersionDistance("1").toString());
Assert.assertEquals("1:1.2.0", new VersionDistance("1:1.2.0").toString());
Assert.assertEquals("2:1.2.0", new VersionDistance("2:1.2").toString());
Assert.assertEquals("0:1.?.?", new VersionDistance(1, -1,-1).toString());
Assert.assertEquals("0:0.2.?", new VersionDistance(0, 2, -1).toString());
Assert.assertEquals("0:0.2.?", new VersionDistance("0:0.2").toString());
Assert.assertEquals("0:2.?.?", new VersionDistance("2").toString());

Assert.assertThrows(NumberFormatException.class, () -> new VersionDistance("ax").toString());
Assert.assertThrows(NumberFormatException.class, () -> new VersionDistance("1a").toString());
Assert.assertThrows(NumberFormatException.class, () -> new VersionDistance("1.2.3.4").toString());
Assert.assertThrows(NumberFormatException.class, () -> new VersionDistance("1a.2b.3c").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("1.0.0").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("1.1.0").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("?:1.0.0").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("0:?.0.0").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("?:1.0.0").toString());
Assert.assertThrows(IllegalArgumentException.class, () -> new VersionDistance("0:?.1.0").toString());
}

@Test
public void testCompareTo() {
Assert.assertTrue(new VersionDistance("2.1.1").compareTo(new VersionDistance("1.1.1")) > 0);
Assert.assertEquals(0, new VersionDistance(null).compareTo(new VersionDistance("0")));
Assert.assertTrue(new VersionDistance("2.?.?").compareTo(new VersionDistance("1.?.?")) > 0);

Assert.assertEquals(0, new VersionDistance().compareTo(new VersionDistance()));
Assert.assertEquals(0, new VersionDistance(null).compareTo(new VersionDistance("0")));
Assert.assertEquals(0, new VersionDistance("0.0").compareTo(new VersionDistance("0")));
Assert.assertEquals(0, new VersionDistance("1.1.1").compareTo(new VersionDistance("1.1.1")));
Assert.assertEquals(0, new VersionDistance("1.?.?").compareTo(new VersionDistance("1.?.?")));

Assert.assertTrue(new VersionDistance("1").compareTo(new VersionDistance()) > 0);
Assert.assertTrue(new VersionDistance("1").compareTo(new VersionDistance(null)) > 0);
Assert.assertTrue(new VersionDistance("1.0").compareTo(new VersionDistance("0")) > 0);
Assert.assertTrue(new VersionDistance("1.1.2").compareTo(new VersionDistance("1.1.1")) > 0);
Assert.assertTrue(new VersionDistance("1.2.1").compareTo(new VersionDistance("1.1.1")) > 0);
Assert.assertTrue(new VersionDistance("2.1.1").compareTo(new VersionDistance("1.1.1")) > 0);

Assert.assertTrue(new VersionDistance("1.?").compareTo(new VersionDistance("0")) > 0);
Assert.assertTrue(new VersionDistance("1.?.?").compareTo(new VersionDistance("0.0")) > 0);
Assert.assertTrue(new VersionDistance("1.?.?").compareTo(new VersionDistance("0.0.0")) > 0);
Assert.assertTrue(new VersionDistance("2.?.?").compareTo(new VersionDistance("1.?.?")) > 0);
Assert.assertTrue(new VersionDistance("1.?.?").compareTo(new VersionDistance("0.1.?")) > 0);
Assert.assertTrue(new VersionDistance("0.1.?").compareTo(new VersionDistance("0.0.1")) > 0);

Assert.assertTrue(new VersionDistance().compareTo(new VersionDistance("1")) < 0);
Assert.assertTrue(new VersionDistance(null).compareTo(new VersionDistance("1")) < 0);
Assert.assertTrue(new VersionDistance("0").compareTo(new VersionDistance("1.0")) < 0);
Assert.assertTrue(new VersionDistance("0").compareTo(new VersionDistance("1.?")) < 0);
Assert.assertTrue(new VersionDistance("0.0").compareTo(new VersionDistance("0.0.1")) < 0);
Assert.assertTrue(new VersionDistance("1.1.1").compareTo(new VersionDistance("1.1.2")) < 0);
Assert.assertTrue(new VersionDistance("1.1.1").compareTo(new VersionDistance("1.2.1")) < 0);
Assert.assertTrue(new VersionDistance("1.1.1").compareTo(new VersionDistance("2.1.1")) < 0);
Assert.assertTrue(new VersionDistance("0.1.2").compareTo(new VersionDistance("0.2.1")) < 0);
Assert.assertTrue(new VersionDistance("0.1.?").compareTo(new VersionDistance("1.?.?")) < 0);
Assert.assertTrue(new VersionDistance("1.?.?").compareTo(new VersionDistance("2.?.?")) < 0);
Assert.assertTrue(new VersionDistance("1.?.?").compareTo(new VersionDistance("2.?.?")) < 0);
Assert.assertTrue(new VersionDistance("0.1.?").compareTo(new VersionDistance("0.2.?")) < 0);
Assert.assertTrue(new VersionDistance("0.0.1").compareTo(new VersionDistance("0.0.2")) < 0);

Assert.assertTrue(VersionDistance.getVersionDistance("1.2.3", "0.1.2").compareTo(new VersionDistance("1.0.0")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.2.3", "1.3.1").compareTo(new VersionDistance("0.1.0")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.2.3", "1.3.1").compareTo(new VersionDistance("0.1.0")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.2.3", "2.1.1").compareTo(new VersionDistance("1.0.0")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.0.0", "0.1.0").compareTo(new VersionDistance("1.?.?")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.1.0", "1.0.0").compareTo(new VersionDistance("0.1.?")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.0.0", "1.1.0").compareTo(new VersionDistance("0.1.?")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.2.3", "2.1.0").compareTo(new VersionDistance("1.?.?")) == 0);
Assert.assertTrue(VersionDistance.getVersionDistance("2.2.2", "2.4.4").compareTo(new VersionDistance("0.1.?")) > 0);
Assert.assertTrue(VersionDistance.getVersionDistance("1.1.1", "1.1.3").compareTo(new VersionDistance("0.0.1")) > 0);
}

@Test
public void testEquals() {
Assert.assertEquals(new VersionDistance("0.0"), new VersionDistance(""));
Assert.assertEquals(new VersionDistance("0:0"), new VersionDistance(null));
Assert.assertEquals(new VersionDistance("4:1.2.3"), new VersionDistance("4:1.2.3"));
Assert.assertEquals(new VersionDistance("1.0.0"), new VersionDistance("1"));
Assert.assertEquals(new VersionDistance("0:1.2.0"), new VersionDistance("1.2"));
Assert.assertEquals(new VersionDistance("4:?.?.?"), new VersionDistance("4:?"));
Assert.assertEquals(new VersionDistance("1.?.?"), new VersionDistance("1"));
Assert.assertEquals(new VersionDistance("0:1.?.?"), new VersionDistance("1.?"));
}

@Test
public void testGetVersionDistance() {
Assert.assertEquals(new VersionDistance("0.0.0"), VersionDistance.getVersionDistance("", null));
Assert.assertEquals(new VersionDistance("0.0.0"), VersionDistance.getVersionDistance(null, ""));
Assert.assertEquals(new VersionDistance("1.0.0"), VersionDistance.getVersionDistance("2", "1.0"));
Assert.assertEquals(new VersionDistance("0.1.0"), VersionDistance.getVersionDistance("1", "1.1.0"));
Assert.assertEquals(new VersionDistance("1.?.?"), VersionDistance.getVersionDistance("2", "1.0"));
Assert.assertEquals(new VersionDistance("0.1.?"), VersionDistance.getVersionDistance("1", "1.1.0"));
Assert.assertEquals(new VersionDistance("0.0.1"), VersionDistance.getVersionDistance("1", "1.0.1"));
Assert.assertEquals(new VersionDistance("2.0.0"), VersionDistance.getVersionDistance("1.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("0:2.0"), VersionDistance.getVersionDistance("1.f", "3.4.0"));
Assert.assertEquals(new VersionDistance("2.0.0"), VersionDistance.getVersionDistance("1.", "3.4.0"));
Assert.assertEquals(new VersionDistance("2.0.0"), VersionDistance.getVersionDistance("1.2.3", "3.4.0"));
Assert.assertEquals(new VersionDistance("3.0.0"), VersionDistance.getVersionDistance("0.1.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("0.2.0"), VersionDistance.getVersionDistance("3.2.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("2.?.?"), VersionDistance.getVersionDistance("1.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("0:2.?"), VersionDistance.getVersionDistance("1.f", "3.4.0"));
Assert.assertEquals(new VersionDistance("2.?.?"), VersionDistance.getVersionDistance("1.", "3.4.0"));
Assert.assertEquals(new VersionDistance("2.?.?"), VersionDistance.getVersionDistance("1.2.3", "3.4.0"));
Assert.assertEquals(new VersionDistance("3.?.?"), VersionDistance.getVersionDistance("0.1.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("0.2.?"), VersionDistance.getVersionDistance("3.2.2", "3.4.0"));
Assert.assertEquals(new VersionDistance("0.0.1"), VersionDistance.getVersionDistance("0.0.1", "0.0.2"));
Assert.assertEquals(new VersionDistance("2.0.0"), VersionDistance.getVersionDistance("3.4.0", "1.2.3"));
Assert.assertEquals(new VersionDistance("3.0.0"), VersionDistance.getVersionDistance("3.4.0", "0.1.2"));
Assert.assertEquals(new VersionDistance("0.2.0"), VersionDistance.getVersionDistance("3.4.0", "3.2.2"));
Assert.assertEquals(new VersionDistance("2.?.?"), VersionDistance.getVersionDistance("3.4.0", "1.2.3"));
Assert.assertEquals(new VersionDistance("3.?.?"), VersionDistance.getVersionDistance("3.4.0", "0.1.2"));
Assert.assertEquals(new VersionDistance("0.2.?"), VersionDistance.getVersionDistance("3.4.0", "3.2.2"));
Assert.assertEquals(new VersionDistance("0.0.1"), VersionDistance.getVersionDistance("0.0.2", "0.0.1"));

Assert.assertThrows(NumberFormatException.class, () -> VersionDistance.getVersionDistance("a:", "1"));
Expand Down

0 comments on commit ce81753

Please sign in to comment.