From bf7fa79357996d6c02fd48585e4d305e6534ea29 Mon Sep 17 00:00:00 2001 From: ndemirca Date: Mon, 27 May 2024 10:52:16 +0200 Subject: [PATCH 1/5] fixed copy method within extractdmg --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 7c4f75eef..d7e13189a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -664,6 +664,9 @@ public void extractDmg(Path file, Path targetDir) { if (appPath == null) { throw new IllegalStateException("Failed to unpack DMG as no MacOS *.app was found in file " + file); } + if (Files.isDirectory(appPath)) { + appPath = appPath.getParent(); + } copy(appPath, targetDir); pc.addArgs("detach", "-force", mountPath); pc.run(); From 3382e548fbeea6680c81fedbdc6e5fb75e62eab9 Mon Sep 17 00:00:00 2001 From: jan-vcapgemini Date: Mon, 27 May 2024 16:03:07 +0200 Subject: [PATCH 2/5] #347: fixed copy added copy recursively with folder name fixed test workarounds removed extra resolve directory workarounds --- .../commandlet/AbstractUpdateCommandlet.java | 23 +++--- .../devonfw/tools/ide/io/FileAccessImpl.java | 33 ++++----- .../devonfw/tools/ide/io/FileCopyMode.java | 23 +++--- .../tools/ide/tool/LocalToolCommandlet.java | 73 +++++++------------ .../com/devonfw/tools/ide/tool/npm/Npm.java | 14 ++-- .../ide/context/AbstractIdeContextTest.java | 70 ++++++++---------- 6 files changed, 103 insertions(+), 133 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java index 22db0443a..b7f3a08b3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractUpdateCommandlet.java @@ -1,11 +1,5 @@ package com.devonfw.tools.ide.commandlet; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import com.devonfw.tools.ide.context.GitContext; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.property.StringProperty; @@ -15,6 +9,12 @@ import com.devonfw.tools.ide.tool.ToolCommandlet; import com.devonfw.tools.ide.variable.IdeVariables; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /** * Abstract {@link Commandlet} base-class for both {@link UpdateCommandlet} and {@link CreateCommandlet}. */ @@ -78,8 +78,8 @@ private void setupConf(Path template, Path conf) { this.context.debug("Configuration {} already exists - skipping to copy from {}", confPath, child); } else { if (!basename.equals("settings.xml")) { - this.context.info("Copying template {} to {}.", child, confPath); - this.context.getFileAccess().copy(child, confPath); + this.context.info("Copying template {} to {}.", child, conf); + this.context.getFileAccess().copy(child, conf); } } } @@ -104,8 +104,8 @@ private void updateSettings() { String message = "Missing your settings at " + settingsPath + " and no SETTINGS_URL is defined.\n" + "Further details can be found here: https://github.com/devonfw/IDEasy/blob/main/documentation/settings.asciidoc\n" + "Please contact the technical lead of your project to get the SETTINGS_URL for your project.\n" - + "In case you just want to test IDEasy you may simply hit return to install the default settings.\n" - + "Settings URL [" + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:"; + + "In case you just want to test IDEasy you may simply hit return to install the default settings.\n" + "Settings URL [" + + IdeContext.DEFAULT_SETTINGS_REPO_URL + "]:"; repository = this.context.askForInput(message, IdeContext.DEFAULT_SETTINGS_REPO_URL); } else if ("-".equals(repository)) { repository = IdeContext.DEFAULT_SETTINGS_REPO_URL; @@ -127,8 +127,7 @@ private void updateSoftware() { Set toolCommandlets = new HashSet<>(); // installed tools in IDE_HOME/software - List softwares = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), - Files::isDirectory); + List softwares = this.context.getFileAccess().listChildren(this.context.getSoftwarePath(), Files::isDirectory); for (Path software : softwares) { String toolName = software.getFileName().toString(); ToolCommandlet toolCommandlet = this.context.getCommandletManager().getToolCommandletOrNull(toolName); diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index d7e13189a..9cdc16da5 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -282,18 +282,18 @@ public void move(Path source, Path targetDir) { @Override public void copy(Path source, Path target, FileCopyMode mode) { + if (mode != FileCopyMode.COPY_TREE_CONTENT) { + // if we want to copy "file.txt" to the existing folder "path/to/folder/" in a shell this will copy "file.txt" + // into that folder + // with Java NIO the raw copy method will fail as we cannot copy the file to the path of the target folder + // even worse if FileCopyMode is override the target folder ("path/to/folder/") would be deleted and the result + // of our "file.txt" would later appear in "path/to/folder". To prevent such bugs we append the filename to + // target + target = target.resolve(source.getFileName()); + } boolean fileOnly = mode.isFileOnly(); if (fileOnly) { this.context.debug("Copying file {} to {}", source, target); - if (Files.isDirectory(target)) { - // if we want to copy "file.txt" to the existing folder "path/to/folder/" in a shell this will copy "file.txt" - // into that folder - // with Java NIO the raw copy method will fail as we cannot copy the file to the path of the target folder - // even worse if FileCopyMode is override the target folder ("path/to/folder/") would be deleted and the result - // of our "file.txt" would later appear in "path/to/folder". To prevent such bugs we append the filename to - // target - target = target.resolve(source.getFileName()); - } } else { this.context.debug("Copying {} recursively to {}", source, target); } @@ -501,12 +501,12 @@ public void extract(Path archiveFile, Path targetDir, Consumer postExtract if (Files.isDirectory(archiveFile)) { Path properInstallDir = archiveFile; // getProperInstallationSubDirOf(archiveFile, archiveFile); - if (extract) { - this.context.warning("Found directory for download at {} hence copying without extraction!", archiveFile); - copy(properInstallDir, targetDir, FileCopyMode.COPY_TREE_OVERRIDE_TREE); - } else { - move(properInstallDir, targetDir); - } + // if (extract) { + this.context.warning("Found directory for download at {} hence copying without extraction!", archiveFile); + copy(properInstallDir, targetDir, FileCopyMode.COPY_TREE_CONTENT); + // } else { + // move(properInstallDir, targetDir); + // } postExtractHook(postExtractHook, properInstallDir); return; } else if (!extract) { @@ -664,9 +664,6 @@ public void extractDmg(Path file, Path targetDir) { if (appPath == null) { throw new IllegalStateException("Failed to unpack DMG as no MacOS *.app was found in file " + file); } - if (Files.isDirectory(appPath)) { - appPath = appPath.getParent(); - } copy(appPath, targetDir); pc.addArgs("detach", "-force", mountPath); pc.run(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java index ec8f906de..b4d4d8b41 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileCopyMode.java @@ -1,14 +1,12 @@ package com.devonfw.tools.ide.io; /** - * {@link Enum} with the available modes to - * {@link FileAccess#copy(java.nio.file.Path, java.nio.file.Path, FileCopyMode) copy} files and folders. + * {@link Enum} with the available modes to {@link FileAccess#copy(java.nio.file.Path, java.nio.file.Path, FileCopyMode) copy} files and folders. */ public enum FileCopyMode { /** - * Copy {@link #isFileOnly() only a single file} and - * {@link #isFailIfExists() fail if the target-file already exists}. + * Copy {@link #isFileOnly() only a single file} and {@link #isFailIfExists() fail if the target-file already exists}. */ COPY_FILE_FAIL_IF_EXISTS, @@ -22,14 +20,18 @@ public enum FileCopyMode { COPY_TREE_OVERRIDE_FILES, /** - * Copy {@link #isRecursive() recursively} and {@link FileAccess#delete(java.nio.file.Path) delete} the target-file if - * it exists before copying. + * Copy {@link #isRecursive() recursively} and {@link FileAccess#delete(java.nio.file.Path) delete} the target-file if it exists before copying. */ - COPY_TREE_OVERRIDE_TREE; + COPY_TREE_OVERRIDE_TREE, /** - * @return {@code true} if only a single file shall be copied. Will fail if a directory is given to copy, - * {@code false} otherwise (to copy folders recursively). + * Copy {@link #isRecursive() recursively} and appends the file name to the target. + */ + COPY_TREE_CONTENT; + + /** + * @return {@code true} if only a single file shall be copied. Will fail if a directory is given to copy, {@code false} otherwise (to copy folders + * recursively). */ public boolean isFileOnly() { @@ -37,8 +39,7 @@ public boolean isFileOnly() { } /** - * @return {@code true} if files and folders shall be copied recursively, {@code false} otherwise - * ({@link #isFileOnly() copy file copy}). + * @return {@code true} if files and folders shall be copied recursively, {@code false} otherwise ({@link #isFileOnly() copy file copy}). */ public boolean isRecursive() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java index 7d7c25d24..11291cc86 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/LocalToolCommandlet.java @@ -1,11 +1,5 @@ package com.devonfw.tools.ide.tool; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.Set; - import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; @@ -15,6 +9,12 @@ import com.devonfw.tools.ide.step.Step; import com.devonfw.tools.ide.version.VersionIdentifier; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Set; + /** * {@link ToolCommandlet} that is installed locally into the IDE. */ @@ -25,8 +25,7 @@ public abstract class LocalToolCommandlet extends ToolCommandlet { * * @param context the {@link IdeContext}. * @param tool the {@link #getName() tool name}. - * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} - * method. + * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method. */ public LocalToolCommandlet(IdeContext context, String tool, Set tags) { @@ -42,14 +41,12 @@ public Path getToolPath() { } /** - * @return the {@link Path} where the executables of the tool can be found. Typically a "bin" folder inside - * {@link #getToolPath() tool path}. + * @return the {@link Path} where the executables of the tool can be found. Typically a "bin" folder inside {@link #getToolPath() tool path}. */ public Path getToolBinPath() { Path toolPath = getToolPath(); - Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"), - false); + Path binPath = this.context.getFileAccess().findFirst(toolPath, path -> path.getFileName().toString().equals("bin"), false); if ((binPath != null) && Files.isDirectory(binPath)) { return binPath; } @@ -70,8 +67,7 @@ protected boolean doInstall(boolean silent) { VersionIdentifier resolvedVersion = installation.resolvedVersion(); if (resolvedVersion.equals(installedVersion) && !installation.newInstallation()) { IdeLogLevel level = silent ? IdeLogLevel.DEBUG : IdeLogLevel.INFO; - this.context.level(level).log("Version {} of tool {} is already installed", installedVersion, - getToolWithEdition()); + this.context.level(level).log("Version {} of tool {} is already installed", installedVersion, getToolWithEdition()); step.success(); return false; } @@ -88,8 +84,7 @@ protected boolean doInstall(boolean silent) { if (installedVersion == null) { step.success("Successfully installed {} in version {}", this.tool, resolvedVersion); } else { - step.success("Successfully installed {} in version {} replacing previous version {}", this.tool, - resolvedVersion, installedVersion); + step.success("Successfully installed {} in version {} replacing previous version {}", this.tool, resolvedVersion, installedVersion); } return true; } catch (RuntimeException e) { @@ -102,12 +97,10 @@ protected boolean doInstall(boolean silent) { } /** - * Performs the installation of the {@link #getName() tool} managed by this - * {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software repository without touching the - * IDE installation. + * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software + * repository without touching the IDE installation. * - * @param version the {@link VersionIdentifier} requested to be installed. May also be a - * {@link VersionIdentifier#isPattern() version pattern}. + * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}. * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}. */ public ToolInstallation installInRepo(VersionIdentifier version) { @@ -116,12 +109,10 @@ public ToolInstallation installInRepo(VersionIdentifier version) { } /** - * Performs the installation of the {@link #getName() tool} managed by this - * {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software repository without touching the - * IDE installation. + * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software + * repository without touching the IDE installation. * - * @param version the {@link VersionIdentifier} requested to be installed. May also be a - * {@link VersionIdentifier#isPattern() version pattern}. + * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}. * @param edition the specific edition to install. * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}. */ @@ -131,12 +122,10 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition) } /** - * Performs the installation of the {@link #getName() tool} managed by this - * {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software repository without touching the - * IDE installation. + * Performs the installation of the {@link #getName() tool} managed by this {@link com.devonfw.tools.ide.commandlet.Commandlet} only in the central software + * repository without touching the IDE installation. * - * @param version the {@link VersionIdentifier} requested to be installed. May also be a - * {@link VersionIdentifier#isPattern() version pattern}. + * @param version the {@link VersionIdentifier} requested to be installed. May also be a {@link VersionIdentifier#isPattern() version pattern}. * @param edition the specific edition to install. * @param toolRepository the {@link ToolRepository} to use. * @return the {@link ToolInstallation} in the central software repository matching the given {@code version}. @@ -144,8 +133,8 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition) public ToolInstallation installInRepo(VersionIdentifier version, String edition, ToolRepository toolRepository) { VersionIdentifier resolvedVersion = toolRepository.resolveVersion(this.tool, edition, version); - Path toolPath = this.context.getSoftwareRepositoryPath().resolve(toolRepository.getId()).resolve(this.tool) - .resolve(edition).resolve(resolvedVersion.toString()); + Path toolPath = this.context.getSoftwareRepositoryPath().resolve(toolRepository.getId()).resolve(this.tool).resolve(edition) + .resolve(resolvedVersion.toString()); Path toolVersionFile = toolPath.resolve(IdeContext.FILE_SOFTWARE_VERSION); FileAccess fileAccess = this.context.getFileAccess(); if (Files.isDirectory(toolPath)) { @@ -153,8 +142,7 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition, if (this.context.isForceMode()) { fileAccess.delete(toolPath); } else { - this.context.debug("Version {} of tool {} is already installed at {}", resolvedVersion, - getToolWithEdition(this.tool, edition), toolPath); + this.context.debug("Version {} of tool {} is already installed at {}", resolvedVersion, getToolWithEdition(this.tool, edition), toolPath); return createToolInstallation(toolPath, resolvedVersion, toolVersionFile); } } else { @@ -166,8 +154,7 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition, fileAccess.mkdirs(toolPath.getParent()); boolean extract = isExtract(); if (!extract) { - this.context.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, - target); + this.context.trace("Extraction is disabled for '{}' hence just moving the downloaded file {}.", this.tool, target); } fileAccess.extract(target, toolPath, this::postExtract, extract); try { @@ -181,8 +168,7 @@ public ToolInstallation installInRepo(VersionIdentifier version, String edition, } /** - * Post-extraction hook that can be overridden to add custom processing after unpacking and before moving to the final - * destination folder. + * Post-extraction hook that can be overridden to add custom processing after unpacking and before moving to the final destination folder. * * @param extractedDir the {@link Path} to the folder with the unpacked tool. */ @@ -190,8 +176,7 @@ protected void postExtract(Path extractedDir) { } - private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile, - boolean newInstallation) { + private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile, boolean newInstallation) { Path linkDir = getMacOsHelper().findLinkDir(rootDir, this.tool); Path binDir = linkDir; @@ -201,14 +186,12 @@ private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier } if (linkDir != rootDir) { assert (!linkDir.equals(rootDir)); - this.context.getFileAccess().copy(toolVersionFile, linkDir.resolve(IdeContext.FILE_SOFTWARE_VERSION), - FileCopyMode.COPY_FILE_OVERRIDE); + this.context.getFileAccess().copy(toolVersionFile, linkDir, FileCopyMode.COPY_FILE_OVERRIDE); } return new ToolInstallation(rootDir, linkDir, binDir, resolvedVersion, newInstallation); } - private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, - Path toolVersionFile) { + private ToolInstallation createToolInstallation(Path rootDir, VersionIdentifier resolvedVersion, Path toolVersionFile) { return createToolInstallation(rootDir, resolvedVersion, toolVersionFile, false); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java index 5cec9001b..4621328aa 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/npm/Npm.java @@ -1,8 +1,5 @@ package com.devonfw.tools.ide.tool.npm; -import java.nio.file.Path; -import java.util.Set; - import com.devonfw.tools.ide.common.Tag; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.io.FileAccess; @@ -10,6 +7,9 @@ import com.devonfw.tools.ide.tool.ToolCommandlet; import com.devonfw.tools.ide.tool.node.Node; +import java.nio.file.Path; +import java.util.Set; + /** * {@link ToolCommandlet} for npm. */ @@ -47,10 +47,10 @@ protected void postExtract(Path extractedDir) { fileAccess.delete(nodeHomePath.resolve(npx)); fileAccess.delete(nodeHomePath.resolve(npx + cmd)); - fileAccess.copy(npmBinBath.resolve(npm), nodeHomePath.resolve(npm)); - fileAccess.copy(npmBinBath.resolve(npm + cmd), nodeHomePath.resolve(npm + cmd)); - fileAccess.copy(npmBinBath.resolve(npx), nodeHomePath.resolve(npx)); - fileAccess.copy(npmBinBath.resolve(npx + cmd), nodeHomePath.resolve(npx + cmd)); + fileAccess.copy(npmBinBath.resolve(npm), nodeHomePath); + fileAccess.copy(npmBinBath.resolve(npm + cmd), nodeHomePath); + fileAccess.copy(npmBinBath.resolve(npx), nodeHomePath); + fileAccess.copy(npmBinBath.resolve(npx + cmd), nodeHomePath); } } } diff --git a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java index 584318ace..53b811b82 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java @@ -1,13 +1,5 @@ package com.devonfw.tools.ide.context; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -import org.assertj.core.api.Assertions; -import org.assertj.core.api.Condition; -import org.assertj.core.api.ListAssert; - import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.io.FileAccessImpl; import com.devonfw.tools.ide.io.FileCopyMode; @@ -15,6 +7,13 @@ import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.log.IdeTestLogger; import com.devonfw.tools.ide.repo.ToolRepositoryMock; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.Condition; +import org.assertj.core.api.ListAssert; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; /** * Abstract base class for tests that need mocked instances of {@link IdeContext}. @@ -37,8 +36,8 @@ public abstract class AbstractIdeContextTest extends Assertions { private static final int CHUNK_SIZE = 1024; /** - * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test project in {@link #TEST_PROJECTS}. E.g. + * "basic". * @return the {@link IdeTestContext} pointing to that project. */ protected IdeTestContext newContext(String testProject) { @@ -47,8 +46,8 @@ protected IdeTestContext newContext(String testProject) { } /** - * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test project in {@link #TEST_PROJECTS}. E.g. + * "basic". * @param projectPath the relative path inside the test project where to create the context. * @return the {@link IdeTestContext} pointing to that project. */ @@ -58,12 +57,11 @@ protected static IdeTestContext newContext(String testProject, String projectPat } /** - * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test - * project in {@link #TEST_PROJECTS}. E.g. "basic". + * @param testProject the (folder)name of the project test case, in this folder a 'project' folder represents the test project in {@link #TEST_PROJECTS}. E.g. + * "basic". * @param projectPath the relative path inside the test project where to create the context. - * @param copyForMutation - {@code true} to create a copy of the project that can be modified by the test, - * {@code false} otherwise (only to save resources if you are 100% sure that your test never modifies anything - * in that project.) + * @param copyForMutation - {@code true} to create a copy of the project that can be modified by the test, {@code false} otherwise (only to save resources if + * you are 100% sure that your test never modifies anything in that project.) * @return the {@link IdeTestContext} pointing to that project. */ protected static IdeTestContext newContext(String testProject, String projectPath, boolean copyForMutation) { @@ -74,7 +72,7 @@ protected static IdeTestContext newContext(String testProject, String projectPat FileAccess fileAccess = new FileAccessImpl(IdeTestContextMock.get()); fileAccess.delete(ideRootCopy); fileAccess.mkdirs(TEST_PROJECTS_COPY); - fileAccess.copy(ideRoot, ideRootCopy, FileCopyMode.COPY_TREE_OVERRIDE_TREE); + fileAccess.copy(ideRoot, TEST_PROJECTS_COPY, FileCopyMode.COPY_TREE_OVERRIDE_TREE); ideRoot = ideRootCopy; } if (projectPath == null) { @@ -110,8 +108,7 @@ protected static IdeTestContext newContext(Path projectPath) { * @param isOnline boolean if it should be run in online mode. * @return the {@link GitContextTestContext} pointing to that project. */ - protected static GitContextTestContext newGitContext(Path projectPath, List errors, List outs, - int exitCode, boolean isOnline) { + protected static GitContextTestContext newGitContext(Path projectPath, List errors, List outs, int exitCode, boolean isOnline) { GitContextTestContext context; context = new GitContextTestContext(isOnline, projectPath); @@ -135,8 +132,8 @@ protected static void assertLogMessage(IdeTestContext context, IdeLogLevel level * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param level the expected {@link IdeLogLevel}. * @param message the expected {@link com.devonfw.tools.ide.log.IdeSubLogger#log(String) log message}. - * @param contains - {@code true} if the given {@code message} may only be a sub-string of the log-message to assert, - * {@code false} otherwise (the entire log message including potential parameters being filled in is asserted). + * @param contains - {@code true} if the given {@code message} may only be a sub-string of the log-message to assert, {@code false} otherwise (the entire log + * message including potential parameters being filled in is asserted). */ protected static void assertLogMessage(IdeTestContext context, IdeLogLevel level, String message, boolean contains) { @@ -158,8 +155,7 @@ public boolean matches(String e) { /** * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param level the expected {@link IdeLogLevel}. - * @param message the {@link com.devonfw.tools.ide.log.IdeSubLogger#log(String) log message} that should not have been - * logged. + * @param message the {@link com.devonfw.tools.ide.log.IdeSubLogger#log(String) log message} that should not have been logged. */ protected static void assertNoLogMessage(IdeTestContext context, IdeLogLevel level, String message) { @@ -169,13 +165,11 @@ protected static void assertNoLogMessage(IdeTestContext context, IdeLogLevel lev /** * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param level the expected {@link IdeLogLevel}. - * @param message the {@link com.devonfw.tools.ide.log.IdeSubLogger#log(String) log message} that should not have been - * logged. - * @param contains - {@code true} if the given {@code message} may only be a sub-string of the log-message to assert, - * {@code false} otherwise (the entire log message including potential parameters being filled in is asserted). + * @param message the {@link com.devonfw.tools.ide.log.IdeSubLogger#log(String) log message} that should not have been logged. + * @param contains - {@code true} if the given {@code message} may only be a sub-string of the log-message to assert, {@code false} otherwise (the entire log + * message including potential parameters being filled in is asserted). */ - protected static void assertNoLogMessage(IdeTestContext context, IdeLogLevel level, String message, - boolean contains) { + protected static void assertNoLogMessage(IdeTestContext context, IdeLogLevel level, String message, boolean contains) { IdeTestLogger logger = context.level(level); ListAssert assertion = assertThat(logger.getMessages()).as(level.name() + "-Log messages"); @@ -193,8 +187,7 @@ public boolean matches(String e) { } /** - * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default - * behavior + * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default behavior * * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param taskName name of the task e.g. Downloading. @@ -203,8 +196,7 @@ public boolean matches(String e) { * @param chunkCount amount of chunks. * @param restSize remaining size. */ - protected static void assertProgressBar(IdeContext context, String taskName, long maxSize, long chunkSize, - int chunkCount, long restSize) { + protected static void assertProgressBar(IdeContext context, String taskName, long maxSize, long chunkSize, int chunkCount, long restSize) { AbstractIdeTestContext testContext = (AbstractIdeTestContext) context; IdeProgressBarTestImpl progressBar = testContext.getProgressBarMap().get(taskName); @@ -223,8 +215,7 @@ protected static void assertProgressBar(IdeContext context, String taskName, lon } /** - * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default - * behaviour, chunk size is fixed. + * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default behaviour, chunk size is fixed. * * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param taskName name of the task e.g. Downloading. @@ -232,15 +223,14 @@ protected static void assertProgressBar(IdeContext context, String taskName, lon * @param chunkCount amount of chunks. * @param restSize remaining size. */ - protected static void assertProgressBar(IdeContext context, String taskName, long maxSize, int chunkCount, - long restSize) { + protected static void assertProgressBar(IdeContext context, String taskName, long maxSize, int chunkCount, long restSize) { assertProgressBar(context, taskName, maxSize, CHUNK_SIZE, chunkCount, restSize); } /** - * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default - * behaviour, chunk size is fixed, chunk count and rest size get automatically calculated. + * Checks if a {@link com.devonfw.tools.ide.io.IdeProgressBar} was implemented correctly and reflects a default behaviour, chunk size is fixed, chunk count + * and rest size get automatically calculated. * * @param context the {@link IdeContext} that was created via the {@link #newContext(String) newContext} method. * @param taskName name of the task e.g. Downloading. From 349d071dd650fe5b5a843ae46b7656da65dcff7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Tue, 28 May 2024 13:20:06 +0200 Subject: [PATCH 3/5] constructive review: improve comment to the changed situation --- .../com/devonfw/tools/ide/io/FileAccessImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 9cdc16da5..7453c34cd 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -283,12 +283,14 @@ public void move(Path source, Path targetDir) { public void copy(Path source, Path target, FileCopyMode mode) { if (mode != FileCopyMode.COPY_TREE_CONTENT) { - // if we want to copy "file.txt" to the existing folder "path/to/folder/" in a shell this will copy "file.txt" - // into that folder - // with Java NIO the raw copy method will fail as we cannot copy the file to the path of the target folder - // even worse if FileCopyMode is override the target folder ("path/to/folder/") would be deleted and the result - // of our "file.txt" would later appear in "path/to/folder". To prevent such bugs we append the filename to - // target + // if we want to copy the file or folder "source" to the existing folder "target" in a shell this will copy + // source into that folder so that we as a result have a copy in "target/source". + // With Java NIO the raw copy method will fail as we cannot copy "source" to the path of the "target" folder. + // For folders we want the same behavior as the linux "cp -r" command so that the "source" folder is copied + // and not only its content what also makes it consistent with the move method that also behaves this way. + // Therefore we need to add the filename (foldername) of "source" to the "target" path before. + // For the rare cases, where we want to copy the content of a folder (cp -r source/* target) we support + // it via the COPY_TREE_CONTENT mode. target = target.resolve(source.getFileName()); } boolean fileOnly = mode.isFileOnly(); From 9b48b11528f6a25388e30abc45cb0db4b3ec6460 Mon Sep 17 00:00:00 2001 From: ndemirca <157051033+ndemirca@users.noreply.github.com> Date: Tue, 28 May 2024 14:05:10 +0200 Subject: [PATCH 4/5] Update cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index 7453c34cd..cd8d912ba 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -503,7 +503,6 @@ public void extract(Path archiveFile, Path targetDir, Consumer postExtract if (Files.isDirectory(archiveFile)) { Path properInstallDir = archiveFile; // getProperInstallationSubDirOf(archiveFile, archiveFile); - // if (extract) { this.context.warning("Found directory for download at {} hence copying without extraction!", archiveFile); copy(properInstallDir, targetDir, FileCopyMode.COPY_TREE_CONTENT); // } else { From 4bde7bb2f92a7b46791f0f341ae2fc5491c14d2a Mon Sep 17 00:00:00 2001 From: ndemirca <157051033+ndemirca@users.noreply.github.com> Date: Tue, 28 May 2024 14:05:15 +0200 Subject: [PATCH 5/5] Update cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Hohwiller --- cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java index cd8d912ba..a72fb2b43 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java +++ b/cli/src/main/java/com/devonfw/tools/ide/io/FileAccessImpl.java @@ -505,9 +505,6 @@ public void extract(Path archiveFile, Path targetDir, Consumer postExtract Path properInstallDir = archiveFile; // getProperInstallationSubDirOf(archiveFile, archiveFile); this.context.warning("Found directory for download at {} hence copying without extraction!", archiveFile); copy(properInstallDir, targetDir, FileCopyMode.COPY_TREE_CONTENT); - // } else { - // move(properInstallDir, targetDir); - // } postExtractHook(postExtractHook, properInstallDir); return; } else if (!extract) {