mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gui-test] reorganize GuiTestRunner and Launcher hierarchy
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
+57
-90
@@ -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<Pair<String, String>> = 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<Pair<String, String>> = 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<Pair<String, String>> = 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")
|
||||
}
|
||||
|
||||
+9
@@ -25,4 +25,13 @@ fun getIdeFromAnnotation(testClass: Class<*>): Ide {
|
||||
val annotation = testClass.annotations.filterIsInstance<RunWithIde>().firstOrNull()?.value
|
||||
val ideType = if (annotation != null) (annotation as KClass<out IdeType>).java.newInstance() else CommunityIde()
|
||||
return Ide(ideType, 0, 0)
|
||||
}
|
||||
|
||||
fun getSystemPropertiesFromAnnotation(testClass: Class<*>): List<Pair<String, String>>? {
|
||||
return testClass
|
||||
.annotations
|
||||
.filterIsInstance<SystemProperties>()
|
||||
.firstOrNull()
|
||||
?.keyValueArray
|
||||
?.map { val (key, value) = it.split("="); Pair(key, value) }
|
||||
}
|
||||
+2
-4
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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"
|
||||
|
||||
-220
@@ -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<URL> 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<URL> classpath) throws MalformedURLException {
|
||||
Class<BootstrapClassLoaderUtil> 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<URL> 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<URL> 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<URL> filterClassPath(@NotNull List<URL> classpath) {
|
||||
String ignoreProperty = System.getProperty(PROPERTY_IGNORE_CLASSPATH);
|
||||
if (ignoreProperty != null) {
|
||||
Pattern pattern = Pattern.compile(ignoreProperty);
|
||||
for (Iterator<URL> 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;
|
||||
}
|
||||
|
||||
}
|
||||
+17
-30
@@ -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<String> = runner.getTestClassesNames()
|
||||
private var currentClass = 0
|
||||
private var additionalJvmOptions: List<Pair<String, String>>? = null
|
||||
|
||||
companion object {
|
||||
private val LOGGER = org.apache.log4j.Logger.getLogger("#com.intellij.testGuiFramework.framework.dtrace.GuiDTTestRunner")!!
|
||||
private var currentClass = 0
|
||||
var additionalJvmOptions: Array<Pair<String, String>>? = null
|
||||
}
|
||||
|
||||
override fun runIde(port: Int, ide: Ide, additionalJvmOptions: Array<Pair<String, String>>) {
|
||||
if (GuiDTTestRunner.additionalJvmOptions == null)
|
||||
GuiDTTestRunner.additionalJvmOptions = additionalJvmOptions
|
||||
override fun runIde(ide: Ide, additionalJvmOptions: List<Pair<String, String>>) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
}
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+74
@@ -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<String> {
|
||||
val result = mutableListOf<String>()
|
||||
return with(result) {
|
||||
add("$gradleWrapper")
|
||||
add("runIde")
|
||||
if (GuiTestOptions.isDebug) add("--debug-jvm")
|
||||
if (GuiTestOptions.isPassPrivacyPolicy) add("-Djb.privacy.policy.text=\"<!--999.999-->\"")
|
||||
if (GuiTestOptions.isPassDataSharing) add("-Djb.consents.confirmation.enabled=false")
|
||||
addIdeaAndJbProperties()
|
||||
add("-Dexec.args=$COMMAND_NAME,port=$port")
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<String>.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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+15
-15
@@ -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<String> = emptyList(),
|
||||
additionalJvmOptions: Array<Pair<String, String>> = emptyArray()) {
|
||||
additionalJvmOptions: List<Pair<String, String>> = 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<Pair<String, String>> = emptyArray()) {
|
||||
additionalJvmOptions: List<Pair<String, String>> = 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<String>,
|
||||
additionalJvmOptions: Array<Pair<String, String>> = emptyArray()):
|
||||
additionalJvmOptions: List<Pair<String, String>> = emptyList()):
|
||||
List<String> = createArgsBase(ide = ide,
|
||||
mainClass = mainClass,
|
||||
commandName = GuiTestStarter.COMMAND_NAME,
|
||||
@@ -221,7 +221,7 @@ object GuiTestLocalLauncher {
|
||||
firstStartClassName: String = "undefined",
|
||||
port: Int,
|
||||
testClassNames: List<String>,
|
||||
additionalJvmOptions: Array<Pair<String, String>> = emptyArray()): List<String> {
|
||||
additionalJvmOptions: List<Pair<String, String>> = emptyList()): List<String> {
|
||||
val customVmOptions = getCustomPassedOptions()
|
||||
var resultingArgs = listOf<String>()
|
||||
.plus(getCurrentJavaExec())
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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<Pair<String, String>>) -> 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<Pair<String, String>> = 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<Pair<String, String>>, 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<Pair<String, String>> = 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")
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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<Pair<String, String>>) : RestartIdeAndResumeContainer(
|
||||
data class RunWithSystemPropertiesContainer(val systemProperties: List<Pair<String, String>>) : RestartIdeAndResumeContainer(
|
||||
RestartIdeCause.RUN_WITH_SYSTEM_PROPERTIES, systemProperties)
|
||||
+3
-3
@@ -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<Pair<String, String>>,
|
||||
setUpBlock: (Array<Pair<String, String>>) -> Unit = { }) {
|
||||
fun restartIdeWithSystemProperties(systemProperties: List<Pair<String, String>>,
|
||||
setUpBlock: (List<Pair<String, String>>) -> 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<Pair<String, String>>) =
|
||||
private fun createTransportMessage(systemProperties: List<Pair<String, String>>) =
|
||||
TransportMessage(MessageType.RESTART_IDE_AND_RESUME, RunWithSystemPropertiesContainer(systemProperties))
|
||||
|
||||
companion object {
|
||||
|
||||
-38
@@ -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") }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user