Fixed and extended performance tests

This commit is contained in:
Vladimir Dolzhenko
2019-06-11 10:08:30 +02:00
parent 5010824167
commit 47a16cb252
11 changed files with 573 additions and 56 deletions
+42
View File
@@ -0,0 +1,42 @@
# Goal
The main goal of these performance tests is to collect statistics over
routines (open project, highlighting, inspection, autocompletion etc) for
further analysis like anomalies, degradations and reference for optimizations.
## Resources
- Clone another copy of kotlin project to `${KOTLIN_PROJECT}/../perfTestProject`
- `${KOTLIN_PROJECT}/idea/testData/perfTest/helloKotlin` as _hello world_ project in Kotlin
## Performance test types
### AbstractKotlinProjectsPerformanceTest
Subclasses of `AbstractKotlinProjectsPerformanceTest` provide statistics
(execution time in **ms**) over some **particular** (semi-synthetic) actions.
The output is provided to console in TeamCity format like
```
##teamcity[testSuiteStarted name='Kotlin']
##teamcity[testStarted name='Project opening perfTestProject' captureStandardOutput='true']
##teamcity[testMetadata key='Project opening perfTestProject' type='number' value='63']
##teamcity[testFinished name='Project opening perfTestProject' duration='63']
##teamcity[testSuiteFinished name='Kotlin']
```
as well in `build/stats.csv`
File | ProcessID | Time
--- | --- | ---
HelloMain.kt | Highlighting file warm-up HelloMain.kt | 114
KtFile.kt | Highlighting file KtFile.kt | 1255
### WholeProjectPerformanceTest
**TBD**: drop `@Ignore` from some test cases
Subclasses of `WholeProjectPerformanceTest` collect performance metrics to
figure out some significant anomalies (e.g. inspection takes ages or
even **fails**) over huge number of files (like `kotlin` project itself)
on various **real** use cases.
@@ -0,0 +1,279 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInspection.ex.InspectionProfileImpl
import com.intellij.ide.highlighter.ModuleFileType
import com.intellij.idea.IdeaTestApplication
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleTypeId
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.Computable
import com.intellij.openapi.vcs.changes.ChangeListManager
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.RunAll
import com.intellij.testFramework.UsefulTestCase
import com.intellij.testFramework.fixtures.IdeaTestExecutionPolicy
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
import com.intellij.testFramework.fixtures.TempDirTestFixture
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl
import com.intellij.util.ThrowableRunnable
import com.intellij.util.io.exists
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.core.util.toPsiFile
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import java.io.File
import java.nio.file.Paths
abstract class AbstractKotlinProjectsPerformanceTest : UsefulTestCase() {
// myProject is not required for all potential perf test cases
private var myProject: Project? = null
private lateinit var jdk18: Sdk
private lateinit var myApplication: IdeaTestApplication
private val stats: Stats = Stats("-perf")
override fun setUp() {
super.setUp()
myApplication = IdeaTestApplication.getInstance()
ApplicationManager.getApplication().runWriteAction {
val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx()
val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") {
jdkTableImpl.internalJdk.homeDirectory!!.parent.path
} else {
jdkTableImpl.internalJdk.homePath!!
}
val javaSdk = JavaSdk.getInstance()
jdk18 = javaSdk.createJdk("1.8", homePath)
val internal = javaSdk.createJdk("IDEA jdk", homePath)
val jdkTable = ProjectJdkTable.getInstance()
jdkTable.addJdk(jdk18, testRootDisposable)
jdkTable.addJdk(internal, testRootDisposable)
KotlinSdkType.setUpIfNeeded()
}
InspectionProfileImpl.INIT_INSPECTIONS = true
// warm up: open simple small project
val project = innerPerfOpenProject("helloKotlin", "warm-up ")
val perfHighlightFile = perfHighlightFile(project, "src/HelloMain.kt", "warm-up ")
assertTrue("kotlin project has been not imported properly", perfHighlightFile.isNotEmpty())
ProjectManagerEx.getInstanceEx().forceCloseProject(project, true)
}
override fun tearDown() {
var runAll = RunAll()
if (myProject != null) {
runAll = runAll
.append(ThrowableRunnable { LightPlatformTestCase.doTearDown(myProject!!, myApplication) })
}
runAll.append(ThrowableRunnable { super.tearDown() })
.run()
}
protected fun getTempDirFixture(): TempDirTestFixture {
val policy = IdeaTestExecutionPolicy.current()
return if (policy != null)
policy.createTempDirTestFixture()
else
LightTempDirTestFixtureImpl(true)
}
protected fun perfChangeDocument(fileName: String, note: String = "", block: (document: Document) -> Unit) =
perfChangeDocument(myProject!!, fileName, note, block)
private fun perfChangeDocument(
project: Project,
fileName: String,
nameOfChange: String,
block: (document: Document) -> Unit
) {
val openFileInEditor = openFileInEditor(project, fileName)
val document = openFileInEditor.document
val manager = PsiDocumentManager.getInstance(project)
CommandProcessor.getInstance().executeCommand(project, {
ApplicationManager.getApplication().runWriteAction {
tcSimplePerfTest(fileName, "Changing doc $nameOfChange", stats) {
block(document)
manager.commitDocument(document)
}
}
}, "change doc $fileName $nameOfChange", "")
}
protected fun perfOpenProject(name: String, path: String = "idea/testData/perfTest") {
myProject = innerPerfOpenProject(name, path = path, note = "")
}
private fun innerPerfOpenProject(
name: String,
note: String,
path: String = "idea/testData/perfTest"
): Project {
lateinit var project: Project
val projectPath = "$path/$name"
tcSimplePerfTest("", "Project ${note}opening $name", stats) {
project = ProjectManager.getInstance().loadAndOpenProject(projectPath)!!
if (!Paths.get(projectPath, ".idea").exists()) {
initKotlinProject(project, projectPath, name)
}
ProjectManagerEx.getInstanceEx().openTestProject(project)
disposeOnTearDown(Disposable { ProjectManagerEx.getInstanceEx().forceCloseProject(project, true) })
}
val changeListManagerImpl = ChangeListManager.getInstance(project) as ChangeListManagerImpl
changeListManagerImpl.waitUntilRefreshed()
return project
}
private fun initKotlinProject(
project: Project,
projectPath: String,
name: String
) {
val modulePath = "$projectPath/$name${ModuleFileType.DOT_DEFAULT_EXTENSION}"
val projectFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(projectPath))!!
val srcFile = projectFile.findChild("src")!!
val module = ApplicationManager.getApplication().runWriteAction(Computable<Module> {
val projectRootManager = ProjectRootManager.getInstance(project)
with(projectRootManager) {
projectSdk = jdk18
}
val moduleManager = ModuleManager.getInstance(project)
val module = moduleManager.newModule(modulePath, ModuleTypeId.JAVA_MODULE)
PsiTestUtil.addSourceRoot(module, srcFile)
module!!
})
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, jdk18)
}
protected fun perfHighlightFile(name: String): List<HighlightInfo> =
perfHighlightFile(myProject!!, name)
private fun perfHighlightFile(
project: Project,
name: String,
note: String = ""
): List<HighlightInfo> {
val fileInEditor = openFileInEditor(project, name)
val file = fileInEditor.psiFile
var highlightFile: List<HighlightInfo> = emptyList()
tcSimplePerfTest(file.name, "Highlighting file $note${file.name}", stats) {
highlightFile = highlightFile(file)
}
return highlightFile
}
inline fun attempts(block: (v: Int) -> Unit) {
attempts(3, block)
}
inline fun attempts(count: Int, block: (v: Int) -> Unit) {
for (attempt in 0..count) {
block(attempt)
}
}
fun perfAutoCompletion(
name: String,
before: String,
suggestions: Array<String>,
type: String,
after: String
) {
val factory = IdeaTestFixtureFactory.getFixtureFactory()
val fixtureBuilder = factory.createLightFixtureBuilder(KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE)
val tempDirFixture = getTempDirFixture()
val fixture = factory.createCodeInsightFixture(fixtureBuilder.fixture, tempDirFixture)
with(fixture) {
setUp()
configureByText(KotlinFileType.INSTANCE, before)
}
var complete: Array<LookupElement>? = null
tcSimplePerfTest("", "Auto completion $name", stats) {
with(fixture) {
complete = complete(CompletionType.BASIC)
}
}
val actualSuggestions = complete?.map { it.lookupString }?.toList() ?: emptyList()
assertTrue(actualSuggestions.containsAll(suggestions.toList()))
with(fixture) {
type(type)
checkResult(after)
tearDown()
}
}
private fun highlightFile(psiFile: PsiFile): List<HighlightInfo> {
val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!!
val editor = EditorFactory.getInstance().getEditors(document).first()
return CodeInsightTestFixtureImpl.instantiateAndRun(psiFile, editor, intArrayOf(), false)
}
data class EditorFile(val psiFile: PsiFile, val document: Document)
private fun openFileInEditor(project: Project, name: String): EditorFile {
val psiFile = projectFileByName(project, name)
val vFile = psiFile.virtualFile
val fileEditorManager = FileEditorManager.getInstance(project)
fileEditorManager.openFile(vFile, true)
val document = FileDocumentManager.getInstance().getDocument(vFile)!!
assertNotNull(EditorFactory.getInstance().getEditors(document))
disposeOnTearDown(Disposable { fileEditorManager.closeFile(vFile) })
return EditorFile(psiFile = psiFile, document = document)
}
private fun projectFileByName(project: Project, name: String): PsiFile {
val fileManager = VirtualFileManager.getInstance()
val url = "file://${File("${project.basePath}/$name").absolutePath}"
val virtualFile = fileManager.refreshAndFindFileByUrl(url)
return virtualFile!!.toPsiFile(project)!!
}
}
@@ -34,7 +34,9 @@ import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.fir.IdeFirDependenciesSymbolProvider
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import kotlin.test.Ignore
@Ignore(value = "[Simon Ogorodnik] turned off as it is suppose to be run manually")
class FirTotalKotlinResolveInIdeTest : ModuleTestCase() {
private val projectRootFile = File(".")
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.perf
class KotlinProjectsPerformanceTest : AbstractKotlinProjectsPerformanceTest() {
fun testHelloWorldProject() {
tcSuite("HelloWorld") {
perfOpenProject("helloKotlin")
// highlight
perfHighlightFile("src/HelloMain.kt")
perfHighlightFile("src/HelloMain2.kt")
// change document
perfChangeDocument("src/HelloMain2.kt", "type a single char") { doc ->
val text = doc.text
val offset = text.indexOf("println")
doc.insertString(offset, "p\n")
}
perfChangeDocument("src/HelloMain.kt", "type val expression") { doc ->
val text = doc.text
val offset = text.indexOf("println")
doc.insertString(offset, "val s =\n")
}
}
}
fun testAutoCompletion() {
tcSuite("AutoCompletion") {
attempts {
perfAutoCompletion(
"inside of method: println$it",
"""
fun bar() {
print<caret>
}
""",
suggestions = arrayOf("println", "print"),
type = "l\r",
after = """
fun bar() {
println()
}
"""
)
}
attempts {
perfAutoCompletion(
"outside of method: arrayListOf$it",
before = """
val f: List<String> = array<caret>
fun bar(){ }
""",
type = "\n",
suggestions = arrayOf("arrayOf", "arrayOfNulls", "emptyArray"),
after = """
val f: List<String> = arrayListOf()
fun bar(){ }
"""
)
}
}
}
fun testKotlinProject() {
tcSuite("Kotlin") {
perfOpenProject("perfTestProject", "..")
perfHighlightFile("compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt")
perfHighlightFile("compiler/psi/src/org/jetbrains/kotlin/psi/KtElement.kt")
}
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.perf
import org.jetbrains.kotlin.idea.perf.WholeProjectPerformanceTest.Companion.nsToMs
import java.io.*
import kotlin.system.measureNanoTime
class Stats(name: String = "") : Closeable {
private val statsFile: File = File("build/stats$name.csv").absoluteFile
private val statsOutput: BufferedWriter
init {
statsOutput = statsFile.bufferedWriter()
statsOutput.appendln("File, ProcessID, Time")
}
fun append(file: String, id: String, nanoTime: Long) {
statsOutput.appendln(buildString {
append(file)
append(", ")
append(id)
append(", ")
append(nanoTime.nsToMs)
})
statsOutput.flush()
}
override fun close() {
statsOutput.flush()
statsOutput.close()
}
}
inline fun tcSuite(name: String, block: () -> Unit) {
println("##teamcity[testSuiteStarted name='$name']")
block()
println("##teamcity[testSuiteFinished name='$name']")
}
inline fun tcTest(name: String, block: () -> Pair<Long, List<Throwable>>) {
println("##teamcity[testStarted name='$name' captureStandardOutput='true']")
val (time, errors) = block()
if (errors.isNotEmpty()) {
val detailsWriter = StringWriter()
val errorDetailsPrintWriter = PrintWriter(detailsWriter)
errors.forEach {
it.printStackTrace(errorDetailsPrintWriter)
errorDetailsPrintWriter.println()
}
errorDetailsPrintWriter.close()
val details = detailsWriter.toString()
println("##teamcity[testFailed name='$name' message='Exceptions reported' details='${details.tcEscape()}']")
}
println("##teamcity[testFinished name='$name' duration='$time']")
}
inline fun tcSimplePerfTest(file: String, name: String, stats: Stats, block: () -> Unit) {
var spentMs = 0L
tcTest(name) {
val errors = mutableListOf<Throwable>()
val spentNs = measureNanoTime {
try {
block()
} catch (t: Throwable) {
errors += t
}
}
stats.append(file, name, spentNs)
spentMs = spentNs.nsToMs
spentMs to errors
}
println("##teamcity[buildStatisticValue key='$name' value='$spentMs']")
}
fun String.tcEscape(): String {
return this
.replace("|", "||")
.replace("[", "|[")
.replace("]", "|]")
.replace("\r", "|r")
.replace("\n", "|n")
.replace("'", "|'")
}
@@ -15,7 +15,9 @@ import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import kotlin.test.Ignore
@Ignore(value = "[VD] disabled temporary for further investigation: too much noise, have no clue how to handle it")
class WholeProjectJavaInspectionTest : WholeProjectInspectionTest() {
override fun provideFiles(project: Project): Collection<VirtualFile> {
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInspection.ex.Tools
import kotlin.test.Ignore
@Ignore(value = "[VD] disabled temporary for further investigation: too much noise, have no clue how to handle it")
class WholeProjectKotlinInspectionTest : WholeProjectInspectionTest(), WholeProjectKotlinFileProvider {
override fun isEnabledInspection(tools: Tools) = tools.tool.language in setOf(null, "kotlin", "UAST")
}
@@ -15,7 +15,9 @@ import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtVisitorVoid
import java.util.*
import kotlin.system.measureNanoTime
import kotlin.test.Ignore
@Ignore(value = "[VD] disabled temporary for further investigation: too much noise, have no clue how to handle it")
class WholeProjectLightClassTest : WholeProjectPerformanceTest(), WholeProjectKotlinFileProvider {
override fun doTest(file: VirtualFile): PerFileTestResult {
@@ -6,28 +6,80 @@
package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
import com.intellij.codeInsight.daemon.ImplicitUsageProvider
import com.intellij.codeInsight.daemon.ProblemHighlightFilter
import com.intellij.codeInsight.intention.IntentionManager
import com.intellij.ide.impl.ProjectUtil
import com.intellij.idea.IdeaTestApplication
import com.intellij.lang.ExternalAnnotatorsFilter
import com.intellij.lang.LanguageAnnotators
import com.intellij.lang.StdLanguages
import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ex.ApplicationEx
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import com.intellij.psi.impl.search.IndexPatternBuilder
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.psi.xml.XmlFileNSInfoProvider
import com.intellij.xml.XmlSchemaProvider
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import java.io.*
abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProjectFileProvider {
private val rootProjectFile: File = File("../perfTestProject").absoluteFile
private val statsFile: File = File("build/stats.csv").absoluteFile
private val perfStats: Stats = Stats()
private val tmp = rootProjectFile
override fun isStressTest(): Boolean = false
override fun setUp() {
IdeaTestApplication.getInstance()
// to prevent leaked SDKs: 1.8 and Kotlin SDK
ApplicationManager.getApplication().runWriteAction {
val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx()
val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") {
jdkTableImpl.internalJdk.homeDirectory!!.parent.path
} else {
jdkTableImpl.internalJdk.homePath!!
}
val javaSdk = JavaSdk.getInstance()
val j8 = javaSdk.createJdk("1.8", homePath)
val jdkTable = ProjectJdkTable.getInstance()
jdkTable.addJdk(j8, testRootDisposable)
KotlinSdkType.setUpIfNeeded()
}
super.setUp()
// fix due to stress test disabled
IntentionManager.getInstance().availableIntentionActions // hack to avoid slowdowns in PyExtensionFactory
// don't need test data
//PathManagerEx.getTestDataPath() // to cache stuff
ReferenceProvidersRegistry.getInstance() // pre-load tons of classes
InjectedLanguageManager.getInstance(project) // zillion of Dom Sem classes
LanguageAnnotators.INSTANCE.allForLanguage(JavaLanguage.INSTANCE) // pile of annotator classes loads
LanguageAnnotators.INSTANCE.allForLanguage(StdLanguages.XML)
ProblemHighlightFilter.EP_NAME.extensions
ImplicitUsageProvider.EP_NAME.extensionList
XmlSchemaProvider.EP_NAME.extensionList
XmlFileNSInfoProvider.EP_NAME.extensionList
ExternalAnnotatorsFilter.EXTENSION_POINT_NAME.extensionList
IndexPatternBuilder.EP_NAME.extensionList
}
override fun setUpProject() {
println("Using project in $tmp")
tmp.resolve(".idea").deleteRecursively()
(ApplicationManager.getApplication() as ApplicationEx).doNotSave()
myProject = ProjectUtil.openOrImport(tmp.path, null, false)
}
@@ -41,24 +93,15 @@ abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProj
tcSuite(this::class.simpleName ?: "Unknown") {
val totals = mutableMapOf<String, Long>()
val statsOutput = statsFile.bufferedWriter()
statsOutput.appendln("File, ProcessID, Time")
fun appendInspectionResult(file: String, id: String, nanoTime: Long) {
totals.merge(id, nanoTime) { a, b -> a + b }
statsOutput.appendln(buildString {
append(file)
append(", ")
append(id)
append(", ")
append(nanoTime.nsToMs)
})
perfStats.append(file, id, nanoTime)
}
tcSuite("TotalPerFile") {
val files = provideFiles(project)
// TODO: [VD] temp to limit number of files (and total time to run)
val files = provideFiles(project).sortedBy { it.name }.take(10)
files.forEach {
val filePath = File(it.path).relativeTo(tmp).path.replace(File.separatorChar, '/')
@@ -72,10 +115,6 @@ abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProj
}
}
statsOutput.flush()
statsOutput.close()
tcSuite("Total") {
totals.forEach { (k, v) ->
tcTest(k) {
@@ -86,39 +125,6 @@ abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProj
}
}
inline fun tcSuite(name: String, block: () -> Unit) {
println("##teamcity[testSuiteStarted name='$name']")
block()
println("##teamcity[testSuiteFinished name='$name']")
}
inline fun tcTest(name: String, block: () -> Pair<Long, List<Throwable>>) {
println("##teamcity[testStarted name='$name' captureStandardOutput='true']")
val (time, errors) = block()
if (errors.isNotEmpty()) {
val detailsWriter = StringWriter()
val errorDetailsPrintWriter = PrintWriter(detailsWriter)
errors.forEach {
it.printStackTrace(errorDetailsPrintWriter)
errorDetailsPrintWriter.println()
}
errorDetailsPrintWriter.close()
val details = detailsWriter.toString()
println("##teamcity[testFailed name='$name' message='Exceptions reported' details='${details.tcEscape()}']")
}
println("##teamcity[testFinished name='$name' duration='$time']")
}
fun String.tcEscape(): String {
return this
.replace("|", "||")
.replace("[", "|[")
.replace("]", "|]")
.replace("\r", "|r")
.replace("\n", "|n")
.replace("'", "|'")
}
protected fun PsiElement.acceptRecursively(visitor: PsiElementVisitor) {
this.accept(visitor)
for (child in this.children) {
+4
View File
@@ -0,0 +1,4 @@
fun main(args: Array<String>) {
println("Hello World!")
}
+4
View File
@@ -0,0 +1,4 @@
fun main(args: Array<String>) {
println("Hello World!")
}