diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestPaths.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestPaths.kt new file mode 100644 index 000000000000..5a75439e2ec9 --- /dev/null +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestPaths.kt @@ -0,0 +1,46 @@ +// 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. +package com.intellij.testGuiFramework.framework + +import com.intellij.openapi.application.PathManager +import com.intellij.openapi.util.io.FileUtil.ensureExists +import com.intellij.openapi.util.io.FileUtil.toSystemDependentName +import com.intellij.testGuiFramework.launcher.GuiTestOptions +import org.fest.assertions.Assertions.assertThat +import java.io.File + +object GuiTestPaths { + + val failedTestScreenshotDir: File by lazy { + val dirPath = File(guiTestRootDirPath, "failures") + ensureExists(dirPath) + return@lazy dirPath + } + + val failedTestVideoDirPath: File by lazy { + val dirPath = File(failedTestScreenshotDir, "video") + ensureExists(dirPath) + return@lazy dirPath + } + + val testScreenshotDirPath: File by lazy { + val dirPath = File(guiTestRootDirPath, "screenshots") + ensureExists(dirPath) + return@lazy dirPath + } + + private val guiTestRootDirPath: File by lazy { + if (!GuiTestOptions.guiTestRootDirPath.isNullOrEmpty()) { + val rootDirPath = File(GuiTestOptions.guiTestRootDirPath) + if (rootDirPath.isDirectory) { + return@lazy rootDirPath + } + } + val systemDir = toSystemDependentName(PathManager.getSystemPath()) + assertThat(systemDir).isNotEmpty + val logDir = File(systemDir, "log") + val guiTestsDir = File(logDir, "gui-tests") + ensureExists(guiTestsDir) + return@lazy guiTestsDir + } + +} diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunner.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunner.kt index 4811cbd6185d..fde57b98081f 100755 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunner.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunner.kt @@ -20,9 +20,15 @@ import com.intellij.openapi.util.Ref import com.intellij.testGuiFramework.impl.GuiTestStarter import com.intellij.testGuiFramework.impl.GuiTestThread import com.intellij.testGuiFramework.impl.GuiTestUtilKt -import com.intellij.testGuiFramework.launcher.GuiTestLocalLauncher.runIdeLocally +import com.intellij.testGuiFramework.launcher.GradleLauncher +import com.intellij.testGuiFramework.launcher.GuiTestLocalLauncher +import com.intellij.testGuiFramework.launcher.GuiTestOptions import com.intellij.testGuiFramework.launcher.ide.Ide -import com.intellij.testGuiFramework.remote.IdeProcessControlManager +import com.intellij.testGuiFramework.remote.IdeControl.closeIde +import com.intellij.testGuiFramework.remote.IdeControl.ensureIdeIsRunning +import com.intellij.testGuiFramework.remote.IdeControl.restartIde +import com.intellij.testGuiFramework.remote.IdeControl.resumeTest +import com.intellij.testGuiFramework.remote.IdeControl.runTest import com.intellij.testGuiFramework.remote.server.JUnitServer import com.intellij.testGuiFramework.remote.server.JUnitServerHolder import com.intellij.testGuiFramework.remote.transport.* @@ -37,14 +43,17 @@ import org.junit.runner.notification.RunListener import org.junit.runner.notification.RunNotifier import org.junit.runners.model.FrameworkMethod import java.net.SocketException -import java.util.concurrent.TimeUnit open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInterface) { - private val SERVER_LOG = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.GuiTestRunner")!! + private val SERVER_LOG = org.apache.log4j.Logger.getLogger("com.intellij.testGuiFramework.framework.GuiTestRunner")!! private val criticalError = Ref(false) + private val myServer: JUnitServer + get() = JUnitServerHolder.getServer() + + fun runChild(method: FrameworkMethod, notifier: RunNotifier) { if (!GuiTestStarter.isGuiTestThread()) runOnServerSide(method, notifier) @@ -61,6 +70,8 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte private fun runOnServerSide(method: FrameworkMethod, notifier: RunNotifier) { val description = runner.describeChild(method) + val localIde = runner.ide ?: getIdeFromAnnotation(method.declaringClass) + val systemProperties = getSystemPropertiesFromAnnotation(method.declaringClass) val eachNotifier = EachTestNotifier(notifier, description) if (criticalError.get()) { @@ -69,19 +80,11 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte val testName = runner.getTestName(method.name) SERVER_LOG.info("Starting test on server side: $testName") - val server = JUnitServerHolder.getServer() try { - if (!server.isConnected()) { - val localIde = runner.ide ?: getIdeFromAnnotation(method.declaringClass) - SERVER_LOG.info("Starting IDE ($localIde) with port for running tests: ${server.getPort()}") - runIde(port = server.getPort(), ide = localIde) - if (!server.isStarted()) server.start() - } - val jUnitTestContainer = JUnitTestContainer(method.declaringClass, testName) - server.send(TransportMessage(MessageType.RUN_TEST, jUnitTestContainer)) - } - catch (e: Exception) { + ensureIdeIsRunning(localIde, systemProperties ?: emptyList(), ::runIde) + runTest(method, testName) + } catch (e: Exception) { SERVER_LOG.error(e) notifier.fireTestIgnored(description) Assert.fail(e.message) @@ -89,35 +92,34 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte var testIsRunning = true while (testIsRunning) { try { - val message = server.receive() + val message = myServer.receive() if (message.content is JUnitInfo && message.content.testClassAndMethodName == JUnitInfo.getClassAndMethodName(description)) { - testIsRunning = processJUnitEvent(message.content, eachNotifier, testIsRunning) + testIsRunning = processJUnitEvent(message.content, eachNotifier) } if (message.type == MessageType.RESTART_IDE) { - restartIde(server, getIdeFromMethod(method)) - sendRunTestCommand(method, testName, server) + restartIde(ide = getIdeFromMethod(method), runIde = ::runIde) + runTest(method, testName) } if (message.type == MessageType.RESTART_IDE_AND_RESUME) { if (message.content !is RestartIdeAndResumeContainer) throw Exception( "Transport exception: Message with type RESTART_IDE_AND_RESUME should have content type RestartIdeAndResumeContainer but has a ${message.content?.javaClass?.canonicalName}") when (message.content.restartIdeCause) { RestartIdeCause.PLUGIN_INSTALLED -> { - restartIde(server, getIdeFromMethod(method)) - sendResumeTestCommand(method, server, PLUGINS_INSTALLED) + restartIde(ide = getIdeFromMethod(method), runIde = ::runIde) + resumeTest(method, PLUGINS_INSTALLED) } RestartIdeCause.RUN_WITH_SYSTEM_PROPERTIES -> { if (message.content !is RunWithSystemPropertiesContainer) throw Exception( "Transport exception: message.content caused by RUN_WITH_SYSTEM_PROPERTIES should have RunWithSystemPropertiesContainer type, but have: ${message.content.javaClass.canonicalName}") - restartIde(server, getIdeFromMethod(method), additionalJvmOptions = message.content.systemProperties) - sendResumeTestCommand(method, server, SYSTEM_PROPERTIES) + restartIde(getIdeFromMethod(method), additionalJvmOptions = message.content.systemProperties, runIde = ::runIde) + resumeTest(method, SYSTEM_PROPERTIES) } } } - } - catch (se: SocketException) { + } catch (se: SocketException) { //let's fail this test and move to the next one test - SERVER_LOG.warn("Server client connection is dead. Going to kill IDE process.") - stopServerAndKillIde(server) + SERVER_LOG.warn("Server client connection is dead. Going to kill IDE processStdIn.") + closeIde() eachNotifier.addFailure(se) eachNotifier.fireTestFinished() testIsRunning = false @@ -126,15 +128,18 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte } protected fun processJUnitEvent(content: JUnitInfo, - eachNotifier: EachTestNotifier, - testIsRunning: Boolean): Boolean { - var testIsRunning1 = testIsRunning - when (content.type) { - Type.STARTED -> eachNotifier.fireTestStarted() - Type.ASSUMPTION_FAILURE -> eachNotifier.addFailedAssumption( - (content.obj as Failure).exception as AssumptionViolatedException) + eachNotifier: EachTestNotifier): Boolean { + return when (content.type) { + Type.STARTED -> { + eachNotifier.fireTestStarted(); true + } + Type.ASSUMPTION_FAILURE -> { + eachNotifier.addFailedAssumption((content.obj as Failure).exception as AssumptionViolatedException) + false + } Type.IGNORED -> { - eachNotifier.fireTestIgnored(); testIsRunning1 = false + eachNotifier.fireTestIgnored() + false } Type.FAILURE -> { //reconstruct Throwable @@ -142,18 +147,28 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte val throwable = Throwable("thrown from $className: $messageFromException") throwable.stackTrace = stackTraceFromException eachNotifier.addFailure(throwable) + true } Type.FINISHED -> { - testIsRunning1 = processTestFinished(eachNotifier) + processTestFinished(eachNotifier) + false } else -> throw UnsupportedOperationException("Unable to recognize received from JUnitClient") } - return testIsRunning1 } - protected open fun processTestFinished(eachNotifier: EachTestNotifier): Boolean { + protected open fun runIde(ide: Ide, additionalJvmOptions: List> = emptyList()) { + if (GuiTestOptions.isGradleRunner) { + GradleLauncher.runIde(JUnitServerHolder.getServer().getPort()) + } else { + val testClassNames = runner.getTestClassesNames() + if (testClassNames.isEmpty()) throw Exception("Test classes are not declared.") + GuiTestLocalLauncher.runIdeLocally(ide, JUnitServerHolder.getServer().getPort()) + } + } + + protected open fun processTestFinished(eachNotifier: EachTestNotifier) { eachNotifier.fireTestFinished() - return false } protected fun getIdeFromMethod(method: FrameworkMethod): Ide { @@ -164,39 +179,6 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte * @additionalJvmOptions - an array of key-value pairs written without -D, for example: {@code arrayOf(Pair("idea.debug.mode", "true")) * By default set as an empty array – no additional JVM options */ - protected fun restartIde(server: JUnitServer, ide: Ide, additionalJvmOptions: Array> = emptyArray()) { - stopServerAndKillIde(server) - //restart JUnitServer to let accept a new connection - //start a new one IDE - runIde(port = server.getPort(), ide = ide, additionalJvmOptions = additionalJvmOptions) - server.start() - } - - protected open fun stopServerAndKillIde(server: JUnitServer) { - closeIde(server) - IdeProcessControlManager.killIdeProcess() - server.stopServer() - } - - protected fun closeIde(server: JUnitServer) { - //close previous IDE - server.send(TransportMessage(MessageType.CLOSE_IDE)) - //await to close previous process - IdeProcessControlManager.waitForCurrentProcess(2, TimeUnit.MINUTES) - } - - private fun sendRunTestCommand(method: FrameworkMethod, - testName: String, - server: JUnitServer) { - val jUnitTestContainer = JUnitTestContainer(method.declaringClass, testName) - server.send(TransportMessage(MessageType.RUN_TEST, jUnitTestContainer)) - } - - private fun sendResumeTestCommand(method: FrameworkMethod, - server: JUnitServer, resumeTestLabel: String) { - val jUnitTestContainer = JUnitTestContainer(method.declaringClass, method.name, additionalInfo = resumeTestLabel) - server.send(TransportMessage(MessageType.RESUME_TEST, jUnitTestContainer)) - } private fun runOnClientSide(method: FrameworkMethod, notifier: RunNotifier) { val testName = runner.getTestName(method.name) @@ -229,34 +211,19 @@ open class GuiTestRunner internal constructor(open val runner: GuiTestRunnerInte val restartIdeMessage = TransportMessage(MessageType.RESTART_IDE, "IDE has fatal errors from previous test, let's start a new instance") GuiTestThread.client?.send(restartIdeMessage) ?: throw Exception("JUnitClient is accidentally null") - } - else { + } else { if (!GuiTestStarter.isGuiTestThread()) - runIdeLocally() //TODO: investigate this case + TODO("Investigate this case") else { runner.doRunChild(method, notifier) } } - } - catch (e: Exception) { + } catch (e: Exception) { LOG.error(e) throw e } } - /** - * @additionalJvmOptions - an array of key-value pairs written without -D, for example: {@code arrayOf(Pair("idea.debug.mode", "true")) - * By default set as an empty array – no additional JVM options - */ - protected open fun runIde(port: Int, ide: Ide, additionalJvmOptions: Array> = emptyArray()) { - val testClassNames = runner.getTestClassesNames() - if (testClassNames.isEmpty()) throw Exception("Test classes are not declared.") - runIdeLocally(port = port, - ide = ide, - testClassNames = testClassNames, - additionalJvmOptions = additionalJvmOptions) - } - companion object { private val LOG = Logger.getInstance("#com.intellij.testGuiFramework.framework.GuiTestRunner") } diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunnerInterface.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunnerInterface.kt index 017e6fd3c0e0..142fab33c21c 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunnerInterface.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestRunnerInterface.kt @@ -25,4 +25,13 @@ fun getIdeFromAnnotation(testClass: Class<*>): Ide { val annotation = testClass.annotations.filterIsInstance().firstOrNull()?.value val ideType = if (annotation != null) (annotation as KClass).java.newInstance() else CommunityIde() return Ide(ideType, 0, 0) +} + +fun getSystemPropertiesFromAnnotation(testClass: Class<*>): List>? { + return testClass + .annotations + .filterIsInstance() + .firstOrNull() + ?.keyValueArray + ?.map { val (key, value) = it.split("="); Pair(key, value) } } \ No newline at end of file diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestSuite.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestSuite.kt index 666a004a395e..88d4c9119599 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestSuite.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestSuite.kt @@ -15,11 +15,10 @@ */ package com.intellij.testGuiFramework.framework -import com.intellij.testGuiFramework.remote.IdeProcessControlManager +import com.intellij.testGuiFramework.remote.IdeControl import org.junit.AfterClass import org.junit.BeforeClass - open class GuiTestSuite { companion object { @@ -31,8 +30,7 @@ open class GuiTestSuite { @AfterClass @JvmStatic fun tearDown() { - // todo: GUI-142 GuiTestRunner needs refactoring - IdeProcessControlManager.killIdeProcess() + IdeControl.closeIde() } } } diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt index b5de3407bc1a..dbbe5ac6945a 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt @@ -153,7 +153,7 @@ object GuiTestUtil { return !fatalErrors.isEmpty() } - // Called by IdeTestApplication via reflection. + // Called by GuiTestPaths via reflection. fun setUpDefaultGeneralSettings() { } @@ -182,7 +182,7 @@ object GuiTestUtil { RecentProjectsManager.getInstance().lastProjectCreationLocation = PathUtil.toSystemIndependentName(projectsFolder.path) } - // Called by IdeTestApplication via reflection. + // Called by GuiTestPaths via reflection. fun waitForIdeToStart() { val firstStart = getSystemPropertyOrEnvironmentVariable(FIRST_START) val isFirstStart = firstStart != null && firstStart.toLowerCase() == "true" diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/IdeTestApplication.java b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/IdeTestApplication.java deleted file mode 100644 index 0bb5815ac62f..000000000000 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/IdeTestApplication.java +++ /dev/null @@ -1,220 +0,0 @@ -// 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. -package com.intellij.testGuiFramework.framework; - -import com.intellij.ide.BootstrapClassLoaderUtil; -import com.intellij.ide.IdeEventQueue; -import com.intellij.ide.WindowsCommandLineProcessor; -import com.intellij.idea.Main; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.application.impl.ApplicationImpl; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.testGuiFramework.launcher.GuiTestOptions; -import com.intellij.util.SystemProperties; -import com.intellij.util.lang.UrlClassLoader; -import com.intellij.util.text.StringTokenizer; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Paths; -import java.util.*; -import java.util.regex.Pattern; - -import static com.intellij.openapi.application.PathManager.PROPERTY_CONFIG_PATH; -import static com.intellij.openapi.application.PathManager.PROPERTY_SYSTEM_PATH; -import static com.intellij.openapi.util.io.FileUtil.*; -import static com.intellij.openapi.util.text.StringUtil.isNotEmpty; -import static com.intellij.util.ArrayUtil.EMPTY_STRING_ARRAY; -import static org.fest.assertions.Assertions.assertThat; -import static org.fest.reflect.core.Reflection.method; -import static org.junit.Assert.assertNotNull; - -public class IdeTestApplication { - - private static final Logger LOG = Logger.getInstance(IdeTestApplication.class); - private static final String PROPERTY_IGNORE_CLASSPATH = "ignore.classpath"; - private static final String PROPERTY_ALLOW_BOOTSTRAP_RESOURCES = "idea.allow.bootstrap.resources"; - private static final String PROPERTY_ADDITIONAL_CLASSPATH = "idea.additional.classpath"; - private static final String CUSTOM_CONFIG_PATH= "CUSTOM_CONFIG_PATH"; - private static final String CUSTOM_SYSTEM_PATH= "CUSTOM_SYSTEM_PATH"; - - private static IdeTestApplication ourInstance; - - protected ClassLoader myIdeClassLoader; - - @NotNull - public ClassLoader getIdeClassLoader() { - return myIdeClassLoader; - } - - @NotNull - public static File getFailedTestScreenshotDirPath() throws IOException { - File dirPath = new File(getGuiTestRootDirPath(), "failures"); - ensureExists(dirPath); - return dirPath; - } - - @NotNull - public static File getFailedTestVideoDirPath() throws IOException { - final File dirPath = new File(getFailedTestScreenshotDirPath(), "video"); - ensureExists(dirPath); - return dirPath; - } - - @NotNull - public static File getTestScreenshotDirPath() throws IOException { - File dirPath = new File(getGuiTestRootDirPath(), "screenshots"); - ensureExists(dirPath); - return dirPath; - } - - @NotNull - protected static File getGuiTestRootDirPath() throws IOException { - String guiTestRootDirPathProperty = System.getProperty("gui.tests.root.dir.path"); - if (isNotEmpty(guiTestRootDirPathProperty)) { - File rootDirPath = new File(guiTestRootDirPathProperty); - if (rootDirPath.isDirectory()) { - return rootDirPath; - } - } - String homeDirPath = toSystemDependentName(PathManager.getHomePath()); - assertThat(homeDirPath).isNotEmpty(); - File rootDirPath = new File(homeDirPath, "gui-tests"); - ensureExists(rootDirPath); - return rootDirPath; - } - - @NotNull - private static File getConfigDirPath() throws IOException { - File dirPath = new File(getGuiTestRootDirPath(), "config"); - ensureExists(dirPath); - return dirPath; - } - - private static void recreateDirectory(@NotNull File path) throws IOException { - delete(path); - ensureExists(path); - } - - private IdeTestApplication() throws Exception { - String[] args = EMPTY_STRING_ARRAY; - - LOG.assertTrue(ourInstance == null, "Only one instance allowed."); - ourInstance = this; - - pluginManagerStart(args); - - myIdeClassLoader = createClassLoader(); - Thread.currentThread().setContextClassLoader(myIdeClassLoader); - - WindowsCommandLineProcessor.ourMirrorClass = Class.forName(WindowsCommandLineProcessor.class.getName(), true, myIdeClassLoader); - - Class classUtilCoreClass = Class.forName("com.intellij.ide.ClassUtilCore", true, myIdeClassLoader); - method("clearJarURLCache").in(classUtilCoreClass).invoke(); - - Class pluginManagerClass = Class.forName("com.intellij.ide.plugins.PluginManager", true, myIdeClassLoader); - method("start").withParameterTypes(String.class, String.class, String[].class) - .in(pluginManagerClass) - .invoke("com.intellij.idea.MainImpl", "start", args); - } - - // This method replaces BootstrapClassLoaderUtil.initClassLoader. The reason behind it is that when running UI tests the ClassLoader - // containing the URLs for the plugin jars is loaded by a different ClassLoader and it gets ignored. The result is test failing because - // classes like AndroidPlugin cannot be found. - @NotNull - private static ClassLoader createClassLoader() throws MalformedURLException { - Collection classpath = new LinkedHashSet<>(); - addIdeaLibraries(classpath); - addAdditionalClassPath(classpath); - - UrlClassLoader.Builder builder = UrlClassLoader.build() - .urls(filterClassPath(new ArrayList<>(classpath))) - //.parent(IdeTestApplication.class.getClassLoader()) - .allowLock(false) - .usePersistentClasspathIndexForLocalClassDirectories(); - if (SystemProperties.getBooleanProperty(PROPERTY_ALLOW_BOOTSTRAP_RESOURCES, true)) { - builder.allowBootstrapResources(); - } - - return builder.get(); - } - - private static void addIdeaLibraries(@NotNull Collection classpath) throws MalformedURLException { - Class aClass = BootstrapClassLoaderUtil.class; - String selfRoot = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class"); - assertNotNull(selfRoot); - - URL selfRootUrl = new File(selfRoot).getAbsoluteFile().toURI().toURL(); - classpath.add(selfRootUrl); - - File libFolder = new File(PathManager.getLibPath()); - addLibraries(classpath, libFolder, selfRootUrl); - addLibraries(classpath, new File(libFolder, "ext"), selfRootUrl); - addLibraries(classpath, new File(libFolder, "ant/lib"), selfRootUrl); - } - - private static void addLibraries(@NotNull Collection classPath, @NotNull File fromDir, @NotNull URL selfRootUrl) - throws MalformedURLException { - for (File file : notNullize(fromDir.listFiles())) { - if (isJarOrZip(file)) { - URL url = file.toURI().toURL(); - if (!selfRootUrl.equals(url)) { - classPath.add(url); - } - } - } - } - - private static void addAdditionalClassPath(@NotNull Collection classpath) throws MalformedURLException { - StringTokenizer tokenizer = new StringTokenizer(System.getProperty(PROPERTY_ADDITIONAL_CLASSPATH, ""), File.pathSeparator, false); - while (tokenizer.hasMoreTokens()) { - String pathItem = tokenizer.nextToken(); - classpath.add(new File(pathItem).toURI().toURL()); - } - } - - private static List filterClassPath(@NotNull List classpath) { - String ignoreProperty = System.getProperty(PROPERTY_IGNORE_CLASSPATH); - if (ignoreProperty != null) { - Pattern pattern = Pattern.compile(ignoreProperty); - for (Iterator i = classpath.iterator(); i.hasNext(); ) { - String url = i.next().toExternalForm(); - if (pattern.matcher(url).matches()) { - i.remove(); - } - } - } - return classpath; - } - - private static void pluginManagerStart(@NotNull String[] args) { - // Duplicates what PluginManager#start does. - Main.setFlags(args); - } - - public void dispose() { - disposeInstance(); - } - - public static synchronized void disposeInstance() { - if (!isLoaded()) { - return; - } - - IdeEventQueue.getInstance().flushQueue(); - final Application application = ApplicationManager.getApplication(); - ((ApplicationImpl)application).exit(true, true, false); - - ourInstance = null; - } - - public static synchronized boolean isLoaded() { - return ourInstance != null; - } - -} diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestRunner.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestRunner.kt index 78c7af7e45a1..7b207c485c67 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestRunner.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestRunner.kt @@ -5,7 +5,7 @@ import com.intellij.testGuiFramework.framework.GuiTestRunner import com.intellij.testGuiFramework.framework.GuiTestRunnerInterface import com.intellij.testGuiFramework.launcher.GuiTestLocalLauncher import com.intellij.testGuiFramework.launcher.ide.Ide -import com.intellij.testGuiFramework.remote.IdeProcessControlManager +import com.intellij.testGuiFramework.remote.IdeControl import com.intellij.testGuiFramework.remote.server.JUnitServerHolder import org.junit.internal.runners.model.EachTestNotifier import org.junit.runners.model.FrameworkMethod @@ -13,21 +13,19 @@ import org.junit.runners.model.FrameworkMethod class GuiDTTestRunner internal constructor(runner: GuiTestRunnerInterface) : GuiTestRunner(runner) { + private val LOGGER = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.dtrace.GuiDTTestRunner")!! + private val testClassNames: List = runner.getTestClassesNames() + private var currentClass = 0 + private var additionalJvmOptions: List>? = null - companion object { - private val LOGGER = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.dtrace.GuiDTTestRunner")!! - private var currentClass = 0 - var additionalJvmOptions: Array>? = null - } - - override fun runIde(port: Int, ide: Ide, additionalJvmOptions: Array>) { - if (GuiDTTestRunner.additionalJvmOptions == null) - GuiDTTestRunner.additionalJvmOptions = additionalJvmOptions + override fun runIde(ide: Ide, additionalJvmOptions: List>) { + if (this.additionalJvmOptions == null) + this.additionalJvmOptions = additionalJvmOptions if (currentClass < testClassNames.size) { LOGGER.info("" + currentClass + ". running test " + testClassNames[currentClass]) - GuiTestLocalLauncher.runIdeWithDTraceLocally(port = port, + GuiTestLocalLauncher.runIdeWithDTraceLocally(port = JUnitServerHolder.getServer().getPort(), ide = ide, testClassName = testClassNames[currentClass], additionalJvmOptions = additionalJvmOptions) @@ -35,34 +33,23 @@ class GuiDTTestRunner internal constructor(runner: GuiTestRunnerInterface) : Gui } fun finishTest(method: FrameworkMethod) { - - val server = JUnitServerHolder.getServer() - - ++currentClass - if (currentClass < testClassNames.size) { - IdeProcessControlManager.killIdeProcess() - server.stopServer() - - runIde(port = server.getPort(), ide = getIdeFromMethod(method), additionalJvmOptions = GuiDTTestRunner.additionalJvmOptions!!) - server.start() + if (++currentClass < testClassNames.size) { + IdeControl.restartIde(getIdeFromMethod(method), additionalJvmOptions!!, ::runIde) } else - stopServerAndKillIde(server) + IdeControl.closeIde() } - override fun processTestFinished(eachNotifier: EachTestNotifier): Boolean { - val inputStream = IdeProcessControlManager.getInputStream() - - val server = JUnitServerHolder.getServer() - closeIde(server) + override fun processTestFinished(eachNotifier: EachTestNotifier) { + val inputStream = IdeControl.getInputStream() + IdeControl.closeIde() try { val testInstance = Class.forName(testClassNames[currentClass]).newInstance() as GuiDTTestCase GuiDTTestCase::checkDtraceLog.invoke(testInstance, inputStream) } catch (e: AssertionError) { - eachNotifier.addFailure(e) + eachNotifier.addFailure(e); } - eachNotifier.fireTestFinished() - return false + eachNotifier.fireTestFinished(); } } \ No newline at end of file diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestSuiteRunner.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestSuiteRunner.kt index c91cd6102d6f..749d1ea88713 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestSuiteRunner.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/dtrace/GuiDTTestSuiteRunner.kt @@ -5,23 +5,22 @@ import com.intellij.testGuiFramework.framework.GuiTestLocalRunner import com.intellij.testGuiFramework.framework.GuiTestSuiteRunner import com.intellij.testGuiFramework.launcher.GuiTestLocalLauncher import com.intellij.testGuiFramework.launcher.ide.Ide -import com.intellij.testGuiFramework.remote.IdeProcessControlManager +import com.intellij.testGuiFramework.remote.IdeControl import org.apache.log4j.Logger import org.junit.runners.model.RunnerBuilder class GuiDTTestSuiteRunner(suiteClass: Class<*>, builder: RunnerBuilder) : GuiTestSuiteRunner(suiteClass, builder) { - companion object { - private val LOG: Logger = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.dtrace.GuiDTTestSuiteRunner")!! - } + private val LOG: Logger = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.dtrace.GuiDTTestSuiteRunner")!! override fun firstStart() { if (myFirstStartClassName == UNDEFINED_FIRST_CLASS) return - GuiDTTestSuiteRunner.LOG.info("IDE is configuring for the first time...") + LOG.info("IDE is configuring for the first time...") GuiTestLocalLauncher.firstStartIdeLocally(myIde, myFirstStartClassName) isFirstStart = false } override fun createGuiTestLocalRunner(testClass:Class<*>, suiteClass:Class<*>, myIde: Ide): GuiTestLocalRunner { + IdeControl.closeIde() return GuiDTTestLocalRunner(testClass, suiteClass, myIde) } diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCase.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCase.kt index 7e943ab6a57a..c90ec0a7716d 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCase.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCase.kt @@ -9,7 +9,7 @@ import com.intellij.testGuiFramework.fixtures.extended.RowFixture import com.intellij.testGuiFramework.fixtures.newProjectWizard.NewProjectWizardFixture import com.intellij.testGuiFramework.framework.GuiTestLocalRunner import com.intellij.testGuiFramework.framework.GuiTestUtil -import com.intellij.testGuiFramework.framework.IdeTestApplication.getTestScreenshotDirPath +import com.intellij.testGuiFramework.framework.GuiTestPaths.testScreenshotDirPath import com.intellij.testGuiFramework.framework.Timeouts import com.intellij.testGuiFramework.framework.toPrintable import com.intellij.testGuiFramework.impl.GuiTestUtilKt.typeMatcher @@ -75,6 +75,11 @@ open class GuiTestCase { @JvmField val guiTestRule = GuiTestRule(projectsFolder.root.canonicalFile) + @get:Rule + val testRootPath: TemporaryFolder by lazy { + TemporaryFolder() + } + val settingsTitle: String = if (isMac()) "Preferences" else "Settings" // val defaultSettingsTitle: String = if (isMac()) "Default Preferences" else "Default Settings" val defaultSettingsTitle: String = if (isMac()) "Preferences for New Projects" else "Settings for New Projects" @@ -265,8 +270,6 @@ open class GuiTestCase { func(this.editor()) } - fun JDialogFixture.editor(func: EditorFixture.() -> Unit) = func(this.editor) - //*********COMMON FUNCTIONS WITHOUT CONTEXT /** * Type text by symbol with a constant delay. Generate system key events, so entered text will aply to a focused component. @@ -306,7 +309,7 @@ open class GuiTestCase { fun screenshot(component: Component, screenshotName: String) { val extension = "${getScaleSuffix()}.jpg" - val pathWithTestFolder = getTestScreenshotDirPath().path + slash + this.guiTestRule.getTestName() + val pathWithTestFolder = testScreenshotDirPath.path + slash + this.guiTestRule.getTestName() val fileWithTestFolder = File(pathWithTestFolder) FileUtil.ensureExists(fileWithTestFolder) var screenshotFilePath = File(fileWithTestFolder, screenshotName + extension) diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestRule.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestRule.kt index df4a1838f67b..02b07f665486 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestRule.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestRule.kt @@ -20,7 +20,7 @@ import com.intellij.testGuiFramework.fixtures.IdeFrameFixture import com.intellij.testGuiFramework.fixtures.WelcomeFrameFixture import com.intellij.testGuiFramework.fixtures.newProjectWizard.NewProjectWizardFixture import com.intellij.testGuiFramework.framework.GuiTestUtil -import com.intellij.testGuiFramework.framework.IdeTestApplication.getFailedTestVideoDirPath +import com.intellij.testGuiFramework.framework.GuiTestPaths.failedTestVideoDirPath import com.intellij.testGuiFramework.framework.Timeouts import com.intellij.testGuiFramework.impl.GuiTestUtilKt.computeOnEdt import com.intellij.testGuiFramework.impl.GuiTestUtilKt.runOnEdt @@ -100,7 +100,7 @@ class GuiTestRule(private val projectsFolder: File) : TestRule { return Class.forName("org.jetbrains.intellij.deps.screenrecorder.ScreenRecorderRule", true, classLoader) .constructors .singleOrNull { it.parameterCount == 3 } - ?.newInstance(Duration.ofMinutes(videoDuration), getFailedTestVideoDirPath().absolutePath, testsToRecord) as TestRule? + ?.newInstance(Duration.ofMinutes(videoDuration), failedTestVideoDirPath.absolutePath, testsToRecord) as TestRule? } catch (e: Exception) { return null diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotOnFailure.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotOnFailure.kt index 472971d9a718..0bcee05ba423 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotOnFailure.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotOnFailure.kt @@ -16,7 +16,7 @@ package com.intellij.testGuiFramework.impl import com.intellij.openapi.diagnostic.Logger -import com.intellij.testGuiFramework.framework.IdeTestApplication +import com.intellij.testGuiFramework.framework.GuiTestPaths import com.intellij.testGuiFramework.util.ScreenshotTaker import org.fest.swing.core.BasicComponentPrinter import org.fest.swing.exception.ComponentLookupException @@ -52,9 +52,9 @@ class ScreenshotOnFailure: TestWatcher() { } private fun getOrCreateScreenshotFile(screenshotName: String): File { - var file = File(IdeTestApplication.getFailedTestScreenshotDirPath(), "$screenshotName.jpg") + var file = File(GuiTestPaths.failedTestScreenshotDir, "$screenshotName.jpg") if (file.exists()) - file = File(IdeTestApplication.getFailedTestScreenshotDirPath(), "$screenshotName.${getDateAndTime()}.jpg") + file = File(GuiTestPaths.failedTestScreenshotDir, "$screenshotName.${getDateAndTime()}.jpg") file.delete() return file } diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotsDuringTest.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotsDuringTest.kt index d9ae91068292..c864dd6ad740 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotsDuringTest.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ScreenshotsDuringTest.kt @@ -2,7 +2,7 @@ package com.intellij.testGuiFramework.impl import com.intellij.openapi.util.io.FileUtil.ensureExists import com.intellij.openapi.util.io.FileUtilRt -import com.intellij.testGuiFramework.framework.IdeTestApplication +import com.intellij.testGuiFramework.framework.GuiTestPaths import com.intellij.testGuiFramework.util.ScreenshotTaker import org.junit.rules.TestWatcher import org.junit.runner.Description @@ -28,7 +28,7 @@ class ScreenshotsDuringTest @JvmOverloads constructor(private val myPeriod: Int override fun starting(description: Description) { val folderName = description.testClass.simpleName + "-" + description.methodName try { - myFolder = File(IdeTestApplication.getFailedTestScreenshotDirPath(), folderName) + myFolder = File(GuiTestPaths.failedTestScreenshotDir, folderName) ensureExists(myFolder!!) } catch (e: IOException) { diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GradleLauncher.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GradleLauncher.kt new file mode 100644 index 000000000000..795478af8965 --- /dev/null +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GradleLauncher.kt @@ -0,0 +1,74 @@ +package com.intellij.testGuiFramework.launcher + +import com.intellij.testGuiFramework.impl.GuiTestStarter.Companion.COMMAND_NAME +import com.intellij.testGuiFramework.launcher.system.SystemInfo +import com.intellij.testGuiFramework.remote.IdeControl +import org.apache.log4j.Logger +import java.io.BufferedReader +import java.io.InputStreamReader +import kotlin.concurrent.thread + + +object GradleLauncher { + + val LOG: Logger = Logger.getLogger(GradleLauncher::class.java) + + fun runIde(port: Int) { + val composedArgs = composeCommandLineArgs(port) + LOG.info("Composed command line to run IDE: $composedArgs") + val process = ProcessBuilder().inheritIO().command(composedArgs).start() + IdeControl.submitIdeProcess(process) + val stdInput = BufferedReader(InputStreamReader(process.inputStream)) + val stdError = BufferedReader(InputStreamReader(process.errorStream)) + thread(start = true, name = "processStdIn") { processStdIn(stdInput) } + thread(start = true, name = "processStdErr") { processStdErr(stdError) } + } + + private val gradleWrapper: String by lazy { + return@lazy if (SystemInfo.isWin()) "gradlew.bat" else "./gradlew" + } + + private fun composeCommandLineArgs(port: Int): MutableList { + val result = mutableListOf() + return with(result) { + add("$gradleWrapper") + add("runIde") + if (GuiTestOptions.isDebug) add("--debug-jvm") + if (GuiTestOptions.isPassPrivacyPolicy) add("-Djb.privacy.policy.text=\"\"") + if (GuiTestOptions.isPassDataSharing) add("-Djb.consents.confirmation.enabled=false") + addIdeaAndJbProperties() + add("-Dexec.args=$COMMAND_NAME,port=$port") + this + } + } + + private fun MutableList.addIdeaAndJbProperties() { + System.getProperties().filter { + val s = it.key as String + s.startsWith("idea") || s.startsWith("jb") + }.forEach{ + this.add("-D${it.key}=${it.value}") + } + } + + private fun String.quoteValueIfHasSpaces(): String { + return if (Regex("\\s").containsMatchIn(this)) return "\"$this\"" else this + } + + private fun processStdIn(stdInput: BufferedReader) { + var s: String? = stdInput.readLine() + while (s != null) { + LOG.info("[IDE output]: $s") + s = stdInput.readLine() + } + } + + private fun processStdErr(stdInput: BufferedReader) { + var s: String? = stdInput.readLine() + while (s != null) { + LOG.warn("[IDE warn]: $s") + s = stdInput.readLine() + } + } + +} \ No newline at end of file diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestLocalLauncher.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestLocalLauncher.kt index ead7e96d5546..c9f8f0bd71d7 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestLocalLauncher.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestLocalLauncher.kt @@ -24,7 +24,7 @@ import com.intellij.testGuiFramework.launcher.classpath.PathUtils import com.intellij.testGuiFramework.launcher.ide.CommunityIde import com.intellij.testGuiFramework.launcher.ide.Ide import com.intellij.testGuiFramework.launcher.system.SystemInfo -import com.intellij.testGuiFramework.remote.IdeProcessControlManager +import com.intellij.testGuiFramework.remote.IdeControl import org.apache.log4j.Logger import org.jetbrains.jps.model.JpsElementFactory import org.jetbrains.jps.model.JpsProject @@ -80,7 +80,7 @@ object GuiTestLocalLauncher { fun runIdeLocally(ide: Ide = Ide(CommunityIde(), 0, 0), port: Int = 0, testClassNames: List = emptyList(), - additionalJvmOptions: Array> = emptyArray()) { + additionalJvmOptions: List> = emptyList()) { val args = createArgs(ide = ide, port = port, testClassNames = testClassNames, additionalJvmOptions = additionalJvmOptions) return startIde(ide = ide, ideaStartTest = ProcessBuilder().inheritIO().command(args)) } @@ -88,7 +88,7 @@ object GuiTestLocalLauncher { fun runIdeWithDTraceLocally(ide: Ide = Ide(CommunityIde(), 0, 0), port: Int = 0, testClassName: String = "", - additionalJvmOptions: Array> = emptyArray()) { + additionalJvmOptions: List> = emptyList()) { val args = createArgs(ide = ide, port = port, testClassNames = listOf(testClassName), additionalJvmOptions = additionalJvmOptions) @@ -148,32 +148,32 @@ object GuiTestLocalLauncher { val conditionalTimeout = if (GuiTestOptions.isDebug) 0 else timeOut val startLatch = CountDownLatch(1) thread(start = true, name = "IdeaThread") { - IdeProcessControlManager.submitIdeProcess(ideaStartTest.start()) + IdeControl.submitIdeProcess(ideaStartTest.start()) startLatch.countDown() } if (needToWait) { startLatch.await() if (conditionalTimeout != 0L) - IdeProcessControlManager.waitForCurrentProcess(conditionalTimeout, timeOutUnit) + IdeControl.waitForCurrentProcess(conditionalTimeout, timeOutUnit) else - IdeProcessControlManager.waitForCurrentProcess() + IdeControl.waitForCurrentProcess() try { - if (IdeProcessControlManager.exitValue() == 0) { - println("${ide.ideType} process completed successfully") - LOG.info("${ide.ideType} process completed successfully") + if (IdeControl.exitValue() == 0) { + println("${ide.ideType} processStdIn completed successfully") + LOG.info("${ide.ideType} processStdIn completed successfully") } else { - System.err.println("${ide.ideType} process execution error:") - val collectedError = BufferedReader(InputStreamReader(IdeProcessControlManager.getErrorStream())).lines().collect( + System.err.println("${ide.ideType} processStdIn execution error:") + val collectedError = BufferedReader(InputStreamReader(IdeControl.getErrorStream())).lines().collect( Collectors.joining("\n")) System.err.println(collectedError) - LOG.error("${ide.ideType} process execution error:") + LOG.error("${ide.ideType} processStdIn execution error:") LOG.error(collectedError) fail("Starting ${ide.ideType} failed.") } } catch (e: IllegalThreadStateException) { - IdeProcessControlManager.killIdeProcess() + IdeControl.closeIde() throw e } } @@ -192,7 +192,7 @@ object GuiTestLocalLauncher { mainClass: String = "com.intellij.idea.Main", port: Int = 0, testClassNames: List, - additionalJvmOptions: Array> = emptyArray()): + additionalJvmOptions: List> = emptyList()): List = createArgsBase(ide = ide, mainClass = mainClass, commandName = GuiTestStarter.COMMAND_NAME, @@ -221,7 +221,7 @@ object GuiTestLocalLauncher { firstStartClassName: String = "undefined", port: Int, testClassNames: List, - additionalJvmOptions: Array> = emptyArray()): List { + additionalJvmOptions: List> = emptyList()): List { val customVmOptions = getCustomPassedOptions() var resultingArgs = listOf() .plus(getCurrentJavaExec()) diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestOptions.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestOptions.kt index fa9592a0f9d8..a17be9471c37 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestOptions.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/launcher/GuiTestOptions.kt @@ -28,7 +28,12 @@ object GuiTestOptions { val configPath: String by lazy { getSystemProperty("idea.config.path", configDefaultPath) } val systemPath: String by lazy { getSystemProperty("idea.system.path", systemDefaultPath) } + val guiTestRootDirPath: String? by lazy { System.getProperty("idea.gui.tests.root.dir.path", null) } + val isGradleRunner: Boolean by lazy { getSystemProperty("idea.gui.tests.gradle.runner", false) } + val isDebug: Boolean by lazy { getSystemProperty("idea.debug.mode", false) } + val isPassPrivacyPolicy: Boolean by lazy { getSystemProperty("idea.pass.privacy.policy", true) } + val isPassDataSharing: Boolean by lazy { getSystemProperty("idea.pass.data.sharing", true) } val suspendDebug: String by lazy { if (isDebug) "y" else "n" } val isInternal: Boolean by lazy { getSystemProperty("idea.is.internal", true) } val useAppleScreenMenuBar: Boolean by lazy { getSystemProperty("apple.laf.useScreenMenuBar", false) } diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/IdeControl.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/IdeControl.kt new file mode 100644 index 000000000000..8b494f42f2f4 --- /dev/null +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/IdeControl.kt @@ -0,0 +1,103 @@ +// 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. +package com.intellij.testGuiFramework.remote + +import com.intellij.testGuiFramework.launcher.ide.Ide +import com.intellij.testGuiFramework.remote.server.JUnitServer +import com.intellij.testGuiFramework.remote.server.JUnitServerHolder +import com.intellij.testGuiFramework.remote.transport.JUnitTestContainer +import com.intellij.testGuiFramework.remote.transport.MessageType +import com.intellij.testGuiFramework.remote.transport.TransportMessage +import org.apache.log4j.Logger +import org.junit.runners.model.FrameworkMethod +import java.io.InputStream +import java.util.concurrent.TimeUnit + +typealias RunIdeFun = (Ide, List>) -> Unit + +object IdeControl { + + + private val LOG = Logger.getLogger(this.javaClass) + + @Volatile + private var currentIdeProcess: Process? = null + + /** + * returns current Ide processStdIn + */ + private fun getIdeProcess(): Process { + return currentIdeProcess ?: throw Exception("Current IDE processStdIn is not initialised or already been killed") + } + + private val myServer: JUnitServer + get() = JUnitServerHolder.getServer() + + /** + * adds a new IDE processStdIn to control + */ + fun submitIdeProcess(ideProcess: Process) { + currentIdeProcess = ideProcess + } + + fun waitForCurrentProcess(): Int = getIdeProcess().waitFor() + + fun waitForCurrentProcess(timeout: Long, timeUnit: TimeUnit): Boolean = getIdeProcess().waitFor(timeout, timeUnit) + + fun exitValue(): Int = getIdeProcess().exitValue() + + fun getErrorStream(): InputStream = getIdeProcess().errorStream + + fun getInputStream(): InputStream = getIdeProcess().inputStream + + fun closeIde() { + sendCloseIdeSignal() + IdeControl.killIdeProcess() + myServer.stopServer() + } + + fun restartIde(ide: Ide, additionalJvmOptions: List> = emptyList(), runIde: RunIdeFun) { + IdeControl.closeIde() + startIdeAndServer(ide, additionalJvmOptions, runIde) + } + + /** + * @additionalJvmOptions - an array of key-value pairs written without -D, for example: {@code arrayOf(Pair("idea.debug.mode", "true")) + * By default set as an empty array – no additional JVM options + */ + + fun runTest(method: FrameworkMethod, + testName: String) { + val jUnitTestContainer = JUnitTestContainer(method.declaringClass, testName) + myServer.send(TransportMessage(MessageType.RUN_TEST, jUnitTestContainer)) + } + + fun ensureIdeIsRunning(ide: Ide, additionalJvmOptions: List>, runIde: RunIdeFun) { + if (!myServer.isConnected()) { + LOG.info("Starting IDE ($ide) with port for running tests: ${myServer.getPort()}") + startIdeAndServer(ide = ide, additionalJvmOptions = emptyList(), runIde = runIde) + } + } + + fun resumeTest(method: FrameworkMethod, resumeTestLabel: String) { + val jUnitTestContainer = JUnitTestContainer(method.declaringClass, method.name, additionalInfo = resumeTestLabel) + myServer.send(TransportMessage(MessageType.RESUME_TEST, jUnitTestContainer)) + } + + private fun sendCloseIdeSignal() { + myServer.send(TransportMessage(MessageType.CLOSE_IDE)) + IdeControl.waitForCurrentProcess(2, TimeUnit.MINUTES) + } + + private fun startIdeAndServer(ide: Ide, additionalJvmOptions: List> = emptyList(), runIde: RunIdeFun) { + runIde(ide, additionalJvmOptions) + myServer.start() + } + + /** + * kills IDE java processStdIn if the pipeline for sockets has been crashed or IDE doesn't respond + */ + private fun killIdeProcess() { + currentIdeProcess?.destroyForcibly()?.waitFor(2, TimeUnit.MINUTES) + ?: throw Exception("Current IDE processStdIn is not initialised or already been killed") + } +} diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/transport/RestartIdeAndResumeContainer.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/transport/RestartIdeAndResumeContainer.kt index 01d0d4dc0399..9980016bc9a4 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/transport/RestartIdeAndResumeContainer.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/remote/transport/RestartIdeAndResumeContainer.kt @@ -5,5 +5,5 @@ import java.io.Serializable enum class RestartIdeCause { PLUGIN_INSTALLED, RUN_WITH_SYSTEM_PROPERTIES } open class RestartIdeAndResumeContainer(val restartIdeCause: RestartIdeCause, val dataObject: Any? = null) : Serializable -data class RunWithSystemPropertiesContainer(val systemProperties: Array>) : RestartIdeAndResumeContainer( +data class RunWithSystemPropertiesContainer(val systemProperties: List>) : RestartIdeAndResumeContainer( RestartIdeCause.RUN_WITH_SYSTEM_PROPERTIES, systemProperties) \ No newline at end of file diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/testCases/SystemPropertiesTestCase.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/testCases/SystemPropertiesTestCase.kt index 0cb6322a0f01..08617adb5472 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/testCases/SystemPropertiesTestCase.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/testCases/SystemPropertiesTestCase.kt @@ -22,8 +22,8 @@ open class SystemPropertiesTestCase : GuiTestCase() { * @setUpBlock block which is executed before test running. Using of ideFrame in this block is deprecated. Used for files manipulating or * setting up environment. */ - fun restartIdeWithSystemProperties(systemProperties: Array>, - setUpBlock: (Array>) -> Unit = { }) { + fun restartIdeWithSystemProperties(systemProperties: List>, + setUpBlock: (List>) -> Unit = { }) { if (guiTestRule.getTestName() == GuiTestOptions.resumeTestName && GuiTestOptions.resumeInfo == SYSTEM_PROPERTIES) return setUpBlock(systemProperties) GuiTestThread.client?.send(createTransportMessage(systemProperties)) ?: throw Exception( @@ -31,7 +31,7 @@ open class SystemPropertiesTestCase : GuiTestCase() { GuiTestUtilKt.waitUntil("IDE will be closed", timeout = Timeouts.minutes02) { false } } - private fun createTransportMessage(systemProperties: Array>) = + private fun createTransportMessage(systemProperties: List>) = TransportMessage(MessageType.RESTART_IDE_AND_RESUME, RunWithSystemPropertiesContainer(systemProperties)) companion object { diff --git a/platform/testGuiFramework/testSrc/com/intellij/testGuiFramework/tests/community/PluginInstallTest.kt b/platform/testGuiFramework/testSrc/com/intellij/testGuiFramework/tests/community/PluginInstallTest.kt deleted file mode 100644 index e5a0c6bfcd62..000000000000 --- a/platform/testGuiFramework/testSrc/com/intellij/testGuiFramework/tests/community/PluginInstallTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2000-2017 JetBrains s.r.o. - * - * 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. - */ -package com.intellij.testGuiFramework.tests.community - -import com.intellij.testGuiFramework.testCases.PluginTestCase -import org.junit.Test - -class PluginInstallTest : PluginTestCase() { - - @Test - fun testPluginInstall() { - installPluginAndRestart { installPlugins("Dart") } - checkPluginsFunctionality("Dart") - } - - @Test - fun testPluginByPath() { - installPluginAndRestart { installPluginFromDisk("/Users/jetbrains/Downloads/Dart-172.4155.35.zip", "Dart") } - checkPluginsFunctionality("Dart") - } - - private fun checkPluginsFunctionality(vararg pluginNames: String) { - pluginNames.forEach { println("Plugin '$it' is OK") } - } -} \ No newline at end of file diff --git a/platform/testGuiFramework/testSrc/com/intellij/testGuiFramework/tests/samples/ProjectSampleTest.kt b/platform/testGuiFramework/testSrc/com/intellij/testGuiFramework/tests/samples/ProjectSampleTest.kt deleted file mode 100644 index e69de29bb2d1..000000000000