Commit 7bce2115 authored by Sergey Karashevich's avatar Sergey Karashevich
Browse files

[gui-test] change project output path if needed; cleanup; fixes

Build script output could interfere with project output path if we starting GUI tests with GuiTestLocalLauncher. It builds a classpath from main IDE and testGuiFramework modules output paths, so we should be careful with it and change a project output if needed (see GuiTestLocalLauncher#changeProjectOutputIfNeeded()). To detect a using of a build script output we are looking at URL for GuiTestLocalLauncher to load this class. If it is not same to path calculated from project model we are assuming that output path from build script is used and changing the project's output on it.

Groovy build scripts (CompilationContextImpl.groovy, BuildOptions.grovy) are reverting to its initial states.
parent 2bad0eb9
Showing with 103 additions and 82 deletions
+103 -82
......@@ -33,13 +33,9 @@ import java.util.function.BiFunction
*/
target("default": "Run tests") {
String home = IdeaProjectLoader.guessHome(this)
String outputDir = "$home/out"
String outputDir = "$home/out/tests"
def compilationOptions = new BuildOptions()
// compilationOptions.useCompiledClassesFromProjectOutput = true
// compilationOptions.incrementalCompilation = true
// def context = CompilationContextImpl.create(home, home, outputDir, this)
GantBinding binding = (GantBinding) this.binding
binding.includeTool << JpsGantTool
......@@ -47,8 +43,5 @@ target("default": "Run tests") {
{ p, m -> outputDir } as BiFunction<JpsProject, BuildMessages, String>, compilationOptions)
def testingOptions = new TestingOptions()
// testingOptions.setTestGroups("GUI_TESTS")
// testingOptions.setTestPatterns("com.intellij.testGuiFramework.tests.community.*")
TestingTasks.create(context, testingOptions).runTests([], "testGuiFramework", null)
}
......@@ -28,16 +28,7 @@ class BuildOptions {
* invoked on a developer machine). Pass 'true' to this system property to skip compilation step and use compiled classes from the project output instead.
*/
public static final String USE_COMPILED_CLASSES_PROPERTY = "intellij.build.use.compiled.classes"
/**
* Pass 'true' to this system property when building to default project output. This case may be useful when classpath building by JpsModel
* needed to be same on a developer's machine and remote (e.g. on TeamCity)
*/
public static final String BUILD_TO_PROJECT_OUTPUT_DIRECTORY = "intellij.build.use.project.out.dir"
boolean useCompiledClassesFromProjectOutput = SystemProperties.getBooleanProperty(USE_COMPILED_CLASSES_PROPERTY, false)
boolean useProjectOutputDirToBuild = SystemProperties.getBooleanProperty(BUILD_TO_PROJECT_OUTPUT_DIRECTORY, false)
/**
* Specifies for which operating systems distributions should be built.
......
......@@ -140,23 +140,22 @@ class CompilationContextImpl implements CompilationContext {
def classesDirName = "classes"
def classesOutput = "$paths.buildOutputRoot/$classesDirName"
def outputProjectDir = getProjectOutputDirectory()
List<String> outputDirectoriesToKeep = ["log"]
if (options.pathToCompiledClassesArchive != null) {
def unpackOutputDir = (options.useProjectOutputDirToBuild ? outputProjectDir.absolutePath : classesOutput) as String
unpackCompiledClasses(messages, ant, unpackOutputDir, options)
outputDirectoriesToKeep.addAll([classesOutput, outputProjectDir.name])
unpackCompiledClasses(messages, ant, classesOutput, options)
outputDirectoriesToKeep.add(classesDirName)
}
if (options.incrementalCompilation) {
outputDirectoriesToKeep.add(dataDirName)
outputDirectoriesToKeep.add(classesDirName)
}
if (!options.useCompiledClassesFromProjectOutput && !options.useProjectOutputDirToBuild) {
if (!options.useCompiledClassesFromProjectOutput) {
projectBuilder.targetFolder = classesOutput
}
else {
if (!outputProjectDir.exists()) {
messages.error("$BuildOptions.USE_COMPILED_CLASSES_PROPERTY is enabled, but the project output directory $outputProjectDir.absolutePath doesn't exist")
def outputDir = getProjectOutputDirectory()
if (!outputDir.exists()) {
messages.error("$BuildOptions.USE_COMPILED_CLASSES_PROPERTY is enabled, but the project output directory $outputDir.absolutePath doesn't exist")
}
}
......
......@@ -17,6 +17,7 @@ package com.intellij.testGuiFramework.launcher
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.testGuiFramework.impl.GuiTestStarter
import com.intellij.testGuiFramework.launcher.classpath.ClassPathBuilder
import com.intellij.testGuiFramework.launcher.classpath.ClassPathBuilder.Companion.isWin
......@@ -24,6 +25,7 @@ import com.intellij.testGuiFramework.launcher.classpath.PathUtils
import com.intellij.testGuiFramework.launcher.ide.Ide
import com.intellij.testGuiFramework.launcher.ide.IdeType
import org.jetbrains.jps.model.JpsElementFactory
import org.jetbrains.jps.model.JpsProject
import org.jetbrains.jps.model.java.JpsJavaExtensionService
import org.jetbrains.jps.model.module.JpsModule
import org.jetbrains.jps.model.serialization.JpsModelSerializationDataService
......@@ -50,6 +52,9 @@ object GuiTestLocalLauncher {
var process: Process? = null
var cachedGuiTestFrameworkModule: JpsModule? = null
var cachedModulesList: MutableList<JpsModule> = ArrayList<JpsModule>()
fun killProcessIfPossible() {
try {
if (process?.isAlive ?: false) process!!.destroyForcibly()
......@@ -102,7 +107,8 @@ object GuiTestLocalLauncher {
if (process!!.exitValue() != 1) {
println("${ide.ideType} process completed successfully")
LOG.info("${ide.ideType} process completed successfully")
} else {
}
else {
System.err.println("${ide.ideType} process execution error:")
val collectedError = BufferedReader(InputStreamReader(process!!.errorStream)).lines().collect(Collectors.joining("\n"))
System.err.println(collectedError)
......@@ -115,22 +121,22 @@ object GuiTestLocalLauncher {
}
private fun startIdeAndWait(ide: Ide, args: List<String>): Unit
= startIde(ide = ide, needToWait = true, args = args)
= startIde(ide = ide, needToWait = true, args = args)
private fun createArgs(ide: Ide, mainClass: String = "com.intellij.idea.Main", port: Int = 0): List<String>
= createArgsBase(ide, mainClass, GuiTestStarter.COMMAND_NAME, port)
= createArgsBase(ide, mainClass, GuiTestStarter.COMMAND_NAME, port)
private fun createArgsForFirstStart(ide: Ide, port: Int = 0): List<String>
= createArgsBase(ide, "com.intellij.testGuiFramework.impl.FirstStarterKt", null, port)
= createArgsBase(ide, "com.intellij.testGuiFramework.impl.FirstStarterKt", null, port)
private fun createArgsBase(ide: Ide, mainClass: String, commandName: String?, port: Int): List<String> {
var resultingArgs = listOf<String>()
.plus(getCurrentJavaExec())
.plus(getDefaultVmOptions(ide))
.plus("-classpath")
.plus(getOsSpecificClasspath(ide.ideType.mainModule))
.plus(mainClass)
.plus(getCurrentJavaExec())
.plus(getDefaultVmOptions(ide))
.plus("-classpath")
.plus(getOsSpecificClasspath(ide.ideType.mainModule))
.plus(mainClass)
if (commandName != null) resultingArgs = resultingArgs.plus(commandName)
if (port != 0) resultingArgs = resultingArgs.plus("port=$port")
......@@ -144,21 +150,20 @@ object GuiTestLocalLauncher {
val classpath = PathUtils(path).makeClassPathBuilder().build(emptyList())
val resultingArgs = listOf<String>()
.plus(getCurrentJavaExec())
.plus(getDefaultVmOptions(ide))
.plus("-classpath")
.plus(classpath)
.plus(com.intellij.testGuiFramework.impl.FirstStarter::class.qualifiedName!! + "Kt")
.plus(getCurrentJavaExec())
.plus(getDefaultVmOptions(ide))
.plus("-classpath")
.plus(classpath)
.plus(com.intellij.testGuiFramework.impl.FirstStarter::class.qualifiedName!! + "Kt")
return resultingArgs
}
private fun createArgsByPath(path: String, port: Int = 0): List<String> {
var resultingArgs = listOf<String>()
.plus("open")
.plus(path) //path to exec
.plus("--args")
.plus(GuiTestStarter.COMMAND_NAME)
.plus("-Didea.additional.classpath=/Users/jetbrains/IdeaProjects/idea-ultimate/out/classes/test/testGuiFramework/")
.plus("open")
.plus(path) //path to exec
.plus("--args")
.plus(GuiTestStarter.COMMAND_NAME)
if (port != 0) resultingArgs = resultingArgs.plus("port=$port")
LOG.info("Running with args: ${resultingArgs.joinToString(" ")}")
return resultingArgs
......@@ -166,36 +171,36 @@ object GuiTestLocalLauncher {
private fun getDefaultVmOptions(ide: Ide,
configPath: String = "./config",
systemPath: String = "./system",
bootClasspath: String = "./out/classes/production/boot",
configPath: String = "../config",
systemPath: String = "../system",
bootClasspath: String = "../out/classes/production/boot",
encoding: String = "UTF-8",
isInternal: Boolean = true,
useMenuScreenBar: Boolean = true,
debugPort: Int = 5009,
suspendDebug: String = "n"): List<String> =
listOf<String>()
.plus("-ea")
.plus("-Xbootclasspath/p:$bootClasspath")
.plus("-Dsun.awt.disablegrab=true")
.plus("-Dsun.io.useCanonCaches=false")
.plus("-Djava.net.preferIPv4Stack=true")
.plus("-Dapple.laf.useScreenMenuBar=${useMenuScreenBar.toString()}")
.plus("-Didea.is.internal=${isInternal.toString()}")
.plus("-Didea.config.path=$configPath")
.plus("-Didea.system.path=$systemPath")
.plus("-Dfile.encoding=$encoding")
.plus("-Didea.platform.prefix=${ide.ideType.platformPrefix}")
.plus("-Xdebug")
.plus(
"-Xrunjdwp:transport=dt_socket,server=y,suspend=$suspendDebug,address=$debugPort") //todo: add System.getProperty(...) to customize debug port
listOf<String>()
.plus("-ea")
.plus("-Xbootclasspath/p:$bootClasspath")
.plus("-Dsun.awt.disablegrab=true")
.plus("-Dsun.io.useCanonCaches=false")
.plus("-Djava.net.preferIPv4Stack=true")
.plus("-Dapple.laf.useScreenMenuBar=${useMenuScreenBar.toString()}")
.plus("-Didea.is.internal=${isInternal.toString()}")
.plus("-Didea.config.path=$configPath")
.plus("-Didea.system.path=$systemPath")
.plus("-Dfile.encoding=$encoding")
.plus("-Didea.platform.prefix=${ide.ideType.platformPrefix}")
.plus("-Xdebug")
.plus(
"-Xrunjdwp:transport=dt_socket,server=y,suspend=$suspendDebug,address=$debugPort") //todo: add System.getProperty(...) to customize debug port
private fun getCurrentJavaExec(): String {
return PathUtils.getJreBinPath()
}
private fun getOsSpecificClasspath(moduleName: String): String = ClassPathBuilder.buildOsSpecific(
getFullClasspath(moduleName).map { it.path })
getFullClasspath(moduleName).map { it.path })
/**
......@@ -230,33 +235,77 @@ object GuiTestLocalLauncher {
* return union of classpaths for @moduleName and testGuiFramework modules
*/
private fun getExtendedClasspath(moduleName: String): MutableSet<File> {
val modules = getModulesList()
// here we trying to analyze output path for project from classloader path and from modules classpath.
// If they didn't match than change it to output path from classpath
changeProjectOutputIfNeeded()
val resultSet = LinkedHashSet<File>()
val module = modules.module(moduleName)!!
val module = getModulesList().module(moduleName)!!
resultSet.addAll(module.getClasspath())
val testGuiFrameworkModule = modules.module("testGuiFramework")!!
resultSet.addAll(testGuiFrameworkModule.getClasspath())
val testGuiFrameworkModule = getTestGuiFrameworkModule()
resultSet.addAll(testGuiFrameworkModule!!.getClasspath())
return resultSet
}
private fun List<JpsModule>.module(moduleName: String): JpsModule? =
this.filter { it.name == moduleName }.firstOrNull()
this.filter { it.name == moduleName }.firstOrNull()
private fun JpsModule.getClasspath(): MutableCollection<File> =
JpsJavaExtensionService.dependencies(this).productionOnly().runtimeOnly().recursively().classes().roots
JpsJavaExtensionService.dependencies(this).productionOnly().runtimeOnly().recursively().classes().roots
private fun getClassloaderOutputDir(): File? {
val pathFromClassloader = PathManager.getJarPathForClass(GuiTestLocalLauncher::class.java)
return File(pathFromClassloader).parentFile.parentFile
}
private fun getModuleOutputDir(): File {
val testGuiFrameworkModule = getTestGuiFrameworkModule()
return testGuiFrameworkModule!!.getClasspath().filter { it.path.contains(testGuiFrameworkModule.name) }.first().parentFile.parentFile
}
private fun getRelativeClassloaderOutputPath(): String? =
FileUtilRt.getRelativePath(File(PathManager.getHomePath()), getClassloaderOutputDir())
private fun getRelativeModuleOutputPath(): String? =
FileUtilRt.getRelativePath(File(PathManager.getHomePath()), getModuleOutputDir())
/**
* @return true if classloader's output path is the same to module's output path (and also same to project)
*/
private fun isClassloaderOutputSame(): Boolean =
(getRelativeClassloaderOutputPath() == getRelativeModuleOutputPath())
private fun changeProjectOutputIfNeeded() {
if (!isClassloaderOutputSame()) {
val project = getProject()
val projectExtension = JpsJavaExtensionService.getInstance().getProjectExtension(project)
projectExtension!!.outputUrl = getClassloaderOutputDir()!!.toURI().toURL().toString()
}
}
private fun getModulesList(): MutableList<JpsModule> {
if (cachedModulesList.isNotEmpty()) return cachedModulesList
val home = PathManager.getHomePath()
val model = JpsElementFactory.getInstance().createModel()
val pathVariables = JpsModelSerializationDataService.computeAllPathVariables(model.global)
JpsProjectLoader.loadProject(model.project, pathVariables, home)
return model.project.modules
cachedModulesList.addAll(model.project.modules)
return cachedModulesList
}
private fun getTestGuiFrameworkModule(): JpsModule? {
if (cachedGuiTestFrameworkModule != null) return cachedGuiTestFrameworkModule
cachedGuiTestFrameworkModule = getModulesList().module("testGuiFramework")
return cachedGuiTestFrameworkModule
}
private fun getProject(): JpsProject =
getTestGuiFrameworkModule()!!.project
}
fun main(args: Array<String>) {
GuiTestLocalLauncher.firstStartIdeLocally(Ide(ideType = IdeType.WEBSTORM, version = 0, build = 0))
// GuiTestLocalLauncher.firstStartIdeByPath("/Users/jetbrains/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/172.2300/IntelliJ IDEA 2017.2 EAP.app")
}
\ No newline at end of file
......@@ -38,7 +38,6 @@ public class Main {
@Test
fun testProjectCreate() {
welcomeFrame {
actionLink("Create New Project").click()
dialog("New Project") {
......@@ -57,20 +56,10 @@ public class Main {
path(project.name, "src", "com.company", "Main").doubleClick()
}
}
editor {
moveTo(118)
moveTo(121)
}
val editorCode = editor.getCurrentFileContents(false)
assertTrue(codeText.lowerCaseNoSpace() == editorCode!!.lowerCaseNoSpace())
assertTrue(codeText == editorCode)
closeProject()
}
}
fun String.lowerCaseNoSpace() = this.toLowerCase().removeSpaces()
fun String.removeSpaces(): String {
val WHITE_SPACE_PATTERN = Pattern.compile("\\s")
return WHITE_SPACE_PATTERN.matcher(this).replaceAll("")
}
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment