blob: 4e87db2a8eb037a39b5094af0f3cc3892876fb3c [file] [log] [blame]
package com.intellij.updater;
import com.intellij.openapi.util.io.FileUtil;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import static org.junit.Assert.*;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class PatchFileCreatorTest extends PatchTestCase {
private File myFile;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
myFile = getTempFile("patch.zip");
}
@Test
public void testCreatingAndApplying() throws Exception {
createPatch();
assertAppliedAndRevertedCorrectly(PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI));
}
@Test
public void failOnEmptySourceJar() throws Exception {
final File sourceJar = new File(myOlderDir, "lib/empty.jar");
if (sourceJar.exists()) sourceJar.delete();
assertTrue(sourceJar.createNewFile());
try {
final File targetJar = new File(myNewerDir, "lib/empty.jar");
FileUtil.copy(new File(myNewerDir, "lib/annotations.jar"), targetJar);
try {
createPatch();
fail("Should have failed to create a patch from empty .jar");
}
catch (IOException e) {
final String reason = e.getMessage();
assertEquals("Corrupted source file: " + sourceJar, reason);
}
finally {
targetJar.delete();
}
}
finally {
sourceJar.delete();
}
}
@Test
public void failOnEmptyTargetJar() throws Exception {
final File sourceJar = new File(myOlderDir, "lib/empty.jar");
FileUtil.copy(new File(myOlderDir, "lib/annotations.jar"), sourceJar);
try {
final File targetJar = new File(myNewerDir, "lib/empty.jar");
if (targetJar.exists()) targetJar.delete();
assertTrue(targetJar.createNewFile());
try {
createPatch();
fail("Should have failed to create a patch against empty .jar");
}
catch (IOException e) {
final String reason = e.getMessage();
assertEquals("Corrupted target file: " + targetJar, reason);
}
finally {
targetJar.delete();
}
}
finally {
sourceJar.delete();
}
}
@Test
public void testReverting() throws Exception {
createPatch();
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction());
assertNothingHasChanged(preparationResult, new HashMap<String, ValidationResult.Option>());
}
@Test
public void testApplyingWithAbsentFileToDelete() throws Exception {
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
new File(myOlderDir, "bin/idea.bat").delete();
assertAppliedAndRevertedCorrectly(PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI));
}
@Test
public void testApplyingWithAbsentOptionalFile() throws Exception {
FileUtil.writeToFile(new File(myNewerDir, "bin/idea.bat"), "new content".getBytes());
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.singletonList("bin/idea.bat"), TEST_UI);
new File(myOlderDir, "bin/idea.bat").delete();
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
assertTrue(preparationResult.validationResults.isEmpty());
assertAppliedAndRevertedCorrectly(preparationResult);
}
@Test
public void testRevertingWithAbsentFileToDelete() throws Exception {
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
new File(myOlderDir, "bin/idea.bat").delete();
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
preparationResult.patch.getActions().add(new MyFailOnApplyPatchAction());
assertNothingHasChanged(preparationResult, new HashMap<String, ValidationResult.Option>());
}
@Test
public void testApplyingWithoutCriticalFiles() throws Exception {
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
assertTrue(PatchFileCreator.apply(preparationResult, new HashMap<String, ValidationResult.Option>(), TEST_UI));
}
@Test
public void testApplyingWithCriticalFiles() throws Exception {
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Arrays.asList("lib/annotations.jar"),
Collections.<String>emptyList(), TEST_UI);
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
assertTrue(PatchFileCreator.apply(preparationResult, new HashMap<String, ValidationResult.Option>(), TEST_UI));
assertAppliedCorrectly();
}
@Test
public void testApplyingWithCaseChangedNames() throws Exception {
FileUtil.rename(new File(myOlderDir, "Readme.txt"),
new File(myOlderDir, "README.txt"));
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
assertAppliedAndRevertedCorrectly(PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI));
}
@Test
public void testCreatingAndApplyingWhenDirectoryBecomesFile() throws Exception {
File file = new File(myOlderDir, "Readme.txt");
file.delete();
file.mkdirs();
new File(file, "subFile.txt").createNewFile();
new File(file, "subDir").mkdir();
new File(file, "subDir/subFile.txt").createNewFile();
FileUtil.copy(new File(myOlderDir, "lib/boot.jar"),
new File(myOlderDir, "lib/boot_with_directory_becomes_file.jar"));
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
assertAppliedAndRevertedCorrectly(PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI));
}
@Test
public void testCreatingAndApplyingWhenFileBecomesDirectory() throws Exception {
File file = new File(myOlderDir, "bin");
assertTrue(FileUtil.delete(file));
file.createNewFile();
FileUtil.copy(new File(myOlderDir, "lib/boot_with_directory_becomes_file.jar"),
new File(myOlderDir, "lib/boot.jar"));
PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.<String>emptyList(), Collections.<String>emptyList(),
Collections.<String>emptyList(), TEST_UI);
assertAppliedAndRevertedCorrectly(PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI));
}
@Test
public void testConsideringOptions() throws Exception {
createPatch();
PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI);
Map<String, ValidationResult.Option> options = new HashMap<String, ValidationResult.Option>();
for (PatchAction each : preparationResult.patch.getActions()) {
options.put(each.getPath(), ValidationResult.Option.IGNORE);
}
assertNothingHasChanged(preparationResult, options);
}
private void createPatch() throws IOException, OperationCancelledException {
PatchFileCreator.create(myOlderDir, myNewerDir, myFile,
Collections.<String>emptyList(), Collections.<String>emptyList(), Collections.<String>emptyList(), TEST_UI);
assertTrue(myFile.exists());
}
private void assertNothingHasChanged(PatchFileCreator.PreparationResult preparationResult, Map<String, ValidationResult.Option> options)
throws Exception {
Map<String, Long> before = Digester.digestFiles(myOlderDir, Collections.<String>emptyList(), TEST_UI);
PatchFileCreator.apply(preparationResult, options, TEST_UI);
Map<String, Long> after = Digester.digestFiles(myOlderDir, Collections.<String>emptyList(), TEST_UI);
DiffCalculator.Result diff = DiffCalculator.calculate(before, after);
assertTrue(diff.filesToCreate.isEmpty());
assertTrue(diff.filesToDelete.isEmpty());
assertTrue(diff.filesToUpdate.isEmpty());
}
private void assertAppliedAndRevertedCorrectly(PatchFileCreator.PreparationResult preparationResult)
throws IOException, OperationCancelledException {
Map<String, Long> original = Digester.digestFiles(myOlderDir, Collections.<String>emptyList(), TEST_UI);
File backup = getTempFile("backup");
for (ValidationResult each : preparationResult.validationResults) {
assertTrue(each.toString(), each.kind != ValidationResult.Kind.ERROR);
}
List<PatchAction> appliedActions =
PatchFileCreator.apply(preparationResult, new HashMap<String, ValidationResult.Option>(), backup, TEST_UI).appliedActions;
assertAppliedCorrectly();
assertFalse(original.equals(Digester.digestFiles(myOlderDir, Collections.<String>emptyList(), TEST_UI)));
PatchFileCreator.revert(preparationResult, appliedActions, backup, TEST_UI);
assertEquals(original, Digester.digestFiles(myOlderDir, Collections.<String>emptyList(), TEST_UI));
}
protected void assertAppliedCorrectly() throws IOException {
File newFile = new File(myOlderDir, "newDir/newFile.txt");
assertTrue(newFile.exists());
assertEquals("hello", FileUtil.loadFile(newFile));
File changedFile = new File(myOlderDir, "Readme.txt");
assertTrue(changedFile.exists());
assertEquals("hello", FileUtil.loadFile(changedFile));
assertFalse(new File(myOlderDir, "bin/idea.bat").exists());
// do not remove unchanged
checkZipEntry("lib/annotations.jar", "org/jetbrains/annotations/Nls.class", 502);
// add new
checkZipEntry("lib/annotations.jar", "org/jetbrains/annotations/NewClass.class", 453);
// update changed
checkZipEntry("lib/annotations.jar", "org/jetbrains/annotations/Nullable.class", 546);
// remove obsolete
checkNoZipEntry("lib/annotations.jar", "org/jetbrains/annotations/TestOnly.class");
// test for archives with only deleted files
checkNoZipEntry("lib/bootstrap.jar", "com/intellij/ide/ClassloaderUtil.class");
// packing directories too
checkZipEntry("lib/annotations.jar", "org/", 0);
checkZipEntry("lib/annotations.jar", "org/jetbrains/", 0);
checkZipEntry("lib/annotations.jar", "org/jetbrains/annotations/", 0);
checkZipEntry("lib/bootstrap.jar", "com/", 0);
checkZipEntry("lib/bootstrap.jar", "com/intellij/", 0);
checkZipEntry("lib/bootstrap.jar", "com/intellij/ide/", 0);
}
private void checkZipEntry(String jar, String entryName, int expectedSize) throws IOException {
ZipFile zipFile = new ZipFile(new File(myOlderDir, jar));
try {
ZipEntry entry = zipFile.getEntry(entryName);
assertNotNull(entry);
assertEquals(expectedSize, entry.getSize());
}
finally {
zipFile.close();
}
}
private void checkNoZipEntry(String jar, String entryName) throws IOException {
ZipFile zipFile = new ZipFile(new File(myOlderDir, jar));
try {
assertNull(zipFile.getEntry(entryName));
}
finally {
zipFile.close();
}
}
private static class MyFailOnApplyPatchAction extends PatchAction {
public MyFailOnApplyPatchAction() {
super("_dummy_file_", -1);
}
@Override
protected void doBuildPatchFile(File olderFile, File newerFile, ZipOutputStream patchOutput) throws IOException {
throw new UnsupportedOperationException();
}
@Override
protected ValidationResult doValidate(File toFile) throws IOException {
throw new UnsupportedOperationException();
}
@Override
protected void doApply(ZipFile patchFile, File toFile) throws IOException {
throw new IOException("dummy exception");
}
@Override
protected void doBackup(File toFile, File backupFile) throws IOException {
}
@Override
protected void doRevert(File toFile, File backupFile) throws IOException {
}
}
}