[gui-test] reorganize GuiTestRunner and Launcher hierarchy

This commit is contained in:
Sergey Karashevich
2018-10-01 10:44:30 +03:00
parent bf5c2069b4
commit 0dc3ca9d42
20 changed files with 352 additions and 419 deletions
@@ -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
}
}
@@ -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")
}
@@ -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) }
}
@@ -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()
}
}
}
@@ -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"
@@ -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;
}
}
@@ -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();
}
}
@@ -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
@@ -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,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) {
@@ -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()
}
}
}
@@ -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")
}
}
@@ -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)
@@ -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 {
@@ -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") }
}
}