From c35f3ba75ce8a5a33b7efc8f760ec7e5bf8c9228 Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Fri, 11 Jan 2019 10:35:57 -0600 Subject: [PATCH] Add more tests for JENKINS-55448 This also refactors some test calls to mklink into a package-private Util method due to additional tests needing to create junction points. Signed-off-by: Matt Sicker --- core/src/main/java/hudson/Util.java | 33 +++++++++++++ core/src/test/java/hudson/FilePathTest.java | 7 +-- .../RemoveWindowsDirectoryJunctionTest.java | 12 +---- core/src/test/java/hudson/UtilTest.java | 46 ++++++++++++++++--- .../java/jenkins/util/io/PathRemoverTest.java | 33 +++++++++++++ 5 files changed, 108 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 45d8f3f5e2..a55b90d05e 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -23,6 +23,7 @@ */ package hudson; +import com.google.common.annotations.VisibleForTesting; import hudson.model.TaskListener; import jenkins.util.MemoryReductionUtil; import hudson.util.QuotedStringTokenizer; @@ -1277,6 +1278,38 @@ public class Util { } } + /** + * Creates an NTFS junction point if supported. Similar to symbolic links, NTFS provides junction points which + * provide different features than symbolic links. This method is primarily useful for unit testing on Windows. + * @param junction NTFS junction point to create + * @param target target directory to junction + * @return the newly created junction point + * @throws IOException if the call to mklink exits with a non-zero status code + * @throws InterruptedException if the call to mklink is interrupted before completing + * @throws AssertionError if this method is called on a non-Windows platform + */ + static @Nonnull File createJunction(@Nonnull File junction, @Nonnull File target) throws IOException, InterruptedException { + if (!Functions.isWindows()) throw new AssertionError("Cannot create junctions on non-Windows platforms"); + String command = "mklink /J " + escapeCmdArgument(junction.getAbsolutePath()) + ' ' + escapeCmdArgument(target.getAbsolutePath()); + Process process = new ProcessBuilder("cmd.exe", "/C", command).start(); + int result = process.waitFor(); + if (result != 0) throw new IOException("Command `" + command + "` failed with status code " + result); + return junction; + } + + private static final Pattern CMD_METACHARS = Pattern.compile("[()%!^\"<>&|]"); + + /** + * Escapes a Windows cmd argument. Cmd supports escaping characters with carats which simplifies nested escapes. + * @param arg argument to escape + * @return the quoted argument for use in cmd.exe + * @see