From 72d74ff88867f322da374564583fa7008d1069fb Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Tue, 2 Jul 2019 14:28:42 +0300 Subject: [PATCH] Add build gradle kts performance tests --- idea/build.gradle.kts | 1 + ...stractPerformanceCompletionHandlerTests.kt | 13 +- ...ormanceCompletionIncrementalResolveTest.kt | 16 +- .../AbstractPerformanceHighlightingTest.kt | 19 +- .../perf/AbstractPerformanceImportTest.kt | 14 +- ...anceJavaToKotlinCopyPasteConversionTest.kt | 17 +- .../perf/AbstractPerformanceProjectsTest.kt | 301 +++++++++++++++--- .../idea/perf/PerformanceProjectsTest.kt | 32 ++ .../org/jetbrains/kotlin/idea/perf/Stats.kt | 60 ++-- .../idea/perf/WholeProjectPerformanceTest.kt | 3 +- .../kotlin/idea/perf/projectRoutines.kt | 19 +- .../kotlin/idea/perf/projectRoutines.kt.182 | 12 +- 12 files changed, 394 insertions(+), 113 deletions(-) diff --git a/idea/build.gradle.kts b/idea/build.gradle.kts index 32e71ebe80f..b570bbc24dc 100644 --- a/idea/build.gradle.kts +++ b/idea/build.gradle.kts @@ -181,6 +181,7 @@ dependencies { performanceTestCompile(sourceSets["test"].output) performanceTestCompile(sourceSets["main"].output) performanceTestCompile(project(":nj2k")) + performanceTestCompile(intellijPluginDep("gradle")) performanceTestRuntime(sourceSets["performanceTest"].output) } diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt index be1fe57858e..5b5d26127fa 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionHandlerTests.kt @@ -8,9 +8,8 @@ package org.jetbrains.kotlin.idea.perf import org.jetbrains.kotlin.idea.completion.test.handlers.CompletionHandlerTestBase import com.intellij.application.options.CodeStyle 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 org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.completion.test.ExpectedCompletionUtils import org.jetbrains.kotlin.idea.completion.test.configureWithExtraFile import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings @@ -127,7 +126,7 @@ abstract class AbstractPerformanceCompletionHandlerTests( val testName = getTestName(false) val stats = stats() - stats.perfTest( + stats.perfTest( testName = testName, setUp = { setUpFixture(testPath) @@ -136,11 +135,9 @@ abstract class AbstractPerformanceCompletionHandlerTests( perfTestCore(completionType, time, lookupString, itemText, tailText, completionChar) }, tearDown = { - assertNotNull(it) - - FileDocumentManager.getInstance().reloadFromDisk(editor.document) - fixture.configureByText(KotlinFileType.INSTANCE, "") - commitAllDocuments() + runWriteAction { + myFixture.file.delete() + } }) } diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionIncrementalResolveTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionIncrementalResolveTest.kt index 1783cdf4955..f2f90588367 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionIncrementalResolveTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceCompletionIncrementalResolveTest.kt @@ -6,7 +6,8 @@ package org.jetbrains.kotlin.idea.perf 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 org.jetbrains.kotlin.idea.KotlinFileType 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) { CompletionBindingContextProvider.ENABLED = true try { stats.perfTest( testName = name, setUp = setUpBody, - test = { perfTestCore() }, + test = { it.value = perfTestCore() }, tearDown = { // no reasons to validate output as it is a performance test - assertNotNull(it) - - FileDocumentManager.getInstance().reloadFromDisk(editor.document) - myFixture.configureByText(KotlinFileType.INSTANCE, "") - commitAllDocuments() + assertNotNull(it.value) + runWriteAction { + myFixture.file.delete() + } } ) } finally { diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceHighlightingTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceHighlightingTest.kt index 30f8a6cb883..9bac9fa6ff5 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceHighlightingTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceHighlightingTest.kt @@ -6,7 +6,7 @@ package org.jetbrains.kotlin.idea.perf 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 org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase @@ -71,17 +71,16 @@ abstract class AbstractPerformanceHighlightingTest : KotlinLightCodeInsightFixtu } } - private fun innerPerfTest(name: String, setUpBody: () -> Unit) { - stats.perfTest( + private fun innerPerfTest(name: String, setUpBody: (TestData>) -> Unit) { + stats.perfTest>( testName = name, - setUp = { setUpBody() }, - test = { perfTestCore() }, + setUp = { setUpBody(it) }, + test = { it.value = perfTestCore() }, tearDown = { - assertNotNull("no reasons to validate output as it is a performance test", it) - - FileDocumentManager.getInstance().reloadFromDisk(editor.document) - myFixture.configureByText(KotlinFileType.INSTANCE, "") - commitAllDocuments() + assertNotNull("no reasons to validate output as it is a performance test", it.value) + runWriteAction { + myFixture.file.delete() + } } ) } diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceImportTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceImportTest.kt index 758120b1cf8..85908924b3d 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceImportTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceImportTest.kt @@ -6,7 +6,7 @@ package org.jetbrains.kotlin.idea.perf 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.codeStyle.PackageEntry import com.intellij.testFramework.LightProjectDescriptor @@ -81,7 +81,7 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest val importInsertHelper = ImportInsertHelper.getInstance(project) val psiDocumentManager = PsiDocumentManager.getInstance(project) - stats().perfTest( + stats().perfTest( testName = testName, setUp = { fixture.configureByFile(testPath) @@ -90,11 +90,12 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest fileText = file.text }, test = { - project.executeWriteCommand("") { + it.value = project.executeWriteCommand("") { perfTestCore(file, fqName, filter, descriptorName, importInsertHelper, psiDocumentManager) } }, - tearDown = { log: String? -> + tearDown = { + val log = it.value KotlinTestUtils.assertEqualsToFile(File("$testPath.after"), fixture.file.text) if (log != null) { val logFile = File("$testPath.log") @@ -104,8 +105,9 @@ abstract class AbstractPerformanceImportTest : KotlinLightCodeInsightFixtureTest TestCase.assertFalse(logFile.exists()) } } - commitAllDocuments() - FileDocumentManager.getInstance().reloadFromDisk(editor.document) + runWriteAction { + myFixture.file.delete() + } }) } finally { CodeStyle.dropTemporarySettings(project) diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt index da59672f3ec..3cfc238f660 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.idea.perf import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.actionSystem.IdeActions +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.PsiDocumentManager import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest 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() { - stats().perfTest( + stats().perfTest( testName = "warm-up", setUp = { with(myFixture) { @@ -64,9 +61,13 @@ abstract class AbstractPerformanceJavaToKotlinCopyPasteConversionTest(private va myFixture.performEditorAction(IdeActions.ACTION_PASTE) }, tearDown = { - commitAllDocuments() + val document = myFixture.getDocument(myFixture.file) + PsiDocumentManager.getInstance(project).commitDocument(document) kotlin.test.assertFalse(!ConvertJavaCopyPasteProcessor.conversionPerformed, "No conversion to Kotlin suggested") 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 noConversionExpected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// NO_CONVERSION_EXPECTED").isNotEmpty() - stats().perfTest( + stats().perfTest( testName = testName, setUp = { myFixture.configureByFiles("$testName.java") diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt index 5a927ce33c6..f8a5945911a 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt @@ -5,51 +5,82 @@ 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.codeInspection.ex.InspectionProfileImpl +import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory 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.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.editor.Document import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleTypeId +import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project 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.ProjectJdkTable import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl +import com.intellij.openapi.roots.FileIndexFacade import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.startup.StartupManager 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.ChangeListManagerImpl import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiFile -import com.intellij.testFramework.PsiTestUtil -import com.intellij.testFramework.RunAll -import com.intellij.testFramework.UsefulTestCase +import com.intellij.psi.PsiManager +import com.intellij.psi.impl.search.IndexPatternBuilder +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.propertyBased.MadTestingUtil import com.intellij.util.ThrowableRunnable +import com.intellij.util.indexing.UnindexedFilesUpdater 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.framework.KotlinSdkType 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.nio.file.Paths +import java.util.concurrent.TimeUnit abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { // 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 myApplication: IdeaTestApplication + override fun isStressTest(): Boolean = true + + override fun isPerformanceTest(): Boolean = false + override fun setUp() { super.setUp() @@ -71,7 +102,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { jdkTable.addJdk(internal, testRootDisposable) KotlinSdkType.setUpIfNeeded() } - InspectionProfileImpl.INIT_INSPECTIONS = true } override fun tearDown() { @@ -79,6 +109,9 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { ThrowableRunnable { super.tearDown() }, ThrowableRunnable { 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!!) myProject = null } @@ -100,7 +133,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { note: String, path: String = "idea/testData/perfTest" ): Project { - val projectPath = "$path/$name" + val projectPath = File("$path/$name").canonicalPath val warmUpIterations = 1 val iterations = 3 @@ -109,39 +142,126 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { var lastProject: Project? = null var counter = 0 - stats.perfTest( + stats.perfTest>( warmUpIterations = warmUpIterations, iterations = iterations, testName = "open project${if (note.isNotEmpty()) " $note" else ""}", test = { - - val project = projectManagerEx.loadAndOpenProject(projectPath)!! - if (!Paths.get(projectPath, ".idea").exists()) { + val projectPathExists = Paths.get(projectPath, ".idea").exists() + val project = if (projectPathExists) { + val project = ProjectUtil.openProject(projectPath, null, false)!! + project + } else { + val project = projectManagerEx.loadAndOpenProject(projectPath)!! initKotlinProject(project, projectPath, name) + project } + (project as ProjectImpl).registerComponentImplementation( + FileEditorManager::class.java, + FileEditorManagerImpl::class.java + ) + projectManagerEx.openTestProject(project) - val changeListManagerImpl = ChangeListManager.getInstance(project) as ChangeListManagerImpl - changeListManagerImpl.waitUntilRefreshed() + dispatchAllInvocationEvents() - project - }, - tearDown = { project -> - lastProject = project - val prj = project!! - - // close all project but last - we're going to return and use it further - if (counter < warmUpIterations + iterations - 1) { - closeProject(prj) + with(StartupManager.getInstance(project) as StartupManagerImpl) { + scheduleInitialVfsRefresh() + runPostStartupActivities() + } + + with(ChangeListManager.getInstance(project) as ChangeListManagerImpl) { + waitUntilRefreshed() + } + + 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!! } + 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( project: Project, projectPath: String, @@ -166,7 +286,6 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { protected fun perfHighlightFile(name: String, stats: Stats): List = perfHighlightFile(myProject!!, name, stats) - protected fun perfHighlightFile( project: Project, fileName: String, @@ -174,42 +293,125 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { note: String = "" ): List { var highlightInfos: List = emptyList() - stats.perfTest( - testName = "highlighting ${if (note.isNotEmpty()) "$note " else ""}${simpleFilename(fileName)}", - setUp = { - val fileInEditor = openFileInEditor(project, fileName) - fileInEditor.psiFile - }, - test = { file -> - highlightFile(file!!) - }, - tearDown = { - highlightInfos = it ?: emptyList() - commitAllDocuments() - } - ) + IdentifierHighlighterPassFactory.doWithHighlightingEnabled { + stats.perfTest>( + testName = "highlighting ${if (note.isNotEmpty()) "$note " else ""}${simpleFilename(fileName)}", + setUp = { + it.setUpValue = openFileInEditor(project, fileName) + }, + test = { + val file = it.setUpValue + it.value = highlightFile(project, file!!.psiFile) + }, + tearDown = { + 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 } - private fun highlightFile(psiFile: PsiFile): List { + private fun highlightFile(project: Project, psiFile: PsiFile): List { val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!! 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>) -> 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>) -> Unit { + return { + val fileInEditor = it.setUpValue!! + it.value = highlightFile(project, fileInEditor.psiFile) + } + } + + private fun perfFileAnalysisTearDown( + fileName: String, + project: Project + ): (TestData>) -> 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 { + val fileDocumentManager = FileDocumentManager.getInstance() + val fileEditorManager = FileEditorManager.getInstance(project) + val psiFile = projectFileByName(project, name) 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) } @@ -219,4 +421,7 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() { val virtualFile = fileManager.refreshAndFindFileByUrl(url) return virtualFile!!.toPsiFile(project)!! } + + private data class EditorFile(val psiFile: PsiFile, val document: Document) + } \ No newline at end of file diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/PerformanceProjectsTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/PerformanceProjectsTest.kt index be79aa28f92..b66144a00a6 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/PerformanceProjectsTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/PerformanceProjectsTest.kt @@ -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/") + } + } \ No newline at end of file diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/Stats.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/Stats.kt index 2b02b86a244..58889908b7f 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/Stats.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/Stats.kt @@ -37,22 +37,22 @@ class Stats(val name: String = "", val header: Array = arrayOf("Name", " append(arrayOf(file, id, nanoTime.nsToMs)) } - fun perfTest( + fun perfTest( testName: String, warmUpIterations: Int = 3, iterations: Int = 10, - setUp: () -> T? = { null }, - test: (t: T?) -> K, - tearDown: (t: K?) -> Unit = {} + setUp: (TestData) -> Unit = { null }, + test: (TestData) -> Unit, + tearDown: (TestData) -> Unit = { null } ) { val namePrefix = "$name: $testName" val timingsNs = LongArray(iterations) val errors = Array(iterations, init = { null }) tcSuite(namePrefix) { - warmUpPhase(warmUpIterations, namePrefix, setUp, test, tearDown) + warmUpPhase(warmUpIterations, namePrefix, setUp, test, tearDown) - mainPhase(iterations, setUp, test, timingsNs, namePrefix, errors, tearDown) + mainPhase(iterations, setUp, test, tearDown, timingsNs, namePrefix, errors) val meanNs = timingsNs.average() val meanMs = meanNs.toLong().nsToMs @@ -81,27 +81,29 @@ class Stats(val name: String = "", val header: Array = arrayOf("Name", " private fun mainPhase( iterations: Int, - setUp: () -> T?, - test: (t: T?) -> K, + setUp: (TestData) -> Unit, + test: (TestData) -> Unit, + tearDown: (TestData) -> Unit, timingsNs: LongArray, namePrefix: String, - errors: Array, - tearDown: (t: K?) -> Unit + errors: Array ) { + val testData = TestData(null, null) try { for (attempt in 0 until iterations) { - val setupValue: T? = setUp() - var value: K? = null + testData.reset() + setUp(testData) try { val spentNs = measureNanoTime { - value = test(setupValue) + test(testData) } timingsNs[attempt] = spentNs } catch (t: Throwable) { println("error at $namePrefix #$attempt:") + t.printStackTrace() errors[attempt] = t } finally { - tearDown(value) + tearDown(testData) } } } catch (t: Throwable) { @@ -113,27 +115,34 @@ class Stats(val name: String = "", val header: Array = arrayOf("Name", " private fun warmUpPhase( warmUpIterations: Int, namePrefix: String, - setUp: () -> T?, - test: (t: T?) -> K, - tearDown: (t: K?) -> Unit + setUp: (TestData) -> Unit, + test: (TestData) -> Unit, + tearDown: (TestData) -> Unit ) { + val testData = TestData(null, null) for (attempt in 0 until warmUpIterations) { + testData.reset() val n = "$namePrefix warm-up #$attempt" println("##teamcity[testStarted name='$n' captureStandardOutput='true']") try { - val setupValue: T? = setUp() - var value: K? = null + setUp(testData) var spentNs: Long = 0 try { spentNs = measureNanoTime { - value = test(setupValue) + test(testData) } } catch (t: Throwable) { - println("error at $n:") + println("error at $n:\n") + + t.printStackTrace() + + println("\n") + tcPrintErrors(n, listOf(t)) + throw t } finally { - tearDown(value) + tearDown(testData) } val spentMs = spentNs.nsToMs println("##teamcity[buildStatisticValue key='$n' value='$spentMs']") @@ -153,6 +162,13 @@ class Stats(val name: String = "", val header: Array = arrayOf("Name", " } } +data class TestData(var setUpValue: SV?, var value: V?) { + fun reset() { + setUpValue = null + value = null + } +} + inline fun tcSuite(name: String, block: () -> Unit) { println("##teamcity[testSuiteStarted name='$name']") block() diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/WholeProjectPerformanceTest.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/WholeProjectPerformanceTest.kt index e9d5c42d0c4..33f7f0cdbfe 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/WholeProjectPerformanceTest.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/WholeProjectPerformanceTest.kt @@ -18,6 +18,7 @@ import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.lang.java.JavaLanguage import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ex.ApplicationEx +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl @@ -43,7 +44,7 @@ abstract class WholeProjectPerformanceTest : DaemonAnalyzerTestCase(), WholeProj IdeaTestApplication.getInstance() // to prevent leaked SDKs: 1.8 and Kotlin SDK - ApplicationManager.getApplication().runWriteAction { + runWriteAction { val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx() val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") { jdkTableImpl.internalJdk.homeDirectory!!.parent.path diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt index 4d0a1dae502..a121c865ea2 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt @@ -11,6 +11,8 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.impl.PsiDocumentManagerBase import com.intellij.testFramework.EdtTestUtil import com.intellij.util.ThrowableRunnable +import com.intellij.util.ui.UIUtil +import org.jetbrains.kotlin.idea.parameterInfo.HintType fun commitAllDocuments() { 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) { - commitAllDocuments() + dispatchAllInvocationEvents() val projectManagerEx = ProjectManagerEx.getInstanceEx() projectManagerEx.forceCloseProject(project, true) } diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt.182 b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt.182 index 62d46f9efaf..210923ad508 100644 --- a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt.182 +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/projectRoutines.kt.182 @@ -11,6 +11,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.impl.PsiDocumentManagerBase import com.intellij.testFramework.EdtTestUtil import com.intellij.util.ThrowableRunnable +import com.intellij.util.ui.UIUtil fun commitAllDocuments() { 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) { - commitAllDocuments() + UIUtil.dispatchAllInvocationEvents() val projectManagerEx = ProjectManagerEx.getInstanceEx() projectManagerEx.closeAndDispose(project) }