diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java b/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java index dad65e308..4b8bd6551 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/AbstractEnvironmentVariables.java @@ -28,6 +28,8 @@ public abstract class AbstractEnvironmentVariables implements EnvironmentVariabl // Variable surrounded with "${" and "}" such as "${JAVA_HOME}" 1......2........ private static final Pattern VARIABLE_SYNTAX = Pattern.compile("(\\$\\{([^}]+)})"); + private static final String SELF_REFERENCING_NOT_FOUND = ""; + private static final int MAX_RECURSION = 9; private static final String VARIABLE_PREFIX = "${"; @@ -161,19 +163,44 @@ public EnvironmentVariables resolved() { @Override public String resolve(String string, Object src) { - return resolve(string, src, 0, src, string); + return resolve(string, src, 0, src, string, this); } - private String resolve(String value, Object src, int recursion, Object rootSrc, String rootValue) { + /** + * This method is called recursively. This allows you to resolve variables that are defined by other variables. + * + * @param value the {@link String} that potentially contains variables in the syntax "${«variable«}". Those will be + * resolved by this method and replaced with their {@link #get(String) value}. + * @param src the source where the {@link String} to resolve originates from. Should have a reasonable + * {@link Object#toString() string representation} that will be used in error or log messages if a variable + * could not be resolved. + * @param recursion the current recursion level. This is used to interrupt endless recursion. + * @param rootSrc the root source where the {@link String} to resolve originates from. + * @param rootValue the root value to resolve. + * @param resolvedVars this is a reference to an object of {@link EnvironmentVariablesResolved} being the lowest level + * in the {@link EnvironmentVariablesType hierarchy} of variables. In case of a self-referencing variable + * {@code x} the resolving has to continue one level higher in the {@link EnvironmentVariablesType hierarchy} + * to avoid endless recursion. The {@link EnvironmentVariablesResolved} is then used if another variable + * {@code y} must be resolved, since resolving this variable has to again start at the lowest level. For + * example: For levels {@code l1, l2} with {@code l1 < l2} and {@code x=${x} foo} and {@code y=bar} defined at + * level {@code l1} and {@code x=test ${y}} defined at level {@code l2}, {@code x} is first resolved at level + * {@code l1} and then up the {@link EnvironmentVariablesType hierarchy} at {@code l2} to avoid endless + * recursion. However, {@code y} must be resolved starting from the lowest level in the + * {@link EnvironmentVariablesType hierarchy} and therefore {@link EnvironmentVariablesResolved} is used. + * @return the given {@link String} with the variables resolved. + */ + private String resolve(String value, Object src, int recursion, Object rootSrc, String rootValue, + AbstractEnvironmentVariables resolvedVars) { if (value == null) { return null; } if (recursion > MAX_RECURSION) { - throw new IllegalStateException("Reached maximum recursion resolving " + value + " for root valiable " + rootSrc + throw new IllegalStateException("Reached maximum recursion resolving " + value + " for root variable " + rootSrc + " with value '" + rootValue + "'."); } recursion++; + Matcher matcher = VARIABLE_SYNTAX.matcher(value); if (!matcher.find()) { return value; @@ -181,16 +208,43 @@ private String resolve(String value, Object src, int recursion, Object rootSrc, StringBuilder sb = new StringBuilder(value.length() + EXTRA_CAPACITY); do { String variableName = matcher.group(2); - String variableValue = getValue(variableName); + String variableValue = resolvedVars.getValue(variableName); if (variableValue == null) { this.context.warning("Undefined variable {} in '{}={}' for root '{}={}'", variableName, src, value, rootSrc, rootValue); - } else { - String replacement = resolve(variableValue, variableName, recursion, rootSrc, rootValue); + continue; + } + EnvironmentVariables lowestFound = findVariable(variableName); + boolean isNotSelfReferencing = lowestFound == null || !lowestFound.getFlat(variableName).equals(value); + + if (isNotSelfReferencing) { + // looking for "variableName" starting from resolved upwards the hierarchy + String replacement = resolvedVars.resolve(variableValue, variableName, recursion, rootSrc, rootValue, + resolvedVars); + matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + } else { // is self referencing + // finding next occurrence of "variableName" up the hierarchy of EnvironmentVariablesType + EnvironmentVariables next = lowestFound.getParent(); + while (next != null) { + if (next.getFlat(variableName) != null) { + break; + } + next = next.getParent(); + } + if (next == null) { + matcher.appendReplacement(sb, Matcher.quoteReplacement(SELF_REFERENCING_NOT_FOUND)); + continue; + } + // resolving a self referencing variable one level up the hierarchy of EnvironmentVariablesType, i.e. at "next", + // to avoid endless recursion + String replacement = ((AbstractEnvironmentVariables) next).resolve(next.getFlat(variableName), variableName, + recursion, rootSrc, rootValue, resolvedVars); matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + } } while (matcher.find()); matcher.appendTail(sb); + String resolved = sb.toString(); return resolved; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java index 5e5fc6aff..d99e67d1f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariables.java @@ -186,7 +186,7 @@ default EnvironmentVariables findVariable(String name) { * @param source the source where the {@link String} to resolve originates from. Should have a reasonable * {@link Object#toString() string representation} that will be used in error or log messages if a variable * could not be resolved. - * @return the the given {@link String} with the variables resolved. + * @return the given {@link String} with the variables resolved. * @see com.devonfw.tools.ide.tool.ide.IdeToolCommandlet */ String resolve(String string, Object source); diff --git a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java index a106f5f3b..963a67996 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java +++ b/cli/src/main/java/com/devonfw/tools/ide/environment/EnvironmentVariablesPropertiesFile.java @@ -222,9 +222,9 @@ public String set(String name, String value, boolean export) { String oldValue = this.variables.put(name, value); boolean flagChanged = export != this.exportedVariables.contains(name); if (Objects.equals(value, oldValue) && !flagChanged) { - this.context.trace("Set valiable '{}={}' caused no change in {}", name, value, this.propertiesFilePath); + this.context.trace("Set variable '{}={}' caused no change in {}", name, value, this.propertiesFilePath); } else { - this.context.debug("Set valiable '{}={}' in {}", name, value, this.propertiesFilePath); + this.context.debug("Set variable '{}={}' in {}", name, value, this.propertiesFilePath); this.modifiedVariables.add(name); if (export && (value != null)) { this.exportedVariables.add(name); diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java new file mode 100644 index 000000000..c8abed59a --- /dev/null +++ b/cli/src/main/java/com/devonfw/tools/ide/version/BoundaryType.java @@ -0,0 +1,19 @@ +package com.devonfw.tools.ide.version; + +/** + * Enum representing the type of interval regarding its boundaries. + */ +public enum BoundaryType { + + /** Closed interval - includes the specified values at the boundaries. */ + CLOSED, + + /** Open interval - excludes the specified values at the boundaries. */ + OPEN, + + /** Left open interval - excludes the lower bound but includes the upper bound. */ + LEFT_OPEN, + + /** Right open interval - includes the lower bound but excludes the upper bound. */ + RIGHT_OPEN +} diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java index 5b4b4c871..c72e9d9f8 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java @@ -3,12 +3,11 @@ /** * Abstract base interface for a version object such as {@link VersionIdentifier} and {@link VersionSegment}. * - * * {@link Comparable} for versions with an extended contract. If two versions are not strictly comparable (e.g. * "1.apple" and "1.banana") we fall back to some heuristics (e.g. lexicographical comparison for - * {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore you can - * use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional information - * as {@link VersionComparisonResult#isUnsafe() unsafe} flag. + * {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore, you + * can use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional + * information as {@link VersionComparisonResult#isUnsafe() unsafe} flag. * * @param type of the object to compare (this class itself). */ diff --git a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java index d09021a2b..df27e3612 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java +++ b/cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java @@ -1,7 +1,12 @@ package com.devonfw.tools.ide.version; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + /** - * Container for a range of versions. + * Container for a range of versions. The lower and upper bounds can be exclusive or inclusive. If a bound is null, it + * means that this direction is unbounded. The boolean defining whether this bound is inclusive or exclusive is ignored + * in this case. */ public final class VersionRange implements Comparable { @@ -9,6 +14,45 @@ public final class VersionRange implements Comparable { private final VersionIdentifier max; + private final boolean leftIsExclusive; + + private final boolean rightIsExclusive; + + private static final String VERSION_SEPARATOR = ">"; + + private static final String START_EXCLUDING_PREFIX = "("; + + private static final String START_INCLUDING_PREFIX = "["; + + private static final String END_EXCLUDING_SUFFIX = ")"; + + private static final String END_INCLUDING_SUFFIX = "]"; + + public static String getVersionSeparator() { + + return VERSION_SEPARATOR; + } + + public static String getStartExcludingPrefix() { + + return START_EXCLUDING_PREFIX; + } + + public static String getStartIncludingPrefix() { + + return START_INCLUDING_PREFIX; + } + + public static String getEndExcludingSuffix() { + + return END_EXCLUDING_SUFFIX; + } + + public static String getEndIncludingSuffix() { + + return END_INCLUDING_SUFFIX; + } + /** * The constructor. * @@ -20,6 +64,42 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) { super(); this.min = min; this.max = max; + this.leftIsExclusive = false; + this.rightIsExclusive = false; + } + + /** + * The constructor. + * + * @param min the {@link #getMin() minimum}. + * @param max the {@link #getMax() maximum}. + * @param boundaryType the {@link BoundaryType} defining whether the boundaries of the range are inclusive or + * exclusive. + */ + public VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) { + + super(); + this.min = min; + this.max = max; + this.leftIsExclusive = BoundaryType.LEFT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); + this.rightIsExclusive = BoundaryType.RIGHT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType); + } + + /** + * The constructor. + * + * @param min the {@link #getMin() minimum}. + * @param max the {@link #getMax() maximum}. + * @param leftIsExclusive - {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. + * @param rightIsExclusive - {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. + */ + public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIsExclusive, boolean rightIsExclusive) { + + super(); + this.min = min; + this.max = max; + this.leftIsExclusive = leftIsExclusive; + this.rightIsExclusive = rightIsExclusive; } /** @@ -38,6 +118,38 @@ public VersionIdentifier getMax() { return this.max; } + /** + * @return {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise. + */ + public boolean isLeftExclusive() { + + return this.leftIsExclusive; + } + + /** + * @return {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise. + */ + public boolean isRightExclusive() { + + return this.rightIsExclusive; + } + + /** + * @return the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive. + */ + public BoundaryType getBoundaryType() { + + if (this.leftIsExclusive && this.rightIsExclusive) { + return BoundaryType.OPEN; + } else if (this.leftIsExclusive) { + return BoundaryType.LEFT_OPEN; + } else if (this.rightIsExclusive) { + return BoundaryType.RIGHT_OPEN; + } else { + return BoundaryType.CLOSED; + } + } + /** * @param version the {@link VersionIdentifier} to check. * @return {@code true} if the given {@link VersionIdentifier} is contained in this {@link VersionRange}, @@ -46,12 +158,18 @@ public VersionIdentifier getMax() { public boolean contains(VersionIdentifier version) { if (this.min != null) { - if (version.isLess(this.min)) { + VersionComparisonResult compareMin = version.compareVersion(this.min); + if (compareMin.isLess()) { + return false; + } else if (compareMin.isEqual() && this.leftIsExclusive) { return false; } } if (this.max != null) { - if (version.isGreater(this.max)) { + VersionComparisonResult compareMax = version.compareVersion(this.max); + if (compareMax.isGreater()) { + return false; + } else if (compareMax.isEqual() && this.rightIsExclusive) { return false; } } @@ -69,20 +187,48 @@ public int compareTo(VersionRange o) { } return -1; } - return this.min.compareTo(o.min); + int compareMins = this.min.compareTo(o.min); + if (compareMins == 0) { + return this.leftIsExclusive == o.leftIsExclusive ? 0 : this.leftIsExclusive ? 1 : -1; + } else { + return compareMins; + } } @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } else if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + VersionRange o = (VersionRange) obj; + if (this.min == null && this.max == null) { + return o.min == null && o.max == null; + } else if (this.min == null) { + return o.min == null && this.max.equals(o.max) && this.rightIsExclusive == o.rightIsExclusive; + } else if (this.max == null) { + return this.min.equals(o.min) && o.max == null && this.leftIsExclusive == o.leftIsExclusive; + } + return this.min.equals(o.min) && this.leftIsExclusive == o.leftIsExclusive && this.max.equals(o.max) + && this.rightIsExclusive == o.rightIsExclusive; + } + + @Override + @JsonValue public String toString() { StringBuilder sb = new StringBuilder(); + sb.append(this.leftIsExclusive ? START_EXCLUDING_PREFIX : START_INCLUDING_PREFIX); if (this.min != null) { sb.append(this.min); } - sb.append('>'); + sb.append(VERSION_SEPARATOR); if (this.max != null) { sb.append(this.max); } + sb.append(this.rightIsExclusive ? END_EXCLUDING_SUFFIX : END_INCLUDING_SUFFIX); return sb.toString(); } @@ -90,12 +236,18 @@ public String toString() { * @param value the {@link #toString() string representation} of a {@link VersionRange} to parse. * @return the parsed {@link VersionRange}. */ + @JsonCreator public static VersionRange of(String value) { - int index = value.indexOf('>'); + boolean leftIsExclusive = value.startsWith(START_EXCLUDING_PREFIX); + boolean rightIsExclusive = value.endsWith(END_EXCLUDING_SUFFIX); + value = removeAffixes(value); + + int index = value.indexOf(VERSION_SEPARATOR); if (index == -1) { return null; // log warning? } + VersionIdentifier min = null; if (index > 0) { min = VersionIdentifier.of(value.substring(0, index)); @@ -105,7 +257,22 @@ public static VersionRange of(String value) { if (!maxString.isEmpty()) { max = VersionIdentifier.of(maxString); } - return new VersionRange(min, max); + return new VersionRange(min, max, leftIsExclusive, rightIsExclusive); + } + + private static String removeAffixes(String value) { + + if (value.startsWith(START_EXCLUDING_PREFIX)) { + value = value.substring(START_EXCLUDING_PREFIX.length()); + } else if (value.startsWith(START_INCLUDING_PREFIX)) { + value = value.substring(START_INCLUDING_PREFIX.length()); + } + if (value.endsWith(END_EXCLUDING_SUFFIX)) { + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); + } else if (value.endsWith(END_INCLUDING_SUFFIX)) { + value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length()); + } + return value; } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/InstallCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/InstallCommandletTest.java new file mode 100644 index 000000000..6633aff01 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/InstallCommandletTest.java @@ -0,0 +1,112 @@ +package com.devonfw.tools.ide.commandlet; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeContext; +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; + +/** + * Integration test of {@link InstallCommandlet}. + */ + +public class InstallCommandletTest extends AbstractIdeContextTest { + + private static WireMockServer server; + + private static Path resourcePath = Paths.get("src/test/resources"); + + @BeforeAll + static void setUp() throws IOException { + + server = new WireMockServer(WireMockConfiguration.wireMockConfig().port(1111)); + server.start(); + } + + @AfterAll + static void tearDown() throws IOException { + + server.shutdownServer(); + } + + private void mockWebServer() throws IOException { + + Path windowsFilePath = resourcePath.resolve("__files").resolve("java-17.0.6-windows-x64.zip"); + String windowsLength = String.valueOf(Files.size(windowsFilePath)); + server.stubFor( + get(urlPathEqualTo("/installTest/windows")).willReturn(aResponse().withHeader("Content-Type", "application/zip") + .withHeader("Content-Length", windowsLength).withStatus(200).withBodyFile("java-17.0.6-windows-x64.zip"))); + + Path linuxFilePath = resourcePath.resolve("__files").resolve("java-17.0.6-linux-x64.tgz"); + String linuxLength = String.valueOf(Files.size(linuxFilePath)); + server.stubFor( + get(urlPathEqualTo("/installTest/linux")).willReturn(aResponse().withHeader("Content-Type", "application/tgz") + .withHeader("Content-Length", linuxLength).withStatus(200).withBodyFile("java-17.0.6-linux-x64.tgz"))); + + server.stubFor( + get(urlPathEqualTo("/installTest/macOS")).willReturn(aResponse().withHeader("Content-Type", "application/tgz") + .withHeader("Content-Length", linuxLength).withStatus(200).withBodyFile("java-17.0.6-linux-x64.tgz"))); + } + + /** + * Test of {@link InstallCommandlet} run, when Installed Version is null. + */ + @Test + public void testInstallCommandletRunWithVersion() throws IOException { + + // arrange + String path = "workspaces/foo-test/my-git-repo"; + IdeContext context = newContext("basic", path, true); + InstallCommandlet install = context.getCommandletManager().getCommandlet(InstallCommandlet.class); + install.tool.setValueAsString("java"); + mockWebServer(); + // act + install.run(); + // assert + assertTestInstall(context); + } + + /** + * Test of {@link InstallCommandlet} run, when Installed Version is set. + */ + @Test + public void testInstallCommandletRunWithVersionAndVersionIdentifier() throws IOException { + + // arrange + String path = "workspaces/foo-test/my-git-repo"; + IdeContext context = newContext("basic", path, true); + InstallCommandlet install = context.getCommandletManager().getCommandlet(InstallCommandlet.class); + install.tool.setValueAsString("java"); + install.version.setValueAsString("17.0.6"); + mockWebServer(); + + // act + install.run(); + // assert + assertTestInstall(context); + } + + private void assertTestInstall(IdeContext context) { + + assertThat(context.getSoftwarePath().resolve("java")).exists(); + assertThat(context.getSoftwarePath().resolve("java/InstallTest.txt")).hasContent("This is a test file."); + assertThat(context.getSoftwarePath().resolve("java/bin/HelloWorld.txt")).hasContent("Hello World!"); + if(context.getSystemInfo().isWindows()){ + assertThat(context.getSoftwarePath().resolve("java/bin/java.cmd")).exists(); + } else if (context.getSystemInfo().isLinux() || context.getSystemInfo().isMac()) { + assertThat(context.getSoftwarePath().resolve("java/bin/java")).exists(); + } + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionSetCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionSetCommandletTest.java index bc82d1f35..a43227b28 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionSetCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/VersionSetCommandletTest.java @@ -44,6 +44,15 @@ public void testVersionSetCommandletRun() throws IOException { IDE_TOOLS=mvn,eclipse BAR=bar-${SOME} - """); + + TEST_ARGS1=${TEST_ARGS1} settings1 + TEST_ARGS4=${TEST_ARGS4} settings4 + TEST_ARGS5=${TEST_ARGS5} settings5 + TEST_ARGS6=${TEST_ARGS6} settings6 + TEST_ARGS7=${TEST_ARGS7} settings7 + TEST_ARGS8=settings8 + TEST_ARGS9=settings9 + TEST_ARGSb=${TEST_ARGS10} settingsb ${TEST_ARGSa} ${TEST_ARGSb} + TEST_ARGSc=${TEST_ARGSc} settingsc"""); } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/environment/EnvironmentVariablesTest.java b/cli/src/test/java/com/devonfw/tools/ide/environment/EnvironmentVariablesTest.java new file mode 100644 index 000000000..7f001c490 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/environment/EnvironmentVariablesTest.java @@ -0,0 +1,60 @@ +package com.devonfw.tools.ide.environment; + +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeTestContext; +import org.junit.jupiter.api.Test; + +/** + * Test of {@link EnvironmentVariables}. + */ +public class EnvironmentVariablesTest extends AbstractIdeContextTest { + + /** + * Test of {@link EnvironmentVariables#resolve(String, Object)} with self referencing variables. + */ + @Test + public void testProperEvaluationOfVariables() { + + // arrange + String path = "workspaces/foo-test/my-git-repo"; + IdeTestContext context = newContext(PROJECT_BASIC, path, false); + EnvironmentVariables variables = context.getVariables(); + + // act + String TEST_ARGS1 = variables.get("TEST_ARGS1"); + String TEST_ARGS2 = variables.get("TEST_ARGS2"); + String TEST_ARGS3 = variables.get("TEST_ARGS3"); + String TEST_ARGS4 = variables.get("TEST_ARGS4"); + String TEST_ARGS5 = variables.get("TEST_ARGS5"); + String TEST_ARGS6 = variables.get("TEST_ARGS6"); + String TEST_ARGS7 = variables.get("TEST_ARGS7"); + String TEST_ARGS8 = variables.get("TEST_ARGS8"); + String TEST_ARGS9 = variables.get("TEST_ARGS9"); + String TEST_ARGS10 = variables.get("TEST_ARGS10"); + // some more advanced cases + String TEST_ARGSa = variables.get("TEST_ARGSa"); + String TEST_ARGSb = variables.get("TEST_ARGSb"); + String TEST_ARGSc = variables.get("TEST_ARGSc"); + String TEST_ARGSd = variables.get("TEST_ARGSd"); + + // assert + assertThat(TEST_ARGS1).isEqualTo(" user1 settings1 workspace1 conf1"); + assertThat(TEST_ARGS2).isEqualTo(" user2 conf2"); + assertThat(TEST_ARGS3).isEqualTo(" user3 workspace3"); + assertThat(TEST_ARGS4).isEqualTo(" settings4"); + assertThat(TEST_ARGS5).isEqualTo(" settings5 conf5"); + assertThat(TEST_ARGS6).isEqualTo(" settings6 workspace6 conf6"); + + assertThat(TEST_ARGS7).isEqualTo("user7 settings7 workspace7 conf7"); + assertThat(TEST_ARGS8).isEqualTo("settings8 workspace8 conf8"); + assertThat(TEST_ARGS9).isEqualTo("settings9 workspace9"); + assertThat(TEST_ARGS10).isEqualTo("user10 workspace10"); + + assertThat(TEST_ARGSa).isEqualTo(" user1 settings1 workspace1 conf1 user3 workspace3 confa"); + assertThat(TEST_ARGSb) + .isEqualTo("user10 workspace10 settingsb user1 settings1 workspace1 conf1 user3 workspace3 confa userb"); + + assertThat(TEST_ARGSc).isEqualTo(" user1 settings1 workspace1 conf1 userc settingsc confc"); + assertThat(TEST_ARGSd).isEqualTo(" user1 settings1 workspace1 conf1 userd workspaced"); + } +} diff --git a/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java new file mode 100644 index 000000000..0a305f8a6 --- /dev/null +++ b/cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java @@ -0,0 +1,152 @@ +package com.devonfw.tools.ide.version; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Test of {@link VersionRange}. + */ +public class VersionRangeTest extends Assertions { + + /** + * Test of {@link VersionRange#of(String)}. + */ + @Test + public void testOf() { + + // arrange + String v1String = "1.2>3"; + String v2String = "1>)"; + String v3String = "(1.2>3.4]"; + + // act + VersionRange v1 = VersionRange.of(v1String); + VersionRange v2 = VersionRange.of(v2String); + VersionRange v3 = VersionRange.of(v3String); + + // assert + // v1 + assertThat(v1.getMin()).isEqualTo(VersionIdentifier.of("1.2")); + assertThat(v1.getMax()).isEqualTo(VersionIdentifier.of("3")); + assertThat(v1.isLeftExclusive()).isFalse(); + assertThat(v1.isRightExclusive()).isFalse(); + // v2 + assertThat(v2.getMin()).isEqualTo(VersionIdentifier.of("1")); + assertThat(v2.getMax()).isEqualTo(null); + assertThat(v2.isLeftExclusive()).isFalse(); + assertThat(v2.isRightExclusive()).isTrue(); + // v3 + assertThat(v3.getMin()).isEqualTo(VersionIdentifier.of("1.2")); + assertThat(v3.getMax()).isEqualTo(VersionIdentifier.of("3.4")); + assertThat(v3.isLeftExclusive()).isTrue(); + assertThat(v3.isRightExclusive()).isFalse(); + } + + /** + * Test of {@link VersionRange#toString()}. + */ + @Test + public void testToString() { + + assertThat(VersionRange.of("1.2>3").toString()).isEqualTo("[1.2>3]"); + assertThat(VersionRange.of("1>)").toString()).isEqualTo("[1>)"); + assertThat(VersionRange.of("(1.2>3.4]").toString()).isEqualTo("(1.2>3.4]"); + } + + /** + * Test of {@link VersionRange#equals(Object)}. + */ + @Test + public void testEquals() { + + // assert + // equals + assertThat(VersionRange.of("1.2>")).isEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("(1.2>")).isEqualTo(VersionRange.of("(1.2>)")); + assertThat(VersionRange.of("1.2>3")).isEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of("[1.2>3")).isEqualTo(VersionRange.of("1.2>3]")); + assertThat(VersionRange.of(">3)")).isEqualTo(VersionRange.of(">3)")); + assertThat(VersionRange.of(">")).isEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of("[>)")).isEqualTo(VersionRange.of("(>]")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isEqualTo(VersionRange.of("8u302b08>11.0.14_9")); + // not equals + assertThat(VersionRange.of("1>")).isNotEqualTo(null); + assertThat(VersionRange.of("1.2>")).isNotEqualTo(VersionRange.of("1>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of("1.2>")); + assertThat(VersionRange.of("(1.2>3")).isNotEqualTo(VersionRange.of("1.2.3>")); + assertThat(VersionRange.of("1.2>3")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("[1.2>")).isNotEqualTo(VersionRange.of("[1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of("1.2>3")); + assertThat(VersionRange.of(">3")).isNotEqualTo(VersionRange.of(">")); + assertThat(VersionRange.of(">")).isNotEqualTo(VersionRange.of(">3")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("(8u302b08>11.0.14_9)")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.15_9")); + assertThat(VersionRange.of("8u302b08>11.0.14_9")).isNotEqualTo(VersionRange.of("8u302b08>11.0.14_0")); + } + + /** + * Test of {@link VersionRange#contains(VersionIdentifier)} and testing if a {@link VersionIdentifier version} is + * contained in the {@link VersionRange}. + */ + @Test + public void testContains() { + + // assert + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.2"))).isTrue(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4"))).isTrue(); + + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2.1"))).isTrue(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("2"))).isTrue(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.3.9"))).isTrue(); + } + + /** + * Test of {@link VersionRange#contains(VersionIdentifier)} and testing if a {@link VersionIdentifier version} is not + * contained in the {@link VersionRange}. + */ + @Test + public void testNotContains() { + + // assert + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("1.1"))).isFalse(); + assertThat(VersionRange.of("1.2>3.4").contains(VersionIdentifier.of("3.4.1"))).isFalse(); + + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("1.2"))).isFalse(); + assertThat(VersionRange.of("(1.2>3.4)").contains(VersionIdentifier.of("3.4"))).isFalse(); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if versions are compared to be the same. + */ + @Test + public void testCompareToIsSame() { + + // assert + assertThat(VersionRange.of("1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(0); + assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("(1.2>3"))).isEqualTo(0); + assertThat(VersionRange.of("[1.2>3]").compareTo(VersionRange.of("[1.2>4)"))).isEqualTo(0); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is smaller than second. + */ + @Test + public void testCompareToIsSmaller() { + + // assert + assertThat(VersionRange.of("1.1.2>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(-1); + assertThat(VersionRange.of("[1.2>3").compareTo(VersionRange.of("(1.2>4"))).isEqualTo(-1); + } + + /** + * Test of {@link VersionRange#compareTo(VersionRange)} and testing if first version is larger than second. + */ + @Test + public void testCompareToIsLarger() { + + // assert + assertThat(VersionRange.of("1.2.1>3").compareTo(VersionRange.of("1.2>3"))).isEqualTo(1); + assertThat(VersionRange.of("(1.2>3").compareTo(VersionRange.of("1.2>4"))).isEqualTo(1); + } +} diff --git a/cli/src/test/resources/__files/java-17.0.6-linux-x64.tgz b/cli/src/test/resources/__files/java-17.0.6-linux-x64.tgz new file mode 100644 index 000000000..9a592c991 Binary files /dev/null and b/cli/src/test/resources/__files/java-17.0.6-linux-x64.tgz differ diff --git a/cli/src/test/resources/__files/java-17.0.6-windows-x64.zip b/cli/src/test/resources/__files/java-17.0.6-windows-x64.zip new file mode 100644 index 000000000..eb4f3482e Binary files /dev/null and b/cli/src/test/resources/__files/java-17.0.6-windows-x64.zip differ diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.sha256 b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.sha256 new file mode 100644 index 000000000..438dd2a03 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.sha256 @@ -0,0 +1 @@ +c2de7dfbd9f8faaa21b4cdd8518f826dd558c9ab24a0616b3ed28437a674a97b \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.urls b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.urls new file mode 100644 index 000000000..42e8cf6cc --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/linux_x64.urls @@ -0,0 +1 @@ +http://localhost:1111/installTest/linux \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.sha256 b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.sha256 new file mode 100644 index 000000000..438dd2a03 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.sha256 @@ -0,0 +1 @@ +c2de7dfbd9f8faaa21b4cdd8518f826dd558c9ab24a0616b3ed28437a674a97b \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.urls b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.urls new file mode 100644 index 000000000..384fe79a4 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/mac_x64.urls @@ -0,0 +1 @@ +http://localhost:1111/installTest/macOS \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/status.json b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/status.json new file mode 100644 index 000000000..b58452d90 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/status.json @@ -0,0 +1,20 @@ +{ + "manual" : true, + "urls" : { + "-680270697" : { + "success" : { + "timestamp" : "2023-04-28T16:27:32.819394600Z" + } + }, + "-896197542" : { + "success" : { + "timestamp" : "2023-04-28T16:27:47.658175400Z" + } + }, + "-310367019" : { + "success" : { + "timestamp" : "2023-04-28T16:28:02.221367500Z" + } + } + } +} \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls new file mode 100644 index 000000000..93010df08 --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls @@ -0,0 +1 @@ +http://localhost:1111/installTest/windows \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls.sha256 b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls.sha256 new file mode 100644 index 000000000..fe3cecaad --- /dev/null +++ b/cli/src/test/resources/ide-projects/_ide/urls/java/java/17.0.6/windows_x64.urls.sha256 @@ -0,0 +1 @@ +aa64bee5f7ba56fbbd60d766f3a652600f81571ae5e996804694c69bf731af8b \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/basic/conf/ide.properties b/cli/src/test/resources/ide-projects/basic/conf/ide.properties index 7b2a83438..224a2cf25 100644 --- a/cli/src/test/resources/ide-projects/basic/conf/ide.properties +++ b/cli/src/test/resources/ide-projects/basic/conf/ide.properties @@ -4,4 +4,13 @@ M2_REPO=~/.m2/repository -SOME=some-${UNDEFINED} \ No newline at end of file +SOME=some-${UNDEFINED} + +TEST_ARGS1=${TEST_ARGS1} conf1 +TEST_ARGS2=${TEST_ARGS2} conf2 +TEST_ARGS5=${TEST_ARGS5} conf5 +TEST_ARGS6=${TEST_ARGS6} conf6 +TEST_ARGS7=${TEST_ARGS7} conf7 +TEST_ARGS8=${TEST_ARGS8} conf8 +TEST_ARGSa=${TEST_ARGS1} ${TEST_ARGS3} confa +TEST_ARGSc=${TEST_ARGSc} confc \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/basic/home/.ide/ide.properties b/cli/src/test/resources/ide-projects/basic/home/.ide/ide.properties index a61d5066f..ec0c3e7f6 100644 --- a/cli/src/test/resources/ide-projects/basic/home/.ide/ide.properties +++ b/cli/src/test/resources/ide-projects/basic/home/.ide/ide.properties @@ -3,4 +3,13 @@ #******************************************************************************** DOCKER_EDITION=docker -FOO=foo-${BAR} \ No newline at end of file +FOO=foo-${BAR} + +TEST_ARGS1=${TEST_ARGS1} user1 +TEST_ARGS2=${TEST_ARGS2} user2 +TEST_ARGS3=${TEST_ARGS3} user3 +TEST_ARGS7=user7 +TEST_ARGS10=user10 +TEST_ARGSb=userb +TEST_ARGSc=${TEST_ARGS1} userc +TEST_ARGSd=${TEST_ARGS1} userd \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/basic/settings/ide.properties b/cli/src/test/resources/ide-projects/basic/settings/ide.properties index bfa447f6a..c80f1e604 100644 --- a/cli/src/test/resources/ide-projects/basic/settings/ide.properties +++ b/cli/src/test/resources/ide-projects/basic/settings/ide.properties @@ -9,4 +9,14 @@ INTELLIJ_EDITION=ultimate IDE_TOOLS=mvn,eclipse -BAR=bar-${SOME} \ No newline at end of file +BAR=bar-${SOME} + +TEST_ARGS1=${TEST_ARGS1} settings1 +TEST_ARGS4=${TEST_ARGS4} settings4 +TEST_ARGS5=${TEST_ARGS5} settings5 +TEST_ARGS6=${TEST_ARGS6} settings6 +TEST_ARGS7=${TEST_ARGS7} settings7 +TEST_ARGS8=settings8 +TEST_ARGS9=settings9 +TEST_ARGSb=${TEST_ARGS10} settingsb ${TEST_ARGSa} ${TEST_ARGSb} +TEST_ARGSc=${TEST_ARGSc} settingsc \ No newline at end of file diff --git a/cli/src/test/resources/ide-projects/basic/workspaces/foo-test/ide.properties b/cli/src/test/resources/ide-projects/basic/workspaces/foo-test/ide.properties new file mode 100644 index 000000000..fefd6bede --- /dev/null +++ b/cli/src/test/resources/ide-projects/basic/workspaces/foo-test/ide.properties @@ -0,0 +1,12 @@ +#******************************************************************************** +# Type of {@link EnvironmentVariables} from the +# {@link com.devonfw.tools.ide.context.IdeContext#getWorkspacePath() workspace directory}. +#******************************************************************************** +TEST_ARGS1=${TEST_ARGS1} workspace1 +TEST_ARGS3=${TEST_ARGS3} workspace3 +TEST_ARGS6=${TEST_ARGS6} workspace6 +TEST_ARGS7=${TEST_ARGS7} workspace7 +TEST_ARGS8=${TEST_ARGS8} workspace8 +TEST_ARGS9=${TEST_ARGS9} workspace9 +TEST_ARGS10=${TEST_ARGS10} workspace10 +TEST_ARGSd=${TEST_ARGSd} workspaced \ No newline at end of file