[kotlin] performance tests migration to suite dsl

Relates to #KTIJ-20250

GitOrigin-RevId: 0e9090c1b27ed961bb8959241e9ec0e2af7f03ed
This commit is contained in:
Vladimir Dolzhenko
2021-12-02 21:04:24 +00:00
committed by intellij-monorepo-bot
parent 5abfa67bfe
commit fd2fe222be
15 changed files with 702 additions and 550 deletions
@@ -7,7 +7,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.idea.perf.util.ExternalProject
import org.jetbrains.kotlin.idea.perf.util.lastPathSegment
import org.jetbrains.kotlin.idea.perf.util.suite
import org.jetbrains.kotlin.idea.perf.suite.suite
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
import org.junit.runner.RunWith
@@ -4,7 +4,6 @@ package org.jetbrains.kotlin.idea.perf.live
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.editor.EditorFactory
@@ -27,9 +26,8 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.perf.ProjectBuilder
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.WARM_UP
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.runAndMeasure
import org.jetbrains.kotlin.idea.perf.util.PerformanceSuite.ApplicationScope.Companion.initApp
import org.jetbrains.kotlin.idea.perf.util.PerformanceSuite.ApplicationScope.Companion.initSdk
import org.jetbrains.kotlin.idea.perf.suite.PerformanceSuite.ApplicationScope.Companion.initApp
import org.jetbrains.kotlin.idea.perf.suite.PerformanceSuite.ApplicationScope.Companion.initSdk
import org.jetbrains.kotlin.idea.perf.util.ProfileTools.Companion.initDefaultProfile
import org.jetbrains.kotlin.idea.perf.util.logMessage
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
@@ -38,7 +36,7 @@ import org.jetbrains.kotlin.idea.testFramework.*
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.close
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFileInEditor
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openInEditor
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFixture
import org.jetbrains.kotlin.test.KotlinRoot
import java.io.File
@@ -233,59 +231,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
myApplication.setDataProvider(TestDataProvider(project))
}
fun perfTypeAndAutocomplete(
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
lookupElements: List<String>,
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) = perfTypeAndAutocomplete(
project(), stats, fileName, marker, insertString, surroundItems,
lookupElements = lookupElements, typeAfterMarker = typeAfterMarker,
revertChangesAtTheEnd = revertChangesAtTheEnd, note = note
)
fun perfTypeAndAutocomplete(
project: Project,
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
lookupElements: List<String>,
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) {
assertTrue("lookupElements has to be not empty", lookupElements.isNotEmpty())
perfTypeAndDo(
project,
fileName,
"typeAndAutocomplete",
note,
stats,
marker,
typeAfterMarker,
surroundItems,
insertString,
setupBlock = {},
testBlock = { fixture: Fixture ->
fixture.complete()
},
tearDownCheck = { fixture, value: Array<LookupElement>? ->
val items = value?.map { e -> e.lookupString }?.toList() ?: emptyList()
for (lookupElement in lookupElements) {
assertTrue("'$lookupElement' has to be present in items $items", items.contains(lookupElement))
}
},
revertChangesAtTheEnd = revertChangesAtTheEnd
)
}
fun perfTypeAndUndo(
project: Project,
stats: Stats,
@@ -342,7 +287,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
val editor = fixture.editor
val initialText = editor.document.text
updateScriptDependenciesIfNeeded(fileName, fixture)
fixture.updateScriptDependenciesIfNeeded()
performanceTest<Unit, V> {
name("$typeTestPrefix ${notePrefix(note)}$fileName")
@@ -427,7 +372,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
val editor = fixture.editor
val initialText = editor.document.text
updateScriptDependenciesIfNeeded(fileName, fixture)
fixture.updateScriptDependenciesIfNeeded()
val tasksIdx = editor.document.text.indexOf(marker)
assertTrue("marker '$marker' not found in $fileName", tasksIdx > 0)
@@ -471,94 +416,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
}
}
fun perfCopyAndPaste(
stats: Stats,
sourceFileName: String,
sourceInitialMarker: String? = null,
sourceFinalMarker: String? = null,
targetFileName: String,
targetInitialMarker: String? = null,
targetFinalMarker: String? = null,
note: String = ""
) = perfCopyAndPaste(
project(), stats,
sourceFileName, sourceInitialMarker, sourceFinalMarker,
targetFileName, targetInitialMarker, targetFinalMarker,
note
)
fun perfCopyAndPaste(
project: Project,
stats: Stats,
sourceFileName: String,
sourceInitialMarker: String? = null,
sourceFinalMarker: String? = null,
targetFileName: String,
targetInitialMarker: String? = null,
targetFinalMarker: String? = null,
note: String = ""
) {
performanceTest<Pair<Array<Fixture>, String>, Boolean> {
name("${notePrefix(note)}$sourceFileName")
stats(stats)
warmUpIterations(8)
iterations(15)
setUp {
val fixture1 = openFixture(project, sourceFileName)
val fixture2 = openFixture(project, targetFileName)
val initialText2 = fixture2.document.text
updateScriptDependenciesIfNeeded(sourceFileName, fixture1)
updateScriptDependenciesIfNeeded(sourceFileName, fixture2)
fixture1.selectMarkers(sourceInitialMarker, sourceFinalMarker)
fixture2.selectMarkers(targetInitialMarker, targetFinalMarker)
it.setUpValue = Pair(arrayOf(fixture1, fixture2), initialText2)
}
test {
it.setUpValue?.let { setUpValue ->
val fixture1 = setUpValue.first[0]
val fixture2 = setUpValue.first[1]
it.value = fixture1.performEditorAction(IdeActions.ACTION_COPY) &&
fixture2.performEditorAction(IdeActions.ACTION_PASTE)
dispatchAllInvocationEvents()
}
}
tearDown {
try {
commitAllDocuments()
it.value?.let { performed ->
assertTrue("copy-n-paste has not performed well", performed)
// files could be different due to spaces
//assertEquals(it.setUpValue!!.first.document.text, it.setUpValue!!.second.document.text)
}
} finally {
it.setUpValue?.let { setUpValue ->
// pair.second.performEditorAction(IdeActions.ACTION_UNDO)
val fixture2 = setUpValue.first[1]
fixture2.applyText(setUpValue.second)
}
commitAllDocuments()
}
}
profilerConfig.enabled = true
}
}
private fun updateScriptDependenciesIfNeeded(
fileName: String,
fixture: Fixture
) {
if (isAKotlinScriptFile(fileName)) {
runAndMeasure("update script dependencies for $fileName") {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile)
}
}
}
protected fun perfHighlightFile(
name: String,
stats: Stats,
@@ -596,7 +453,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
iterations(if (isWarmUp) 2 else iterations)
checkStability(checkStability)
setUp {
it.setUpValue = openFileInEditor(project, fileName)
it.setUpValue = openInEditor(project, fileName)
}
test {
val file = it.setUpValue
@@ -652,7 +509,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
stats(stats)
warmUpIterations(20)
iterations(50)
setUp { it.setUpValue = openFileInEditor(project, fileName) }
setUp { it.setUpValue = openInEditor(project, fileName) }
test {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(it.setUpValue!!.psiFile)
it.value = it.setUpValue
@@ -4,15 +4,15 @@ package org.jetbrains.kotlin.idea.perf.live
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightVisitor
import org.jetbrains.kotlin.idea.perf.profilers.ProfilerConfig
import org.jetbrains.kotlin.idea.perf.suite.*
import org.jetbrains.kotlin.idea.perf.util.*
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.TEST_KEY
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.runAndMeasure
import org.jetbrains.kotlin.idea.perf.util.ExternalProject
import org.jetbrains.kotlin.idea.perf.util.Metric
import org.jetbrains.kotlin.idea.perf.util.TeamCity.suite
import org.jetbrains.kotlin.idea.testFramework.*
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
@@ -50,7 +50,30 @@ open class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
warmUp.warmUp(this)
}
protected open fun stats(name: String) = Stats(name)
protected open fun profileConfig(): ProfilerConfig = ProfilerConfig()
protected open fun outputConfig(): OutputConfig = OutputConfig()
protected open fun suiteWithConfig(suiteName: String, block: PerformanceSuite.StatsScope.() -> Unit) {
suite(
suiteName,
config = StatsScopeConfig(outputConfig = outputConfig(), profilerConfig = profileConfig()),
block = block
)
}
private fun PerformanceSuite.StatsScope.defaultStatsConfig() {
with(config) {
warmup = 8
iterations = 15
}
}
protected fun stats(name: String) = Stats(name, profilerConfig = profileConfig(), outputConfig = outputConfig())
protected open fun PerformanceSuite.ApplicationScope.kotlinProject(block: PerformanceSuite.ProjectScope.() -> Unit) {
project(name = "kotlin", path = ExternalProject.KOTLIN_PROJECT_PATH, openWith = GRADLE_PROJECT, block = block)
}
fun testHelloWorldProject() {
suite("Hello world project") {
@@ -79,240 +102,267 @@ open class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
}
fun testKotlinProject() {
suite("Kotlin project") {
stats("kotlin project").use {
perfOpenKotlinProject(it)
suiteWithConfig("kotlin project") {
app {
warmUpProject()
val filesToHighlight = arrayOf(
"idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/PsiPrecedences.kt",
"compiler/psi/src/org/jetbrains/kotlin/psi/KtElement.kt",
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"core/builtins/native/kotlin/Primitives.kt",
kotlinProject {
defaultStatsConfig()
"compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt",
"compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt",
val filesToHighlight = arrayOf(
//"idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/PsiPrecedences.kt",
"compiler/psi/src/org/jetbrains/kotlin/psi/KtElement.kt",
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"core/builtins/native/kotlin/Primitives.kt",
"compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt",
"compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt"
)
"compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt",
"compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt",
filesToHighlight.forEach { file -> perfHighlightFile(file, stats = it) }
filesToHighlight.forEach { file -> perfHighlightFileEmptyProfile(file, stats = it) }
"compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt",
"compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt"
)
perfTypeAndHighlight(
it,
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"override fun getDeclarations(): List<KtDeclaration> {",
"val q = import",
note = "in-method getDeclarations-import"
)
profile(EmptyProfile)
perfTypeAndHighlight(
it,
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"override fun getDeclarations(): List<KtDeclaration> {",
"val q = import",
typeAfterMarker = false,
note = "out-of-method import"
)
filesToHighlight.forEach {fileToHighlight ->
fixture(fileToHighlight).use { fixture ->
measureHighlight(fixture, "empty profile")
}
}
profile(DefaultProfile)
filesToHighlight.forEach {fileToHighlight ->
fixture(fileToHighlight).use { fixture ->
measureHighlight(fixture)
}
}
fixture("compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt").use { fixture ->
with(fixture.typingConfig) {
marker = "override fun getDeclarations(): List<KtDeclaration> {"
insertString = "val q = import"
typeAfterMarker = true
}
measureTypeAndHighlight(fixture, "typeAndHighlight in-method getDeclarations-import")
with(fixture.typingConfig) {
typeAfterMarker = false
}
measureTypeAndHighlight(fixture, "typeAndHighlight out-of-method import")
}
}
}
}
}
fun testKotlinProjectCopyAndPaste() {
suite("Kotlin copy-and-paste") {
stats("Kotlin copy-and-paste").use { stat ->
perfOpenKotlinProjectFast(stat)
suiteWithConfig("Kotlin copy-and-paste") {
app {
warmUpProject()
perfCopyAndPaste(
stat,
sourceFileName = "compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
targetFileName = "compiler/psi/src/org/jetbrains/kotlin/psi/KtImportInfo.kt"
)
kotlinProject {
profile(DefaultProfile)
defaultStatsConfig()
fixture("compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt").use { originalFixture ->
fixture("compiler/psi/src/org/jetbrains/kotlin/psi/KtImportInfo.kt").use { targetFixture ->
var copied = false
measure<Unit>(originalFixture) {
before = {
copied = false
targetFixture.storeText()
originalFixture.cursorConfig.select()
targetFixture.cursorConfig.select()
}
test = {
copied = originalFixture.performEditorAction(IdeActions.ACTION_COPY) &&
targetFixture.performEditorAction(IdeActions.ACTION_PASTE)
dispatchAllInvocationEvents()
}
after = {
try {
commitAllDocuments()
assertTrue("copy-n-paste has not performed well", copied)
// files could be different due to spaces
//assertEquals(it.setUpValue!!.first.document.text, it.setUpValue!!.second.document.text)
} finally {
targetFixture.restoreText()
commitAllDocuments()
}
}
}
}
}
}
}
}
}
fun testKotlinProjectCompletionKtFile() {
suite("Kotlin completion ktFile") {
stats("Kotlin completion ktFile").use { stat ->
perfOpenKotlinProjectFast(stat)
suiteWithConfig("Kotlin completion ktFile") {
app {
warmUpProject()
perfTypeAndAutocomplete(
stat,
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"override fun getDeclarations(): List<KtDeclaration> {",
"val q = import",
lookupElements = listOf("importDirectives"),
note = "in-method getDeclarations-import"
)
kotlinProject {
profile(DefaultProfile)
perfTypeAndAutocomplete(
stat,
"compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt",
"override fun getDeclarations(): List<KtDeclaration> {",
"val q = import",
typeAfterMarker = false,
lookupElements = listOf("importDirectives"),
note = "out-of-method import"
)
defaultStatsConfig()
perfTypeAndAutocomplete(
stat,
fileName = "compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt",
marker = "fun mapOwner(descriptor: DeclarationDescriptor): Type {",
insertString = "val b = bind",
typeAfterMarker = true,
lookupElements = listOf("bindingContext"),
note = "in-method completion for KotlinTypeMapper"
)
fixture("compiler/psi/src/org/jetbrains/kotlin/psi/KtFile.kt").use { fixture ->
with(fixture.typingConfig) {
marker = "override fun getDeclarations(): List<KtDeclaration> {"
insertString = "val q = import"
typeAfterMarker = true
}
perfTypeAndAutocomplete(
stat,
fileName = "compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt",
marker = "fun mapOwner(descriptor: DeclarationDescriptor): Type {",
insertString = "val b = bind",
typeAfterMarker = false,
lookupElements = listOf("bindingContext"),
note = "out-of-method completion for KotlinTypeMapper"
)
measureTypeAndAutoCompletion(fixture, "in-method getDeclarations-import") {
lookupElements = listOf("importDirectives")
}
perfTypeAndAutocomplete(
stat,
fileName = "compiler/tests/org/jetbrains/kotlin/util/ArgsToParamsMatchingTest.kt",
marker = "fun testMatchNamed() {",
insertString = "testMatch",
typeAfterMarker = true,
lookupElements = listOf("testMatchNamed"),
note = "in-method completion for ArgsToParamsMatchingTest"
)
with(fixture.typingConfig) {
typeAfterMarker = false
}
perfTypeAndAutocomplete(
stat,
fileName = "compiler/tests/org/jetbrains/kotlin/util/ArgsToParamsMatchingTest.kt",
marker = "class ArgsToParamsMatchingTest {",
insertString = "val me = ",
typeAfterMarker = true,
lookupElements = listOf("ArgsToParamsMatchingTest"),
note = "out-of-method completion for ArgsToParamsMatchingTest"
)
measureTypeAndAutoCompletion(fixture, "out-of-method import") {
lookupElements = listOf("importDirectives")
}
}
fixture("compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt").use { fixture ->
with(fixture.typingConfig) {
marker = "fun mapOwner(descriptor: DeclarationDescriptor): Type {"
insertString = "val b = bind"
typeAfterMarker = true
}
measureTypeAndAutoCompletion(fixture, "in-method completion for KotlinTypeMapper") {
lookupElements = listOf("bindingContext")
}
with(fixture.typingConfig) {
typeAfterMarker = false
}
measureTypeAndAutoCompletion(fixture, "out-of-method completion for KotlinTypeMapper") {
lookupElements = listOf("bindingContext")
}
}
fixture("compiler/tests/org/jetbrains/kotlin/util/ArgsToParamsMatchingTest.kt").use { fixture ->
with(fixture.typingConfig) {
marker = "fun testMatchNamed() {"
insertString = "testMatch"
typeAfterMarker = true
}
measureTypeAndAutoCompletion(fixture, "in-method completion for ArgsToParamsMatchingTest") {
lookupElements = listOf("testMatchNamed")
}
with(fixture.typingConfig) {
typeAfterMarker = false
}
measureTypeAndAutoCompletion(fixture, "out-of-method completion for ArgsToParamsMatchingTest") {
lookupElements = listOf("testMatchNamed")
}
}
}
}
}
}
fun testKotlinProjectCompletionBuildGradle() {
suite("Kotlin completion gradle.kts") {
stats("kotlin completion gradle.kts").use { stat ->
runAndMeasure("open kotlin project") {
perfOpenKotlinProjectFast(stat)
}
suiteWithConfig("kotlin completion gradle.kts") {
app {
warmUpProject()
runAndMeasure("type and autocomplete") {
perfTypeAndAutocomplete(
stat,
"build.gradle.kts",
"tasks {",
"reg",
lookupElements = listOf("register"),
note = "tasks-create"
)
}
kotlinProject {
profile(DefaultProfile)
runAndMeasure("type and undo") {
perfTypeAndUndo(
project(),
stat,
"build.gradle.kts",
"tasks {",
"register",
note = "type-undo"
)
}
defaultStatsConfig()
fixture("build.gradle.kts").use { fixture ->
with(fixture.typingConfig) {
marker = "tasks {"
insertString = "reg"
typeAfterMarker = true
}
measureTypeAndAutoCompletion(fixture, "tasks-create") {
lookupElements = listOf("register")
}
with(fixture.typingConfig) {
marker = "tasks {"
insertString = "register"
typeAfterMarker = true
}
measureTypeAndUndo(fixture, "type-undo") {}
}
}
}
}
}
fun testKotlinProjectScriptDependenciesBuildGradle() {
suite("Kotlin scriptDependencies gradle.kts") {
stats("kotlin scriptDependencies gradle.kts").use { stat ->
perfOpenKotlinProjectFast(stat)
suiteWithConfig("kotlin scriptDependencies gradle.kts") {
app {
warmUpProject()
perfScriptDependenciesBuildGradleKts(stat)
perfScriptDependenciesIdeaBuildGradleKts(stat)
// perfScriptDependenciesJpsGradleKts(stat)
// perfScriptDependenciesVersionGradleKts(stat)
kotlinProject {
profile(DefaultProfile)
defaultStatsConfig()
fixture("build.gradle.kts", updateScriptDependenciesIfNeeded = false).use { fixture ->
measure<Unit>(combineNameWithSimpleFileName("updateScriptDependencies", fixture)) {
before = {
fixture.openInEditor()
}
test = {
fixture.updateScriptDependenciesIfNeeded()
}
}
}
}
}
}
}
fun testKotlinProjectBuildGradle() {
suite("Kotlin gradle.kts") {
stats("kotlin gradle.kts").use { stat ->
perfOpenKotlinProjectFast(stat)
suiteWithConfig("kotlin gradle.kts") {
app {
warmUpProject()
perfFileAnalysisBuildGradleKts(stat)
perfFileAnalysisIdeaBuildGradleKts(stat)
// perfFileAnalysisJpsGradleKts(stat)
// perfFileAnalysisVersionGradleKts(stat)
kotlinProject {
profile(DefaultProfile)
defaultStatsConfig()
fixture("build.gradle.kts").use { fixture ->
measure<List<HighlightInfo>>(combineNameWithSimpleFileName("fileAnalysis", fixture)) {
before = {
fixture.openInEditor()
}
test = {
fixture.doHighlighting()
}
}
}
}
}
}
}
private fun perfOpenKotlinProjectFast(stats: Stats) = perfOpenKotlinProject(stats, fast = true)
private fun perfOpenKotlinProject(stats: Stats, fast: Boolean = false) {
myProject = openProject("kotlin", stats, "", ExternalProject.KOTLIN_PROJECT_PATH, GRADLE_PROJECT, fast)
}
protected open fun openProject(
name: String,
stats: Stats,
note: String,
path: String,
openAction: ProjectOpenAction,
fast: Boolean
) = perfOpenProject(
name = name,
stats = stats,
note = note,
path = path,
openAction = openAction,
fast = fast
)
private fun perfScriptDependenciesBuildGradleKts(it: Stats) {
perfScriptDependencies("build.gradle.kts", stats = it)
}
private fun perfScriptDependenciesIdeaBuildGradleKts(it: Stats) {
perfScriptDependencies("idea/build.gradle.kts", stats = it, note = "idea/")
}
private fun perfScriptDependenciesJpsGradleKts(it: Stats) {
perfScriptDependencies("gradle/jps.gradle.kts", stats = it, note = "gradle/")
}
private fun perfScriptDependenciesVersionGradleKts(it: Stats) {
perfScriptDependencies("gradle/versions.gradle.kts", stats = it, note = "gradle/")
}
private fun perfFileAnalysisBuildGradleKts(it: Stats) {
perfKtsFileAnalysis("build.gradle.kts", stats = it)
}
private fun perfFileAnalysisIdeaBuildGradleKts(it: Stats) {
perfKtsFileAnalysis("idea/build.gradle.kts", stats = it, note = "idea/")
}
private fun perfFileAnalysisJpsGradleKts(it: Stats) {
perfKtsFileAnalysis("gradle/jps.gradle.kts", stats = it, note = "gradle/")
}
private fun perfFileAnalysisVersionGradleKts(it: Stats) {
perfKtsFileAnalysis("gradle/versions.gradle.kts", stats = it, note = "gradle/")
}
private fun perfKtsFileAnalysis(
fileName: String,
stats: Stats,
@@ -374,7 +424,7 @@ open class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
}
private fun replaceWithCustomHighlighter() {
org.jetbrains.kotlin.idea.testFramework.replaceWithCustomHighlighter(
replaceWithCustomHighlighter(
testRootDisposable,
KotlinHighlightVisitor::class.java.name,
TestKotlinHighlightVisitor::class.java.name
@@ -1,30 +1,21 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.stats
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.perf.live.PerformanceProjectsTest
import org.jetbrains.kotlin.idea.perf.profilers.ProfilerConfig
import org.jetbrains.kotlin.idea.perf.util.ExternalProject
import org.jetbrains.kotlin.idea.perf.util.OutputConfig
import org.jetbrains.kotlin.idea.perf.suite.PerformanceSuite
import org.jetbrains.kotlin.idea.testFramework.ProjectOpenAction
class PerformanceProjectsStatNamesTest: PerformanceProjectsTest() {
override fun stats(name: String): Stats =
Stats(name, outputConfig = statsOutputConfig, profilerConfig = statsProfilerConfig)
override fun profileConfig(): ProfilerConfig = statsProfilerConfig
override fun openProject(
name: String,
stats: Stats,
note: String,
path: String,
openAction: ProjectOpenAction,
fast: Boolean
): Project {
try {
return openProjectNormal(name, path, ProjectOpenAction.EXISTING_IDEA_PROJECT)
} catch (e: Exception) {
e.printStackTrace()
}
return openProjectNormal(name, path, openAction)
override fun outputConfig(): OutputConfig = statsOutputConfig
override fun PerformanceSuite.ApplicationScope.kotlinProject(block: PerformanceSuite.ProjectScope.() -> Unit) {
project(name = "kotlin", path = ExternalProject.KOTLIN_PROJECT_PATH, openWith = ProjectOpenAction.EXISTING_IDEA_PROJECT, block = block)
}
}
@@ -22,9 +22,11 @@ fun compareBenchmarkWithSample(benchmark: Benchmark): Collection<String>? {
buildId = null
val statsFile = statsFile()
buildId = id
statsFile.name
}
val sampleStatFile = File((Benchmark::class as Any).javaClass.getResource("/stats-samples/$statsFileName").path)
val resource = (Benchmark::class as Any).javaClass.getResource("/stats-samples/$statsFileName") ?: error("no sample for ${statsFileName}")
val sampleStatFile = File(resource.path)
val sampleBenchmark = sampleStatFile.loadBenchmark()
return compare(sampleBenchmark, benchmark).takeIf { it.isNotEmpty() }
}
@@ -0,0 +1,38 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import org.jetbrains.kotlin.idea.testFramework.Fixture
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.testFramework.commitAllDocuments
abstract class AbstractFixtureMeasurementScope<T>(
protected val fixture: Fixture,
typeTestPrefix: String = "",
name: String,
stats: Stats,
config: StatsScopeConfig,
after: (() -> Unit)? = null,
var revertChangesAtTheEnd: Boolean = true,
) : MeasurementScope<T>(listOf(typeTestPrefix, name).filter { it.isNotEmpty() }.joinToString(" "), stats, config, after = after) {
protected fun <V> doFixturePerformanceTest(setUp: () -> Unit = {}, test:() -> V?, tearDown: () -> Unit = {}) {
doPerformanceTest(
setUp = {
fixture.storeText()
setUp()
before.invoke()
},
test = test,
tearDown = {
try {
tearDown()
} finally {
if (revertChangesAtTheEnd) {
fixture.restoreText()
commitAllDocuments()
}
after?.invoke()
}
}
)
}
}
@@ -0,0 +1,35 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.testFramework.performanceTest
open class MeasurementScope<T>(
val name: String,
val stats: Stats,
val config: StatsScopeConfig,
var before: () -> Unit = {},
var test: (() -> T?)? = null,
var after: (() -> Unit)? = null
) {
open fun run(): List<T?> {
val t = test ?: error("test procedure isn't set")
val value = mutableListOf<T?>()
doPerformanceTest(before, test = { value.add(t.invoke()) }, tearDown = after?: {})
return value
}
protected fun <V> doPerformanceTest(setUp: () -> Unit = {}, test:() -> V?, tearDown: () -> Unit = {}) {
performanceTest<Unit, T> {
name(name)
stats(stats)
warmUpIterations(config.warmup)
iterations(config.iterations)
fastIterations(config.fastIterations)
setUp { setUp() }
test { test() }
tearDown { tearDown() }
profilerConfig(config.profilerConfig)
}
}
}
@@ -0,0 +1,38 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import com.intellij.codeInsight.lookup.LookupElement
import junit.framework.TestCase.assertTrue
import org.jetbrains.kotlin.idea.testFramework.Fixture
import org.jetbrains.kotlin.idea.testFramework.Stats
class TypeAndAutoCompletionMeasurementScope(
fixture: Fixture,
typeTestPrefix: String = "",
name: String,
stats: Stats,
config: StatsScopeConfig,
after: (() -> Unit)? = null,
var lookupElements: List<String> = listOf(),
revertChangesAtTheEnd: Boolean = true,
): AbstractFixtureMeasurementScope<String>(fixture, typeTestPrefix, name, stats, config, after, revertChangesAtTheEnd) {
override fun run(): List<String?> {
assertTrue("lookupElements has to be not empty", lookupElements.isNotEmpty())
var value: Array<LookupElement>? = null
doFixturePerformanceTest(
setUp = {
fixture.typingConfig.moveCursor()
fixture.type()
},
test = { value = fixture.complete() },
tearDown = {
val items = value?.map { e -> e.lookupString }?.toList() ?: emptyList()
for (lookupElement in lookupElements) {
assertTrue("'$lookupElement' has to be present in items $items", items.contains(lookupElement))
}
}
)
return value?.map { e -> e.lookupString }?.toList() ?: emptyList()
}
}
@@ -0,0 +1,34 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.testFramework.UsefulTestCase.assertNotEmpty
import org.jetbrains.kotlin.idea.testFramework.Fixture
import org.jetbrains.kotlin.idea.testFramework.Stats
class TypeAndHighlightMeasurementScope(
fixture: Fixture,
typeTestPrefix: String = "",
name: String,
stats: Stats,
config: StatsScopeConfig,
after: (() -> Unit)? = null,
revertChangesAtTheEnd: Boolean = true,
) : AbstractFixtureMeasurementScope<HighlightInfo>(fixture, typeTestPrefix, name, stats, config, after, revertChangesAtTheEnd) {
override fun run(): List<HighlightInfo?> {
var highlightInfos: List<HighlightInfo>? = null
doFixturePerformanceTest(
setUp = {
fixture.typingConfig.moveCursor()
},
test = {
highlightInfos = fixture.typeAndHighlight()
highlightInfos
},
tearDown = {
highlightInfos?.let { assertNotEmpty(it) }
}
)
return highlightInfos ?: emptyList()
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.testFramework.Fixture
import org.jetbrains.kotlin.idea.testFramework.Stats
class TypeAndUndoMeasurementScope(
fixture: Fixture,
typeTestPrefix: String = "",
name: String,
stats: Stats,
config: StatsScopeConfig,
after: (() -> Unit)? = null,
revertChangesAtTheEnd: Boolean = true,
) : AbstractFixtureMeasurementScope<String>(fixture, typeTestPrefix, name, stats, config, after, revertChangesAtTheEnd) {
override fun run(): List<String?> {
doFixturePerformanceTest(
setUp = {
fixture.typingConfig.moveCursor()
fixture.type()
},
test = {
fixture.performEditorAction(IdeActions.ACTION_UNDO)
UIUtil.dispatchAllInvocationEvents()
},
tearDown = {
val text = fixture.document.text
val savedText = fixture.savedText
assert(savedText != text) { "undo has to change document text\nbefore undo:\n$savedText\n\nafter undo:\n$text" }
}
)
return listOf(fixture.document.text)
}
}
@@ -0,0 +1,87 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.suite
import com.intellij.testFramework.EditorTestUtil
import org.jetbrains.kotlin.idea.perf.profilers.ProfilerConfig
import org.jetbrains.kotlin.idea.perf.util.ExternalProject
import org.jetbrains.kotlin.idea.perf.util.OutputConfig
import org.jetbrains.kotlin.idea.perf.util.lastPathSegment
import org.jetbrains.kotlin.idea.testFramework.Fixture
import org.jetbrains.kotlin.idea.testFramework.ProjectOpenAction
data class CursorConfig(
val fixture: Fixture,
var marker: String? = null,
var finalMarker: String? = null
) : AutoCloseable {
fun select() {
fixture.selectMarkers(marker, finalMarker)
}
override fun close() {
fixture.close()
}
}
data class TypingConfig(
val fixture: Fixture,
/**
* Find the place in a file and place cursor at [marker],
* If [typeAfterMarker] is true cursor is placed after the [marker],
* otherwise at the beggining of the [marker].
*/
var marker: String? = null,
var typeAfterMarker: Boolean = true,
var insertString: String? = null,
var surroundItems: String = "\n",
var note: String = "",
var delayMs: Long? = null
) : AutoCloseable {
fun moveCursor() {
val editor = fixture.editor
val tasksIdx = marker?.let { marker ->
fixture.text.indexOf(marker).also {
check(it > 0) { "marker '$marker' not found in ${fixture.fileName}" }
}
} ?: 0
if (typeAfterMarker || marker == null) {
editor.caretModel.moveToOffset(tasksIdx + (marker?.let { it.length + 1 } ?: 0))
} else {
editor.caretModel.moveToOffset(tasksIdx - 1)
}
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - if (typeAfterMarker) 1 else 2)
if (!typeAfterMarker) {
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
}
}
override fun close() {
fixture.close()
}
}
class StatsScopeConfig(
var name: String? = null,
var warmup: Int = 2,
var iterations: Int = 5,
var fastIterations: Boolean = false,
var outputConfig: OutputConfig = OutputConfig(),
var profilerConfig: ProfilerConfig = ProfilerConfig()
)
class ProjectScopeConfig(val path: String, val openWith: ProjectOpenAction, val refresh: Boolean = false, private val name: String? = null) {
val projectName: String = name ?: path.lastPathSegment()
constructor(externalProject: ExternalProject, refresh: Boolean) : this(externalProject.path, externalProject.openWith, refresh)
}
@@ -1,6 +1,6 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.perf.util
package org.jetbrains.kotlin.idea.perf.suite
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.lang.annotation.HighlightSeverity
@@ -19,7 +19,6 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiDocumentManagerBase
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.*
@@ -34,7 +33,7 @@ import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.perf.ProjectBuilder
import org.jetbrains.kotlin.idea.testFramework.Stats
import org.jetbrains.kotlin.idea.testFramework.Stats.Companion.runAndMeasure
import org.jetbrains.kotlin.idea.perf.profilers.ProfilerConfig
import org.jetbrains.kotlin.idea.perf.util.*
import org.jetbrains.kotlin.idea.perf.util.ProfileTools.Companion.disableAllInspections
import org.jetbrains.kotlin.idea.perf.util.ProfileTools.Companion.enableAllInspections
import org.jetbrains.kotlin.idea.perf.util.ProfileTools.Companion.enableInspections
@@ -43,6 +42,7 @@ import org.jetbrains.kotlin.idea.perf.util.ProfileTools.Companion.initDefaultPro
import org.jetbrains.kotlin.idea.test.GradleProcessOutputInterceptor
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
import org.jetbrains.kotlin.idea.testFramework.*
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.close
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -51,7 +51,6 @@ import java.io.File
class PerformanceSuite {
companion object {
fun suite(
name: String,
stats: StatsScope,
@@ -97,67 +96,21 @@ class PerformanceSuite {
fun <T> measure(name: String, f: MeasurementScope<T>.() -> Unit, after: (() -> Unit)?): List<T?> =
MeasurementScope<T>(name, stats, config, after = after).apply(f).run()
fun measureTypeAndAutoCompletion(name: String, fixture: Fixture, f: TypeAndAutoCompletionMeasurementScope.() -> Unit, after: (() -> Unit)?): List<String?> =
TypeAndAutoCompletionMeasurementScope(fixture, typeTestPrefix = "typeAndAutocomplete", name = name, stats = stats, config = config, after = after).apply(f).run()
fun measureTypeAndUndo(name: String, fixture: Fixture, f: TypeAndUndoMeasurementScope.() -> Unit, after: (() -> Unit)?): List<String?> =
TypeAndUndoMeasurementScope(fixture, typeTestPrefix = "typeAndUndo", name = name, stats = stats, config = config, after = after).apply(f).run()
fun measureTypeAndHighlight(name: String, fixture: Fixture, f: TypeAndHighlightMeasurementScope.() -> Unit, after: (() -> Unit)?): List<HighlightInfo?> =
TypeAndHighlightMeasurementScope(fixture, typeTestPrefix = "", name = name, stats = stats, config = config, after = after).apply(f).run()
fun logStatValue(name: String, value: Any) {
logMessage { "buildStatisticValue key='${stats.name}: $name' value='$value'" }
TeamCity.statValue("${stats.name}: $name", value)
}
}
data class CursorConfig(
val fixture: Fixture,
val marker: String
) : AutoCloseable {
override fun close() {
fixture.close()
}
}
data class TypingConfig(
val fixture: Fixture,
val marker: String,
val insertString: String,
val surroundItems: String = "\n",
val typeAfterMarker: Boolean = true,
val note: String = "",
val delayMs: Long? = null
) : AutoCloseable {
override fun close() {
fixture.close()
}
}
class MeasurementScope<T>(
val name: String,
val stats: Stats,
val config: StatsScopeConfig,
var before: () -> Unit = {},
var test: (() -> T?)? = null,
var after: (() -> Unit)? = null
) {
fun run(): List<T?> {
val t = test ?: error("test procedure isn't set")
val value = mutableListOf<T?>()
performanceTest<Unit, T> {
name(name)
stats(stats)
warmUpIterations(config.warmup)
iterations(config.iterations)
fastIterations(config.fastIterations)
setUp {
before()
}
test {
value.add(t.invoke())
}
tearDown {
after?.invoke()
}
profilerConfig(config.profilerConfig)
}
return value
}
}
class ApplicationScope(val rootDisposable: Disposable, val stats: StatsScope) : AutoCloseable {
val application = initApp(rootDisposable)
val jdk: Sdk = initSdk(rootDisposable)
@@ -168,11 +121,11 @@ class PerformanceSuite {
fun project(block: ProjectWithDescriptorScope.() -> Unit) =
ProjectWithDescriptorScope(this).use(block)
fun project(path: String, openWith: ProjectOpenAction = ProjectOpenAction.EXISTING_IDEA_PROJECT, block: ProjectScope.() -> Unit) =
ProjectScope(ProjectScopeConfig(path, openWith), this).use(block)
fun project(name: String? = null, path: String, openWith: ProjectOpenAction = ProjectOpenAction.EXISTING_IDEA_PROJECT, block: ProjectScope.() -> Unit) =
ProjectScope(ProjectScopeConfig(path, openWith, name = name), this).use(block)
fun gradleProject(path: String, refresh: Boolean = false, block: ProjectScope.() -> Unit) =
ProjectScope(ProjectScopeConfig(path, ProjectOpenAction.GRADLE_PROJECT, refresh), this).use(block)
fun gradleProject(name: String? = null, path: String, refresh: Boolean = false, block: ProjectScope.() -> Unit) =
ProjectScope(ProjectScopeConfig(path, ProjectOpenAction.GRADLE_PROJECT, refresh, name = name), this).use(block)
fun warmUpProject() = project {
descriptor {
@@ -235,22 +188,6 @@ class PerformanceSuite {
}
}
class StatsScopeConfig(
var name: String? = null,
var warmup: Int = 2,
var iterations: Int = 5,
var fastIterations: Boolean = false,
var outputConfig: OutputConfig = OutputConfig(),
var profilerConfig: ProfilerConfig = ProfilerConfig()
)
class ProjectScopeConfig(val path: String, val openWith: ProjectOpenAction, val refresh: Boolean = false) {
val name: String = path.lastPathSegment()
constructor(externalProject: ExternalProject, refresh: Boolean) : this(externalProject.path, externalProject.openWith, refresh)
}
abstract class AbstractProjectScope(val app: ApplicationScope) : AutoCloseable {
abstract val project: Project
val openFiles = mutableListOf<VirtualFile>()
@@ -276,58 +213,6 @@ class PerformanceSuite {
fun highlight(editorFile: PsiFile?, toIgnore: IntArray = ArrayUtilRt.EMPTY_INT_ARRAY) =
editorFile?.highlightFile(toIgnore) ?: error("editor isn't ready for highlight")
fun moveCursor(config: CursorConfig) {
val fixture = config.fixture
val editor = fixture.editor
updateScriptDependenciesIfNeeded(fixture)
val marker = config.marker
val tasksIdx = fixture.text.indexOf(marker)
check(tasksIdx > 0) {
"marker '$marker' not found in ${fixture.fileName}"
}
editor.caretModel.moveToOffset(tasksIdx)
}
fun moveCursor(config: TypingConfig) {
val fixture = config.fixture
val editor = fixture.editor
updateScriptDependenciesIfNeeded(fixture)
val marker = config.marker
val tasksIdx = fixture.text.indexOf(marker)
check(tasksIdx > 0) {
"marker '$marker' not found in ${fixture.fileName}"
}
if (config.typeAfterMarker) {
editor.caretModel.moveToOffset(tasksIdx + marker.length + 1)
} else {
editor.caretModel.moveToOffset(tasksIdx - 1)
}
for (surroundItem in config.surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - if (config.typeAfterMarker) 1 else 2)
if (!config.typeAfterMarker) {
for (surroundItem in config.surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
}
}
fun typeAndHighlight(config: TypingConfig): List<HighlightInfo> {
val string = config.insertString
for (i in string.indices) {
config.fixture.type(string[i])
config.delayMs?.let { d -> Thread.sleep(d) }
}
return config.fixture.doHighlighting()
}
fun findUsages(config: CursorConfig): Set<Usage> {
val offset = config.fixture.editor.caretModel.offset
val psiFile = config.fixture.psiFile
@@ -353,24 +238,24 @@ class PerformanceSuite {
this.project.enableAllInspections()
fun editor(path: String) =
Fixture.openFileInEditor(project, path).psiFile.also { openFiles.add(it.virtualFile) }
Fixture.openInEditor(project, path).psiFile.also { openFiles.add(it.virtualFile) }
fun fixture(path: String): Fixture {
return fixture(Fixture.projectFileByName(project, path).virtualFile)
fun fixture(path: String, updateScriptDependenciesIfNeeded: Boolean = true): Fixture {
return fixture(Fixture.projectFileByName(project, path).virtualFile, updateScriptDependenciesIfNeeded)
}
fun fixture(file: VirtualFile): Fixture {
fun fixture(file: VirtualFile, updateScriptDependenciesIfNeeded: Boolean = true): Fixture {
val fixture = Fixture.openFixture(project, file)
openFiles.add(fixture.vFile)
val fileName = file.name
if (Fixture.isAKotlinScriptFile(fileName)) {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile)
}
if (fileName.endsWith(KotlinFileType.EXTENSION)) {
assert(fixture.psiFile is KtFile) {
"$file expected to be a Kotlin file"
}
}
if (updateScriptDependenciesIfNeeded) {
fixture.updateScriptDependenciesIfNeeded()
}
return fixture
}
@@ -385,12 +270,71 @@ class PerformanceSuite {
}
fun <T> measure(vararg name: String, clearCaches: Boolean = true, f: MeasurementScope<T>.() -> Unit): List<T?> {
val after = { if (clearCaches) PsiManager.getInstance(project).dropPsiCaches() }
val after = wrapAfter(clearCaches)
return app.stats.measure(name.joinToString("-"), f, after)
}
fun <T> measure(vararg name: String, fixture: Fixture, f: MeasurementScope<T>.() -> Unit): List<T?> =
measure("${name.joinToString("-")} ${fixture.fileName}", f = f)
private fun wrapAfter(clearCaches: Boolean): () -> Unit {
val after = if (clearCaches) {
fun() { project.cleanupCaches() }
} else {
fun() {}
}
return after
}
fun <T> measure(fixture: Fixture, f: MeasurementScope<T>.() -> Unit): List<T?> =
measure(fixture.fileName, f = f)
fun <T> measure(fixture: Fixture, vararg name: String, f: MeasurementScope<T>.() -> Unit): List<T?> =
measure(combineName(fixture, *name), f = f)
fun measureTypeAndHighlight(
fixture: Fixture,
vararg name: String,
f: TypeAndHighlightMeasurementScope.() -> Unit = {}
): List<HighlightInfo?> {
val after = wrapAfter(true)
return app.stats.measureTypeAndHighlight(combineName(fixture, *name), fixture, f, after)
}
fun measureHighlight(fixture: Fixture, vararg name: String): List<List<HighlightInfo>?> {
return measure(combineNameWithSimpleFileName("highlighting", fixture, *name)) {
before = {
fixture.openInEditor()
}
test = {
val document = FileDocumentManager.getInstance().getDocument(fixture.vFile)!!
val editor = EditorFactory.getInstance().getEditors(document).first()
commitAllDocuments()
CodeInsightTestFixtureImpl.instantiateAndRun(fixture.psiFile, editor, ArrayUtilRt.EMPTY_INT_ARRAY, true)
}
after = {
fixture.close()
project.cleanupCaches()
}
}
}
fun measureTypeAndAutoCompletion(fixture: Fixture, vararg name: String, clearCaches: Boolean = true, f: TypeAndAutoCompletionMeasurementScope.() -> Unit): List<String?> {
val after = wrapAfter(clearCaches)
return app.stats.measureTypeAndAutoCompletion(combineName(fixture, *name), fixture, f, after)
}
fun measureTypeAndUndo(fixture: Fixture, vararg name: String, clearCaches: Boolean = true, f: TypeAndUndoMeasurementScope.() -> Unit): List<String?> {
val after = wrapAfter(clearCaches)
return app.stats.measureTypeAndUndo(combineName(fixture, *name), fixture, f, after)
}
fun combineName(fixture: Fixture, vararg name: String) =
listOf(name.joinToString("-"), fixture.simpleFilename())
.filter { it.isNotEmpty() }
.joinToString(" ")
fun combineNameWithSimpleFileName(type: String, fixture: Fixture, vararg name: String): String =
listOf(type, name.joinToString("-"), fixture.simpleFilename())
.filter { it.isNotEmpty() }
.joinToString(" ")
override fun close(): Unit = RunAll(
{ compilerTester?.tearDown() },
@@ -425,7 +369,7 @@ class PerformanceSuite {
val openProject = OpenProject(
projectPath = projectPath,
projectName = config.name,
projectName = config.projectName,
jdk = app.jdk,
projectOpenAction = config.openWith
)
@@ -464,7 +408,7 @@ data class CustomProfile(val inspectionNames: List<String>) : ProjectProfile()
fun UsefulTestCase.suite(
suiteName: String? = null,
config: PerformanceSuite.StatsScopeConfig = PerformanceSuite.StatsScopeConfig(),
config: StatsScopeConfig = StatsScopeConfig(),
block: PerformanceSuite.StatsScope.() -> Unit
) {
val stats = Stats(config.name ?: suiteName ?: name, outputConfig = config.outputConfig, profilerConfig = config.profilerConfig)
@@ -2,17 +2,17 @@
package org.jetbrains.kotlin.idea.perf.synthetic
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.testFramework.UsefulTestCase
import com.intellij.usages.Usage
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.perf.Parameter
import org.jetbrains.kotlin.idea.perf.profilers.ProfilerConfig
import org.jetbrains.kotlin.idea.perf.suite.DefaultProfile
import org.jetbrains.kotlin.idea.perf.suite.PerformanceSuite
import org.jetbrains.kotlin.idea.perf.suite.StatsScopeConfig
import org.jetbrains.kotlin.idea.perf.suite.suite
import org.jetbrains.kotlin.idea.perf.util.*
import org.jetbrains.kotlin.idea.perf.util.PerformanceSuite.TypingConfig
import org.jetbrains.kotlin.idea.perf.util.registerLoadingErrorsHeadlessNotifier
import org.jetbrains.kotlin.idea.test.IDEA_TEST_DATA_DIR
import org.jetbrains.kotlin.idea.testFramework.commitAllDocuments
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners
import org.junit.runner.RunWith
@@ -23,6 +23,14 @@ open class PerformanceStressTest : UsefulTestCase() {
protected open fun outputConfig(): OutputConfig = OutputConfig()
protected open fun suiteWithConfig(suiteName: String, name: String? = null, block: PerformanceSuite.StatsScope.() -> Unit) {
suite(
suiteName,
config = StatsScopeConfig(name = name, outputConfig = outputConfig(), profilerConfig = profileConfig()),
block = block
)
}
override fun setUp() {
super.setUp()
@@ -44,10 +52,7 @@ open class PerformanceStressTest : UsefulTestCase() {
val numberOfPackagesWithCandidates = 50
val name = "findUsages${numberOfFuns}_$numberOfPackagesWithCandidates" + if (withCompilerIndex) "_with_cri" else ""
suite(
suiteName = name,
config = PerformanceSuite.StatsScopeConfig(name = name, outputConfig = outputConfig(), profilerConfig = profileConfig()),
) {
suiteWithConfig(name) {
app {
warmUpProject()
@@ -96,30 +101,27 @@ open class PerformanceStressTest : UsefulTestCase() {
}
fixture("src/pkg1/DataClass.kt").use { fixture ->
val typingConfig = PerformanceSuite.CursorConfig(fixture, marker = "DataClass")
with(fixture.cursorConfig) { marker = "DataClass" }
with(config) {
warmup = 8
iterations = 15
}
measure<Set<Usage>>("findUsages", fixture = fixture) {
measure<Set<Usage>>(fixture, "findUsages") {
before = {
moveCursor(typingConfig)
fixture.moveCursor()
}
test = {
val findUsages = findUsages(typingConfig)
val findUsages = findUsages(fixture.cursorConfig)
// 1 from import
// + numberOfUsages as function argument
// + numberOfUsages as return type functions
// + numberOfUsages as new instance in a body of function
// in a SomeService
TestCase.assertEquals(1 + 3 * numberOfFuns, findUsages.size)
assertEquals(1 + 3 * numberOfFuns, findUsages.size)
findUsages
}
after = {
}
}
}
}
@@ -132,10 +134,7 @@ open class PerformanceStressTest : UsefulTestCase() {
val generatedTypes = mutableListOf(listOf<String>())
generateTypes(arrayOf("Int", "String", "Long", "List<Int>", "Array<Int>"), generatedTypes)
suite(
suiteName = "Lots of overloaded method project",
config = PerformanceSuite.StatsScopeConfig(name = "kt-35135 project", outputConfig = outputConfig(), profilerConfig = profileConfig()),
) {
suiteWithConfig("Lots of overloaded method project", "kt-35135 project") {
app {
warmUpProject()
@@ -177,30 +176,18 @@ open class PerformanceStressTest : UsefulTestCase() {
profile(DefaultProfile)
fixture("src/main/java/pkg/SomeClass.kt").use { fixture ->
val typingConfig = TypingConfig(
fixture,
marker = "ov",
insertString = "override fun foo(): String = TODO()",
with(fixture.typingConfig) {
marker = "ov"
insertString = "override fun foo(): String = TODO()"
delayMs = 50
)
}
with(config) {
warmup = 8
iterations = 15
}
measure<List<HighlightInfo>>("type override fun foo()", fixture = fixture) {
before = {
moveCursor(typingConfig)
}
test = {
typeAndHighlight(typingConfig)
}
after = {
fixture.restoreText()
commitAllDocuments()
}
}
measureTypeAndHighlight(fixture, "type override fun foo()")
}
}
}
@@ -11,6 +11,9 @@ import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.perf.suite.DefaultProfile
import org.jetbrains.kotlin.idea.perf.suite.EmptyProfile
import org.jetbrains.kotlin.idea.perf.suite.suite
import org.jetbrains.kotlin.idea.perf.util.*
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
import org.jetbrains.kotlin.idea.testFramework.relativePath
@@ -47,6 +47,8 @@ import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.util.toPsiFile
import org.jetbrains.kotlin.idea.perf.suite.CursorConfig
import org.jetbrains.kotlin.idea.perf.suite.TypingConfig
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
@@ -59,7 +61,11 @@ class Fixture(
) : AutoCloseable {
private var delegate = EditorTestFixture(project, editor, vFile)
private var savedText: String? = null
var savedText: String? = null
get() = field
private set(value) {
field = value
}
val document: Document
get() = editor.document
@@ -71,6 +77,11 @@ class Fixture(
storeText()
}
fun simpleFilename(): String {
val lastIndexOf = fileName.lastIndexOf('/')
return if (lastIndexOf >= 0) fileName.substring(lastIndexOf + 1) else fileName
}
fun doHighlighting(): List<HighlightInfo> = delegate.doHighlighting()
fun findUsages(psiElement: PsiElement): Set<Usage> {
@@ -79,6 +90,14 @@ class Fixture(
return findUsagesManager.doFindUsages(arrayOf(psiElement), emptyArray(), handler, handler.findUsagesOptions, false).usages
}
fun type() {
val string = typingConfig.insertString ?: error("insertString has to be specified")
for (i in string.indices) {
type(string[i])
typingConfig.delayMs?.let(Thread::sleep)
}
}
fun type(s: String) {
delegate.type(s)
}
@@ -87,6 +106,21 @@ class Fixture(
delegate.type(c)
}
fun typeAndHighlight(): List<HighlightInfo> {
type()
return doHighlighting()
}
fun moveCursor() {
val tasksIdx = cursorConfig.marker?.let { marker ->
text.indexOf(marker).also {
check(it > 0) { "marker '$marker' not found in ${fileName}" }
}
} ?: 0
editor.caretModel.moveToOffset(tasksIdx)
}
fun performEditorAction(actionId: String): Boolean {
selectEditor()
return delegate.performEditorAction(actionId)
@@ -144,11 +178,27 @@ class Fixture(
dispatchAllInvocationEvents()
}
fun openInEditor() {
openInEditor(project, vFile)
}
fun updateScriptDependenciesIfNeeded() {
if (isAKotlinScriptFile(fileName)) {
Stats.runAndMeasure("update script dependencies for $fileName") {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(psiFile)
}
}
}
override fun close() {
savedText = null
project.close(vFile)
}
val cursorConfig = CursorConfig(this)
val typingConfig = TypingConfig(this)
companion object {
// quite simple impl - good so far
fun isAKotlinScriptFile(fileName: String) = fileName.endsWith(KotlinParserDefinition.STD_SCRIPT_EXT)
@@ -195,7 +245,7 @@ class Fixture(
fun openFixture(project: Project, fileName: String): Fixture {
val fileInEditor = openFileInEditor(project, fileName)
val fileInEditor = openInEditor(project, fileName)
val file = fileInEditor.psiFile
val editorFactory = EditorFactory.getInstance()
val editor = editorFactory.getEditors(fileInEditor.document, project)[0]
@@ -204,7 +254,7 @@ class Fixture(
}
fun openFixture(project: Project, file: VirtualFile): Fixture {
val fileInEditor = openFileInEditor(project, file)
val fileInEditor = openInEditor(project, file)
val psiFile = fileInEditor.psiFile
val editorFactory = EditorFactory.getInstance()
val editor = editorFactory.getEditors(fileInEditor.document, project)[0]
@@ -242,12 +292,12 @@ class Fixture(
return virtualFiles.iterator().next().toPsiFile(project)!!
}
fun openFileInEditor(project: Project, name: String): EditorFile {
fun openInEditor(project: Project, name: String): EditorFile {
val psiFile = projectFileByName(project, name)
return openFileInEditor(project, psiFile.virtualFile)
return openInEditor(project, psiFile.virtualFile)
}
fun openFileInEditor(project: Project, vFile: VirtualFile): EditorFile {
fun openInEditor(project: Project, vFile: VirtualFile): EditorFile {
val fileDocumentManager = FileDocumentManager.getInstance()
val fileEditorManager = FileEditorManager.getInstance(project)
@@ -281,7 +331,7 @@ class Fixture(
lookupElements: List<String>,
revertChangesAtTheEnd: Boolean = true
) {
val fileInEditor = openFileInEditor(project, fileName)
val fileInEditor = openInEditor(project, fileName)
val editor = EditorFactory.getInstance().getEditors(fileInEditor.document, project)[0]
val fixture = Fixture(fileName, project, editor, fileInEditor.psiFile)