Add build gradle kts performance tests

This commit is contained in:
Vladimir Dolzhenko
2019-07-02 14:28:42 +03:00
parent c292380aff
commit 72d74ff888
12 changed files with 394 additions and 113 deletions
+1
View File
@@ -181,6 +181,7 @@ dependencies {
performanceTestCompile(sourceSets["test"].output) performanceTestCompile(sourceSets["test"].output)
performanceTestCompile(sourceSets["main"].output) performanceTestCompile(sourceSets["main"].output)
performanceTestCompile(project(":nj2k")) performanceTestCompile(project(":nj2k"))
performanceTestCompile(intellijPluginDep("gradle"))
performanceTestRuntime(sourceSets["performanceTest"].output) performanceTestRuntime(sourceSets["performanceTest"].output)
} }
@@ -8,9 +8,8 @@ package org.jetbrains.kotlin.idea.perf
import org.jetbrains.kotlin.idea.completion.test.handlers.CompletionHandlerTestBase import org.jetbrains.kotlin.idea.completion.test.handlers.CompletionHandlerTestBase
import com.intellij.application.options.CodeStyle import com.intellij.application.options.CodeStyle
import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.completion.CompletionType
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils
import org.jetbrains.kotlin.idea.completion.test.configureWithExtraFile import org.jetbrains.kotlin.idea.completion.test.configureWithExtraFile
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
@@ -127,7 +126,7 @@ abstract class AbstractPerformanceCompletionHandlerTests(
val testName = getTestName(false) val testName = getTestName(false)
val stats = stats() val stats = stats()
stats.perfTest( stats.perfTest<Unit, Unit>(
testName = testName, testName = testName,
setUp = { setUp = {
setUpFixture(testPath) setUpFixture(testPath)
@@ -136,11 +135,9 @@ abstract class AbstractPerformanceCompletionHandlerTests(
perfTestCore(completionType, time, lookupString, itemText, tailText, completionChar) perfTestCore(completionType, time, lookupString, itemText, tailText, completionChar)
}, },
tearDown = { tearDown = {
assertNotNull(it) runWriteAction {
myFixture.file.delete()
FileDocumentManager.getInstance().reloadFromDisk(editor.document) }
fixture.configureByText(KotlinFileType.INSTANCE, "")
commitAllDocuments()
}) })
} }
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.idea.perf package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.completion.CompletionType import com.intellij.codeInsight.completion.CompletionType
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider import org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider
@@ -97,20 +98,19 @@ abstract class AbstractPerformanceCompletionIncrementalResolveTest : KotlinLight
} }
} }
private fun innerPerfTest(name: String, setUpBody: () -> Unit) { private fun innerPerfTest(name: String, setUpBody: (TestData<Unit, Array<LookupElement>>) -> Unit) {
CompletionBindingContextProvider.ENABLED = true CompletionBindingContextProvider.ENABLED = true
try { try {
stats.perfTest( stats.perfTest(
testName = name, testName = name,
setUp = setUpBody, setUp = setUpBody,
test = { perfTestCore() }, test = { it.value = perfTestCore() },
tearDown = { tearDown = {
// no reasons to validate output as it is a performance test // no reasons to validate output as it is a performance test
assertNotNull(it) assertNotNull(it.value)
runWriteAction {
FileDocumentManager.getInstance().reloadFromDisk(editor.document) myFixture.file.delete()
myFixture.configureByText(KotlinFileType.INSTANCE, "") }
commitAllDocuments()
} }
) )
} finally { } finally {
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.perf package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.application.runWriteAction
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl.ensureIndexesUpToDate import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl.ensureIndexesUpToDate
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
@@ -71,17 +71,16 @@ abstract class AbstractPerformanceHighlightingTest : KotlinLightCodeInsightFixtu
} }
} }
private fun innerPerfTest(name: String, setUpBody: () -> Unit) { private fun innerPerfTest(name: String, setUpBody: (TestData<Unit, MutableList<HighlightInfo>>) -> Unit) {
stats.perfTest( stats.perfTest<Unit, MutableList<HighlightInfo>>(
testName = name, testName = name,
setUp = { setUpBody() }, setUp = { setUpBody(it) },
test = { perfTestCore() }, test = { it.value = perfTestCore() },
tearDown = { tearDown = {
assertNotNull("no reasons to validate output as it is a performance test", it) assertNotNull("no reasons to validate output as it is a performance test", it.value)
runWriteAction {
FileDocumentManager.getInstance().reloadFromDisk(editor.document) myFixture.file.delete()
myFixture.configureByText(KotlinFileType.INSTANCE, "") }
commitAllDocuments()
} }
) )
} }
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.idea.perf package org.jetbrains.kotlin.idea.perf
import com.intellij.application.options.CodeStyle import com.intellij.application.options.CodeStyle
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.application.runWriteAction
import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.codeStyle.PackageEntry import com.intellij.psi.codeStyle.PackageEntry
import com.intellij.testFramework.LightProjectDescriptor import com.intellij.testFramework.LightProjectDescriptor
@@ -81,7 +81,7 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest
val importInsertHelper = ImportInsertHelper.getInstance(project) val importInsertHelper = ImportInsertHelper.getInstance(project)
val psiDocumentManager = PsiDocumentManager.getInstance(project) val psiDocumentManager = PsiDocumentManager.getInstance(project)
stats().perfTest( stats().perfTest<Unit, String>(
testName = testName, testName = testName,
setUp = { setUp = {
fixture.configureByFile(testPath) fixture.configureByFile(testPath)
@@ -90,11 +90,12 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest
fileText = file.text fileText = file.text
}, },
test = { test = {
project.executeWriteCommand<String?>("") { it.value = project.executeWriteCommand<String?>("") {
perfTestCore(file, fqName, filter, descriptorName, importInsertHelper, psiDocumentManager) perfTestCore(file, fqName, filter, descriptorName, importInsertHelper, psiDocumentManager)
} }
}, },
tearDown = { log: String? -> tearDown = {
val log = it.value
KotlinTestUtils.assertEqualsToFile(File("$testPath.after"), fixture.file.text) KotlinTestUtils.assertEqualsToFile(File("$testPath.after"), fixture.file.text)
if (log != null) { if (log != null) {
val logFile = File("$testPath.log") val logFile = File("$testPath.log")
@@ -104,8 +105,9 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest
TestCase.assertFalse(logFile.exists()) TestCase.assertFalse(logFile.exists())
} }
} }
commitAllDocuments() runWriteAction {
FileDocumentManager.getInstance().reloadFromDisk(editor.document) myFixture.file.delete()
}
}) })
} finally { } finally {
CodeStyle.dropTemporarySettings(project) CodeStyle.dropTemporarySettings(project)
@@ -7,7 +7,9 @@ package org.jetbrains.kotlin.idea.perf
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiDocumentManager
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest
import org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPasteProcessor import org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPasteProcessor
@@ -44,13 +46,8 @@ abstract class AbstractPerformanceJavaToKotlinCopyPasteConversionTest(private va
} }
} }
override fun tearDown() {
commitAllDocuments()
super.tearDown()
}
private fun doWarmUpPerfTest() { private fun doWarmUpPerfTest() {
stats().perfTest( stats().perfTest<Unit, Unit>(
testName = "warm-up", testName = "warm-up",
setUp = { setUp = {
with(myFixture) { with(myFixture) {
@@ -64,9 +61,13 @@ abstract class AbstractPerformanceJavaToKotlinCopyPasteConversionTest(private va
myFixture.performEditorAction(IdeActions.ACTION_PASTE) myFixture.performEditorAction(IdeActions.ACTION_PASTE)
}, },
tearDown = { tearDown = {
commitAllDocuments() val document = myFixture.getDocument(myFixture.file)
PsiDocumentManager.getInstance(project).commitDocument(document)
kotlin.test.assertFalse(!ConvertJavaCopyPasteProcessor.conversionPerformed, "No conversion to Kotlin suggested") kotlin.test.assertFalse(!ConvertJavaCopyPasteProcessor.conversionPerformed, "No conversion to Kotlin suggested")
assertEquals("class Foo {\n private val value: String? = null\n}", myFixture.file.text) assertEquals("class Foo {\n private val value: String? = null\n}", myFixture.file.text)
runWriteAction {
myFixture.file.delete()
}
} }
) )
} }
@@ -85,7 +86,7 @@ abstract class AbstractPerformanceJavaToKotlinCopyPasteConversionTest(private va
val fileText = myFixture.editor.document.text val fileText = myFixture.editor.document.text
val noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty() val noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty()
stats().perfTest( stats().perfTest<Unit, Unit>(
testName = testName, testName = testName,
setUp = { setUp = {
myFixture.configureByFiles("$testName.java") myFixture.configureByFiles("$testName.java")
@@ -5,51 +5,82 @@
package org.jetbrains.kotlin.idea.perf package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.daemon.*
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl.waitForAllEditorsFinallyLoaded
import com.intellij.codeInsight.daemon.impl.HighlightInfo import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInspection.ex.InspectionProfileImpl import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory
import com.intellij.ide.highlighter.ModuleFileType import com.intellij.ide.highlighter.ModuleFileType
import com.intellij.ide.impl.ProjectUtil
import com.intellij.ide.startup.impl.StartupManagerImpl
import com.intellij.idea.IdeaTestApplication import com.intellij.idea.IdeaTestApplication
import com.intellij.openapi.Disposable 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.ApplicationManager
import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleTypeId import com.intellij.openapi.module.ModuleTypeId
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.project.impl.ProjectImpl
import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
import com.intellij.openapi.roots.FileIndexFacade
import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.util.Computable import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vcs.changes.ChangeListManager import com.intellij.openapi.vcs.changes.ChangeListManager
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.testFramework.PsiTestUtil import com.intellij.psi.PsiManager
import com.intellij.testFramework.RunAll import com.intellij.psi.impl.search.IndexPatternBuilder
import com.intellij.testFramework.UsefulTestCase import com.intellij.psi.xml.XmlFileNSInfoProvider
import com.intellij.testFramework.*
import com.intellij.testFramework.fixtures.EditorTestFixture
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.testFramework.propertyBased.MadTestingUtil
import com.intellij.util.ThrowableRunnable import com.intellij.util.ThrowableRunnable
import com.intellij.util.indexing.UnindexedFilesUpdater
import com.intellij.util.io.exists import com.intellij.util.io.exists
import com.intellij.xml.XmlSchemaProvider
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
import org.jetbrains.kotlin.idea.core.util.toPsiFile import org.jetbrains.kotlin.idea.core.util.toPsiFile
import org.jetbrains.kotlin.idea.framework.KotlinSdkType import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
import org.jetbrains.plugins.gradle.service.project.GradleProjectOpenProcessor
import java.io.File import java.io.File
import java.nio.file.Paths import java.nio.file.Paths
import java.util.concurrent.TimeUnit
abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
// myProject is not required for all potential perf test cases // myProject is not required for all potential perf test cases
private var myProject: Project? = null protected var myProject: Project? = null
private lateinit var jdk18: Sdk private lateinit var jdk18: Sdk
private lateinit var myApplication: IdeaTestApplication private lateinit var myApplication: IdeaTestApplication
override fun isStressTest(): Boolean = true
override fun isPerformanceTest(): Boolean = false
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
@@ -71,7 +102,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
jdkTable.addJdk(internal, testRootDisposable) jdkTable.addJdk(internal, testRootDisposable)
KotlinSdkType.setUpIfNeeded() KotlinSdkType.setUpIfNeeded()
} }
InspectionProfileImpl.INIT_INSPECTIONS = true
} }
override fun tearDown() { override fun tearDown() {
@@ -79,6 +109,9 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
ThrowableRunnable { super.tearDown() }, ThrowableRunnable { super.tearDown() },
ThrowableRunnable { ThrowableRunnable {
if (myProject != null) { if (myProject != null) {
DaemonCodeAnalyzerSettings.getInstance().isImportHintEnabled = true // return default value to avoid unnecessary save
(StartupManager.getInstance(myProject!!) as StartupManagerImpl).checkCleared()
(DaemonCodeAnalyzer.getInstance(myProject!!) as DaemonCodeAnalyzerImpl).cleanupAfterTest()
closeProject(myProject!!) closeProject(myProject!!)
myProject = null myProject = null
} }
@@ -100,7 +133,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
note: String, note: String,
path: String = "idea/testData/perfTest" path: String = "idea/testData/perfTest"
): Project { ): Project {
val projectPath = "$path/$name" val projectPath = File("$path/$name").canonicalPath
val warmUpIterations = 1 val warmUpIterations = 1
val iterations = 3 val iterations = 3
@@ -109,39 +142,126 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
var lastProject: Project? = null var lastProject: Project? = null
var counter = 0 var counter = 0
stats.perfTest<Project, Project>( stats.perfTest<Unit, Pair<Project, Boolean>>(
warmUpIterations = warmUpIterations, warmUpIterations = warmUpIterations,
iterations = iterations, iterations = iterations,
testName = "open project${if (note.isNotEmpty()) " $note" else ""}", testName = "open project${if (note.isNotEmpty()) " $note" else ""}",
test = { test = {
val projectPathExists = Paths.get(projectPath, ".idea").exists()
val project = projectManagerEx.loadAndOpenProject(projectPath)!! val project = if (projectPathExists) {
if (!Paths.get(projectPath, ".idea").exists()) { val project = ProjectUtil.openProject(projectPath, null, false)!!
project
} else {
val project = projectManagerEx.loadAndOpenProject(projectPath)!!
initKotlinProject(project, projectPath, name) initKotlinProject(project, projectPath, name)
project
} }
(project as ProjectImpl).registerComponentImplementation(
FileEditorManager::class.java,
FileEditorManagerImpl::class.java
)
projectManagerEx.openTestProject(project) projectManagerEx.openTestProject(project)
val changeListManagerImpl = ChangeListManager.getInstance(project) as ChangeListManagerImpl dispatchAllInvocationEvents()
changeListManagerImpl.waitUntilRefreshed()
project with(StartupManager.getInstance(project) as StartupManagerImpl) {
}, scheduleInitialVfsRefresh()
tearDown = { project -> runPostStartupActivities()
lastProject = project }
val prj = project!!
with(ChangeListManager.getInstance(project) as ChangeListManagerImpl) {
// close all project but last - we're going to return and use it further waitUntilRefreshed()
if (counter < warmUpIterations + iterations - 1) { }
closeProject(prj)
it.value = Pair(project, projectPathExists)
},
tearDown = {
it.value?.let { pair ->
val project = pair.first
// import gradle project if `$project/.idea` is present but modules are not imported
// it is a temporary dirty hack as it is fixed in latest IC:
// ProjectUtil.openProject picks up gradle import via extension point
if (pair.second && ModuleManager.getInstance(project).modules.isEmpty()) {
dispatchAllInvocationEvents()
val virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(projectPath)!!
FileDocumentManager.getInstance().saveAllDocuments()
GradleProjectOpenProcessor.openGradleProject(project, null, Paths.get(virtualFile.path))
dispatchAllInvocationEvents()
runInEdtAndWait {
PlatformTestUtil.saveProject(project)
}
}
assertTrue(
"project has to have at least one module",
ModuleManager.getInstance(project).modules.isNotEmpty()
)
lastProject = project
VirtualFileManager.getInstance().syncRefresh()
// close all project but last - we're going to return and use it further
if (counter < warmUpIterations + iterations - 1) {
closeProject(project)
}
counter++
} }
counter++
} }
) )
// indexing
lastProject?.let { project ->
invalidateLibraryCache(project)
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project)
dispatchAllInvocationEvents()
with(DumbService.getInstance(project)) {
queueTask(UnindexedFilesUpdater(project))
completeJustSubmittedTasks()
}
dispatchAllInvocationEvents()
enableAnnotatorsAndLoadDefinitions(project)
}
return lastProject!! return lastProject!!
} }
protected fun enableAnnotatorsAndLoadDefinitions() = enableAnnotatorsAndLoadDefinitions(myProject!!)
protected fun enableAnnotatorsAndLoadDefinitions(project: Project) {
InjectedLanguageManager.getInstance(project) // zillion of Dom Sem classes
LanguageAnnotators.INSTANCE.allForLanguage(JavaLanguage.INSTANCE) // pile of annotator classes loads
LanguageAnnotators.INSTANCE.allForLanguage(StdLanguages.XML)
LanguageAnnotators.INSTANCE.allForLanguage(KotlinLanguage.INSTANCE)
DaemonAnalyzerTestCase.assertTrue(
"side effect: to load extensions",
ProblemHighlightFilter.EP_NAME.extensions.toMutableList()
.plus(ImplicitUsageProvider.EP_NAME.extensions)
.plus(XmlSchemaProvider.EP_NAME.extensions)
.plus(XmlFileNSInfoProvider.EP_NAME.extensions)
.plus(ExternalAnnotatorsFilter.EXTENSION_POINT_NAME.extensions)
.plus(IndexPatternBuilder.EP_NAME.extensions).isNotEmpty()
)
// side effect: to load script definitions"
val scriptDefinitionsManager = ScriptDefinitionsManager.getInstance(project)
scriptDefinitionsManager.getAllDefinitions()
dispatchAllInvocationEvents()
assertTrue(scriptDefinitionsManager.isReady())
assertFalse(KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled)
}
private fun initKotlinProject( private fun initKotlinProject(
project: Project, project: Project,
projectPath: String, projectPath: String,
@@ -166,7 +286,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
protected fun perfHighlightFile(name: String, stats: Stats): List<HighlightInfo> = protected fun perfHighlightFile(name: String, stats: Stats): List<HighlightInfo> =
perfHighlightFile(myProject!!, name, stats) perfHighlightFile(myProject!!, name, stats)
protected fun perfHighlightFile( protected fun perfHighlightFile(
project: Project, project: Project,
fileName: String, fileName: String,
@@ -174,42 +293,125 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
note: String = "" note: String = ""
): List<HighlightInfo> { ): List<HighlightInfo> {
var highlightInfos: List<HighlightInfo> = emptyList() var highlightInfos: List<HighlightInfo> = emptyList()
stats.perfTest( IdentifierHighlighterPassFactory.doWithHighlightingEnabled {
testName = "highlighting ${if (note.isNotEmpty()) "$note " else ""}${simpleFilename(fileName)}", stats.perfTest<EditorFile, List<HighlightInfo>>(
setUp = { testName = "highlighting ${if (note.isNotEmpty()) "$note " else ""}${simpleFilename(fileName)}",
val fileInEditor = openFileInEditor(project, fileName) setUp = {
fileInEditor.psiFile it.setUpValue = openFileInEditor(project, fileName)
}, },
test = { file -> test = {
highlightFile(file!!) val file = it.setUpValue
}, it.value = highlightFile(project, file!!.psiFile)
tearDown = { },
highlightInfos = it ?: emptyList() tearDown = {
commitAllDocuments() highlightInfos = it.value ?: emptyList()
} commitAllDocuments()
) FileEditorManager.getInstance(project).closeFile(it.setUpValue!!.psiFile.virtualFile)
PsiManager.getInstance(project).dropPsiCaches()
}
)
}
//println("${"-".repeat(40)}\n$fileName ->\n${highlightInfos.joinToString("\n")}\n")
return highlightInfos return highlightInfos
} }
private fun highlightFile(psiFile: PsiFile): List<HighlightInfo> { private fun highlightFile(project: Project, psiFile: PsiFile): List<HighlightInfo> {
val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!! val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!!
val editor = EditorFactory.getInstance().getEditors(document).first() val editor = EditorFactory.getInstance().getEditors(document).first()
return CodeInsightTestFixtureImpl.instantiateAndRun(psiFile, editor, intArrayOf(), false) val fixture = EditorTestFixture(project, editor, psiFile.virtualFile)
return fixture.doHighlighting(true)
} }
data class EditorFile(val psiFile: PsiFile, val document: Document) protected fun perfFileAnalysis(name: String, stats: Stats, note: String = "") =
perfFileAnalysis(myProject!!, name, stats, note = note)
private fun perfFileAnalysis(
project: Project,
fileName: String,
stats: Stats,
note: String = ""
) {
val disposable = Disposer.newDisposable("perfFileAnalysis $fileName")
MadTestingUtil.enableAllInspections(project, disposable)
try {
IdentifierHighlighterPassFactory.doWithHighlightingEnabled {
stats.perfTest(
testName = "fileAnalysis ${if (note.isNotEmpty()) "$note " else ""}${simpleFilename(fileName)}",
setUp = perfFileAnalysisSetUp(project, fileName),
test = perfFileAnalysisTest(project),
tearDown = perfFileAnalysisTearDown(fileName, project)
)
}
} finally {
Disposer.dispose(disposable)
}
}
private fun perfFileAnalysisSetUp(
project: Project,
fileName: String
): (TestData<EditorFile, List<HighlightInfo>>) -> Unit {
return {
val fileInEditor = openFileInEditor(project, fileName)
// Note: Kotlin scripts require dependencies to be loaded
if (isAKotlinScriptFile(fileName)) {
val vFile = fileInEditor.psiFile.virtualFile
ScriptDependenciesManager.updateScriptDependenciesSynchronously(vFile, project)
}
//enableHints(false)
println("fileAnalysis -> $fileName\n")
it.setUpValue = fileInEditor
}
}
// quite simple impl - good so far
fun isAKotlinScriptFile(fileName: String) = fileName.endsWith(".kts")
private fun perfFileAnalysisTest(project: Project): (TestData<EditorFile, List<HighlightInfo>>) -> Unit {
return {
val fileInEditor = it.setUpValue!!
it.value = highlightFile(project, fileInEditor.psiFile)
}
}
private fun perfFileAnalysisTearDown(
fileName: String,
project: Project
): (TestData<EditorFile, List<HighlightInfo>>) -> Unit {
return {
commitAllDocuments()
//println("fileAnalysis <- $fileName:\n${it.value?.joinToString("\n")}\n")
println("fileAnalysis <- $fileName:\n${it.value?.size ?: 0} highlightInfos\n")
FileEditorManager.getInstance(project).closeFile(it.setUpValue!!.psiFile.virtualFile)
PsiManager.getInstance(project).dropPsiCaches()
}
}
private fun openFileInEditor(project: Project, name: String): EditorFile { private fun openFileInEditor(project: Project, name: String): EditorFile {
val fileDocumentManager = FileDocumentManager.getInstance()
val fileEditorManager = FileEditorManager.getInstance(project)
val psiFile = projectFileByName(project, name) val psiFile = projectFileByName(project, name)
val vFile = psiFile.virtualFile val vFile = psiFile.virtualFile
FileDocumentManager.getInstance().reloadFiles(vFile) assertTrue("file $vFile is not indexed yet", FileIndexFacade.getInstance(project).isInContent(vFile))
runInEdtAndWait {
fileEditorManager.openFile(vFile, true)
}
val document = fileDocumentManager.getDocument(vFile)!!
assertNotNull("doc not found for $vFile", EditorFactory.getInstance().getEditors(document))
assertTrue("expected non empty doc", document.text.isNotEmpty())
waitForAllEditorsFinallyLoaded(project, 30, TimeUnit.SECONDS)
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) return EditorFile(psiFile = psiFile, document = document)
} }
@@ -219,4 +421,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
val virtualFile = fileManager.refreshAndFindFileByUrl(url) val virtualFile = fileManager.refreshAndFindFileByUrl(url)
return virtualFile!!.toPsiFile(project)!! return virtualFile!!.toPsiFile(project)!!
} }
private data class EditorFile(val psiFile: PsiFile, val document: Document)
} }
@@ -67,4 +67,36 @@ class PerformanceProjectsTest : AbstractPerformanceProjectsTest() {
} }
} }
fun testKotlinProjectHighlightBuildGradle() {
tcSuite("Kotlin project highlight build gradle") {
val stats = Stats("kotlin project highlight build gradle")
stats.use {
perfOpenProject("perfTestProject", stats = it, path = "..")
enableAnnotatorsAndLoadDefinitions()
perfFileAnalysisBuildGradleKts(it)
perfFileAnalysisIdeaBuildGradleKts(it)
perfFileAnalysisJpsGradleKts(it)
perfFileAnalysisVersionGradleKts(it)
}
}
}
private fun perfFileAnalysisBuildGradleKts(it: Stats) {
perfFileAnalysis("build.gradle.kts", stats = it)
}
private fun perfFileAnalysisIdeaBuildGradleKts(it: Stats) {
perfFileAnalysis("idea/build.gradle.kts", stats = it, note = "idea/")
}
private fun perfFileAnalysisJpsGradleKts(it: Stats) {
perfFileAnalysis("gradle/jps.gradle.kts", stats = it, note = "gradle/")
}
private fun perfFileAnalysisVersionGradleKts(it: Stats) {
perfFileAnalysis("gradle/versions.gradle.kts", stats = it, note = "gradle/")
}
} }
@@ -37,22 +37,22 @@ class Stats(val name: String = "", val header: Array<String> = arrayOf("Name", "
append(arrayOf(file, id, nanoTime.nsToMs)) append(arrayOf(file, id, nanoTime.nsToMs))
} }
fun <T, K> perfTest( fun <K, T> perfTest(
testName: String, testName: String,
warmUpIterations: Int = 3, warmUpIterations: Int = 3,
iterations: Int = 10, iterations: Int = 10,
setUp: () -> T? = { null }, setUp: (TestData<K, T>) -> Unit = { null },
test: (t: T?) -> K, test: (TestData<K, T>) -> Unit,
tearDown: (t: K?) -> Unit = {} tearDown: (TestData<K, T>) -> Unit = { null }
) { ) {
val namePrefix = "$name: $testName" val namePrefix = "$name: $testName"
val timingsNs = LongArray(iterations) val timingsNs = LongArray(iterations)
val errors = Array<Throwable?>(iterations, init = { null }) val errors = Array<Throwable?>(iterations, init = { null })
tcSuite(namePrefix) { tcSuite(namePrefix) {
warmUpPhase<K, T>(warmUpIterations, namePrefix, setUp, test, tearDown) warmUpPhase(warmUpIterations, namePrefix, setUp, test, tearDown)
mainPhase<K, T>(iterations, setUp, test, timingsNs, namePrefix, errors, tearDown) mainPhase(iterations, setUp, test, tearDown, timingsNs, namePrefix, errors)
val meanNs = timingsNs.average() val meanNs = timingsNs.average()
val meanMs = meanNs.toLong().nsToMs val meanMs = meanNs.toLong().nsToMs
@@ -81,27 +81,29 @@ class Stats(val name: String = "", val header: Array<String> = arrayOf("Name", "
private fun <K, T> mainPhase( private fun <K, T> mainPhase(
iterations: Int, iterations: Int,
setUp: () -> T?, setUp: (TestData<K, T>) -> Unit,
test: (t: T?) -> K, test: (TestData<K, T>) -> Unit,
tearDown: (TestData<K, T>) -> Unit,
timingsNs: LongArray, timingsNs: LongArray,
namePrefix: String, namePrefix: String,
errors: Array<Throwable?>, errors: Array<Throwable?>
tearDown: (t: K?) -> Unit
) { ) {
val testData = TestData<K, T>(null, null)
try { try {
for (attempt in 0 until iterations) { for (attempt in 0 until iterations) {
val setupValue: T? = setUp() testData.reset()
var value: K? = null setUp(testData)
try { try {
val spentNs = measureNanoTime { val spentNs = measureNanoTime {
value = test(setupValue) test(testData)
} }
timingsNs[attempt] = spentNs timingsNs[attempt] = spentNs
} catch (t: Throwable) { } catch (t: Throwable) {
println("error at $namePrefix #$attempt:") println("error at $namePrefix #$attempt:")
t.printStackTrace()
errors[attempt] = t errors[attempt] = t
} finally { } finally {
tearDown(value) tearDown(testData)
} }
} }
} catch (t: Throwable) { } catch (t: Throwable) {
@@ -113,27 +115,34 @@ class Stats(val name: String = "", val header: Array<String> = arrayOf("Name", "
private fun <K, T> warmUpPhase( private fun <K, T> warmUpPhase(
warmUpIterations: Int, warmUpIterations: Int,
namePrefix: String, namePrefix: String,
setUp: () -> T?, setUp: (TestData<K, T>) -> Unit,
test: (t: T?) -> K, test: (TestData<K, T>) -> Unit,
tearDown: (t: K?) -> Unit tearDown: (TestData<K, T>) -> Unit
) { ) {
val testData = TestData<K, T>(null, null)
for (attempt in 0 until warmUpIterations) { for (attempt in 0 until warmUpIterations) {
testData.reset()
val n = "$namePrefix warm-up #$attempt" val n = "$namePrefix warm-up #$attempt"
println("##teamcity[testStarted name='$n' captureStandardOutput='true']") println("##teamcity[testStarted name='$n' captureStandardOutput='true']")
try { try {
val setupValue: T? = setUp() setUp(testData)
var value: K? = null
var spentNs: Long = 0 var spentNs: Long = 0
try { try {
spentNs = measureNanoTime { spentNs = measureNanoTime {
value = test(setupValue) test(testData)
} }
} catch (t: Throwable) { } catch (t: Throwable) {
println("error at $n:") println("error at $n:\n")
t.printStackTrace()
println("\n")
tcPrintErrors(n, listOf(t)) tcPrintErrors(n, listOf(t))
throw t
} finally { } finally {
tearDown(value) tearDown(testData)
} }
val spentMs = spentNs.nsToMs val spentMs = spentNs.nsToMs
println("##teamcity[buildStatisticValue key='$n' value='$spentMs']") println("##teamcity[buildStatisticValue key='$n' value='$spentMs']")
@@ -153,6 +162,13 @@ class Stats(val name: String = "", val header: Array<String> = arrayOf("Name", "
} }
} }
data class TestData<SV, V>(var setUpValue: SV?, var value: V?) {
fun reset() {
setUpValue = null
value = null
}
}
inline fun tcSuite(name: String, block: () -> Unit) { inline fun tcSuite(name: String, block: () -> Unit) {
println("##teamcity[testSuiteStarted name='$name']") println("##teamcity[testSuiteStarted name='$name']")
block() block()
@@ -18,6 +18,7 @@ import com.intellij.lang.injection.InjectedLanguageManager
import com.intellij.lang.java.JavaLanguage import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ex.ApplicationEx import com.intellij.openapi.application.ex.ApplicationEx
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
@@ -43,7 +44,7 @@ abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProj
IdeaTestApplication.getInstance() IdeaTestApplication.getInstance()
// to prevent leaked SDKs: 1.8 and Kotlin SDK // to prevent leaked SDKs: 1.8 and Kotlin SDK
ApplicationManager.getApplication().runWriteAction { runWriteAction {
val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx() val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx()
val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") { val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") {
jdkTableImpl.internalJdk.homeDirectory!!.parent.path jdkTableImpl.internalJdk.homeDirectory!!.parent.path
@@ -11,6 +11,8 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.impl.PsiDocumentManagerBase import com.intellij.psi.impl.PsiDocumentManagerBase
import com.intellij.testFramework.EdtTestUtil import com.intellij.testFramework.EdtTestUtil
import com.intellij.util.ThrowableRunnable import com.intellij.util.ThrowableRunnable
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.parameterInfo.HintType
fun commitAllDocuments() { fun commitAllDocuments() {
ProjectManagerEx.getInstanceEx().openProjects.forEach { project -> ProjectManagerEx.getInstanceEx().openProjects.forEach { project ->
@@ -23,8 +25,23 @@ fun commitAllDocuments() {
} }
} }
fun enableHints(enable: Boolean) =
HintType.values().forEach { it.option.set(enable) }
fun runInEdtAndWait(block: () -> Unit) {
EdtTestUtil.runInEdtAndWait(ThrowableRunnable {
block()
})
}
fun dispatchAllInvocationEvents() {
runInEdtAndWait {
UIUtil.dispatchAllInvocationEvents()
}
}
fun closeProject(project: Project) { fun closeProject(project: Project) {
commitAllDocuments() dispatchAllInvocationEvents()
val projectManagerEx = ProjectManagerEx.getInstanceEx() val projectManagerEx = ProjectManagerEx.getInstanceEx()
projectManagerEx.forceCloseProject(project, true) projectManagerEx.forceCloseProject(project, true)
} }
@@ -11,6 +11,7 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.impl.PsiDocumentManagerBase import com.intellij.psi.impl.PsiDocumentManagerBase
import com.intellij.testFramework.EdtTestUtil import com.intellij.testFramework.EdtTestUtil
import com.intellij.util.ThrowableRunnable import com.intellij.util.ThrowableRunnable
import com.intellij.util.ui.UIUtil
fun commitAllDocuments() { fun commitAllDocuments() {
ProjectManagerEx.getInstanceEx().openProjects.forEach { project -> ProjectManagerEx.getInstanceEx().openProjects.forEach { project ->
@@ -23,8 +24,17 @@ fun commitAllDocuments() {
} }
} }
fun enableHints(enable: Boolean) =
HintType.values().forEach { it.option.set(enable) }
fun runInEdtAndWait(block: () -> Unit) {
EdtTestUtil.runInEdtAndWait(ThrowableRunnable {
block()
})
}
fun closeProject(project: Project) { fun closeProject(project: Project) {
commitAllDocuments() UIUtil.dispatchAllInvocationEvents()
val projectManagerEx = ProjectManagerEx.getInstanceEx() val projectManagerEx = ProjectManagerEx.getInstanceEx()
projectManagerEx.closeAndDispose(project) projectManagerEx.closeAndDispose(project)
} }