IDE perf tests for K/N: check successful Gradle project import

This commit is contained in:
Dmitriy Dolovov
2020-02-27 22:23:06 +07:00
parent bb5a639153
commit c4a89c0201
8 changed files with 249 additions and 71 deletions
@@ -82,6 +82,8 @@ abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
jdkTable.addJdk(internal, testRootDisposable) jdkTable.addJdk(internal, testRootDisposable)
KotlinSdkType.setUpIfNeeded() KotlinSdkType.setUpIfNeeded()
} }
GradleProcessOutputInterceptor.install(testRootDisposable)
} }
protected fun warmUpProject(stats: Stats, vararg filesToHighlight: String, openProject: () -> Project) { protected fun warmUpProject(stats: Stats, vararg filesToHighlight: String, openProject: () -> Project) {
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.testFramework
import com.intellij.openapi.Disposable
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener.EP_NAME as EP
import com.intellij.testFramework.ExtensionTestUtil.maskExtensions
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.lang.Exception
import kotlin.test.assertNull
interface GradleProcessOutputInterceptor {
companion object {
fun getInstance(): GradleProcessOutputInterceptor? = EP.extensions.firstIsInstanceOrNull()
fun install(parentDisposable: Disposable) {
val installedExtensions = EP.extensions
assertNull(
installedExtensions.firstIsInstanceOrNull<GradleProcessOutputInterceptor>(),
"Another ${GradleProcessOutputInterceptor::class.java.simpleName} is already installed"
)
maskExtensions(
EP,
listOf(GradleProcessOutputInterceptorImpl()) + installedExtensions,
parentDisposable
)
}
}
fun reset()
fun getOutput(): String
}
private class GradleProcessOutputInterceptorImpl : GradleProcessOutputInterceptor, ExternalSystemTaskNotificationListener {
private val buffer = StringBuilder()
override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
if (id.projectSystemId == GRADLE_SYSTEM_ID && text.isNotEmpty())
buffer.append(text)
}
override fun reset() = buffer.setLength(0)
override fun getOutput() = buffer.toString()
override fun onSuccess(id: ExternalSystemTaskId) = Unit
override fun onFailure(id: ExternalSystemTaskId, e: Exception) = Unit
override fun onStatusChange(event: ExternalSystemTaskNotificationEvent) = Unit
override fun onCancel(id: ExternalSystemTaskId) = Unit
override fun onEnd(id: ExternalSystemTaskId) = Unit
override fun beforeCancel(id: ExternalSystemTaskId) = Unit
@Suppress("UnstableApiUsage")
override fun onStart(id: ExternalSystemTaskId) = Unit
}
@@ -0,0 +1,63 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.testFramework
import com.intellij.openapi.Disposable
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationEvent
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
import com.intellij.testFramework.PlatformTestUtil.maskExtensions
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import kotlin.test.assertNull
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener.EP_NAME as EP
interface GradleProcessOutputInterceptor {
companion object {
fun getInstance(): GradleProcessOutputInterceptor? = EP.extensions.firstIsInstanceOrNull()
fun install(parentDisposable: Disposable) {
val installedExtensions = EP.extensions
assertNull(
installedExtensions.firstIsInstanceOrNull<GradleProcessOutputInterceptor>(),
"Another ${GradleProcessOutputInterceptor::class.java.simpleName} is already installed"
)
maskExtensions(
EP,
listOf(GradleProcessOutputInterceptorImpl()) + installedExtensions,
parentDisposable
)
}
}
fun reset()
fun getOutput(): String
}
private class GradleProcessOutputInterceptorImpl : GradleProcessOutputInterceptor, ExternalSystemTaskNotificationListener {
private val buffer = StringBuilder()
override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
if (id.projectSystemId == GRADLE_SYSTEM_ID && text.isNotEmpty())
buffer.append(text)
}
override fun reset() = buffer.setLength(0)
override fun getOutput() = buffer.toString()
override fun onSuccess(id: ExternalSystemTaskId) = Unit
override fun onFailure(id: ExternalSystemTaskId, e: Exception) = Unit
override fun onStatusChange(event: ExternalSystemTaskNotificationEvent) = Unit
override fun onCancel(id: ExternalSystemTaskId) = Unit
override fun onEnd(id: ExternalSystemTaskId) = Unit
override fun beforeCancel(id: ExternalSystemTaskId) = Unit
override fun onQueued(id: ExternalSystemTaskId, workingDir: String?) = Unit
@Suppress("UnstableApiUsage")
override fun onStart(id: ExternalSystemTaskId) = Unit
}
@@ -85,6 +85,11 @@ enum class ProjectOpenAction {
refreshGradleProject(projectPath, project) refreshGradleProject(projectPath, project)
assertTrue(
ModuleManager.getInstance(project).modules.isNotEmpty(),
"Gradle project $projectName at $projectPath has to have at least one module"
)
return project return project
} }
@@ -0,0 +1,84 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.testFramework
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import java.io.Closeable
import kotlin.test.assertFalse
import kotlin.test.fail
class StatefulTestGradleProjectRefreshCallback(
private val projectPath: String,
private val project: Project
) : ExternalProjectRefreshCallback, Closeable {
private class Error(val message: String, val details: String? = null)
private var alreadyUsed = false
private var error: Error? = null
init {
GradleProcessOutputInterceptor.getInstance()?.reset()
}
override fun onSuccess(externalProject: DataNode<ProjectData>?) {
checkAlreadyUsed()
if (externalProject == null) {
error = Error("Got null external project after Gradle import")
return
}
ServiceManager.getService(ProjectDataManager::class.java).importData(externalProject, project, true)
}
override fun onFailure(errorMessage: String, errorDetails: String?) {
checkAlreadyUsed()
error = Error(errorMessage, errorDetails)
}
override fun close() = assertError()
fun assertError() {
val error = error ?: return
val failure = buildString {
appendln("Gradle import failed for ${project.name} at $projectPath")
project.guessProjectDir()
append("=".repeat(40)).appendln(" Error message:")
appendln(error.message.trimEnd())
append("=".repeat(40)).appendln(" Error details:")
appendln(error.details?.trimEnd().orEmpty())
append("=".repeat(40)).appendln(" Gradle process output:")
appendln(GradleProcessOutputInterceptor.getInstance()?.getOutput()?.trimEnd() ?: "<interceptor not installed>")
appendln("=".repeat(40))
}
fail(failure)
}
private fun checkAlreadyUsed() {
assertFalse(
alreadyUsed,
"${StatefulTestGradleProjectRefreshCallback::class.java} can be used only once." +
" Please create a new instance of ${StatefulTestGradleProjectRefreshCallback::class.java} every time you" +
" do import from Gradle."
)
alreadyUsed = true
}
}
@@ -5,13 +5,8 @@
package org.jetbrains.kotlin.idea.testFramework package org.jetbrains.kotlin.idea.testFramework
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
import com.intellij.openapi.externalSystem.model.DataNode
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode
import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
@@ -58,41 +53,20 @@ private fun _attachGradleProjectAndRefresh(
ExternalSystemUtil.ensureToolWindowInitialized(project, GradleConstants.SYSTEM_ID) ExternalSystemUtil.ensureToolWindowInitialized(project, GradleConstants.SYSTEM_ID)
} }
} }
//ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(false)
ExternalProjectsManagerImpl.disableProjectWatcherAutoUpdate(project) ExternalProjectsManagerImpl.disableProjectWatcherAutoUpdate(project)
val settings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID) val settings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID)
if (settings.getLinkedProjectSettings(externalProjectPath) == null) { if (settings.getLinkedProjectSettings(externalProjectPath) == null) {
settings.linkProject(gradleProjectSettings) settings.linkProject(gradleProjectSettings)
} }
//ExternalSystemUtil.refreshProject(project, GradleConstants.SYSTEM_ID, externalProjectPath, false, ProgressExecutionMode.MODAL_SYNC)
val progressExecutionMode = ProgressExecutionMode.MODAL_SYNC StatefulTestGradleProjectRefreshCallback(externalProjectPath, project).use { callback ->
val externalSystemId = GradleConstants.SYSTEM_ID ExternalSystemUtil.refreshProject(
val callback = object : ExternalProjectRefreshCallback { externalProjectPath,
override fun onFailure(errorMessage: String, errorDetails: String?) { ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
super.onFailure(errorMessage, errorDetails) .use(ProgressExecutionMode.MODAL_SYNC)
throw RuntimeException(errorMessage) .callback(callback)
} .build()
)
override fun onFailure(
externalTaskId: ExternalSystemTaskId,
errorMessage: String,
errorDetails: String?
) {
super.onFailure(externalTaskId, errorMessage, errorDetails)
throw RuntimeException(errorMessage)
}
override fun onSuccess(externalProject: DataNode<ProjectData>?) {
if (externalProject == null) {
return
}
val synchronous = progressExecutionMode == ProgressExecutionMode.MODAL_SYNC
ServiceManager.getService(
ProjectDataManager::class.java
).importData(externalProject, project, synchronous)
}
} }
ExternalSystemUtil.refreshProject(project, externalSystemId, externalProjectPath, callback, false, progressExecutionMode, true)
} }
@@ -5,50 +5,27 @@
package org.jetbrains.kotlin.idea.testFramework package org.jetbrains.kotlin.idea.testFramework
import com.intellij.ide.impl.ProjectUtil
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.runInEdtAndWait
import com.intellij.openapi.project.ex.ProjectManagerEx
import org.jetbrains.plugins.gradle.service.project.GradleProjectOpenProcessor import org.jetbrains.plugins.gradle.service.project.GradleProjectOpenProcessor
import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import java.nio.file.Paths import java.nio.file.Paths
fun refreshGradleProject(projectPath: String, project: Project) { fun refreshGradleProject(projectPath: String, project: Project) {
GradleProjectOpenProcessor.openGradleProject(project, null, Paths.get(projectPath)) GradleProjectOpenProcessor.openGradleProject(project, null, Paths.get(projectPath))
val gradleArguments = System.getProperty("kotlin.test.gradle.import.arguments") StatefulTestGradleProjectRefreshCallback(projectPath, project).use { callback ->
ExternalSystemUtil.refreshProjects( ExternalSystemUtil.refreshProjects(
ImportSpecBuilder(project, GradleConstants.SYSTEM_ID) ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
.forceWhenUptodate() .use(ProgressExecutionMode.MODAL_SYNC)
.useDefaultCallback() .forceWhenUptodate()
.use(ProgressExecutionMode.MODAL_SYNC) .withArguments(System.getProperty("kotlin.test.gradle.import.arguments"))
.also { .callback(callback)
gradleArguments?.run(it::withArguments) .build()
} )
) }
dispatchAllInvocationEvents() dispatchAllInvocationEvents()
} }
fun openGradleProject(projectPath: String, project: Project) {
dispatchAllInvocationEvents()
val virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(projectPath)!!
FileDocumentManager.getInstance().saveAllDocuments()
val path = Paths.get(virtualFile.path)
GradleProjectOpenProcessor.openGradleProject(project, null, path)
dispatchAllInvocationEvents()
runInEdtAndWait {
PlatformTestUtil.saveProject(project)
}
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.testFramework package org.jetbrains.kotlin.idea.testFramework
import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder
import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
@@ -16,10 +17,11 @@ import org.jetbrains.plugins.gradle.service.project.open.setupGradleSettings
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.gradle.util.GradleConstants
import org.jetbrains.plugins.gradle.util.GradleLog import org.jetbrains.plugins.gradle.util.GradleLog
import java.io.File
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
fun refreshGradleProject(projectPath: String, project: Project) { fun refreshGradleProject(projectPath: String, project: Project) {
_importProject(projectPath, project) _importProject(File(projectPath).absolutePath, project)
dispatchAllInvocationEvents() dispatchAllInvocationEvents()
} }
@@ -55,6 +57,14 @@ private fun _attachGradleProjectAndRefresh(
if (settings.getLinkedProjectSettings(externalProjectPath) == null) { if (settings.getLinkedProjectSettings(externalProjectPath) == null) {
settings.linkProject(gradleProjectSettings) settings.linkProject(gradleProjectSettings)
} }
//ExternalSystemUtil.refreshProject(project, GradleConstants.SYSTEM_ID, externalProjectPath, true, ProgressExecutionMode.MODAL_SYNC)
ExternalSystemUtil.refreshProject(project, GradleConstants.SYSTEM_ID, externalProjectPath, false, ProgressExecutionMode.MODAL_SYNC) StatefulTestGradleProjectRefreshCallback(externalProjectPath, project).use { callback ->
ExternalSystemUtil.refreshProject(
externalProjectPath,
ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)
.use(ProgressExecutionMode.MODAL_SYNC)
.callback(callback)
.build()
)
}
} }