| /* |
| * 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. |
| */ |
| import java.nio.charset.Charset |
| |
| apply plugin: "application" |
| |
| project.mainClassName = "com.intellij.updater.Runner" |
| |
| defaultTasks "testSuite" |
| |
| repositories { |
| mavenCentral() |
| } |
| |
| dependencies { |
| compile files("../../lib/log4j.jar") |
| testCompile group: "junit", name: "junit", version: "3.+" |
| } |
| |
| sourceSets { |
| main { |
| java { srcDir "../src" } |
| resources { srcDir "../src" } |
| } |
| test { |
| java { srcDir "../tests" } |
| resources { srcDir "../tests" } |
| } |
| } |
| |
| |
| test { |
| testLogging { |
| showStandardStreams = true |
| showStackTraces = true |
| exceptionFormat = "full" |
| } |
| } |
| |
| //--- |
| |
| // Task : Tests updater create + install in the default normal case. |
| // Scope: large test. |
| // Running this will display the install patcher UI. |
| task testUI1(dependsOn: jar) << { |
| println "## Running UI test 1" |
| println "## Jar file: " + jar.archivePath.getAbsolutePath() |
| |
| // Create a temp dir inside the gradle build/tmp dir |
| //noinspection GroovyAssignabilityCheck |
| def buildTmpDir = new File(project.buildDir, "tmp"); |
| def tmpDir = _createTempDir(buildTmpDir) |
| def logDir = _createTempDir(buildTmpDir) |
| |
| try { |
| // create a "from version 1" and "to version 2" directories |
| def dataDir1 = _createTestData(tmpDir, "1") |
| assert _mkFile(dataDir1, "plugins", "v1", "myplugin.jar").exists() |
| assert !_mkFile(dataDir1, "plugins", "v2", "myplugin.jar").exists() |
| assert _getFileContent(_mkFile(dataDir1, "build.txt")).equals("AI-123.45678-1") |
| |
| def dataDir2 = _createTestData(tmpDir, "2") |
| assert !_mkFile(dataDir2, "plugins", "v1", "myplugin.jar").exists() |
| assert _mkFile(dataDir2, "plugins", "v2", "myplugin.jar").exists() |
| assert _getFileContent(_mkFile(dataDir2, "build.txt")).equals("AI-123.45678-2") |
| |
| def patch = _createFileContent(tmpDir, "patch.jar", "patch jar placeholder"); |
| patch.delete() |
| assert !patch.exists() |
| |
| def logFullFile = _mkFile(logDir, "idea_updater.log") |
| def logErrorFile = _mkFile(logDir, "idea_updater_error.log") |
| assert !logFullFile .exists() |
| assert !logErrorFile.exists() |
| |
| // call updater jar to create a diff, resulting in a patch jar. |
| println "## Invoking updater <create>" |
| javaexec { |
| classpath jar.archivePath |
| classpath "../../lib/log4j.jar" |
| main = "com.intellij.updater.Runner" |
| args "create" |
| args "AI-123.45678-1" |
| args "AI-123.45678-2" |
| args dataDir1.getAbsolutePath() |
| args dataDir2.getAbsolutePath() |
| args patch.getAbsolutePath() |
| args logDir.getAbsolutePath() |
| } |
| assert patch.exists() |
| |
| assert logFullFile .exists() |
| assert logErrorFile.exists() // should exist and be empty |
| assert _getFileContent(logErrorFile).equals(null) |
| |
| |
| // that patch jar is self-executable. use it to update dir1 into dir2 in-place. |
| println "## Invoking updater <install>" |
| javaexec { |
| classpath patch |
| classpath "../../lib/log4j.jar" |
| main = "com.intellij.updater.Runner" |
| args "install" |
| args "--exit0" |
| args dataDir1.getAbsolutePath() |
| args logDir.getAbsolutePath() |
| } |
| // build.txt should have changed to v2 |
| assert _getFileContent(_mkFile(dataDir1, "build.txt")).equals("AI-123.45678-2") |
| // plugin v1 should have been replaced by pluging v2 in the dataDir1 directory. |
| assert !_mkFile(dataDir1, "plugins", "v1", "myplugin.jar").exists() |
| assert _mkFile(dataDir1, "plugins", "v2", "myplugin.jar").exists() |
| |
| assert logFullFile .exists() |
| assert logErrorFile.exists() // should exist and be empty |
| assert _getFileContent(logErrorFile).equals(null) |
| |
| } finally { |
| // Cleanup on exit |
| tmpDir.deleteDir() |
| logDir.deleteDir() |
| } |
| } |
| |
| // Task : Tests updater create + install with 2 open files. |
| // Scope: large *interactive* test. |
| // On Windows, the opened files are locked and can't be deleted/overwritten. |
| // On Linux/Mac, they should be writable & deletable. |
| // |
| // Running this will display the install patcher UI, which will fail at |
| // first on Windows. After 5 seconds the open files will be closed. |
| // Hitting "retry" in the UI after this point should perform the whole |
| // install operation again. |
| task testUI2(dependsOn: jar) << { |
| println "## Running UI test 2" |
| println "## Jar file: " + jar.archivePath.getAbsolutePath() |
| |
| // Create a temp dir inside the gradle build/tmp dir |
| //noinspection GroovyAssignabilityCheck |
| def buildTmpDir = new File(project.buildDir, "tmp"); |
| def tmpDir = _createTempDir(buildTmpDir) |
| def logDir = _createTempDir(buildTmpDir) |
| def closeableFiles = [] |
| |
| try { |
| // create a "from version 1" and "to version 2" directories |
| def dataDir1 = _createTestData(tmpDir, "1") |
| assert _mkFile(dataDir1, "plugins", "v1", "myplugin.jar").exists() |
| assert !_mkFile(dataDir1, "plugins", "v2", "myplugin.jar").exists() |
| assert _getFileContent(_mkFile(dataDir1, "build.txt")).equals("AI-123.45678-1") |
| |
| def dataDir2 = _createTestData(tmpDir, "2") |
| assert !_mkFile(dataDir2, "plugins", "v1", "myplugin.jar").exists() |
| assert _mkFile(dataDir2, "plugins", "v2", "myplugin.jar").exists() |
| assert _getFileContent(_mkFile(dataDir2, "build.txt")).equals("AI-123.45678-2") |
| |
| def patch = _createFileContent(tmpDir, "patch.jar", "patch jar placeholder"); |
| patch.delete() |
| assert !patch.exists() |
| |
| def logFullFile = _mkFile(logDir, "idea_updater.log") |
| def logErrorFile = _mkFile(logDir, "idea_updater_error.log") |
| assert !logFullFile .exists() |
| assert !logErrorFile.exists() |
| |
| // keep a couple files open, preventing them from being deleted or replaced on Windows |
| closeableFiles << _openFile(_mkFile(dataDir1, "build.txt")) |
| closeableFiles << _openFile(_mkFile(dataDir1, "plugins", "v1", "myplugin.jar")) |
| |
| // call updater jar to create a diff, resulting in a patch jar. |
| println "## Invoking updater <create>" |
| javaexec { |
| classpath jar.archivePath |
| classpath "../../lib/log4j.jar" |
| main = "com.intellij.updater.Runner" |
| args "create" |
| args "AI-123.45678-1" |
| args "AI-123.45678-2" |
| args dataDir1.getAbsolutePath() |
| args dataDir2.getAbsolutePath() |
| args patch.getAbsolutePath() |
| args logDir.getAbsolutePath() |
| } |
| assert patch.exists() |
| |
| assert logFullFile .exists() |
| assert logErrorFile.exists() // should exist and be empty |
| assert _getFileContent(logErrorFile).equals(null) |
| |
| Thread.start { |
| sleep(5 * 1000) // 5 seconds |
| synchronized(closeableFiles) { |
| println "##" |
| println "## Closing pending open files --> now click 'Retry' in Updater UI." |
| println "##" |
| closeableFiles.each { it.close() } |
| closeableFiles.clear() |
| } |
| } |
| |
| // that patch jar is self-executable. use it to update dir1 into dir2 in-place. |
| println "## Invoking updater <install>" |
| println "##" |
| println "## Note: on Windows some files will be locked for 5 seconds and the installer" |
| println "## window should display a 'Retry' button. This code will wait 5 seconds" |
| println "## then unlock the files, at which point you would click that 'Retry'" |
| println "## button and the install should continue correctly." |
| println "##" |
| |
| javaexec { |
| classpath patch |
| classpath "../../lib/log4j.jar" |
| main = "com.intellij.updater.Runner" |
| args "install" |
| args "--exit0" |
| args dataDir1.getAbsolutePath() |
| args logDir.getAbsolutePath() |
| } |
| // build.txt should have changed to v2 |
| assert _getFileContent(_mkFile(dataDir1, "build.txt")).equals("AI-123.45678-2") |
| // plugin v1 should have been replaced by pluging v2 in the dataDir1 directory. |
| assert !_mkFile(dataDir1, "plugins", "v1", "myplugin.jar").exists() |
| assert _mkFile(dataDir1, "plugins", "v2", "myplugin.jar").exists() |
| |
| assert logFullFile .exists() |
| // the log error file should exist and should not be empty on Windows. |
| assert logErrorFile.exists() |
| if (System.getProperty("os.name").startsWith("Windows")) { |
| assert _getFileContent(logErrorFile) != null |
| } else { |
| assert _getFileContent(logErrorFile).equals(null) |
| } |
| |
| } finally { |
| // Cleanup on exit |
| synchronized(closeableFiles) { |
| closeableFiles.each { it.close() } |
| closeableFiles.clear() |
| } |
| tmpDir.deleteDir() |
| logDir.deleteDir() |
| } |
| } |
| |
| // Task: Test suite to run both tests above. |
| task testSuite(dependsOn: [testUI1, testUI2]) << { |
| } |
| |
| // ---- Helper methods |
| |
| // Convention: all local helper methods start with an underscore to clearly |
| // differentiate them from groovy/gradle methods. |
| |
| |
| // Creates a temp dir with a random name in the build/tmp directory. |
| File _createTempDir(File parent) { |
| def d = File.createTempFile("test", "", parent) |
| d.delete() |
| d.mkdirs() |
| return d |
| } |
| |
| // Creates a new directory with the specific name in the specified parent directory. |
| File _createDir(File parent, String name) { |
| def d = new File(parent, name) |
| d.mkdirs() |
| return d |
| } |
| |
| // Creates a new file with the specified name, in the specified parent directory with the given UTF-8 content. |
| File _createFileContent(File parentDir, String fileName, String fileContent) throws IOException { |
| File f = new File(parentDir, fileName) |
| OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(f), Charset.forName("UTF-8")) |
| try { |
| fw.write(fileContent) |
| } finally { |
| fw.close() |
| } |
| return f |
| } |
| |
| // Opens a file for writing (thus locking it on Windows) but does not close it. |
| // Caller should call Closeable.close() on the returned Closeable object. |
| Closeable _openFile(File file) throws IOException { |
| if (!file.exists()) throw new IOException("File not found, expected file: " + file.getName()) |
| // We need to actually write to the file but not change its content so just read it |
| // then write the same thing back to it, without closing it. |
| String content = _getFileContent(file) |
| FileWriter fw = new FileWriter(file, false /*append*/) |
| fw.append(content) |
| fw.flush() |
| println("## Open file " + file.getName()); |
| return fw // file is not closed |
| } |
| |
| // Returns the first line of the file. |
| // Returns null if the file exists and is empty. |
| // Throws IOException if file does not exist. |
| String _getFileContent(File file) throws IOException { |
| if (!file.exists()) throw new IOException("File not found, expected file: " + file.getName()) |
| BufferedReader br = new BufferedReader(new FileReader(file)) |
| try { |
| return br.readLine() |
| } finally { |
| br.close() |
| } |
| } |
| |
| // Creates a new File() object with the concatenated name segments. |
| File _mkFile(File base, String...segments) { |
| for(String segment : segments) { |
| base = new File(base, segment) |
| } |
| return base |
| } |
| |
| // Creates a mock test data for an idea-based IDE. |
| File _createTestData(File tmpDir, String value) { |
| File root = _createDir(tmpDir, "idea-ide-" + value) |
| File bin = _createDir(root, "bin") |
| File lib = _createDir(root, "lib") |
| File plugins = _createDir(root, "plugins") |
| File myplugin = _createDir(plugins, "v" + value) |
| |
| _createFileContent(root, "build.txt", "AI-123.45678-" + value); |
| _createFileContent(bin, "idea.exe", "binary file " + value); |
| _createFileContent(lib, "idea.jar", "some jar file " + value); |
| _createFileContent(myplugin, "myplugin.jar", "some jar file " + value); |
| |
| return root |
| } |