diff --git a/updater/testData/Readme.txt b/updater/testData/Readme.txt new file mode 100644 index 000000000000..c509af0d5f17 --- /dev/null +++ b/updater/testData/Readme.txt @@ -0,0 +1,166 @@ +IntelliJ IDEA 8.0M1 - README + +Thank you for downloading IntelliJ IDEA! + +IntelliJ IDEA is a multi-platform Java IDE, which includes intelligent editor, rich-featured GUI designer, +visual debugger, javac/jikes/rmic compiler integration, refactoring, enhanced project navigation, +intelligent code inspection and analysis features, J2EE and JDK 1.5 support. + + +Contents: +========= + BUILD.TXT File containing the current build number + KnownIssues.TXT Known issues and workarounds + README.TXT This file + bin/ Startup scripts for launching IntelliJ IDEA + help/ Online help files + jre/ Bundled JRE + lib/ Library files + license/ License files for IntelliJ IDEA and bundled software + plugins/ Standard plugins + redist/ Contains libraries that need to be redistributed with your application if certain IDEA features are used: + - forms_rt.jar should be distributed with your applications that use GUI forms with + the "GridLayoutManager (IntelliJ)" layout manager + - javac2.jar is an Ant task for building applications that use IntelliJ IDEA's UI Designer + bytecode generation or @NotNull assertions generation + - annotations.jar contains JDK 1.5 annotation classes for 'Constant Conditions & Exceptions' inspection tool. + + Install-Linux-tar.txt Installation instructions for Linux (included in Linux installation package only) + Install-Windows-zip.txt Installation instructions for Windows zip-file (included in Windows zip installation package only) + + USER_HOME/.IntelliJIdea80/ + + config/ Configuration files (See INSTALLATION_HOME/bin/idea.properties to tweak location of the configs) + codestyles/ User's code style settings + colors/ User's colors and fonts settings + fileTemplates/ Custom file templates + filetypes/ Custom file types + ideTalk/ ideTalk settings + inspection/ Executable file and auxiliary data for running offline code inspections + keymaps/ Contains files with custom keymaps + migration/ API migration map + options/ IDE options configuration files + plugins/ Directory for custom plugins (it appears after the 1st plugin is installed) + shelf/ Shelved changes (in standard .patch file format) + templates/ Live templates, both built-in and custom + tools/ External tools + idea70.key File containing your license key (editing not recommended) + + system/ Various IDEA internal caches including Local History data storage. + (See INSTALLATION_HOME/bin/idea.properties to tweak location of the caches). + Also log directory with IDEA log files is located there. + + +Installing IntelliJ IDEA +======================== + For Linux and other Generic Unix users: + Please read the contents of the Install-Linux-tar.txt file. + + Installing on Mac OS: + IntelliJ IDEA is distributed as a .dmg file. You only need to drag it to + the destination folder, from which you can start the application. + +Uninstalling IntelliJ IDEA +========================== + If you installed IntelliJ IDEA with the help of Installation Wizard, then + just run the INSTALLATION_HOME/bin/Uninstall.exe file + + To uninstall IntelliJ IDEA after manual installation, simply delete the + contents of the IntelliJ IDEA home installation directory. + + +Licensing & pricing +========================== + Licensing and pricing information can be found at http://www.jetbrains.com/idea/buy/index.html. + + +IntelliJ IDEA Overview +========================== + For general info and facts on IntelliJ IDEA, you can refer to IntelliJ IDEA Info Kit at + http://www.jetbrains.com/idea/documentation/product_info_kit.html. + + +IDEA Development Package +========================== + Contains: + - Source code for the OpenAPI classes; + - Documentation (JavaDocs for the OpenAPI and a number of additional components); + - Simple example plugins demonstrating usage of the OpenAPI; + - Source code for plugins shipped with IDEA: + * Plugin Development Kit + * IDEtalk plugin + * Images support plugin + * Inspection Gadgets + * Intention PowerPack + * J2ME development support plugin + * JavaScript support plugin + * JavaScript inspections plugin + * JavaScript Intention PowerPack + * GWTStudio plugin + * KlassMaster stacktrace unscramble plugin + * StrutsAssistant plugin + * StarTeam, Perforce, Subversion, Visual SourceSafe integration + * Tomcat, Weblogic, WebSphere, Geronimo, JBoss, GlassFish, JSR45 integration + + Download page: http://www.jetbrains.com/idea/download/index.html + + Source code of additional open source plugins shipped with IntelliJ IDEA is available in the Subversion + repository at: + http://svn.jetbrains.org/idea/Trunk/bundled/ + + +Using Plugins +============= + IDEA now smartly integrates with the Community web-site which holds a repository of the third-party plugins to it. + This integration, supplied with a convenient UI, helps you incorporate any available plugins without switching from + IDEA settings dialog (Settings | Plugins). + + You can browse the plugins Web site, rate and comment on plugins at: + http://plugins.intellij.net/ + + +Home Page: +========== + http://www.jetbrains.com + + +IntelliJ Technology Network +=========================== + http://www.intellij.net + Early Access to new products, internal builds and patches, Bug/Features database, Forums/newsgroups + + +IntelliJ Community Site +======================= + http://www.intellij.org + The community-driven Wiki site dedicated to IntelliJ products and technologies. + + +Support +======= + For technical support and assistance, you may find necessary information at the Support page + (http://www.jetbrains.com/support/index.html) or contact us at support@jetbrains.com. + + +Bug Reporting: +============== + Send emails to bugs@jetbrains.com + + +Contacting us: +============== + sales@jetbrains.com - Sales inquiries, billing, order processing questions + support@jetbrains.com - Technical support (all products) + sales.us@jetbrains.com - Sales inquiries in the United States + support.us@jetbrains.com - Technical support for US customers + suggestions@jetbrains.com - Feature suggestions + info@jetbrains.com - Product inquiries + + +============= +You are encouraged to visit our IntelliJ IDEA web site at http://www.jetbrains.com/idea/ +or to contact us via e-mail at feedback@jetbrains.com if you have any comments about +this release. In particular, we are very interested in any ease-of-use, user +interface suggestions that you may have. We will be posting regular updates +of our progress to our online forums and newsgroups. +============= diff --git a/updater/testData/bin/focuskiller.dll b/updater/testData/bin/focuskiller.dll new file mode 100644 index 000000000000..c1f8e043939a Binary files /dev/null and b/updater/testData/bin/focuskiller.dll differ diff --git a/updater/testData/bin/idea.bat b/updater/testData/bin/idea.bat new file mode 100644 index 000000000000..79a087fd4732 --- /dev/null +++ b/updater/testData/bin/idea.bat @@ -0,0 +1,66 @@ +@echo off + +::---------------------------------------------------------------------- +:: IntelliJ IDEA Startup Script +::---------------------------------------------------------------------- + +set JDK_HOME=c:/tools/jdk + +:: --------------------------------------------------------------------- +:: Before you run IntelliJ IDEA specify the location of the +:: JDK 1.5 installation directory which will be used for running IDEA +:: --------------------------------------------------------------------- +IF "%IDEA_JDK%" == "" SET IDEA_JDK=%JDK_HOME% +IF "%IDEA_JDK%" == "" goto error + +:: --------------------------------------------------------------------- +:: Before you run IntelliJ IDEA specify the location of the +:: directory where IntelliJ IDEA is installed +:: In most cases you do not need to change the settings below. +:: --------------------------------------------------------------------- +SET IDEA_HOME=.. + +SET JAVA_EXE=%IDEA_JDK%\jre\bin\java.exe +IF NOT EXIST "%JAVA_EXE%" goto error + +IF "%IDEA_MAIN_CLASS_NAME%" == "" SET IDEA_MAIN_CLASS_NAME=com.intellij.idea.Main + +IF NOT "%IDEA_PROPERTIES%" == "" set IDEA_PROPERTIES_PROPERTY=-Didea.properties.file=%IDEA_PROPERTIES% + +:: --------------------------------------------------------------------- +:: You may specify your own JVM arguments in idea.exe.vmoptions file. Put one option per line there. +:: --------------------------------------------------------------------- +SET ACC= +FOR /F "delims=" %%i in (%IDEA_HOME%\bin\idea.exe.vmoptions) DO call %IDEA_HOME%\bin\append.bat "%%i" + +set REQUIRED_IDEA_JVM_ARGS=-Xbootclasspath/p:%IDEA_HOME%/lib/boot.jar %IDEA_PROPERTIES_PROPERTY% %REQUIRED_IDEA_JVM_ARGS% +SET JVM_ARGS=%ACC% %REQUIRED_IDEA_JVM_ARGS% + +SET OLD_PATH=%PATH% +SET PATH=%IDEA_HOME%\bin;%PATH% + +SET CLASS_PATH=%IDEA_HOME%\lib\bootstrap.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_HOME%\lib\util.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_HOME%\lib\jdom.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_HOME%\lib\log4j.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_HOME%\lib\extensions.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_HOME%\lib\trove4j.jar +SET CLASS_PATH=%CLASS_PATH%;%IDEA_JDK%\lib\tools.jar + +:: --------------------------------------------------------------------- +:: You may specify additional class paths in IDEA_CLASS_PATH variable. +:: It is a good idea to specify paths to your plugins in this variable. +:: --------------------------------------------------------------------- +IF NOT "%IDEA_CLASS_PATH%" == "" SET CLASS_PATH=%CLASS_PATH%;%IDEA_CLASS_PATH% + +"%JAVA_EXE%" %JVM_ARGS% -cp "%CLASS_PATH%" %IDEA_MAIN_CLASS_NAME% %* + +SET PATH=%OLD_PATH% +goto end +:error +echo --------------------------------------------------------------------- +echo ERROR: cannot start IntelliJ IDEA. +echo No JDK found to run IDEA. Please validate either IDEA_JDK or JDK_HOME points to valid JDK installation +echo --------------------------------------------------------------------- +pause +:end diff --git a/updater/testData/bin/idea.properties b/updater/testData/bin/idea.properties new file mode 100644 index 000000000000..37bc736fb833 --- /dev/null +++ b/updater/testData/bin/idea.properties @@ -0,0 +1,105 @@ +# Set up IDEA_PROPERTIES environment variable to specify custom location of this properties file like +# SET IDEA_PROPERTIES=c:\ideaconfig\idea.properties +# before launching idea. +# If not specified it is searched according following sequence (first successful is used). +# 1. ${user.home} +# 2. ${idea.home}/bin + +# Use ${idea.home} macro to specify location relative to IDEA installation home +# Also use ${xxx} where xxx is any java property (including defined in previous lines of this file) to refer to its value +# Note for Windows users: please make sure you're using forward slashes. I.e. c:/idea/system + +#--------------------------------------------------------------------- +# Uncomment this option if you want to customize path to IDEA config folder. Make sure you're using forward slashes +#--------------------------------------------------------------------- +idea.config.path=${idea.home}/config + +#--------------------------------------------------------------------- +# Uncomment this option if you want to customize path to IDEA system folder. Make sure you're using forward slashes +#--------------------------------------------------------------------- +idea.system.path=${idea.home}/system + +#--------------------------------------------------------------------- +# Uncomment this option if you want to customize path to user installed plugins folder. Make sure you're using forward slashes +#--------------------------------------------------------------------- +idea.plugins.path=${idea.home}/config/plugins + +#--------------------------------------------------------------------- +# Maximum file size (kilobytes) IDEA should provide intellisense for. +# The larger file is the slower its editor works and higher overall system memory requirements are +# if intellisense is enabled. Remove this property or set to very large number if you need +# intellisense for any files available regardless their size. +# Please note this option doesn't operate with Java files. Regardless of the option value intellisense will anyway stay there. +#--------------------------------------------------------------------- +idea.max.intellisense.filesize=2048 + +#--------------------------------------------------------------------- +# There are two possible values of idea.popup.weight property: "heavy" and "medium". +# If you have WM configured as "Focus follows mouse with Auto Raise" then you have to +# set this property to "medium". It prevents problems with popup menus on some +# configurations. +# --------------------------------------------------------------------- +idea.popup.weight=heavy + +#---------------------------------------------------------------------- +# Disabling this property may lead to visual glitches like blinking and fail to repaint +# on certain display adapter cards. +#---------------------------------------------------------------------- +sun.java2d.noddraw=true + +#---------------------------------------------------------------------- +# Removing this property may lead to editor performance degradation under Windows. +#---------------------------------------------------------------------- +sun.java2d.d3d=false + +#----------------------------------------------------------------------- +# IDEA copies library jars to prevent their locking. If copying is not desirable, specify "true" +#----------------------------------------------------------------------- +idea.jars.nocopy=false + +#---------------------------------------------------------------------- +# Configure if a special launcher should be used when running processes from within IDEA. +# Using Launcher enables "soft exit" and "thread dump" features +#---------------------------------------------------------------------- +idea.no.launcher=false + +#----------------------------------------------------------------------- +# The VM option value to be used start the JVM in debug mode. +# Some environments define it in a different way (-XXdebug in Oracle VM) +#----------------------------------------------------------------------- +idea.xdebug.key=-Xdebug + +#----------------------------------------------------------------------- +# Switch into JMX 1.0 compatible mode +# Uncomment this option to be able to run IDEA using J2SDK 1.5 while working +# with application servers (like WebLogic) running 1.4 +#----------------------------------------------------------------------- +#jmx.serial.form=1.0 + +#----------------------------------------------------------------------- +# Uncomment this option if you don't like to receive notifications about +# fatal errors that happen to IDEA or plugins installed. +#----------------------------------------------------------------------- +idea.fatal.error.notification=disabled + +# Workaround for slow scrolling in JDK6 +swing.bufferPerWindow=false + +#----------------------------------------------------------------------- +# Uncomment this property to prevent IDEA from throwing ProcessCanceledException when user activity +# detected. This option is only useful for plugin developers, while debugging PSI related activities +# performed in background error analysis thread. +# DO NOT UNCOMMENT THIS UNLESS YOU'RE DEBUGGING IDEA ITSELF. Significant slowdowns and lockups will happen otherwise. +#----------------------------------------------------------------------- +#idea.ProcessCanceledException=disabled + +#---------------------------------------------------------------------- +# Removing this property may lead to editor performance degradation under X-Windows. +#---------------------------------------------------------------------- +sun.java2d.pmoffscreen=false + +#--------------------------------------------------------------------- +# Maximum size (kilobytes) IDEA will load for showing past file contents - +# in Show Diff or when calculating Digest Diff +#--------------------------------------------------------------------- +#idea.max.vcs.loaded.size.kb=20480 \ No newline at end of file diff --git a/updater/testData/lib/annotations.jar b/updater/testData/lib/annotations.jar new file mode 100644 index 000000000000..53f730f5b344 Binary files /dev/null and b/updater/testData/lib/annotations.jar differ diff --git a/updater/testData/lib/annotations_changed.jar b/updater/testData/lib/annotations_changed.jar new file mode 100644 index 000000000000..43af95c94413 Binary files /dev/null and b/updater/testData/lib/annotations_changed.jar differ diff --git a/updater/testData/lib/boot.jar b/updater/testData/lib/boot.jar new file mode 100644 index 000000000000..98b408f647d4 Binary files /dev/null and b/updater/testData/lib/boot.jar differ diff --git a/updater/testData/lib/boot2.jar b/updater/testData/lib/boot2.jar new file mode 100644 index 000000000000..5c9c316cfdb4 Binary files /dev/null and b/updater/testData/lib/boot2.jar differ diff --git a/updater/testData/lib/boot2_changed_with_unchanged_content.jar b/updater/testData/lib/boot2_changed_with_unchanged_content.jar new file mode 100644 index 000000000000..c8eb3d991c53 Binary files /dev/null and b/updater/testData/lib/boot2_changed_with_unchanged_content.jar differ diff --git a/updater/testData/lib/boot_with_directory_becomes_file.jar b/updater/testData/lib/boot_with_directory_becomes_file.jar new file mode 100644 index 000000000000..4c16379df569 Binary files /dev/null and b/updater/testData/lib/boot_with_directory_becomes_file.jar differ diff --git a/updater/testData/lib/bootstrap.jar b/updater/testData/lib/bootstrap.jar new file mode 100644 index 000000000000..3a32734324f2 Binary files /dev/null and b/updater/testData/lib/bootstrap.jar differ diff --git a/updater/testData/lib/bootstrap_deleted.jar b/updater/testData/lib/bootstrap_deleted.jar new file mode 100644 index 000000000000..b0aed416af70 Binary files /dev/null and b/updater/testData/lib/bootstrap_deleted.jar differ diff --git a/updater/testSrc/com/intellij/updater/DigesterTest.java b/updater/testSrc/com/intellij/updater/DigesterTest.java new file mode 100644 index 000000000000..a71041c48f26 --- /dev/null +++ b/updater/testSrc/com/intellij/updater/DigesterTest.java @@ -0,0 +1,21 @@ +package com.intellij.updater; + +import org.junit.Test; + +import java.util.Collections; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class DigesterTest extends UpdaterTestCase { + @Test + public void testBasics() throws Exception { + Map checkSums = Digester.digestFiles(getDataDir(), Collections.emptyList(), TEST_UI); + assertEquals(12, checkSums.size()); + + assertEquals(CHECKSUMS.README_TXT, (long)checkSums.get("Readme.txt")); + assertEquals(CHECKSUMS.FOCUSKILLER_DLL, (long)checkSums.get("bin/focuskiller.dll")); + assertEquals(CHECKSUMS.BOOTSTRAP_JAR, (long)checkSums.get("lib/bootstrap.jar")); + Runner.initLogger(); + } +} \ No newline at end of file diff --git a/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java b/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java new file mode 100644 index 000000000000..4e87db2a8eb0 --- /dev/null +++ b/updater/testSrc/com/intellij/updater/PatchFileCreatorTest.java @@ -0,0 +1,333 @@ +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()); + } + + @Test + public void testApplyingWithAbsentFileToDelete() throws Exception { + PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.emptyList(), Collections.emptyList(), + Collections.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.emptyList(), Collections.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.emptyList(), Collections.emptyList(), + Collections.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()); + } + + @Test + public void testApplyingWithoutCriticalFiles() throws Exception { + PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), TEST_UI); + PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); + + assertTrue(PatchFileCreator.apply(preparationResult, new HashMap(), TEST_UI)); + } + + @Test + public void testApplyingWithCriticalFiles() throws Exception { + PatchFileCreator.create(myOlderDir, myNewerDir, myFile, Collections.emptyList(), Arrays.asList("lib/annotations.jar"), + Collections.emptyList(), TEST_UI); + + PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(myFile, myOlderDir, TEST_UI); + + assertTrue(PatchFileCreator.apply(preparationResult, new HashMap(), 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.emptyList(), Collections.emptyList(), + Collections.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.emptyList(), Collections.emptyList(), + Collections.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.emptyList(), Collections.emptyList(), + Collections.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 options = new HashMap(); + 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.emptyList(), Collections.emptyList(), Collections.emptyList(), TEST_UI); + assertTrue(myFile.exists()); + } + + private void assertNothingHasChanged(PatchFileCreator.PreparationResult preparationResult, Map options) + throws Exception { + Map before = Digester.digestFiles(myOlderDir, Collections.emptyList(), TEST_UI); + PatchFileCreator.apply(preparationResult, options, TEST_UI); + Map after = Digester.digestFiles(myOlderDir, Collections.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 original = Digester.digestFiles(myOlderDir, Collections.emptyList(), TEST_UI); + + File backup = getTempFile("backup"); + + for (ValidationResult each : preparationResult.validationResults) { + assertTrue(each.toString(), each.kind != ValidationResult.Kind.ERROR); + } + + List appliedActions = + PatchFileCreator.apply(preparationResult, new HashMap(), backup, TEST_UI).appliedActions; + assertAppliedCorrectly(); + + assertFalse(original.equals(Digester.digestFiles(myOlderDir, Collections.emptyList(), TEST_UI))); + + PatchFileCreator.revert(preparationResult, appliedActions, backup, TEST_UI); + + assertEquals(original, Digester.digestFiles(myOlderDir, Collections.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 { + } + } +} diff --git a/updater/testSrc/com/intellij/updater/PatchTest.java b/updater/testSrc/com/intellij/updater/PatchTest.java new file mode 100644 index 000000000000..14ff6c0abb18 --- /dev/null +++ b/updater/testSrc/com/intellij/updater/PatchTest.java @@ -0,0 +1,192 @@ +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.FileInputStream; +import java.io.FileOutputStream; +import java.nio.channels.FileLock; +import java.util.*; + +import static org.junit.Assert.assertEquals; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public class PatchTest extends PatchTestCase { + private Patch myPatch; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + myPatch = new Patch(myOlderDir, myNewerDir, Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), TEST_UI); + } + + @Test + public void testBasics() throws Exception { + List expectedActions = Arrays.asList( + new CreateAction("newDir/newFile.txt"), + new UpdateAction("Readme.txt", CHECKSUMS.README_TXT), + new UpdateZipAction("lib/annotations.jar", + Arrays.asList("org/jetbrains/annotations/NewClass.class"), + Arrays.asList("org/jetbrains/annotations/Nullable.class"), + Arrays.asList("org/jetbrains/annotations/TestOnly.class"), + CHECKSUMS.ANNOTATIONS_JAR), + new UpdateZipAction("lib/bootstrap.jar", + Collections.emptyList(), + Collections.emptyList(), + Arrays.asList("com/intellij/ide/ClassloaderUtil.class"), + CHECKSUMS.BOOTSTRAP_JAR), + new DeleteAction("bin/idea.bat", CHECKSUMS.IDEA_BAT)); + List actualActions = new ArrayList(myPatch.getActions()); + Collections.sort(expectedActions, COMPARATOR); + Collections.sort(actualActions, COMPARATOR); + assertEquals(expectedActions, actualActions); + } + + @Test + public void testCreatingWithIgnoredFiles() throws Exception { + myPatch = new Patch(myOlderDir, + myNewerDir, + Arrays.asList("Readme.txt", "bin/idea.bat"), + Collections.emptyList(), + Collections.emptyList(), + TEST_UI); + + List expectedActions = Arrays.asList( + new CreateAction("newDir/newFile.txt"), + new UpdateZipAction("lib/annotations.jar", + Arrays.asList("org/jetbrains/annotations/NewClass.class"), + Arrays.asList("org/jetbrains/annotations/Nullable.class"), + Arrays.asList("org/jetbrains/annotations/TestOnly.class"), + CHECKSUMS.ANNOTATIONS_JAR), + new UpdateZipAction("lib/bootstrap.jar", + Collections.emptyList(), + Collections.emptyList(), + Arrays.asList("com/intellij/ide/ClassloaderUtil.class"), + CHECKSUMS.BOOTSTRAP_JAR)); + List actualActions = new ArrayList(myPatch.getActions()); + Collections.sort(expectedActions, COMPARATOR); + Collections.sort(actualActions, COMPARATOR); + assertEquals(expectedActions, actualActions); + } + + @Test + public void testValidation() throws Exception { + FileUtil.writeToFile(new File(myOlderDir, "bin/idea.bat"), "changed".getBytes()); + new File(myOlderDir, "extraDir").mkdirs(); + new File(myOlderDir, "extraDir/extraFile.txt").createNewFile(); + new File(myOlderDir, "newDir").mkdirs(); + new File(myOlderDir, "newDir/newFile.txt").createNewFile(); + FileUtil.writeToFile(new File(myOlderDir, "Readme.txt"), "changed".getBytes()); + FileUtil.writeToFile(new File(myOlderDir, "lib/annotations.jar"), "changed".getBytes()); + FileUtil.delete(new File(myOlderDir, "lib/bootstrap.jar")); + + assertEquals( + new HashSet(Arrays.asList( + new ValidationResult(ValidationResult.Kind.CONFLICT, + "newDir/newFile.txt", + ValidationResult.Action.CREATE, + ValidationResult.ALREADY_EXISTS_MESSAGE, + ValidationResult.Option.REPLACE, ValidationResult.Option.KEEP), + new ValidationResult(ValidationResult.Kind.ERROR, + "Readme.txt", + ValidationResult.Action.UPDATE, + ValidationResult.MODIFIED_MESSAGE, + ValidationResult.Option.IGNORE), + new ValidationResult(ValidationResult.Kind.ERROR, + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + ValidationResult.MODIFIED_MESSAGE, + ValidationResult.Option.IGNORE), + new ValidationResult(ValidationResult.Kind.ERROR, + "lib/bootstrap.jar", + ValidationResult.Action.UPDATE, + ValidationResult.ABSENT_MESSAGE, + ValidationResult.Option.IGNORE), + new ValidationResult(ValidationResult.Kind.CONFLICT, + "bin/idea.bat", + ValidationResult.Action.DELETE, + ValidationResult.MODIFIED_MESSAGE, + ValidationResult.Option.DELETE, ValidationResult.Option.KEEP))), + new HashSet(myPatch.validate(myOlderDir, TEST_UI))); + } + + @Test + public void testValidationWithOptionalFiles() throws Exception { + FileUtil.writeToFile(new File(myOlderDir, "lib/annotations.jar"), "changed".getBytes()); + assertEquals(new HashSet(Arrays.asList( + new ValidationResult(ValidationResult.Kind.ERROR, + "lib/annotations.jar", + ValidationResult.Action.UPDATE, + ValidationResult.MODIFIED_MESSAGE, + ValidationResult.Option.IGNORE))), + new HashSet(myPatch.validate(myOlderDir, TEST_UI))); + + myPatch = new Patch(myOlderDir, myNewerDir, Collections.emptyList(), Collections.emptyList(), + Arrays.asList("lib/annotations.jar"), TEST_UI); + FileUtil.delete(new File(myOlderDir, "lib/annotations.jar")); + assertEquals(Collections.emptyList(), + myPatch.validate(myOlderDir, TEST_UI)); + } + + @Test + public void testValidatingNonAccessibleFiles() throws Exception { + File f = new File(myOlderDir, "Readme.txt"); + FileOutputStream s = new FileOutputStream(f, true); + try { + FileLock lock = s.getChannel().lock(); + try { + List result = myPatch.validate(myOlderDir, TEST_UI); + assertEquals( + new HashSet(Arrays.asList( + new ValidationResult(ValidationResult.Kind.ERROR, + "Readme.txt", + ValidationResult.Action.UPDATE, + ValidationResult.ACCESS_DENIED_MESSAGE, + ValidationResult.Option.IGNORE))), + new HashSet(result)); + } + finally { + lock.release(); + } + } + finally { + s.close(); + } + } + + @Test + public void testSaveLoad() throws Exception { + File f = getTempFile("file"); + try { + FileOutputStream out = new FileOutputStream(f); + try { + myPatch.write(out); + } + finally { + out.close(); + } + + FileInputStream in = new FileInputStream(f); + try { + assertEquals(myPatch.getActions(), new Patch(in).getActions()); + } + finally { + in.close(); + } + } + finally { + f.delete(); + } + } + + private static final Comparator COMPARATOR = new Comparator() { + @Override + public int compare(PatchAction o1, PatchAction o2) { + return o1.toString().compareTo(o2.toString()); + } + }; +} diff --git a/updater/testSrc/com/intellij/updater/PatchTestCase.java b/updater/testSrc/com/intellij/updater/PatchTestCase.java new file mode 100644 index 000000000000..417c32eb396c --- /dev/null +++ b/updater/testSrc/com/intellij/updater/PatchTestCase.java @@ -0,0 +1,41 @@ +package com.intellij.updater; + +import com.intellij.openapi.util.io.FileUtil; + +import java.io.File; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public abstract class PatchTestCase extends UpdaterTestCase { + protected File myNewerDir; + protected File myOlderDir; + + @Override + public void setUp() throws Exception { + super.setUp(); + myOlderDir = getDataDir(); + myNewerDir = getTempFile("newDir"); + FileUtil.copyDir(myOlderDir, myNewerDir); + + FileUtil.delete(new File(myNewerDir, "bin/idea.bat")); + FileUtil.writeToFile(new File(myNewerDir, "Readme.txt"), "hello".getBytes()); + File newFile = new File(myNewerDir, "newDir/newFile.txt"); + newFile.getParentFile().mkdirs(); + newFile.createNewFile(); + FileUtil.writeToFile(newFile, "hello".getBytes()); + + 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")); + + FileUtil.delete(new File(myOlderDir, "lib/boot2_changed_with_unchanged_content.jar")); + FileUtil.delete(new File(myNewerDir, "lib/boot2.jar")); + FileUtil.rename(new File(myNewerDir, "lib/boot2_changed_with_unchanged_content.jar"), + new File(myNewerDir, "lib/boot2.jar")); + } +} \ No newline at end of file diff --git a/updater/testSrc/com/intellij/updater/RunnerTest.java b/updater/testSrc/com/intellij/updater/RunnerTest.java new file mode 100644 index 000000000000..22464a0fa053 --- /dev/null +++ b/updater/testSrc/com/intellij/updater/RunnerTest.java @@ -0,0 +1,25 @@ +package com.intellij.updater; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + +public class RunnerTest extends UpdaterTestCase { + @Test + public void testExtractingFiles() throws Exception { + String[] args = {"bar", "ignored=xxx;yyy;zzz/zzz", "critical=", "ignored=aaa", "baz", "critical=ccc"}; + Runner.initLogger(); + + assertEquals(Arrays.asList("xxx", "yyy", "zzz/zzz", "aaa"), + Runner.extractFiles(args, "ignored")); + + assertEquals(Arrays.asList("ccc"), + Runner.extractFiles(args, "critical")); + + assertEquals(Collections.emptyList(), + Runner.extractFiles(args, "unknown")); + } +} diff --git a/updater/testSrc/com/intellij/updater/UpdaterTestCase.java b/updater/testSrc/com/intellij/updater/UpdaterTestCase.java new file mode 100644 index 000000000000..9bde5c848507 --- /dev/null +++ b/updater/testSrc/com/intellij/updater/UpdaterTestCase.java @@ -0,0 +1,72 @@ +package com.intellij.updater; + +import com.intellij.openapi.application.ex.PathManagerEx; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TempDirTestFixture; +import org.junit.After; +import org.junit.Before; + +import java.io.File; + +public abstract class UpdaterTestCase { + protected static final UpdaterUI TEST_UI = new ConsoleUpdaterUI(){ + @Override + public void startProcess(String title) { + } + + @Override + public void setStatus(String status) { + } + }; + + protected CheckSums CHECKSUMS; + private TempDirTestFixture myTempDirFixture; + + @Before + public void setUp() throws Exception { + myTempDirFixture = IdeaTestFixtureFactory.getFixtureFactory().createTempDirTestFixture(); + myTempDirFixture.setUp(); + + FileUtil.copyDir(PathManagerEx.findFileUnderCommunityHome("updater/testData"), getDataDir()); + + boolean windowsLineEnds = new File(getDataDir(), "Readme.txt").length() == 7132; + CHECKSUMS = new CheckSums(windowsLineEnds); + } + + @After + public void tearDown() throws Exception { + myTempDirFixture.tearDown(); + Utils.cleanup(); + } + + public File getDataDir() { + return getTempFile("data"); + } + + public File getTempFile(String fileName) { + return new File(myTempDirFixture.getTempDirPath(), fileName); + } + + protected static class CheckSums { + public final long README_TXT; + public final long IDEA_BAT; + public final long ANNOTATIONS_JAR; + public final long BOOTSTRAP_JAR; + public final long FOCUSKILLER_DLL; + + public CheckSums(boolean windowsLineEnds) { + if (windowsLineEnds) { + README_TXT = 1272723667L; + IDEA_BAT = 3088608749L; + } + else { + README_TXT = 7256327L; + IDEA_BAT = 1493936069L; + } + ANNOTATIONS_JAR = 2119442657L; + BOOTSTRAP_JAR = 2082851308L; + FOCUSKILLER_DLL = 1991212227L; + } + } +} \ No newline at end of file diff --git a/updater/updater.iml b/updater/updater.iml index a002577c561e..e55b58d17cd0 100644 --- a/updater/updater.iml +++ b/updater/updater.iml @@ -4,10 +4,13 @@ + + +