From da32cdba623a01a7e3785bd319f70c1330c471f3 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 6 Dec 2023 20:59:00 +0100 Subject: [PATCH] [tests] untying updater tests from the platform and migrating to NIO 2 / JUnit 5 GitOrigin-RevId: 2ed7da4770b3923a15a70d427d3db6cfc1ad7bab --- updater/intellij.platform.updater.iml | 3 +- updater/src/com/intellij/updater/Patch.java | 13 +- updater/src/com/intellij/updater/Utils.java | 39 +- updater/testData/bin/focuskiller.dll | Bin 32768 -> 0 bytes .../com/intellij/updater/DigesterTest.java | 77 +- .../updater/PatchApplyingRevertingTest.java | 1058 ++++++++--------- .../intellij/updater/PatchCreationTest.java | 483 ++++---- .../com/intellij/updater/PatchTestCase.java | 98 -- .../com/intellij/updater/RunnerTest.java | 13 +- .../intellij/updater/SymlinkPatchTest.java | 135 --- .../com/intellij/updater/UpdaterTestCase.java | 216 +++- .../com/intellij/updater/UtilsTest.java | 184 +-- 12 files changed, 1075 insertions(+), 1244 deletions(-) delete mode 100644 updater/testData/bin/focuskiller.dll delete mode 100644 updater/testSrc/com/intellij/updater/PatchTestCase.java delete mode 100644 updater/testSrc/com/intellij/updater/SymlinkPatchTest.java diff --git a/updater/intellij.platform.updater.iml b/updater/intellij.platform.updater.iml index 588a63511148..878da71854f9 100644 --- a/updater/intellij.platform.updater.iml +++ b/updater/intellij.platform.updater.iml @@ -10,8 +10,7 @@ - - + \ No newline at end of file diff --git a/updater/src/com/intellij/updater/Patch.java b/updater/src/com/intellij/updater/Patch.java index 215adde5051d..9caf822db5cf 100644 --- a/updater/src/com/intellij/updater/Patch.java +++ b/updater/src/com/intellij/updater/Patch.java @@ -3,6 +3,7 @@ package com.intellij.updater; import java.io.*; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.*; @@ -367,12 +368,20 @@ public class Patch { return Digester.digestRegularFile(toFile); } + public long digestFile(Path file) throws IOException { + return Digester.digest(file); + } + public Map digestFiles(File dir, Set ignoredFiles) throws IOException { + return digestFiles(dir.toPath(), ignoredFiles); + } + + public Map digestFiles(Path dir, Set ignoredFiles) throws IOException { Map result = new LinkedHashMap<>(); - Utils.collectRelativePaths(dir.toPath()).parallelStream().forEachOrdered(path -> { + Utils.collectRelativePaths(dir).parallelStream().forEachOrdered(path -> { if (!ignoredFiles.contains(path)) { try { - long hash = digestFile(new File(dir, path)); + long hash = digestFile(dir.resolve(path)); synchronized (result) { result.put(path, hash); } diff --git a/updater/src/com/intellij/updater/Utils.java b/updater/src/com/intellij/updater/Utils.java index 9d450dac5108..e7ae9ef513af 100644 --- a/updater/src/com/intellij/updater/Utils.java +++ b/updater/src/com/intellij/updater/Utils.java @@ -22,6 +22,8 @@ public final class Utils { private static final CopyOption[] COPY_STANDARD = {LinkOption.NOFOLLOW_LINKS, StandardCopyOption.COPY_ATTRIBUTES}; private static final CopyOption[] COPY_REPLACE = {LinkOption.NOFOLLOW_LINKS, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING}; + private static final CopyOption[] MOVE_STANDARD = {LinkOption.NOFOLLOW_LINKS, StandardCopyOption.ATOMIC_MOVE}; + private static final CopyOption[] MOVE_REPLACE = {LinkOption.NOFOLLOW_LINKS, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING}; private static final boolean WIN_UNPRIVILEGED = IS_WINDOWS && Boolean.getBoolean("idea.unprivileged.process"); @@ -63,9 +65,12 @@ public final class Utils { } public static void delete(File file) throws IOException { - Path start = file.toPath(); - if (Files.exists(start, LinkOption.NOFOLLOW_LINKS)) { - Files.walkFileTree(start, new SimpleFileVisitor<>() { + delete(file.toPath()); + } + + public static void delete(Path file) throws IOException { + if (Files.exists(file, LinkOption.NOFOLLOW_LINKS)) { + Files.walkFileTree(file, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { tryDelete(file); @@ -119,13 +124,13 @@ public final class Utils { return file.canExecute(); } - public static void setExecutable(File file) throws IOException { - setExecutable(file, true); + public static void setExecutable(Path file) throws IOException { + setExecutable(file.toFile()); } - public static void setExecutable(File file, boolean executable) throws IOException { + public static void setExecutable(File file) throws IOException { LOG.info("Setting executable permissions for: " + file); - if (!file.setExecutable(executable, false)) { + if (!file.setExecutable(true, false)) { throw new IOException("Cannot set executable permissions for: " + file); } } @@ -145,11 +150,13 @@ public final class Utils { } public static void copy(File from, File to, boolean overwrite) throws IOException { - String message = from + (overwrite ? " over " : " into ") + to; - LOG.info(message); + copy(from.toPath(), to.toPath(), overwrite); + } - Path src = from.toPath(), dst = to.toPath(); - BasicFileAttributes attrs = Files.readAttributes(src, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + public static void copy(Path src, Path dst, boolean overwrite) throws IOException { + LOG.info(src + (overwrite ? " over " : " into ") + dst); + + var attrs = Files.readAttributes(src, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); if (attrs.isDirectory()) { Files.createDirectories(dst); } @@ -193,6 +200,16 @@ public final class Utils { }); } + public static void move(Path src, Path dst, boolean overwrite) throws IOException { + Files.createDirectories(dst.getParent()); + Files.move(src, dst, overwrite ? MOVE_REPLACE : MOVE_STANDARD); + } + + public static void writeString(Path file, String data) throws IOException { + Files.createDirectories(file.getParent()); + Files.writeString(file, data); + } + public static void copyFileToStream(File from, OutputStream out) throws IOException { try (InputStream in = new BufferedInputStream(new FileInputStream(from))) { copyStream(in, out); diff --git a/updater/testData/bin/focuskiller.dll b/updater/testData/bin/focuskiller.dll deleted file mode 100644 index c1f8e043939a01f4c03e8e854fb2869a17fc8f14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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 Digester.digest(absoluteLink)) .isInstanceOf(IOException.class) .hasMessageStartingWith("An absolute link"); } - @Test - public void testExecutables() throws Exception { - assumeFalse("Windows-allergic", Utils.IS_WINDOWS); - - Path testFile = Files.copy(dataDir.toPath().resolve("bin/idea.bat"), tempDir.getRoot().toPath().resolve("idea.bat")); - assertThat(Digester.digest(testFile)).isEqualTo(CHECKSUMS.IDEA_BAT); - NioFiles.setExecutable(testFile); - assertThat(Digester.digest(testFile)).isEqualTo(CHECKSUMS.IDEA_BAT | Digester.EXECUTABLE); + @Test @DisabledOnOs(OS.WINDOWS) void executables(@TempDir Path tempDir) throws Exception { + var testFile = Files.copy(dataDir.resolve("bin/idea.bat"), tempDir.resolve("idea.bat")); + assertThat(Digester.digest(testFile)).isEqualTo(UpdaterTestCase.IDEA_BAT); + Files.setPosixFilePermissions(testFile, PosixFilePermissions.fromString("r-xr-xr-x")); + assertThat(Digester.digest(testFile)).isEqualTo(UpdaterTestCase.IDEA_BAT | Digester.EXECUTABLE); } } diff --git a/updater/testSrc/com/intellij/updater/PatchApplyingRevertingTest.java b/updater/testSrc/com/intellij/updater/PatchApplyingRevertingTest.java index b7aa838dcdd1..1221d9e97c88 100644 --- a/updater/testSrc/com/intellij/updater/PatchApplyingRevertingTest.java +++ b/updater/testSrc/com/intellij/updater/PatchApplyingRevertingTest.java @@ -1,12 +1,12 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.updater; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.IoTestUtil; -import com.intellij.openapi.util.io.NioFiles; -import com.intellij.util.containers.ContainerUtil; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; import java.io.ByteArrayInputStream; import java.io.File; @@ -15,119 +15,118 @@ import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.nio.file.StandardCopyOption; +import java.util.*; import java.util.function.BiConsumer; import java.util.stream.Collectors; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; +import static com.intellij.updater.UpdaterTestCase.*; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; -@SuppressWarnings({"JUnit3StyleTestMethodInJUnit4Class", "DuplicateExpressions"}) -public abstract class PatchApplyingRevertingTest extends PatchTestCase { - @SuppressWarnings("JUnitTestCaseWithNoTests") - public static final class StandardModeTest extends PatchApplyingRevertingTest { } +@UpdaterTest +abstract class PatchApplyingRevertingTest { + static final class StandardModeTest extends PatchApplyingRevertingTest { } + static final class NoBackupTest extends PatchApplyingRevertingTest { } - @SuppressWarnings("JUnitTestCaseWithNoTests") - public static final class NoBackupTest extends PatchApplyingRevertingTest { } + private static final class CancellableUI extends ConsoleUpdaterUI { + private boolean cancelled = false; - private File myFile; - private PatchSpec myPatchSpec; - private boolean myDoBackup; + private CancellableUI() { + super(false); + } - private PatchApplyingRevertingTest() { } - - @Before - @Override - public void before() throws Exception { - super.before(); - myFile = getTempFile("patch.zip"); - myPatchSpec = new PatchSpec() - .setOldFolder(myOlderDir.getAbsolutePath()) - .setNewFolder(myNewerDir.getAbsolutePath()); - myDoBackup = !(this instanceof NoBackupTest); - } - - @Test - public void testCreatingAndApplying() throws Exception { - assertAppliedAndReverted(); - } - - @Test - public void testCreatingAndApplyingStrict() throws Exception { - myPatchSpec.setStrict(true); - assertAppliedAndReverted(); - } - - @Test - public void testCreatingAndApplyingOnADifferentRoot() throws Exception { - myPatchSpec.setRoot("bin/"); - myPatchSpec.setStrict(true); - createPatch(); - - assertAppliedAndReverted(PatchFileCreator.prepareAndValidate(myFile, new File(myOlderDir, "bin"), TEST_UI)); - } - - @Test - public void testCreatingAndFailingOnADifferentRoot() throws Exception { - myPatchSpec.setRoot("bin/"); - myPatchSpec.setStrict(true); - createPatch(); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, new File(myOlderDir, "bin"), TEST_UI); - preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction(preparationResult.patch)); - assertNotApplied(preparationResult); - } - - @Test - public void testReverting() throws Exception { - createPatch(); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction(preparationResult.patch)); - assertNotApplied(preparationResult); - } - - @Test - public void testRevertedWhenFileToDeleteIsLocked() throws Exception { - IoTestUtil.assumeWindows(); - doLockedFileTest(); - } - - @Test - public void testRevertedWhenFileToUpdateIsLocked() throws Exception { - IoTestUtil.assumeWindows(); - FileUtil.writeToFile(new File(myNewerDir, "bin/idea.bat"), "new text"); - doLockedFileTest(); - } - - private void doLockedFileTest() throws Exception { - createPatch(); - - try (RandomAccessFile raf = new RandomAccessFile(new File(myOlderDir, "bin/idea.bat"), "rw")) { - int b = raf.read(); - raf.seek(0); - raf.write(b); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - assertNotApplied(preparationResult); + @Override + public void checkCancelled() throws OperationCancelledException { + if (cancelled) throw new OperationCancelledException(); } } - @Test - public void testRevertedWhenDeleteFailed() throws Exception { - createPatch(); + @TempDir Path tempDir; + @UpdaterTestData Path dataDir; - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - PatchAction original = findAction(preparationResult.patch, "bin/idea.bat"); + private final CancellableUI testUI = new CancellableUI(); + private final boolean doBackup = this instanceof StandardModeTest; + + private PatchApplyingRevertingTest() { } + + @BeforeEach void clearUIState() { + testUI.cancelled = false; + } + + @Test void creatingAndApplying() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + + assertAppliedAndReverted(dirs); + } + + @Test void creatingAndApplyingStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + + assertAppliedAndReverted(dirs, patchFile); + } + + @Test void creatingAndApplyingOnADifferentRoot() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setRoot("bin/")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.resolve("bin").toFile(), testUI); + + assertAppliedAndReverted(dirs, preparationResult); + } + + @Test void creatingAndFailingOnADifferentRoot() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setRoot("bin/")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.resolve("bin").toFile(), testUI); + preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction(preparationResult.patch)); + + assertNotApplied(dirs, preparationResult); + } + + @Test void reverting() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction(preparationResult.patch)); + + assertNotApplied(dirs, preparationResult); + } + + @Test @EnabledOnOs(OS.WINDOWS) void revertedWhenFileToDeleteIsLocked() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + doLockedFileTest(dirs); + } + + @Test @EnabledOnOs(OS.WINDOWS) void revertedWhenFileToUpdateIsLocked() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.writeString(dirs.newDir.resolve("bin/idea.bat"), "new text"); + doLockedFileTest(dirs); + } + + private void doLockedFileTest(UpdaterTestCase.Directories dirs) throws Exception { + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + try (var raf = new RandomAccessFile(dirs.oldDir.resolve("bin/idea.bat").toFile(), "rw")) { + var b = raf.read(); + raf.seek(0); + raf.write(b); + + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + assertNotApplied(dirs, preparationResult); + } + } + + @Test void revertedWhenDeleteFailed() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + + var original = findAction(preparationResult.patch, "bin/idea.bat"); assertThat(original).isInstanceOf(DeleteAction.class); - List actions = preparationResult.patch.getActions(); + + var actions = preparationResult.patch.getActions(); actions.set(actions.indexOf(original), new DeleteAction(preparationResult.patch, original.getPath(), original.getChecksum()) { @Override protected void doApply(ZipFile patchFile, File backupDir, File toFile) throws IOException { @@ -135,18 +134,19 @@ public abstract class PatchApplyingRevertingTest extends PatchTestCase { } }); - assertNotApplied(preparationResult); + assertNotApplied(dirs, preparationResult); } - @Test - public void testRevertedWhenUpdateFailed() throws Exception { - FileUtil.writeToFile(new File(myNewerDir, "bin/idea.bat"), "new text"); - createPatch(); + @Test void revertedWhenUpdateFailed() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.writeString(dirs.newDir.resolve("bin/idea.bat"), "new text"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - PatchAction original = findAction(preparationResult.patch, "bin/idea.bat"); + var original = findAction(preparationResult.patch, "bin/idea.bat"); assertThat(original).isInstanceOf(UpdateAction.class); - List actions = preparationResult.patch.getActions(); + + var actions = preparationResult.patch.getActions(); actions.set(actions.indexOf(original), new UpdateAction(preparationResult.patch, original.getPath(), original.getChecksum()) { @Override protected void doApply(ZipFile patchFile, File backupDir, File toFile) throws IOException { @@ -154,570 +154,492 @@ public abstract class PatchApplyingRevertingTest extends PatchTestCase { } }); - assertNotApplied(preparationResult); + assertNotApplied(dirs, preparationResult); } - @Test - public void testCancelledAtBackingUp() throws Exception { - createPatch(); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - List actions = preparationResult.patch.getActions(); + @Test void cancelledAtBackingUp() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + var actions = preparationResult.patch.getActions(); actions.add(new MyFailOnApplyPatchAction(preparationResult.patch) { @Override protected void doBackup(File toFile, File backupFile) { - TEST_UI.cancelled = true; + testUI.cancelled = true; } }); - assertNotApplied(preparationResult); + assertNotApplied(dirs, preparationResult); } - @Test - public void testCancelledAtApplying() throws Exception { - createPatch(); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - List actions = preparationResult.patch.getActions(); + @Test void cancelledAtApplying() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + var actions = preparationResult.patch.getActions(); actions.add(new MyFailOnApplyPatchAction(preparationResult.patch) { @Override protected void doApply(ZipFile patchFile, File backupDir, File toFile) { - TEST_UI.cancelled = true; + testUI.cancelled = true; } }); - assertNotApplied(preparationResult); + assertNotApplied(dirs, preparationResult); } - @Test - public void testApplyingWithAbsentFileToDelete() throws Exception { - createPatch(); - - FileUtil.delete(new File(myOlderDir, "bin/idea.bat")); - - assertAppliedAndReverted(); + @Test void applyingWithAbsentFileToDelete() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.delete(dirs.oldDir.resolve("bin/idea.bat")); + assertAppliedAndReverted(dirs); } - @Test - public void testApplyingWithAbsentFileToUpdateStrict() throws Exception { - myPatchSpec.setStrict(true); - createPatch(); + @Test void applyingWithAbsentFileToUpdateStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + Files.delete(dirs.oldDir.resolve("lib/annotations.jar")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, - "lib/annotations.jar", - ValidationResult.Action.UPDATE, - "Absent", - ValidationResult.Option.NONE)); + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + "Absent", + ValidationResult.Option.NONE)); } - @Test - public void testApplyingWithAbsentOptionalFile() throws Exception { - FileUtil.writeToFile(new File(myNewerDir, "bin/idea.bat"), "new content".getBytes(StandardCharsets.UTF_8)); + @Test void applyingWithAbsentOptionalFile() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.writeString(dirs.newDir.resolve("bin/idea.bat"), "new content"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setOptionalFiles(List.of("bin/idea.bat"))); + Files.delete(dirs.oldDir.resolve("bin/idea.bat")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - myPatchSpec.setOptionalFiles(List.of("bin/idea.bat")); - createPatch(); - - FileUtil.delete(new File(myOlderDir, "bin/idea.bat")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult, (original, target) -> target.remove("bin/idea.bat")); + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> target.remove("bin/idea.bat")); } - @Test - public void testApplyingWithAbsentOptionalDirectory() throws Exception { - Files.createDirectory(myOlderDir.toPath().resolve("opt")); - Files.write(myOlderDir.toPath().resolve("opt/file.txt"), "previous content".getBytes(StandardCharsets.UTF_8)); - Files.createDirectory(myNewerDir.toPath().resolve("opt")); - Files.write(myNewerDir.toPath().resolve("opt/file.txt"), "new content".getBytes(StandardCharsets.UTF_8)); - Files.write(myNewerDir.toPath().resolve("opt/another.txt"), "content".getBytes(StandardCharsets.UTF_8)); + @Test void applyingWithAbsentOptionalDirectory() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createDirectory(dirs.oldDir.resolve("opt")); + Files.writeString(dirs.oldDir.resolve("opt/file.txt"), "previous content"); + Files.createDirectory(dirs.newDir.resolve("opt")); + Files.writeString(dirs.newDir.resolve("opt/file.txt"), "new content"); + Files.writeString(dirs.newDir.resolve("opt/another.txt"), "content"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setOptionalFiles(List.of("opt/file.txt"))); + Utils.delete(dirs.oldDir.resolve("opt")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - myPatchSpec.setOptionalFiles(List.of("opt/file.txt")); - createPatch(); - - FileUtil.delete(myOlderDir.toPath().resolve("opt")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult, (original, target) -> { + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> { target.remove("opt/"); target.remove("opt/file.txt"); target.remove("opt/another.txt"); }); } - @Test - public void testRevertingWithAbsentFileToDelete() throws Exception { - createPatch(); - - FileUtil.delete(new File(myOlderDir, "bin/idea.bat")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); + @Test void revertingWithAbsentFileToDelete() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.delete(dirs.oldDir.resolve("bin/idea.bat")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction(preparationResult.patch)); - assertNotApplied(preparationResult); + + assertNotApplied(dirs, preparationResult); } - @Test - public void testApplyingWithCriticalFiles() throws Exception { - myPatchSpec.setCriticalFiles(List.of("lib/annotations.jar")); - assertAppliedAndReverted(); + @Test void applyingWithCriticalFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setCriticalFiles(List.of("lib/annotations.jar"))); + + assertAppliedAndReverted(dirs, patchFile); } - @Test - public void testApplyingWithModifiedCriticalFiles() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setCriticalFiles(List.of("lib/annotations.jar")); - createPatch(); + @Test void applyingWithModifiedCriticalFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setCriticalFiles(List.of("lib/annotations.jar"))); + modifyFile(dirs.oldDir.resolve("lib/annotations.jar")); - modifyFile(new File(myOlderDir, "lib/annotations.jar")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs, patchFile); } - @Test - public void testApplyingWithRemovedCriticalFiles() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setCriticalFiles(List.of("lib/annotations.jar")); - createPatch(); + @Test void applyingWithRemovedCriticalFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setCriticalFiles(List.of("lib/annotations.jar"))); + Files.delete(dirs.oldDir.resolve("lib/annotations.jar")); - FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs, patchFile); } - @Test - public void testApplyingWithRemovedNonCriticalFilesWithStrict() throws Exception { - myPatchSpec.setStrict(true); - createPatch(); + @Test void applyingWithRemovedNonCriticalFilesWithStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + Files.delete(dirs.oldDir.resolve("lib/annotations.jar")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, - "lib/annotations.jar", - ValidationResult.Action.UPDATE, - "Absent", - ValidationResult.Option.NONE)); + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + "Absent", + ValidationResult.Option.NONE)); } - @Test - public void testApplyingWithRemovedNonCriticalFilesWithoutStrict() throws Exception { - createPatch(); + @Test void applyingWithRemovedNonCriticalFilesWithoutStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.delete(dirs.oldDir.resolve("lib/annotations.jar")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, - "lib/annotations.jar", - ValidationResult.Action.UPDATE, - "Absent", - ValidationResult.Option.IGNORE)); + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + "Absent", + ValidationResult.Option.IGNORE)); } - @Test - public void testApplyingWithModifiedCriticalFilesAndDifferentRoot() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setRoot("lib/"); - myPatchSpec.setCriticalFiles(List.of("lib/annotations.jar")); - createPatch(); + @Test void applyingWithModifiedCriticalFilesAndDifferentRoot() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var spec = createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setRoot("lib/").setCriticalFiles(List.of("lib/annotations.jar")); + var patchFile = createPatch(spec); + modifyFile(dirs.oldDir.resolve("lib/annotations.jar")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.resolve("lib/").toFile(), testUI); - modifyFile(new File(myOlderDir, "lib/annotations.jar")); - - assertAppliedAndReverted(PatchFileCreator.prepareAndValidate(myFile, new File(myOlderDir, "lib/"), TEST_UI)); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testApplyingWithCaseChangedNames() throws Exception { - FileUtil.rename(new File(myOlderDir, "Readme.txt"), new File(myOlderDir, "README.txt")); - assertAppliedAndReverted(); + @Test void applyingWithCaseChangedNames() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.move(dirs.oldDir.resolve("Readme.txt"), dirs.oldDir.resolve("README.txt")); + + assertAppliedAndReverted(dirs); } - @Test - public void testCreatingAndApplyingWhenDirectoryBecomesFile() throws Exception { - File file = new File(myOlderDir, "Readme.txt"); - FileUtil.delete(file); - FileUtil.createDirectory(file); + @Test void creatingAndApplyingWhenDirectoryBecomesFile() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var file = dirs.oldDir.resolve("Readme.txt"); + Files.delete(file); + Files.createDirectory(file); + Files.createFile(file.resolve("subFile.txt")); + Files.createFile(Files.createDirectory(file.resolve("subDir")).resolve("subFile.txt")); + Files.copy(dirs.oldDir.resolve("lib/boot.jar"), dirs.oldDir.resolve("lib/boot_with_directory_becomes_file.jar"), StandardCopyOption.REPLACE_EXISTING); - FileUtil.writeToFile(new File(file, "subFile.txt"), ""); - FileUtil.writeToFile(new File(file, "subDir/subFile.txt"), ""); - - FileUtil.copy(new File(myOlderDir, "lib/boot.jar"), new File(myOlderDir, "lib/boot_with_directory_becomes_file.jar")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testCreatingAndApplyingWhenFileBecomesDirectory() throws Exception { - File file = new File(myOlderDir, "bin"); - FileUtil.delete(file); - FileUtil.writeToFile(file, ""); + @Test void creatingAndApplyingWhenFileBecomesDirectory() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var file = dirs.oldDir.resolve("bin"); + Utils.delete(file); + Files.createFile(file); + Files.copy(dirs.oldDir.resolve("lib/boot_with_directory_becomes_file.jar"), dirs.oldDir.resolve("lib/boot.jar"), StandardCopyOption.REPLACE_EXISTING); - FileUtil.copy(new File(myOlderDir, "lib/boot_with_directory_becomes_file.jar"), new File(myOlderDir, "lib/boot.jar")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testConsideringOptions() throws Exception { - createPatch(); + @Test void consideringOptions() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + var options = preparationResult.patch.getActions().stream().collect(Collectors.toMap(PatchAction::getPath, a -> ValidationResult.Option.IGNORE)); - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - Map options = - preparationResult.patch.getActions().stream().collect(Collectors.toMap(PatchAction::getPath, a -> ValidationResult.Option.IGNORE)); - assertNotApplied(preparationResult, options); + assertNotApplied(dirs, preparationResult, options); } - @Test - public void testApplyWhenCommonFileChanges() throws Exception { - createPatch(); + @Test void applyWhenCommonFileChanges() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.copy(dirs.oldDir.resolve("lib/bootstrap.jar"), dirs.oldDir.resolve("lib/boot.jar"), StandardCopyOption.REPLACE_EXISTING); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.copy(new File(myOlderDir, "lib/bootstrap.jar"), new File(myOlderDir, "lib/boot.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult, (original, target) -> target.put("lib/boot.jar", CHECKSUMS.BOOTSTRAP_JAR)); + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> target.put("lib/boot.jar", UpdaterTestCase.BOOTSTRAP_JAR)); } - @Test - public void testApplyWhenCommonFileChangesStrict() throws Exception { - myPatchSpec.setStrict(true); - createPatch(); + @Test void applyWhenCommonFileChangesStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + Files.copy(dirs.oldDir.resolve("lib/bootstrap.jar"), dirs.oldDir.resolve("lib/boot.jar"), StandardCopyOption.REPLACE_EXISTING); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.copy(new File(myOlderDir, "lib/bootstrap.jar"), new File(myOlderDir, "lib/boot.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, - "lib/boot.jar", - ValidationResult.Action.VALIDATE, - "Modified", - ValidationResult.Option.NONE)); + "lib/boot.jar", + ValidationResult.Action.VALIDATE, + "Modified", + ValidationResult.Option.NONE)); } - @Test - public void testApplyWhenCommonFileChangesStrictFile() throws Exception { - myPatchSpec.setStrictFiles(List.of("lib/annotations.jar")); - createPatch(); + @Test void applyWhenCommonFileChangesStrictFile() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrictFiles(List.of("lib/annotations.jar"))); + Files.copy(dirs.oldDir.resolve("lib/bootstrap.jar"), dirs.oldDir.resolve("lib/annotations.jar"), StandardCopyOption.REPLACE_EXISTING); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.copy(new File(myOlderDir, "lib/bootstrap.jar"), new File(myOlderDir, "lib/annotations.jar")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, - "lib/annotations.jar", - ValidationResult.Action.UPDATE, - "Modified", - ValidationResult.Option.NONE)); + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + "Modified", + ValidationResult.Option.NONE)); } - @Test - public void testApplyWhenNewFileExists() throws Exception { - createPatch(); + @Test void applyWhenNewFileExists() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.copy(dirs.oldDir.resolve("Readme.txt"), dirs.oldDir.resolve("new_file.txt")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - FileUtil.copy(new File(myOlderDir, "Readme.txt"), new File(myOlderDir, "new_file.txt")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult, (original, target) -> target.put("new_file.txt", CHECKSUMS.README_TXT)); + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> target.put("new_file.txt", UpdaterTestCase.README_TXT)); } - @Test - public void testApplyWhenNewFileExistsStrict() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setDeleteFiles(List.of("lib/java_pid.*\\.hprof")); + @Test void applyWhenNewFileExistsStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setDeleteFiles(List.of("lib/java_pid.*\\.hprof"))); + Files.writeString(dirs.oldDir.resolve("new_file.txt"), "hello"); + Files.writeString(dirs.oldDir.resolve("lib/java_pid1234.hprof"), "bye!"); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - createPatch(); - - FileUtil.writeToFile(new File(myOlderDir, "new_file.txt"), "hello"); - FileUtil.writeToFile(new File(myOlderDir, "lib/java_pid1234.hprof"), "bye!"); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.CONFLICT, - "new_file.txt", - ValidationResult.Action.VALIDATE, - "Unexpected file", - ValidationResult.Option.DELETE)); - assertAppliedAndReverted(preparationResult); + "new_file.txt", + ValidationResult.Action.VALIDATE, + "Unexpected file", + ValidationResult.Option.DELETE)); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testApplyWhenNewDeletableFileExistsStrict() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setDeleteFiles(List.of("lib/java_pid.*\\.hprof")); + @Test void applyWhenNewDeletableFileExistsStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setDeleteFiles(List.of("lib/java_pid.*\\.hprof"))); + Files.writeString(dirs.oldDir.resolve("lib/java_pid1234.hprof"), "bye!"); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - createPatch(); - - FileUtil.writeToFile(new File(myOlderDir, "lib/java_pid1234.hprof"), "bye!"); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testApplyWhenNewDirectoryExistsStrict() throws Exception { - myPatchSpec.setStrict(true); - FileUtil.writeToFile(new File(myOlderDir, "delete/delete_me.txt"), "bye!"); + @Test void applyWhenNewDirectoryExistsStrict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Utils.writeString(dirs.oldDir.resolve("delete/delete_me.txt"), "bye!"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + Utils.writeString(dirs.oldDir.resolve("unexpected_new_dir/unexpected.txt"), "bye!"); + Files.createDirectory(dirs.oldDir.resolve("newDir")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - createPatch(); - - FileUtil.writeToFile(new File(myOlderDir, "unexpected_new_dir/unexpected.txt"), "bye!"); - - FileUtil.createDirectory(new File(myOlderDir, "newDir")); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.CONFLICT, - "unexpected_new_dir/unexpected.txt", - ValidationResult.Action.VALIDATE, - "Unexpected file", - ValidationResult.Option.DELETE), + "unexpected_new_dir/unexpected.txt", + ValidationResult.Action.VALIDATE, + "Unexpected file", + ValidationResult.Option.DELETE), new ValidationResult(ValidationResult.Kind.CONFLICT, - "unexpected_new_dir/", - ValidationResult.Action.VALIDATE, - "Unexpected file", - ValidationResult.Option.DELETE), + "unexpected_new_dir/", + ValidationResult.Action.VALIDATE, + "Unexpected file", + ValidationResult.Option.DELETE), new ValidationResult(ValidationResult.Kind.CONFLICT, - "newDir/", - ValidationResult.Action.CREATE, - "Already exists", - ValidationResult.Option.REPLACE)); - FileUtil.delete(new File(myOlderDir, "newDir")); - assertAppliedAndReverted(preparationResult); + "newDir/", + ValidationResult.Action.CREATE, + "Already exists", + ValidationResult.Option.REPLACE)); + + Utils.delete(dirs.oldDir.resolve("newDir")); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testMoveFileByContent() throws Exception { - myPatchSpec.setStrict(true); - FileUtil.writeToFile(new File(myOlderDir, "move/from/this/directory/move.me"), "old_content"); - FileUtil.writeToFile(new File(myOlderDir, "a/deleted/file/that/is/a/copy/move.me"), "new_content"); - FileUtil.writeToFile(new File(myNewerDir, "move/to/this/directory/move.me"), "new_content"); - createPatch(); + @Test void moveFileByContent() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Utils.writeString(dirs.oldDir.resolve("move/from/this/directory/move.me"), "old_content"); + Utils.writeString(dirs.oldDir.resolve("a/deleted/file/that/is/a/copy/move.me"), "new_content"); + Utils.writeString(dirs.newDir.resolve("move/to/this/directory/move.me"), "new_content"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + var hash = Digester.digestStream(new ByteArrayInputStream("new_content".getBytes(StandardCharsets.UTF_8))); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - long hash = Digester.digestStream(new ByteArrayInputStream("new_content".getBytes(StandardCharsets.UTF_8))); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(findAction(preparationResult.patch, "move/to/this/directory/move.me")) .isEqualTo(new UpdateAction(preparationResult.patch, "move/to/this/directory/move.me", "a/deleted/file/that/is/a/copy/move.me", hash, true)); - - assertAppliedAndReverted(preparationResult); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testMoveCriticalFileByContent() throws Exception { - myPatchSpec.setStrict(true); - myPatchSpec.setCriticalFiles(List.of("a/deleted/file/that/is/a/copy/move.me")); - FileUtil.writeToFile(new File(myOlderDir, "move/from/this/directory/move.me"), "old_content"); - FileUtil.writeToFile(new File(myOlderDir, "a/deleted/file/that/is/a/copy/move.me"), "new_content"); - FileUtil.writeToFile(new File(myNewerDir, "move/to/this/directory/move.me"), "new_content"); - createPatch(); + @Test void moveCriticalFileByContent() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Utils.writeString(dirs.oldDir.resolve("move/from/this/directory/move.me"), "old_content"); + Utils.writeString(dirs.oldDir.resolve("a/deleted/file/that/is/a/copy/move.me"), "new_content"); + Utils.writeString(dirs.newDir.resolve("move/to/this/directory/move.me"), "new_content"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true).setCriticalFiles(List.of("a/deleted/file/that/is/a/copy/move.me"))); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - assertThat(findAction(preparationResult.patch, "move/to/this/directory/move.me")) - .isInstanceOf(CreateAction.class); - - assertAppliedAndReverted(preparationResult); + assertThat(findAction(preparationResult.patch, "move/to/this/directory/move.me")).isInstanceOf(CreateAction.class); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testDontMoveFromDirectoryToFile() throws Exception { - myPatchSpec.setStrict(true); - FileUtil.createDirectory(new File(myOlderDir, "from/move.me")); - FileUtil.writeToFile(new File(myNewerDir, "move/to/move.me"), "different"); - createPatch(); - + @Test void noMoveFromDirectoryToFile() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createDirectories(dirs.oldDir.resolve("from/move.me")); + Utils.writeString(dirs.newDir.resolve("move/to/move.me"), "different"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); // creating a patch would have crashed if the directory had been chosen - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + assertThat(preparationResult.validationResults).isEmpty(); assertThat(findAction(preparationResult.patch, "move/to/move.me")).isInstanceOf(CreateAction.class); assertThat(findAction(preparationResult.patch, "from/move.me/")).isInstanceOf(DeleteAction.class); - - assertAppliedAndReverted(preparationResult); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testMoveFileByLocation() throws Exception { - myPatchSpec.setStrict(true); - FileUtil.writeToFile(new File(myOlderDir, "move/from/this/directory/move.me"), "they"); - FileUtil.writeToFile(new File(myOlderDir, "not/from/this/one/move.me"), "are"); - FileUtil.writeToFile(new File(myNewerDir, "move/to/this/directory/move.me"), "different"); - createPatch(); + @Test void moveFileByLocation() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Utils.writeString(dirs.oldDir.resolve("move/from/this/directory/move.me"), "they"); + Utils.writeString(dirs.oldDir.resolve("not/from/this/one/move.me"), "are"); + Utils.writeString(dirs.newDir.resolve("move/to/this/directory/move.me"), "different"); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setStrict(true)); + var hash = Digester.digestStream(new ByteArrayInputStream("they".getBytes(StandardCharsets.UTF_8))); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - long hash = Digester.digestStream(new ByteArrayInputStream("they".getBytes(StandardCharsets.UTF_8))); - - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(findAction(preparationResult.patch, "move/to/this/directory/move.me")) .isEqualTo(new UpdateAction(preparationResult.patch, "move/to/this/directory/move.me", "move/from/this/directory/move.me", hash, false)); - - assertAppliedAndReverted(preparationResult); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testSymlinkAdded() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @Test void symlinkAdded() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("Readme.txt")); - Files.createSymbolicLink(new File(myNewerDir, "Readme.link").toPath(), Path.of("Readme.txt")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testSymlinkRemoved() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @Test void symlinkRemoved() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); - Files.createSymbolicLink(new File(myOlderDir, "Readme.link").toPath(), Path.of("Readme.txt")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testSymlinkRenamed() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @SuppressWarnings("DuplicateExpressions") + @Test void symlinkRenamed() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.lnk"), Path.of("Readme.txt")); - Files.createSymbolicLink(new File(myOlderDir, "Readme.link").toPath(), Path.of("Readme.txt")); - Files.createSymbolicLink(new File(myNewerDir, "Readme.lnk").toPath(), Path.of("Readme.txt")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testSymlinkRetargeted() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @Test void symlinkRetargeted() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("./Readme.txt")); - Files.createSymbolicLink(new File(myOlderDir, "Readme.link").toPath(), Path.of("Readme.txt")); - Files.createSymbolicLink(new File(myNewerDir, "Readme.link").toPath(), Path.of("./Readme.txt")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testZipFileMove() throws Exception { - resetNewerDir(); - FileUtil.rename(new File(myNewerDir, "lib/annotations.jar"), new File(myNewerDir, "lib/redist/annotations.jar")); + @Test void zipFileMove() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Utils.move(dirs.newDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib/redist/annotations.jar"), false); - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testZipFileMoveWithUpdate() throws Exception { - resetNewerDir(); - FileUtil.delete(new File(myNewerDir, "lib/annotations.jar")); - FileUtil.copy(new File(dataDir, "lib/annotations_changed.jar"), new File(myNewerDir, "lib/redist/annotations.jar")); + @Test void zipFileMoveWithUpdate() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.delete(dirs.newDir.resolve("lib/annotations.jar")); + Utils.copy(dataDir.resolve("lib/annotations_changed.jar"), dirs.newDir.resolve("lib/redist/annotations.jar"), false); - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void testReadOnlyFilesAreDeletable() throws Exception { - File file = new File(myOlderDir, "bin/read_only_to_delete"); - FileUtil.writeToFile(file, "bye"); - assertTrue(file.setWritable(false, false)); + @Test void readOnlyFilesAreDeletable() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var file = dirs.oldDir.resolve("bin/read_only_to_delete"); + Files.writeString(file, "bye"); + UpdaterTestCase.setReadOnly(file); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - createPatch(); - PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).isEmpty(); - assertAppliedAndReverted(preparationResult); + assertAppliedAndReverted(dirs, preparationResult); } - @Test - public void testExecutableFlagChange() throws Exception { - assumeFalse("Windows-allergic", Utils.IS_WINDOWS); + @Test @DisabledOnOs(OS.WINDOWS) void executableFlagChange() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.writeString(dirs.oldDir.resolve("bin/to_become_executable"), "to_become_executable"); + Files.writeString(dirs.oldDir.resolve("bin/to_become_plain"), "to_become_plain"); + Utils.setExecutable(dirs.oldDir.resolve("bin/to_become_plain")); + Files.writeString(dirs.newDir.resolve("bin/to_become_executable"), "to_become_executable"); + Files.writeString(dirs.newDir.resolve("bin/to_become_plain"), "to_become_plain"); + Utils.setExecutable(dirs.newDir.resolve("bin/to_become_executable")); - FileUtil.writeToFile(new File(myOlderDir, "bin/to_become_executable"), "to_become_executable"); - FileUtil.writeToFile(new File(myOlderDir, "bin/to_become_plain"), "to_become_plain"); - Utils.setExecutable(new File(myOlderDir, "bin/to_become_plain"), true); - resetNewerDir(); - Utils.setExecutable(new File(myNewerDir, "bin/to_become_plain"), false); - Utils.setExecutable(new File(myNewerDir, "bin/to_become_executable"), true); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void fileToSymlinks() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @Test void fileToSymlinks() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.move(dirs.newDir.resolve("Readme.txt"), dirs.newDir.resolve("Readme.md")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.txt"), Path.of("Readme.md")); - resetNewerDir(); - Files.move(new File(myNewerDir, "Readme.txt").toPath(), new File(myNewerDir, "Readme.md").toPath()); - Files.createSymbolicLink(new File(myNewerDir, "Readme.txt").toPath(), Path.of("Readme.md")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void multipleDirectorySymlinks() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @SuppressWarnings("DuplicateExpressions") + @Test void multipleDirectorySymlinks() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); - resetNewerDir(); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib1.dylib")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib2.dylib")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r1.bin")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r2.bin")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Versions/Current"), Path.of("A")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); + Files.createDirectories(dirs.oldDir.resolve("Home/Frameworks")); + Files.createSymbolicLink(dirs.oldDir.resolve("Home/Frameworks/A.framework"), Path.of("../../A.framework")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib1.dylib")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib2.dylib")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r1.bin")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r2.bin")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Versions/Current"), Path.of("A")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - Files.createDirectories(myOlderDir.toPath().resolve("Home/Frameworks")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("Home/Frameworks/A.framework"), Path.of("../../A.framework")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Libraries/lib1.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Libraries/lib2.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Resources/r1.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Resources/r2.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Libraries/lib1.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Libraries/lib2.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Resources/r1.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Resources/r2.bin")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Versions/Previous"), Path.of("A")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Versions/Current"), Path.of("B")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Libraries/lib1.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Libraries/lib2.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Resources/r1.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Resources/r2.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Libraries/lib1.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Libraries/lib2.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Resources/r1.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Resources/r2.bin")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Versions/Previous"), Path.of("A")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Versions/Current"), Path.of("B")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void symlinksToFilesAndDirectories() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); + @Test void symlinksToFilesAndDirectories() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); - resetNewerDir(); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib1.dylib")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib2.dylib")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r1/res.bin")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r2/res.bin")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Versions/Current"), Path.of("A")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib1.dylib")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib2.dylib")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r1/res.bin")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r2/res.bin")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Versions/Current"), Path.of("A")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); + randomFile(dirs.newDir.resolve("A.framework/Libraries/lib1.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Libraries/lib2.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Resources/r1/res.bin")); + randomFile(dirs.newDir.resolve("A.framework/Resources/r2/res.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Libraries/lib1.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Libraries/lib2.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Resources/r1/res.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Resources/r2/res.bin")); - - assertAppliedAndReverted(); + assertAppliedAndReverted(dirs); } - @Test - public void creatingParentDirectoriesForMissingCriticalFiles() throws Exception { - randomFile(myOlderDir.toPath().resolve("plugins/some/lib/plugin.jar")); - randomFile(myOlderDir.toPath().resolve("plugins/other/lib/plugin.jar")); - resetNewerDir(); - randomFile(myNewerDir.toPath().resolve("plugins/some/lib/plugin.jar")); + @Test void creatingParentDirectoriesForMissingCriticalFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + randomFile(dirs.oldDir.resolve("plugins/some/lib/plugin.jar")); + randomFile(dirs.oldDir.resolve("plugins/other/lib/plugin.jar")); + randomFile(dirs.newDir.resolve("plugins/some/lib/plugin.jar")); + Utils.copy(dirs.oldDir.resolve("plugins/other/lib/plugin.jar"), dirs.newDir.resolve("plugins/other/lib/plugin.jar"), false); + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir).setCriticalFiles(List.of("plugins/some/lib/plugin.jar"))); + Utils.delete(dirs.oldDir.resolve("plugins/some")); + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); - myPatchSpec.setCriticalFiles(List.of("plugins/some/lib/plugin.jar")); - createPatch(); - - NioFiles.deleteRecursively(myOlderDir.toPath().resolve("plugins/some")); - - var preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); assertThat(preparationResult.validationResults).containsExactly( new ValidationResult(ValidationResult.Kind.CONFLICT, "plugins/some/lib/plugin.jar", @@ -725,69 +647,77 @@ public abstract class PatchApplyingRevertingTest extends PatchTestCase { "Absent", ValidationResult.Option.REPLACE, ValidationResult.Option.KEEP) ); - assertAppliedAndReverted(preparationResult, (original, target) -> { + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> { original.put("plugins/some/", Digester.DIRECTORY); original.put("plugins/some/lib/", Digester.DIRECTORY); }); } - @Override - protected Patch createPatch() throws IOException { - assertFalse(myFile.exists()); - var patch = PatchFileCreator.create(myPatchSpec, myFile, null); - assertTrue(myFile.exists()); - return patch; - } - - private static PatchAction findAction(Patch patch, String path) { - return ContainerUtil.find(patch.getActions(), a -> a.getPath().equals(path)); - } - - private static void modifyFile(File file) throws IOException { - try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + private static void modifyFile(Path file) throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "rw")) { raf.seek(20); raf.write(42); } } - private void assertNotApplied(PatchFileCreator.PreparationResult preparationResult) throws Exception { - assertNotApplied(preparationResult, Collections.emptyMap()); + private Path createPatch(PatchSpec spec) throws IOException { + var patchFile = tempDir.resolve("patch.zip"); + PatchFileCreator.create(spec, patchFile.toFile(), null); + assertThat(patchFile).isRegularFile(); + return patchFile; } - private void assertNotApplied(PatchFileCreator.PreparationResult preparationResult, - Map options) throws Exception { - Patch patch = preparationResult.patch; - File backupDir = myDoBackup ? getTempFile("backup") : null; - Map original = digest(patch, myOlderDir); - - PatchFileCreator.ApplicationResult applicationResult = PatchFileCreator.apply(preparationResult, options, backupDir, TEST_UI); - assertFalse(applicationResult.applied); - - if (myDoBackup) { - PatchFileCreator.revert(preparationResult, applicationResult.appliedActions, backupDir, TEST_UI); - assertThat(digest(patch, myOlderDir)).containsExactlyEntriesOf(original); - } + @SuppressWarnings({"SSBasedInspection", "RedundantSuppression"}) + private static PatchAction findAction(Patch patch, String path) { + return patch.getActions().stream().filter(a -> a.getPath().equals(path)).findFirst().orElse(null); } - private void assertAppliedAndReverted() throws Exception { - if (!myFile.exists()) { - createPatch(); - } - var preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); - assertAppliedAndReverted(preparationResult, (original, target) -> {}); + private void assertNotApplied(UpdaterTestCase.Directories dirs, PatchFileCreator.PreparationResult preparationResult) throws Exception { + assertNotApplied(dirs, preparationResult, Map.of()); } - private void assertAppliedAndReverted(PatchFileCreator.PreparationResult preparationResult) throws Exception { - assertAppliedAndReverted(preparationResult, (original, target) -> {}); - } - - private void assertAppliedAndReverted(PatchFileCreator.PreparationResult preparationResult, - BiConsumer, Map> adjuster) throws Exception { + private void assertNotApplied( + UpdaterTestCase.Directories dirs, + PatchFileCreator.PreparationResult preparationResult, + Map options + ) throws Exception { var patch = preparationResult.patch; - var original = digest(patch, myOlderDir); - var target = digest(patch, myNewerDir); + var backupDir = doBackup ? tempDir.resolve("backup").toFile() : null; + var original = digest(patch, dirs.oldDir); + + var applicationResult = PatchFileCreator.apply(preparationResult, options, backupDir, testUI); + assertThat(applicationResult.applied).isFalse(); + + if (doBackup) { + PatchFileCreator.revert(preparationResult, applicationResult.appliedActions, backupDir, testUI); + assertThat(digest(patch, dirs.oldDir)).containsExactlyEntriesOf(original); + } + } + + private void assertAppliedAndReverted(UpdaterTestCase.Directories dirs) throws Exception { + var patchFile = createPatch(createPatchSpec(dirs.oldDir, dirs.newDir)); + assertAppliedAndReverted(dirs, patchFile); + } + + private void assertAppliedAndReverted(UpdaterTestCase.Directories dirs, Path patchFile) throws IOException, OperationCancelledException { + var preparationResult = PatchFileCreator.prepareAndValidate(patchFile.toFile(), dirs.oldDir.toFile(), testUI); + assertAppliedAndReverted(dirs, preparationResult); + } + + private void assertAppliedAndReverted(UpdaterTestCase.Directories dirs, PatchFileCreator.PreparationResult preparationResult) throws IOException { + assertAppliedAndReverted(dirs, preparationResult, (original, target) -> {}); + } + + private void assertAppliedAndReverted( + UpdaterTestCase.Directories dirs, + PatchFileCreator.PreparationResult preparationResult, + BiConsumer, Map> adjuster + ) throws IOException { + var patch = preparationResult.patch; + var original = digest(patch, dirs.oldDir); + var target = digest(patch, dirs.newDir); adjuster.accept(original, target); - var backupDir = myDoBackup ? getTempFile("backup") : null; + var backupDir = doBackup ? tempDir.resolve("backup").toFile() : null; var options = new HashMap(); for (var each : preparationResult.validationResults) { @@ -800,19 +730,23 @@ public abstract class PatchApplyingRevertingTest extends PatchTestCase { } } - var applicationResult = PatchFileCreator.apply(preparationResult, options, backupDir, TEST_UI); + var applicationResult = PatchFileCreator.apply(preparationResult, options, backupDir, testUI); if (applicationResult.error != null) { throw new AssertionError("patch failed", applicationResult.error); } assertThat(applicationResult.applied).isTrue(); - assertThat(digest(patch, myOlderDir)).containsExactlyEntriesOf(target); + assertThat(digest(patch, dirs.oldDir)).containsExactlyEntriesOf(target); - if (myDoBackup) { - PatchFileCreator.revert(preparationResult, applicationResult.appliedActions, backupDir, TEST_UI); - assertThat(digest(patch, myOlderDir)).containsExactlyEntriesOf(original); + if (doBackup) { + PatchFileCreator.revert(preparationResult, applicationResult.appliedActions, backupDir, testUI); + assertThat(digest(patch, dirs.oldDir)).containsExactlyEntriesOf(original); } } + private static Map digest(Patch patch, Path dir) throws IOException { + return new TreeMap<>(patch.digestFiles(dir, Set.of())); + } + private static class MyFailOnApplyPatchAction extends PatchAction { MyFailOnApplyPatchAction(Patch patch) { super(patch, "_dummy_file_", Digester.INVALID); diff --git a/updater/testSrc/com/intellij/updater/PatchCreationTest.java b/updater/testSrc/com/intellij/updater/PatchCreationTest.java index 7b01ae11ebcd..067a482d7370 100644 --- a/updater/testSrc/com/intellij/updater/PatchCreationTest.java +++ b/updater/testSrc/com/intellij/updater/PatchCreationTest.java @@ -1,78 +1,70 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.updater; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.io.FileUtil; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; -import java.io.*; -import java.nio.channels.FileLock; -import java.nio.charset.StandardCharsets; +import java.io.File; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.util.List; -import java.util.Map; +import java.util.Set; -import static com.intellij.openapi.util.io.IoTestUtil.assumeSymLinkCreationIsSupported; +import static com.intellij.updater.UpdaterTestCase.*; import static org.assertj.core.api.Assertions.assertThat; -public class PatchCreationTest extends PatchTestCase { - @Test - public void testDigestFiles() throws Exception { - Patch patch = createPatch(); - Map checkSums = digest(patch, myOlderDir); - assertThat(checkSums).hasSize(10); +@UpdaterTest +class PatchCreationTest { + @TempDir Path tempDir; + @UpdaterTestData Path dataDir; + + private final ConsoleUpdaterUI testUI = new ConsoleUpdaterUI(false); + + @Test void digestFiles() throws Exception { + var patch = new Patch(createPatchSpec(dataDir, dataDir)); + assertThat(patch.digestFiles(dataDir, Set.of())).hasSize(11); } - @Test - public void testBasics() throws Exception { - Patch patch = createPatch(); + @Test void basics() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "bin/idea.bat", CHECKSUMS.IDEA_BAT), + new DeleteAction(patch, "bin/idea.bat", UpdaterTestCase.IDEA_BAT), new CreateAction(patch, "newDir/"), new CreateAction(patch, "newDir/newFile.txt"), - new UpdateAction(patch, "Readme.txt", CHECKSUMS.README_TXT), - new UpdateAction(patch, "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), - new UpdateAction(patch, "lib/bootstrap.jar", CHECKSUMS.BOOTSTRAP_JAR)); + new UpdateAction(patch, "Readme.txt", UpdaterTestCase.README_TXT), + new UpdateAction(patch, "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), + new UpdateAction(patch, "lib/bootstrap.jar", UpdaterTestCase.BOOTSTRAP_JAR)); } - @Test - public void testCreatingWithIgnoredFiles() throws Exception { - PatchSpec spec = new PatchSpec() - .setOldFolder(myOlderDir.getAbsolutePath()) - .setNewFolder(myNewerDir.getAbsolutePath()) - .setIgnoredFiles(List.of("Readme.txt", "bin/idea.bat")); - Patch patch = new Patch(spec); + @Test void creatingWithIgnoredFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir).setIgnoredFiles(List.of("Readme.txt", "bin/idea.bat"))); assertThat(sortActions(patch.getActions())).containsExactly( new CreateAction(patch, "newDir/"), new CreateAction(patch, "newDir/newFile.txt"), - new UpdateAction(patch, "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), - new UpdateAction(patch, "lib/bootstrap.jar", CHECKSUMS.BOOTSTRAP_JAR)); + new UpdateAction(patch, "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), + new UpdateAction(patch, "lib/bootstrap.jar", UpdaterTestCase.BOOTSTRAP_JAR)); } - @Test - public void testValidation() throws Exception { - FileUtil.delete(new File(myNewerDir, "bin/focuskiller.dll")); - FileUtil.copy(new File(myOlderDir, "bin/focuskiller.dll"), new File(myNewerDir, "newDir/focuskiller.dll")); - Patch patch = createPatch(); + @Test void validation() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.writeString(dirs.oldDir.resolve("bin/idea.bat"), "changed"); + Files.createDirectory(dirs.oldDir.resolve("extraDir")); + Files.writeString(dirs.oldDir.resolve("extraDir/extraFile.txt"), ""); + Files.createDirectory(dirs.oldDir.resolve("newDir")); + Files.writeString(dirs.oldDir.resolve("newDir/newFile.txt"), ""); + Files.writeString(dirs.oldDir.resolve("Readme.txt"), "changed"); + Files.writeString(dirs.oldDir.resolve("lib/annotations.jar"), "changed"); + Files.delete(dirs.oldDir.resolve("lib/bootstrap.jar")); - FileUtil.writeToFile(new File(myOlderDir, "bin/idea.bat"), "changed"); - FileUtil.writeToFile(new File(myOlderDir, "bin/focuskiller.dll"), "changed"); - FileUtil.createDirectory(new File(myOlderDir, "extraDir")); - FileUtil.writeToFile(new File(myOlderDir, "extraDir/extraFile.txt"), ""); - FileUtil.createDirectory(new File(myOlderDir, "newDir")); - FileUtil.writeToFile(new File(myOlderDir, "newDir/newFile.txt"), ""); - FileUtil.writeToFile(new File(myOlderDir, "Readme.txt"), "changed"); - FileUtil.writeToFile(new File(myOlderDir, "lib/annotations.jar"), "changed"); - FileUtil.delete(new File(myOlderDir, "lib/bootstrap.jar")); - - assertThat(sortResults(patch.validate(myOlderDir, TEST_UI))).containsExactly( - new ValidationResult(ValidationResult.Kind.CONFLICT, - "bin/focuskiller.dll", - ValidationResult.Action.DELETE, - "Modified", - ValidationResult.Option.DELETE, ValidationResult.Option.KEEP), + assertThat(sortResults(patch.validate(dirs.oldDir.toFile(), testUI))).containsExactly( new ValidationResult(ValidationResult.Kind.CONFLICT, "bin/idea.bat", ValidationResult.Action.DELETE, @@ -93,11 +85,6 @@ public class PatchCreationTest extends PatchTestCase { ValidationResult.Action.UPDATE, "Modified", ValidationResult.Option.IGNORE), - new ValidationResult(ValidationResult.Kind.ERROR, - "bin/focuskiller.dll", - ValidationResult.Action.UPDATE, - "Modified", - ValidationResult.Option.IGNORE), new ValidationResult(ValidationResult.Kind.ERROR, "lib/annotations.jar", ValidationResult.Action.UPDATE, @@ -110,61 +97,67 @@ public class PatchCreationTest extends PatchTestCase { ValidationResult.Option.IGNORE)); } - @Test - public void testValidatingCaseOnlyRename() throws Exception { - Patch patch = createCaseOnlyRenamePatch(); - assertThat(patch.validate(myOlderDir, TEST_UI)).isEmpty(); + @Test void validatingCaseOnlyRename() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + simulateCaseOnlyRename(patch); + + assertThat(patch.validate(dirs.oldDir.toFile(), testUI)).isEmpty(); } - @Test - public void testValidatingCaseOnlyRenameWithConflict() throws Exception { - assertThat(Runner.isCaseSensitiveFs()).isEqualTo(SystemInfo.isFileSystemCaseSensitive); + @Test void validatingCaseOnlyRenameWithConflict() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + simulateCaseOnlyRename(patch); + Files.writeString(dirs.oldDir.resolve("bin/IDEA.bat"), Files.readString(dirs.oldDir.resolve("bin/idea.bat"))); - Patch patch = createCaseOnlyRenamePatch(); - FileUtil.writeToFile(new File(myOlderDir, "bin/IDEA.bat"), FileUtil.loadFileBytes(new File(myOlderDir, "bin/idea.bat"))); - - List results = patch.validate(myOlderDir, TEST_UI); - if (SystemInfo.isFileSystemCaseSensitive) { + var results = patch.validate(dirs.oldDir.toFile(), testUI); + if (Runner.isCaseSensitiveFs()) { assertThat(results).containsExactly( new ValidationResult(ValidationResult.Kind.CONFLICT, - "bin/IDEA.bat", - ValidationResult.Action.CREATE, - "Already exists", - ValidationResult.Option.REPLACE, ValidationResult.Option.KEEP)); + "bin/IDEA.bat", + ValidationResult.Action.CREATE, + "Already exists", + ValidationResult.Option.REPLACE, ValidationResult.Option.KEEP)); } else { assertThat(results).isEmpty(); } } - @Test - public void testValidationWithOptionalFiles() throws Exception { - Patch patch1 = createPatch(); - FileUtil.copy(new File(myOlderDir, "lib/boot.jar"), new File(myOlderDir, "lib/annotations.jar")); - assertThat(patch1.validate(myOlderDir, TEST_UI)).containsExactly( + private static void simulateCaseOnlyRename(Patch patch) { + assertThat(patch.getActions().get(0)) + .isInstanceOf(DeleteAction.class) + .hasFieldOrPropertyWithValue("path", "bin/idea.bat"); + patch.getActions().add(1, new CreateAction(patch, "bin/IDEA.bat")); // simulates rename "idea.bat" -> "IDEA.bat" + } + + @Test void validationWithOptionalFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + + var patch1 = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + Files.copy(dirs.oldDir.resolve("lib/boot.jar"), dirs.oldDir.resolve("lib/annotations.jar"), StandardCopyOption.REPLACE_EXISTING); + assertThat(patch1.validate(dirs.oldDir.toFile(), testUI)).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, "lib/annotations.jar", ValidationResult.Action.UPDATE, "Modified", ValidationResult.Option.IGNORE)); - PatchSpec spec = new PatchSpec() - .setOldFolder(myOlderDir.getAbsolutePath()) - .setNewFolder(myNewerDir.getAbsolutePath()) - .setOptionalFiles(List.of("lib/annotations.jar")); - Patch patch2 = new Patch(spec); - FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); - assertThat(patch2.validate(myOlderDir, TEST_UI)).isEmpty(); + var patch2 = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir).setOptionalFiles(List.of("lib/annotations.jar"))); + Files.delete(dirs.oldDir.resolve("lib/annotations.jar")); + assertThat(patch2.validate(dirs.oldDir.toFile(), testUI)).isEmpty(); } - @Test - public void testValidatingNonAccessibleFiles() throws Exception { - Patch patch = createPatch(); - File f = new File(myOlderDir, "Readme.txt"); - try (FileOutputStream s = new FileOutputStream(f, true); FileLock ignored = s.getChannel().lock()) { - String message = Utils.IS_WINDOWS ? "Locked by: [" + ProcessHandle.current().pid() + "] OpenJDK Platform binary" : "Access denied"; - ValidationResult.Option option = Utils.IS_WINDOWS ? ValidationResult.Option.KILL_PROCESS : ValidationResult.Option.IGNORE; - assertThat(patch.validate(myOlderDir, TEST_UI)).containsExactly( + @Test void validatingNonAccessibleFiles() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, true); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + var file = dirs.oldDir.resolve("Readme.txt"); + try (var channel = FileChannel.open(file, StandardOpenOption.APPEND); var ignored = channel.lock()) { + var message = Utils.IS_WINDOWS ? "Locked by: [" + ProcessHandle.current().pid() + "] OpenJDK Platform binary" : "Access denied"; + var option = Utils.IS_WINDOWS ? ValidationResult.Option.KILL_PROCESS : ValidationResult.Option.IGNORE; + assertThat(patch.validate(dirs.oldDir.toFile(), testUI)).containsExactly( new ValidationResult(ValidationResult.Kind.ERROR, "Readme.txt", ValidationResult.Action.UPDATE, @@ -173,122 +166,112 @@ public class PatchCreationTest extends PatchTestCase { } } - @Test - public void testZipFileMove() throws Exception { - resetNewerDir(); - FileUtil.rename(new File(myNewerDir, "lib/annotations.jar"), new File(myNewerDir, "lib/redist/annotations.jar")); + @Test void zipFileMove() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Utils.move(dirs.newDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib/redist/annotations.jar"), false); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - Patch patch = createPatch(); assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), new CreateAction(patch, "lib/redist/"), - new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR, true)); + new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR, true)); } - @Test - public void testZipFileMoveWithUpdate() throws Exception { - resetNewerDir(); - FileUtil.delete(new File(myNewerDir, "lib/annotations.jar")); - FileUtil.copy(new File(dataDir, "lib/annotations_changed.jar"), new File(myNewerDir, "lib/redist/annotations.jar")); + @Test void zipFileMoveWithUpdate() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.delete(dirs.newDir.resolve("lib/annotations.jar")); + Utils.copy(dataDir.resolve("lib/annotations_changed.jar"), dirs.newDir.resolve("lib/redist/annotations.jar"), false); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - Patch patch = createPatch(); assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), new CreateAction(patch, "lib/redist/"), - new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR, false)); + new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR, false)); } - @Test - public void testZipFileMoveWithAlternatives() throws Exception { - FileUtil.copy(new File(myOlderDir, "lib/annotations.jar"), new File(myOlderDir, "lib64/annotations.jar")); - resetNewerDir(); - FileUtil.rename(new File(myNewerDir, "lib/annotations.jar"), new File(myNewerDir, "lib/redist/annotations.jar")); - FileUtil.rename(new File(myNewerDir, "lib64/annotations.jar"), new File(myNewerDir, "lib64/redist/annotations.jar")); + @Test void zipFileMoveWithAlternatives() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Utils.copy(dirs.oldDir.resolve("lib/annotations.jar"), dirs.oldDir.resolve("lib64/annotations.jar"), false); + Utils.copy(dirs.newDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib64/redist/annotations.jar"), false); + Utils.move(dirs.newDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib/redist/annotations.jar"), false); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - Patch patch = createPatch(); assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), - new DeleteAction(patch, "lib64/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib64/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR), new CreateAction(patch, "lib/redist/"), new CreateAction(patch, "lib64/redist/"), - new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR, true), - new UpdateAction(patch, "lib64/redist/annotations.jar", "lib64/annotations.jar", CHECKSUMS.ANNOTATIONS_JAR, true)); + new UpdateAction(patch, "lib/redist/annotations.jar", "lib/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR, true), + new UpdateAction(patch, "lib64/redist/annotations.jar", "lib64/annotations.jar", UpdaterTestCase.ANNOTATIONS_JAR, true)); } - @Test - public void testNoOptionalFileMove1() throws Exception { - resetNewerDir(); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myOlderDir, "lib/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations_changed.jar"), new File(myOlderDir, "lib64/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myNewerDir, "lib/redist/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myNewerDir, "lib64/redist/annotations.bin")); + @Test void noOptionalFileMove1() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.oldDir.resolve("lib/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations_changed.jar"), dirs.oldDir.resolve("lib64/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib/redist/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib64/redist/annotations.bin"), false); - Patch patch = createPatch(spec -> spec.setOptionalFiles(List.of("lib/annotations.bin", "lib/redist/annotations.bin"))); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir).setOptionalFiles(List.of("lib/annotations.bin", "lib/redist/annotations.bin"))); assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "lib/annotations.bin", CHECKSUMS.ANNOTATIONS_JAR), - new DeleteAction(patch, "lib64/annotations.bin", CHECKSUMS.ANNOTATIONS_CHANGED_JAR), + new DeleteAction(patch, "lib/annotations.bin", UpdaterTestCase.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib64/annotations.bin", UpdaterTestCase.ANNOTATIONS_CHANGED_JAR), new CreateAction(patch, "lib/redist/"), new CreateAction(patch, "lib64/redist/"), - new UpdateAction(patch, "lib/redist/annotations.bin", "lib/annotations.bin", CHECKSUMS.ANNOTATIONS_JAR, true), - new UpdateAction(patch, "lib64/redist/annotations.bin", "lib64/annotations.bin", CHECKSUMS.ANNOTATIONS_CHANGED_JAR, false)); + new UpdateAction(patch, "lib/redist/annotations.bin", "lib/annotations.bin", UpdaterTestCase.ANNOTATIONS_JAR, true), + new UpdateAction(patch, "lib64/redist/annotations.bin", "lib64/annotations.bin", UpdaterTestCase.ANNOTATIONS_CHANGED_JAR, false)); } - @Test - public void testNoOptionalFileMove2() throws Exception { - resetNewerDir(); - FileUtil.copy(new File(dataDir, "lib/annotations_changed.jar"), new File(myOlderDir, "lib/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myOlderDir, "lib64/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myNewerDir, "lib/redist/annotations.bin")); - FileUtil.copy(new File(dataDir, "lib/annotations.jar"), new File(myNewerDir, "lib64/redist/annotations.bin")); + @Test void noOptionalFileMove2() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Utils.copy(dataDir.resolve("lib/annotations_changed.jar"), dirs.oldDir.resolve("lib/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.oldDir.resolve("lib64/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib/redist/annotations.bin"), false); + Utils.copy(dataDir.resolve("lib/annotations.jar"), dirs.newDir.resolve("lib64/redist/annotations.bin"), false); - Patch patch = createPatch(spec -> spec.setOptionalFiles(List.of("lib/annotations.bin", "lib/redist/annotations.bin"))); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir).setOptionalFiles(List.of("lib/annotations.bin", "lib/redist/annotations.bin"))); assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "lib/annotations.bin", CHECKSUMS.ANNOTATIONS_CHANGED_JAR), - new DeleteAction(patch, "lib64/annotations.bin", CHECKSUMS.ANNOTATIONS_JAR), + new DeleteAction(patch, "lib/annotations.bin", UpdaterTestCase.ANNOTATIONS_CHANGED_JAR), + new DeleteAction(patch, "lib64/annotations.bin", UpdaterTestCase.ANNOTATIONS_JAR), new CreateAction(patch, "lib/redist/"), new CreateAction(patch, "lib64/redist/"), - new UpdateAction(patch, "lib/redist/annotations.bin", "lib64/annotations.bin", CHECKSUMS.ANNOTATIONS_JAR, true), - new UpdateAction(patch, "lib64/redist/annotations.bin", "lib64/annotations.bin", CHECKSUMS.ANNOTATIONS_JAR, true)); + new UpdateAction(patch, "lib/redist/annotations.bin", "lib64/annotations.bin", UpdaterTestCase.ANNOTATIONS_JAR, true), + new UpdateAction(patch, "lib64/redist/annotations.bin", "lib64/annotations.bin", UpdaterTestCase.ANNOTATIONS_JAR, true)); } - @Test - public void testSaveLoad() throws Exception { - Patch original = createPatch(); - File f = getTempFile("file"); - try (FileOutputStream out = new FileOutputStream(f)) { + @Test void testSaveLoad() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + var original = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + var f = tempDir.resolve("file"); + try (var out = Files.newOutputStream(f)) { original.write(out); } Patch recreated; - try (FileInputStream in = new FileInputStream(f)) { + try (var in = Files.newInputStream(f)) { recreated = new Patch(in); } assertThat(recreated.getActions()).isEqualTo(original.getActions()); } - @Test - public void testNoSymlinkNoise() throws IOException { - assumeSymLinkCreationIsSupported(); + @SuppressWarnings("DuplicateExpressions") + @Test void noSymlinkNoise() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.writeString(dirs.oldDir.resolve("bin/_target"), "test"); + Files.createSymbolicLink(dirs.oldDir.resolve("bin/_link"), Path.of("_target")); + Files.writeString(dirs.newDir.resolve("bin/_target"), "test"); + Files.createSymbolicLink(dirs.newDir.resolve("bin/_link"), Path.of("_target")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - Files.write(new File(myOlderDir, "bin/_target").toPath(), "test".getBytes(StandardCharsets.UTF_8)); - Files.createSymbolicLink(new File(myOlderDir, "bin/_link").toPath(), Path.of("_target")); - resetNewerDir(); - - Patch patch = createPatch(); assertThat(patch.getActions()).isEmpty(); } - @Test - public void testSymlinkDereferenceAndMove() throws IOException { - assumeSymLinkCreationIsSupported(); + @Test void testSymlinkDereferenceAndMove() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + var checksum = randomFile(dirs.oldDir.resolve("bin/mac_lib.jnilib")); + Files.createSymbolicLink(dirs.oldDir.resolve("bin/mac_lib.dylib"), Path.of("mac_lib.jnilib")); + Utils.copy(dirs.oldDir.resolve("bin/mac_lib.jnilib"), dirs.newDir.resolve("plugins/whatever/bin/mac_lib.dylib"), false); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - long checksum = randomFile(myOlderDir.toPath().resolve("bin/mac_lib.jnilib")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("bin/mac_lib.dylib"), Path.of("mac_lib.jnilib")); - resetNewerDir(); - Utils.delete(new File(myNewerDir, "bin/mac_lib.dylib")); - Files.createDirectories(new File(myNewerDir, "plugins/whatever/bin").toPath()); - Files.move(new File(myNewerDir, "bin/mac_lib.jnilib").toPath(), new File(myNewerDir, "plugins/whatever/bin/mac_lib.dylib").toPath()); - - Patch patch = createPatch(); assertThat(sortActions(patch.getActions())).containsExactly( new DeleteAction(patch, "bin/mac_lib.dylib", linkHash("mac_lib.jnilib")), new DeleteAction(patch, "bin/mac_lib.jnilib", checksum), @@ -298,57 +281,151 @@ public class PatchCreationTest extends PatchTestCase { new CreateAction(patch, "plugins/whatever/bin/mac_lib.dylib")); } - @Test - public void testValidatingSymlinkToDirectory() throws Exception { - assumeSymLinkCreationIsSupported(); + @Test void validatingSymlinkToDirectory() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createDirectories(dirs.oldDir.resolve("other_dir")); + Files.createSymbolicLink(dirs.oldDir.resolve("dir"), Path.of("other_dir")); + Files.createDirectories(dirs.newDir.resolve("dir")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - resetNewerDir(); - Files.createDirectories(myOlderDir.toPath().resolve("other_dir")); - Path target = Path.of("other_dir"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("dir"), target); - Files.createDirectories(myNewerDir.toPath().resolve("dir")); - - Patch patch = createPatch(); assertThat(sortActions(patch.getActions())).containsExactly( new DeleteAction(patch, "dir", linkHash("other_dir")), new DeleteAction(patch, "other_dir/", Digester.DIRECTORY), new CreateAction(patch, "dir/")); - assertThat(patch.validate(myOlderDir, TEST_UI)).isEmpty(); + assertThat(patch.validate(dirs.oldDir.toFile(), testUI)).isEmpty(); } - @Test - public void testValidatingMultipleSymlinkConversion() throws Exception { - assumeSymLinkCreationIsSupported(); - - resetNewerDir(); - - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib.dylib")); - randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r/res.bin")); + @Test void validatingMultipleSymlinkConversion() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib.dylib")); + randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r/res.bin")); Path target2 = Path.of("A"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Versions/Current"), target2); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Versions/Current"), target2); Path target1 = Path.of("Versions/Current/Libraries"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Libraries"), target1); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Libraries"), target1); Path target = Path.of("Versions/Current/Resources"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Resources"), target); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Resources"), target); + randomFile(dirs.newDir.resolve("A.framework/Libraries/lib.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Resources/r/res.bin")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); - randomFile(myNewerDir.toPath().resolve("A.framework/Libraries/lib.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Resources/r/res.bin")); - - Patch patch = createPatch(); - assertThat(patch.validate(myOlderDir, TEST_UI)).isEmpty(); + assertThat(patch.validate(dirs.oldDir.toFile(), testUI)).isEmpty(); } - private Patch createCaseOnlyRenamePatch() throws IOException { - Patch patch = createPatch(); - assertThat(patch.getActions().get(0)) - .isInstanceOf(DeleteAction.class) - .hasFieldOrPropertyWithValue("path", "bin/idea.bat"); - patch.getActions().add(1, new CreateAction(patch, "bin/IDEA.bat")); // simulates rename "idea.bat" -> "IDEA.bat" - return patch; + @SuppressWarnings("DuplicateExpressions") + @Test void same() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("Readme.txt")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(patch.getActions()).containsExactly(); } - private static long linkHash(String target) throws IOException { - return Digester.digestStream(new ByteArrayInputStream(target.getBytes(StandardCharsets.UTF_8))) | Digester.SYM_LINK; + @Test void create() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("Readme.txt")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new CreateAction(patch, "Readme.link")); + } + + @Test void delete() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "Readme.link", UpdaterTestCase.LINK_TO_README_TXT)); + } + + @Test void rename() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + var target = Path.of("Readme.txt"); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.lnk"), target); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), target); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "Readme.lnk", UpdaterTestCase.LINK_TO_README_TXT), + new CreateAction(patch, "Readme.link")); + } + + @Test void retarget() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.link"), Path.of("Readme.txt")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("./Readme.txt")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "Readme.link", UpdaterTestCase.LINK_TO_README_TXT), + new CreateAction(patch, "Readme.link")); + } + + @Test void renameAndRetarget() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.createSymbolicLink(dirs.oldDir.resolve("Readme.lnk"), Path.of("./Readme.txt")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.link"), Path.of("Readme.txt")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "Readme.lnk", + File.separatorChar == '\\' ? UpdaterTestCase.LINK_TO_DOT_README_TXT_DOS : UpdaterTestCase.LINK_TO_DOT_README_TXT_UNIX), + new CreateAction(patch, "Readme.link")); + } + + @Test void fileToLink() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + Files.move(dirs.newDir.resolve("Readme.txt"), dirs.newDir.resolve("Readme.md")); + Files.createSymbolicLink(dirs.newDir.resolve("Readme.txt"), Path.of("Readme.md")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "Readme.txt", UpdaterTestCase.README_TXT), + new CreateAction(patch, "Readme.md"), + new CreateAction(patch, "Readme.txt")); + } + + @SuppressWarnings("DuplicateExpressions") + @Test void multipleDirectorySymlinks() throws Exception { + var dirs = prepareDirectories(tempDir, dataDir, false); + var l1 = randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib1.dylib")); + var l2 = randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Libraries/lib2.dylib")); + var r1 = randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r1.bin")); + var r2 = randomFile(dirs.oldDir.resolve("A.framework/Versions/A/Resources/r2.bin")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Versions/Current"), Path.of("A")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); + Files.createSymbolicLink(dirs.oldDir.resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Libraries/lib1.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Libraries/lib2.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Resources/r1.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/A/Resources/r2.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Libraries/lib1.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Libraries/lib2.dylib")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Resources/r1.bin")); + randomFile(dirs.newDir.resolve("A.framework/Versions/B/Resources/r2.bin")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Versions/Previous"), Path.of("A")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Versions/Current"), Path.of("B")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); + Files.createSymbolicLink(dirs.newDir.resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); + var patch = new Patch(createPatchSpec(dirs.oldDir, dirs.newDir)); + + assertThat(sortActions(patch.getActions())).containsExactly( + new DeleteAction(patch, "A.framework/Versions/Current", linkHash("A")), + new CreateAction(patch, "A.framework/Versions/B/"), + new CreateAction(patch, "A.framework/Versions/B/Libraries/"), + new CreateAction(patch, "A.framework/Versions/B/Libraries/lib1.dylib"), + new CreateAction(patch, "A.framework/Versions/B/Libraries/lib2.dylib"), + new CreateAction(patch, "A.framework/Versions/B/Resources/"), + new CreateAction(patch, "A.framework/Versions/B/Resources/r1.bin"), + new CreateAction(patch, "A.framework/Versions/B/Resources/r2.bin"), + new CreateAction(patch, "A.framework/Versions/Current"), + new CreateAction(patch, "A.framework/Versions/Previous"), + new UpdateAction(patch, "A.framework/Versions/A/Libraries/lib1.dylib", l1), + new UpdateAction(patch, "A.framework/Versions/A/Libraries/lib2.dylib", l2), + new UpdateAction(patch, "A.framework/Versions/A/Resources/r1.bin", r1), + new UpdateAction(patch, "A.framework/Versions/A/Resources/r2.bin", r2)); } } diff --git a/updater/testSrc/com/intellij/updater/PatchTestCase.java b/updater/testSrc/com/intellij/updater/PatchTestCase.java deleted file mode 100644 index 03f5c3b4e4ba..000000000000 --- a/updater/testSrc/com/intellij/updater/PatchTestCase.java +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.updater; - -import com.intellij.openapi.util.io.FileUtil; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.Function; -import java.util.zip.CRC32; - -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.Assertions.assertThat; - -public abstract class PatchTestCase extends UpdaterTestCase { - protected File myNewerDir; - protected File myOlderDir; - - @Override - public void before() throws Exception { - super.before(); - - myOlderDir = getTempFile("oldDir"); - myNewerDir = getTempFile("newDir"); - FileUtil.copyDir(dataDir, myOlderDir); - FileUtil.copyDir(dataDir, myNewerDir); - - FileUtil.delete(new File(myNewerDir, "bin/idea.bat")); - FileUtil.writeToFile(new File(myNewerDir, "Readme.txt"), "hello"); - FileUtil.writeToFile(new File(myNewerDir, "newDir/newFile.txt"), "hello"); - - FileUtil.delete(new File(myOlderDir, "lib/annotations_changed.jar")); - FileUtil.delete(new File(myNewerDir, "lib/annotations.jar")); - FileUtil.rename(new File(myNewerDir, "lib/annotations_changed.jar"), - new File(myNewerDir, "lib/annotations.jar")); - - FileUtil.delete(new File(myOlderDir, "lib/bootstrap_deleted.jar")); - FileUtil.delete(new File(myNewerDir, "lib/bootstrap.jar")); - FileUtil.rename(new File(myNewerDir, "lib/bootstrap_deleted.jar"), - new File(myNewerDir, "lib/bootstrap.jar")); - } - - protected Patch createPatch() throws IOException { - return createPatch(Function.identity()); - } - - protected Patch createPatch(Function tuner) throws IOException { - PatchSpec spec = new PatchSpec() - .setOldFolder(myOlderDir.getAbsolutePath()) - .setNewFolder(myNewerDir.getAbsolutePath()); - return new Patch(tuner.apply(spec)); - } - - protected void resetNewerDir() throws IOException { - Utils.delete(myNewerDir); - Utils.copyDirectory(myOlderDir.toPath(), myNewerDir.toPath()); - } - - protected static Map digest(Patch patch, File dir) throws IOException { - return new TreeMap<>(patch.digestFiles(dir, Set.of())); - } - - protected static List sortActions(List actions) { - return sort(actions, a -> a.getClass().getSimpleName().charAt(0), Comparator.comparing(PatchAction::getPath)); - } - - protected static List sortResults(List results) { - return sort(results, r -> r.action, Comparator.comparing(r -> r.path)); - } - - private static List sort(List list, Function classifier, Comparator sorter) { - // splits the list into groups - Collection> groups = list.stream().collect(groupingBy(classifier, LinkedHashMap::new, toList())).values(); - // verifies the list is monotonic - assertThat(list).isEqualTo(groups.stream().flatMap(Collection::stream).collect(toList())); - // sorts group elements and concatenates groups into a list - return groups.stream() - .flatMap(elements -> elements.stream().sorted(sorter)) - .collect(toList()); - } - - protected static long randomFile(Path file) throws IOException { - Random rnd = new Random(); - int size = (1 + rnd.nextInt(1023)) * 1024; - byte[] data = new byte[size]; - rnd.nextBytes(data); - - Files.createDirectories(file.getParent()); - Files.write(file, data); - - CRC32 crc32 = new CRC32(); - crc32.update(data); - return crc32.getValue(); - } -} diff --git a/updater/testSrc/com/intellij/updater/RunnerTest.java b/updater/testSrc/com/intellij/updater/RunnerTest.java index 92302a267caf..d5d33b711c8a 100644 --- a/updater/testSrc/com/intellij/updater/RunnerTest.java +++ b/updater/testSrc/com/intellij/updater/RunnerTest.java @@ -1,16 +1,15 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.updater; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -public class RunnerTest { - @Test - public void testExtractingFiles() { - String[] args = {"bar", "ignored=xxx;yyy;zzz/zzz", "critical=", "ignored=aaa", "baz", "critical=ccc"}; +class RunnerTest { + @Test void extractingArgs() { + var args = new String[]{"bar", "ignored=xxx;yyy;zzz/zzz", "critical=", "ignored=aaa", "baz", "critical=ccc"}; assertThat(Runner.extractArguments(args, "ignored")).containsExactly("xxx", "yyy", "zzz/zzz", "aaa"); assertThat(Runner.extractArguments(args, "critical")).containsExactly("ccc"); assertThat(Runner.extractArguments(args, "unknown")).isEmpty(); } -} \ No newline at end of file +} diff --git a/updater/testSrc/com/intellij/updater/SymlinkPatchTest.java b/updater/testSrc/com/intellij/updater/SymlinkPatchTest.java deleted file mode 100644 index 774ecf28b1a8..000000000000 --- a/updater/testSrc/com/intellij/updater/SymlinkPatchTest.java +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.intellij.updater; - -import com.intellij.openapi.util.io.IoTestUtil; -import org.junit.Test; - -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; - -public class SymlinkPatchTest extends PatchTestCase { - @Override - public void before() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); - - super.before(); - - resetNewerDir(); - } - - @Test - public void same() throws Exception { - var target = Path.of("Readme.txt"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("Readme.link"), target); - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.link"), target); - assertThat(createPatch().getActions()).containsExactly(); - } - - @Test - public void create() throws Exception { - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.link"), Path.of("Readme.txt")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new CreateAction(patch, "Readme.link")); - } - - @Test - public void delete() throws Exception { - Files.createSymbolicLink(myOlderDir.toPath().resolve("Readme.link"), Path.of("Readme.txt")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "Readme.link", CHECKSUMS.LINK_TO_README_TXT)); - } - - @Test - public void rename() throws Exception { - var target = Path.of("Readme.txt"); - Files.createSymbolicLink(myOlderDir.toPath().resolve("Readme.lnk"), target); - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.link"), target); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "Readme.lnk", CHECKSUMS.LINK_TO_README_TXT), - new CreateAction(patch, "Readme.link")); - } - - @Test - public void retarget() throws Exception { - Files.createSymbolicLink(myOlderDir.toPath().resolve("Readme.link"), Path.of("Readme.txt")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.link"), Path.of("./Readme.txt")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "Readme.link", CHECKSUMS.LINK_TO_README_TXT), - new CreateAction(patch, "Readme.link")); - } - - @Test - public void renameAndRetarget() throws Exception { - Files.createSymbolicLink(myOlderDir.toPath().resolve("Readme.lnk"), Path.of("./Readme.txt")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.link"), Path.of("Readme.txt")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "Readme.lnk", CHECKSUMS.LINK_TO_DOT_README_TXT), - new CreateAction(patch, "Readme.link")); - } - - @Test - public void fileToLink() throws Exception { - Files.move(myNewerDir.toPath().resolve("Readme.txt"), myNewerDir.toPath().resolve("Readme.md")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("Readme.txt"), Path.of("Readme.md")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "Readme.txt", CHECKSUMS.README_TXT), - new CreateAction(patch, "Readme.md"), - new CreateAction(patch, "Readme.txt")); - } - - @Test - @SuppressWarnings("DuplicateExpressions") - public void multipleDirectorySymlinks() throws Exception { - var l1 = randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib1.dylib")); - var l2 = randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Libraries/lib2.dylib")); - var r1 = randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r1.bin")); - var r2 = randomFile(myOlderDir.toPath().resolve("A.framework/Versions/A/Resources/r2.bin")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Versions/Current"), Path.of("A")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); - Files.createSymbolicLink(myOlderDir.toPath().resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Libraries/lib1.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Libraries/lib2.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Resources/r1.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/A/Resources/r2.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Libraries/lib1.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Libraries/lib2.dylib")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Resources/r1.bin")); - randomFile(myNewerDir.toPath().resolve("A.framework/Versions/B/Resources/r2.bin")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Versions/Previous"), Path.of("A")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Versions/Current"), Path.of("B")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Libraries"), Path.of("Versions/Current/Libraries")); - Files.createSymbolicLink(myNewerDir.toPath().resolve("A.framework/Resources"), Path.of("Versions/Current/Resources")); - - var patch = createPatch(); - assertThat(sortActions(patch.getActions())).containsExactly( - new DeleteAction(patch, "A.framework/Versions/Current", 2305843012767948427L), // = crc32("A") | SYM_LINK - new CreateAction(patch, "A.framework/Versions/B/"), - new CreateAction(patch, "A.framework/Versions/B/Libraries/"), - new CreateAction(patch, "A.framework/Versions/B/Libraries/lib1.dylib"), - new CreateAction(patch, "A.framework/Versions/B/Libraries/lib2.dylib"), - new CreateAction(patch, "A.framework/Versions/B/Resources/"), - new CreateAction(patch, "A.framework/Versions/B/Resources/r1.bin"), - new CreateAction(patch, "A.framework/Versions/B/Resources/r2.bin"), - new CreateAction(patch, "A.framework/Versions/Current"), - new CreateAction(patch, "A.framework/Versions/Previous"), - new UpdateAction(patch, "A.framework/Versions/A/Libraries/lib1.dylib", l1), - new UpdateAction(patch, "A.framework/Versions/A/Libraries/lib2.dylib", l2), - new UpdateAction(patch, "A.framework/Versions/A/Resources/r1.bin", r1), - new UpdateAction(patch, "A.framework/Versions/A/Resources/r2.bin", r2)); - } -} diff --git a/updater/testSrc/com/intellij/updater/UpdaterTestCase.java b/updater/testSrc/com/intellij/updater/UpdaterTestCase.java index 1da5ab94b386..ed1e0e39d8d4 100644 --- a/updater/testSrc/com/intellij/updater/UpdaterTestCase.java +++ b/updater/testSrc/com/intellij/updater/UpdaterTestCase.java @@ -1,74 +1,168 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.updater; -import com.intellij.openapi.application.ex.PathManagerEx; -import com.intellij.testFramework.rules.TempDirectory; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; -import java.io.File; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.DosFileAttributeView; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.CRC32; -public abstract class UpdaterTestCase { - static { - UtilsTest.setRequiredDiskSpace(); +import static org.assertj.core.api.Assertions.assertThat; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@ExtendWith(UpdaterTestCase.class) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@interface UpdaterTest { } + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@interface UpdaterTestData { } + +final class UpdaterTestCase implements BeforeAllCallback, AfterEachCallback { + static final long README_TXT = 7256327L; + static final long IDEA_BAT = 1681106766L; + static final long ANNOTATIONS_JAR = 2525796836L; + static final long ANNOTATIONS_CHANGED_JAR = 2587736223L; + static final long BOOT_JAR = 2697993201L; + static final long BOOT_CHANGED_JAR = 2957038758L; + static final long BOOTSTRAP_JAR = 2745721972L; + static final long BOOTSTRAP_DELETED_JAR = 811764767L; + static final long LINK_TO_README_TXT = 2305843011042707672L; + static final long LINK_TO_DOT_README_TXT_DOS = 2305843011210142148L; + static final long LINK_TO_DOT_README_TXT_UNIX = 2305843009503057206L; + + private static Path ourDataDir; + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + System.setProperty("idea.required.space", Long.toString(20_000_000)); + + if (ourDataDir == null) { + var dir = Path.of("community/updater/testData"); + if (!Files.exists(dir)) dir = Path.of("updater/testData"); + if (!Files.exists(dir)) dir = Path.of("testData"); + if (!Files.exists(dir)) throw new IllegalStateException("Cannot find test data directory under " + Path.of(".").toAbsolutePath()); + ourDataDir = dir.toAbsolutePath(); + } + + Runner.checkCaseSensitivity(ourDataDir.toString()); + + @SuppressWarnings("OptionalGetWithoutIsPresent") var testInstance = context.getTestInstance().get(); + var testClass = testInstance.getClass(); + while (!testClass.isAnnotationPresent(UpdaterTest.class)) testClass = testClass.getSuperclass(); + for (var field : testClass.getDeclaredFields()) { + if (field.isAnnotationPresent(UpdaterTestData.class)) { + field.set(testInstance, ourDataDir); + } + } } - protected static class TestUpdaterUI extends ConsoleUpdaterUI { - public boolean cancelled = false; - - protected TestUpdaterUI() { super(false); } - - @Override public void startProcess(String title) { } - @Override public void checkCancelled() throws OperationCancelledException { if (cancelled) throw new OperationCancelledException(); } - @Override public void showError(String message) { } - } - - @Rule public TempDirectory tempDir = new TempDirectory(); - - protected File dataDir; - protected TestUpdaterUI TEST_UI; - protected CheckSums CHECKSUMS; - - @Before - public void before() throws Exception { - dataDir = PathManagerEx.findFileUnderCommunityHome("updater/testData"); - - Runner.checkCaseSensitivity(dataDir.getPath()); - - TEST_UI = new TestUpdaterUI(); - - CHECKSUMS = new CheckSums( - new File(dataDir, "Readme.txt").length() == 7132, - File.separatorChar == '\\'); - } - - @After - public void after() throws Exception { + @Override + public void afterEach(ExtensionContext context) throws Exception { Utils.cleanup(); } - public File getTempFile(String fileName) { - return new File(tempDir.getRoot(), fileName); - } - - @SuppressWarnings("FieldMayBeStatic") - protected static final class CheckSums { - public final long README_TXT; - public final long IDEA_BAT; - public final long ANNOTATIONS_JAR = 2525796836L; - public final long ANNOTATIONS_CHANGED_JAR = 2587736223L; - public final long BOOT_JAR = 2697993201L; - public final long BOOT_CHANGED_JAR = 2957038758L; - public final long BOOTSTRAP_JAR = 2745721972L; - public final long BOOTSTRAP_DELETED_JAR = 811764767L; - public final long LINK_TO_README_TXT = 2305843011042707672L; - public final long LINK_TO_DOT_README_TXT; - - public CheckSums(boolean crLfs, boolean backwardSlashes) { - README_TXT = crLfs ? 1272723667L : 7256327L; - IDEA_BAT = crLfs ? 3535428112L : 1681106766L; - LINK_TO_DOT_README_TXT = backwardSlashes ? 2305843011210142148L : 2305843009503057206L; + static void setReadOnly(Path file) throws IOException { + if (Utils.IS_WINDOWS) { + Files.getFileAttributeView(file, DosFileAttributeView.class).setReadOnly(true); + } + else { + Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("r--------")); } } + + final static class Directories { + final Path oldDir, newDir; + + Directories(Path oldDir, Path newDir) { + this.oldDir = oldDir; + this.newDir = newDir; + } + } + + static Directories prepareDirectories(Path tempDir, Path dataDir, boolean mangle) throws IOException { + var oldDir = Files.createDirectory(tempDir.resolve("oldDir")); + Utils.copyDirectory(dataDir, oldDir); + Files.writeString(oldDir.resolve("Readme.txt"), Files.readString(dataDir.resolve("Readme.txt")).replace("\r\n", "\n")); + Files.writeString(oldDir.resolve("bin/idea.bat"), Files.readString(dataDir.resolve("bin/idea.bat")).replace("\r\n", "\n")); + Files.delete(oldDir.resolve("lib/annotations_changed.jar")); + Files.delete(oldDir.resolve("lib/bootstrap_deleted.jar")); + + var newDir = Files.createDirectory(tempDir.resolve("newDir")); + if (mangle) { + Utils.copyDirectory(dataDir, newDir); + Files.writeString(newDir.resolve("Readme.txt"), "hello"); + Files.delete(newDir.resolve("bin/idea.bat")); + Utils.writeString(newDir.resolve("newDir/newFile.txt"), "hello"); + Files.delete(newDir.resolve("lib/annotations.jar")); + Files.move(newDir.resolve("lib/annotations_changed.jar"), newDir.resolve("lib/annotations.jar")); + Files.delete(newDir.resolve("lib/bootstrap.jar")); + Files.move(newDir.resolve("lib/bootstrap_deleted.jar"), newDir.resolve("lib/bootstrap.jar")); + } + else { + Utils.copyDirectory(oldDir, newDir); + } + + return new Directories(oldDir, newDir); + } + + static PatchSpec createPatchSpec(Path oldDir, Path newDir) { + return new PatchSpec() + .setOldFolder(oldDir.toString()).setOldVersionDescription("") + .setNewFolder(newDir.toString()).setNewVersionDescription(""); + } + + static List sortActions(List actions) { + return sort(actions, a -> a.getClass().getSimpleName().charAt(0), Comparator.comparing(PatchAction::getPath)); + } + + static List sortResults(List results) { + return sort(results, r -> r.action, Comparator.comparing(r -> r.path)); + } + + private static List sort(List list, Function classifier, Comparator sorter) { + // splits the list into groups + var groups = list.stream().collect(Collectors.groupingBy(classifier, LinkedHashMap::new, Collectors.toList())).values(); + // verifies the list is monotonic + assertThat(list).isEqualTo(groups.stream().flatMap(Collection::stream).collect(Collectors.toList())); + // sorts group elements and concatenates groups into a list + return groups.stream() + .flatMap(elements -> elements.stream().sorted(sorter)) + .collect(Collectors.toList()); + } + + static long randomFile(Path file) throws IOException { + var rnd = new Random(); + var size = (1 + rnd.nextInt(1023)) * 1024; + var data = new byte[size]; + rnd.nextBytes(data); + + Files.createDirectories(file.getParent()); + Files.write(file, data); + + var crc32 = new CRC32(); + crc32.update(data); + return crc32.getValue(); + } + + static long linkHash(String target) throws IOException { + return Digester.digestStream(new ByteArrayInputStream(target.getBytes(StandardCharsets.UTF_8))) | Digester.SYM_LINK; + } } diff --git a/updater/testSrc/com/intellij/updater/UtilsTest.java b/updater/testSrc/com/intellij/updater/UtilsTest.java index f6ee79245b7b..e491c8dc1f2b 100644 --- a/updater/testSrc/com/intellij/updater/UtilsTest.java +++ b/updater/testSrc/com/intellij/updater/UtilsTest.java @@ -1,153 +1,93 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.updater; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.io.IoTestUtil; -import com.intellij.testFramework.rules.TempDirectory; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; -import static org.junit.Assume.assumeFalse; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public class UtilsTest { - static { - setRequiredDiskSpace(); +@UpdaterTest +class UtilsTest { + @TempDir Path tempDir; + + @Test void delete() throws Exception { + var file = Files.createFile(tempDir.resolve("temp_file")); + Utils.delete(file); + assertThat(file).doesNotExist(); } - static void setRequiredDiskSpace() { - System.setProperty("idea.required.space", Long.toString(20_000_000)); + @Test void deleteReadonlyFile() throws Exception { + var dir = Files.createDirectory(tempDir.resolve("temp_dir")); + var file = Files.createFile(dir.resolve("temp_file")); + UpdaterTestCase.setReadOnly(file); + + Utils.delete(dir); + assertThat(dir).doesNotExist(); } - @Rule public TempDirectory tempDir = new TempDirectory(); - - @Test - public void testDelete() throws Exception { - File f = tempDir.newFile("temp_file"); - assertTrue(f.exists()); - - Utils.delete(f); - assertFalse(f.exists()); + @Test @EnabledOnOs(OS.WINDOWS) void deleteLockedFileOnWindows() throws Exception { + var file = Files.createFile(tempDir.resolve("temp_file")); + var timing = new AtomicLong(0L); + assertThatThrownBy(() -> { + try (var os = Files.newOutputStream(file)) { + // This locks the file on Windows, preventing it from being deleted. Utils.delete() will retry for about 100 ms. + os.write("test".getBytes(StandardCharsets.UTF_8)); + var t = System.nanoTime(); + try { + Utils.delete(file); + } + finally { + timing.set(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t)); + } + } + }).hasMessage("Cannot delete: " + file.toAbsolutePath()); + assertThat(timing.get()).as("Utils.delete took " + timing + " ms, which is less than expected").isGreaterThanOrEqualTo(95); } - @Test - public void testDeleteReadonlyFile() throws Exception { - File f = tempDir.newFile("temp_dir/temp_file"); - assertTrue(f.setWritable(false, false)); - File d = f.getParentFile(); - assertTrue(d.exists()); - - Utils.delete(d); - assertFalse(d.exists()); - } - - @Test - public void testDeleteLockedFileOnWindows() { - IoTestUtil.assumeWindows(); - - File f = tempDir.newFile("temp_file"); - assertTrue(f.exists()); - - long ts = 0; - try (OutputStream os = new FileOutputStream(f)) { - // This locks the file on Windows, preventing it from being deleted. - // Utils.delete() will retry for about 100 ms. + @Test @DisabledOnOs(OS.WINDOWS) void deleteLockedFileOnUnix() throws Exception { + var file = Files.createFile(tempDir.resolve("temp_file")); + try (var os = Files.newOutputStream(file)) { os.write("test".getBytes(StandardCharsets.UTF_8)); - ts = System.nanoTime(); - - Utils.delete(f); - fail("Utils.delete did not fail with the expected IOException on Windows"); - } - catch (IOException e) { - ts = (System.nanoTime() - ts) / 1_000_000; - assertEquals("Cannot delete: " + f.getAbsolutePath(), e.getMessage()); - assertThat(ts).as("Utils.delete took " + ts + " ms, which is less than expected").isGreaterThanOrEqualTo(95); + Utils.delete(file); } } - @Test - public void testDeleteLockedFileOnUnix() throws Exception { - assumeFalse("Windows-allergic", Utils.IS_WINDOWS); - - File f = tempDir.newFile("temp_file"); - assertTrue(f.exists()); - - try (OutputStream os = new FileOutputStream(f)) { - os.write("test".getBytes(StandardCharsets.UTF_8)); - Utils.delete(f); - } - } - - @Test - public void testRecursiveDelete() throws Exception { - File topDir = tempDir.newDirectory("temp_dir"); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - File file = new File(topDir, "dir" + i + "/file" + j); - FileUtil.writeToFile(file, "test"); - assertTrue(file.exists()); + @Test void recursiveDelete() throws Exception { + var topDir = Files.createDirectory(tempDir.resolve("temp_dir")); + for (var i = 0; i < 3; i++) { + var subDir = Files.createDirectory(topDir.resolve("dir" + i)); + for (var j = 0; j < 3; j++) { + Files.writeString(subDir.resolve("file" + j), "test"); } } Utils.delete(topDir); - assertFalse(topDir.exists()); + assertThat(topDir).doesNotExist(); } - @Test - public void testNonRecursiveSymlinkDelete() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); - - File dir = tempDir.newDirectory("temp_dir"); - File file = new File(dir, "file"); - FileUtil.writeToFile(file, "test"); - assertThat(dir.listFiles()).containsExactly(file); - - File link = new File(tempDir.getRoot(), "link"); - Path link1 = link.toPath(); - Files.createSymbolicLink(link1, dir.toPath().getFileName()); - assertTrue(Utils.isLink(link)); - assertThat(link.listFiles()).hasSize(1); - + @Test void nonRecursiveSymlinkDelete() throws Exception { + var dir = Files.createDirectory(tempDir.resolve("temp_dir")); + var file = Files.createFile(dir.resolve("file")); + var link = Files.createSymbolicLink(tempDir.resolve("link"), dir.getFileName()); Utils.delete(link); - assertFalse(link.exists()); - assertThat(dir.listFiles()).containsExactly(file); + assertThat(link).doesNotExist(); + assertThat(file).exists(); } - @Test - public void testDeleteDanglingSymlink() throws Exception { - IoTestUtil.assumeSymLinkCreationIsSupported(); - - File dir = tempDir.newDirectory("temp_dir"); - File link = new File(dir, "link"); - Path link1 = link.toPath(); - Path target = Path.of("dangling"); - Files.createSymbolicLink(link1, target); - assertThat(dir.listFiles()).containsExactly(link); - + @Test void deleteDanglingSymlink() throws Exception { + var dir = Files.createDirectory(tempDir.resolve("temp_dir")); + var link = Files.createSymbolicLink(dir.resolve("link"), Path.of("dangling")); Utils.delete(link); - assertThat(dir.listFiles()).isEmpty(); + assertThat(dir).isEmptyDirectory(); } }