| /* |
| * Copyright 2010-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/LICENSE.txt file. |
| */ |
| |
| package org.jetbrains.kotlin.idea.perf |
| |
| import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase |
| import com.intellij.ide.impl.ProjectUtil |
| import com.intellij.openapi.application.ApplicationManager |
| import com.intellij.openapi.application.ex.ApplicationEx |
| 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 |
| |
| abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProjectFileProvider { |
| |
| private val rootProjectFile: File = File("../perfTestProject").absoluteFile |
| private val statsFile: File = File("build/stats.csv").absoluteFile |
| private val tmp = rootProjectFile |
| |
| override fun setUpProject() { |
| |
| println("Using project in $tmp") |
| |
| tmp.resolve(".idea").deleteRecursively() |
| |
| (ApplicationManager.getApplication() as ApplicationEx).doNotSave() |
| myProject = ProjectUtil.openOrImport(tmp.path, null, false) |
| } |
| |
| data class PerFileTestResult(val perProcess: Map<String, Long>, val totalNs: Long, val errors: List<Throwable>) |
| |
| protected abstract fun doTest(file: VirtualFile): PerFileTestResult |
| |
| fun testWholeProjectPerformance() { |
| |
| 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) |
| }) |
| } |
| |
| tcSuite("TotalPerFile") { |
| val files = provideFiles(project) |
| |
| files.forEach { |
| val filePath = File(it.path).relativeTo(tmp).path.replace(File.separatorChar, '/') |
| tcTest(filePath) { |
| val result = doTest(it) |
| result.perProcess.forEach { (k, v) -> |
| appendInspectionResult(filePath, k, v) |
| } |
| (result.totalNs.nsToMs) to result.errors |
| } |
| } |
| } |
| |
| statsOutput.flush() |
| statsOutput.close() |
| |
| |
| tcSuite("Total") { |
| totals.forEach { (k, v) -> |
| tcTest(k) { |
| v.nsToMs to emptyList() |
| } |
| } |
| } |
| } |
| } |
| |
| 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) { |
| child.acceptRecursively(visitor) |
| } |
| } |
| |
| companion object { |
| val Long.nsToMs get() = (this * 1e-6).toLong() |
| } |
| |
| } |