diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/listener/ScriptChangesNotifier.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/listener/ScriptChangesNotifier.kt index d28fb35d396..618b3a6f24d 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/listener/ScriptChangesNotifier.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/listener/ScriptChangesNotifier.kt @@ -52,6 +52,10 @@ internal class ScriptChangesNotifier( val listener = getListener(project, file) ?: return + if (ApplicationManager.getApplication().isUnitTestMode) { + listener.documentChanged(file, updater) + } + scriptsQueue.cancelAllRequests() scriptsQueue.addRequest( { listener.documentChanged(file, updater) }, diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/TestingBackgroundExecutor.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/TestingBackgroundExecutor.kt index 8cb752a1e63..7a15e63bcab 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/TestingBackgroundExecutor.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/configuration/utils/TestingBackgroundExecutor.kt @@ -53,4 +53,6 @@ class TestingBackgroundExecutor internal constructor( return copy.isNotEmpty() } + + fun noBackgroundTasks(): Boolean = backgroundQueue.isEmpty() } \ No newline at end of file diff --git a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt index 63a84fd0d5a..57d1c547e80 100644 --- a/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt +++ b/idea/idea-gradle/src/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptListener.kt @@ -8,15 +8,15 @@ package org.jetbrains.kotlin.idea.scripting.gradle import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationCacheScope import com.intellij.util.io.systemIndependentPath +import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationCacheScope import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater import org.jetbrains.plugins.gradle.GradleManager import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.util.GradleConstants -class GradleScriptListener(project: Project) : ScriptChangeListener(project) { +open class GradleScriptListener(project: Project) : ScriptChangeListener(project) { override fun editorActivated(vFile: VirtualFile, updater: ScriptConfigurationUpdater) { if (!isGradleKotlinScript(vFile)) return diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptInputsWatcherTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptInputsWatcherTest.kt new file mode 100644 index 00000000000..ea5473d4ce7 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/GradleScriptInputsWatcherTest.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2019 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.scripting.gradle + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener +import org.jetbrains.kotlin.idea.core.script.isScriptChangesNotifierDisabled +import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationLoadingTest +import org.jetbrains.kotlin.idea.script.addExtensionPointInTest +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.plugins.gradle.util.GradleConstants +import java.io.File + +private const val outsidePlaceholder = "// OUTSIDE_SECTIONS" +private const val insidePlaceholder = "// INSIDE_SECTIONS" + +class GradleScriptInputsWatcherTest : AbstractScriptConfigurationLoadingTest() { + private lateinit var testFiles: TestFiles + + data class TestFiles(val buildKts: KtFile, val settings: KtFile, val prop: PsiFile) + + override fun setUp() { + super.setUp() + + ApplicationManager.getApplication().isScriptChangesNotifierDisabled = false + } + + override fun setUpTestProject() { + addExtensionPointInTest( + ScriptChangeListener.LISTENER, + project, + TestGradleScriptListener(project), + testRootDisposable + ) + + val rootDir = "idea/testData/script/definition/loading/gradle/" + + val settings: KtFile = addFileToProject(rootDir + GradleConstants.KOTLIN_DSL_SETTINGS_FILE_NAME) + val prop: PsiFile = addFileToProject(rootDir + "gradle.properties") + + val buildGradleKts = File(rootDir).walkTopDown().find { it.name == GradleConstants.KOTLIN_DSL_SCRIPT_NAME } + ?: error("Couldn't find main script") + configureScriptFile(rootDir, buildGradleKts) + val build = (myFile as? KtFile) ?: error("") + + testFiles = TestFiles(build, settings, prop) + } + + private inline fun addFileToProject(file: String): T { + createFileAndSyncDependencies(File(file)) + return (myFile as? T) ?: error("Couldn't configure project by $file") + } + + fun testSectionChange() { + assertAndLoadInitialConfiguration(testFiles.buildKts) + + changeBuildKtsInsideSections() + + assertConfigurationUpdateWasDone(testFiles.buildKts) + } + + fun testOutsideSectionChange() { + assertAndLoadInitialConfiguration(testFiles.buildKts) + + changeBuildKtsOutsideSections() + + assertConfigurationUpToDate(testFiles.buildKts) + } + + private fun assertConfigurationUpToDate(file: KtFile) { + scriptConfigurationManager.updater.ensureUpToDatedConfigurationSuggested(file) + assertNoBackgroundTasks() + assertNoLoading() + } + + private fun assertConfigurationUpdateWasDone(file: KtFile) { + scriptConfigurationManager.updater.ensureUpToDatedConfigurationSuggested(file) + assertAndDoAllBackgroundTasks() + assertSingleLoading() + } + + private fun changeBuildKtsInsideSections(text: String = "application") { + changeBuildKts(insidePlaceholder, text) + } + + private fun changeBuildKtsOutsideSections() { + changeBuildKts(outsidePlaceholder, "compile(\"\")") + } + + private fun changeSettingsKtsInsideSections() { + changeSettingsKts(insidePlaceholder, "mavenCentral()") + } + + private fun changeSettingsKtsOutsideSections() { + changeSettingsKts(outsidePlaceholder, "include: 'aaa'") + } + + private fun changeBuildKts(placeHolder: String, text: String) { + changeContents(testFiles.buildKts.text.replace(placeHolder, text), testFiles.buildKts) + testFiles = testFiles.copy(buildKts = myFile as KtFile) + } + + private fun changeSettingsKts(placeHolder: String, text: String) { + changeContents(testFiles.settings.text.replace(placeHolder, text), testFiles.settings) + testFiles = testFiles.copy(settings = myFile as KtFile) + } + + private fun changePropertiesFile() { + changeContents(testFiles.prop.text.replace(outsidePlaceholder.replace("//", "#"), "myProp = true"), testFiles.prop) + testFiles = testFiles.copy(prop = myFile) + } +} \ No newline at end of file diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/TestGradleScriptListener.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/TestGradleScriptListener.kt new file mode 100644 index 00000000000..9f5aaef169e --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/scripting/gradle/TestGradleScriptListener.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2019 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.scripting.gradle + +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +internal class TestGradleScriptListener(project: Project) : GradleScriptListener(project) { + override fun isApplicable(vFile: VirtualFile): Boolean { + return true + } +} \ No newline at end of file diff --git a/idea/testData/script/definition/loading/gradle/build.gradle.kts b/idea/testData/script/definition/loading/gradle/build.gradle.kts new file mode 100644 index 00000000000..200b518bf0a --- /dev/null +++ b/idea/testData/script/definition/loading/gradle/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + // INSIDE_SECTIONS +} + +dependencies { + // OUTSIDE_SECTIONS +} \ No newline at end of file diff --git a/idea/testData/script/definition/loading/gradle/gradle.properties b/idea/testData/script/definition/loading/gradle/gradle.properties new file mode 100644 index 00000000000..de58e15dc51 --- /dev/null +++ b/idea/testData/script/definition/loading/gradle/gradle.properties @@ -0,0 +1 @@ +# OUTSIDE_SECTIONS \ No newline at end of file diff --git a/idea/testData/script/definition/loading/gradle/settings.gradle.kts b/idea/testData/script/definition/loading/gradle/settings.gradle.kts new file mode 100644 index 00000000000..5146fd890b4 --- /dev/null +++ b/idea/testData/script/definition/loading/gradle/settings.gradle.kts @@ -0,0 +1,5 @@ +pluginManagement { + // INSIDE_SECTIONS +} + +// OUTSIDE_SECTIONS \ No newline at end of file diff --git a/idea/testData/script/definition/loading/gradle/template/template.kt b/idea/testData/script/definition/loading/gradle/template/template.kt new file mode 100644 index 00000000000..439174ef1a8 --- /dev/null +++ b/idea/testData/script/definition/loading/gradle/template/template.kt @@ -0,0 +1,37 @@ +package custom.scriptDefinition + +import kotlin.script.dependencies.* +import kotlin.script.experimental.dependencies.* +import kotlin.script.templates.* +import java.io.File +import kotlin.script.experimental.location.* + +class TestDependenciesResolver : AsyncDependenciesResolver { + suspend override fun resolveAsync(scriptContents: ScriptContents, environment: Environment): DependenciesResolver.ResolveResult { + val text = scriptContents.text as String + + val result = when { + text.startsWith("#BAD:") -> DependenciesResolver.ResolveResult.Failure( + reports = listOf(ScriptReport(text)) + ) + else -> DependenciesResolver.ResolveResult.Success( + dependencies = ScriptDependencies( + classpath = listOf(environment["template-classes"] as File), + imports = listOf("x_" + text.replace(Regex("#IGNORE_IN_CONFIGURATION"), "")) + ), + reports = listOf(ScriptReport(text)) + ) + } + + javaClass.classLoader + .loadClass("org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationLoadingTest") + .methods.single { it.name == "loadingScriptConfigurationCallback" } + .invoke(null) + + return result + } +} + +@ScriptExpectedLocations([ScriptExpectedLocation.Everywhere]) +@ScriptTemplateDefinition(TestDependenciesResolver::class, scriptFilePattern = ".*\\.gradle\\.kts") +class Template diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationLoadingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationLoadingTest.kt index fa5240bdb4b..8b819c4c4f7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationLoadingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationLoadingTest.kt @@ -6,8 +6,10 @@ package org.jetbrains.kotlin.idea.script import com.intellij.openapi.components.ServiceManager -import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager import org.jetbrains.kotlin.idea.core.script.applySuggestedScriptConfiguration @@ -20,9 +22,6 @@ import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.psi.KtFile abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurationTest() { - val ktFile: KtFile get() = myFile as KtFile - val virtualFile get() = myFile.virtualFile - lateinit var scriptConfigurationManager: ScriptConfigurationManager companion object { @@ -41,14 +40,6 @@ abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurat super.setUp() testScriptConfigurationNotification = true - addExtensionPointInTest( - DefaultScriptConfigurationManagerExtensions.LOADER, - project, - FileContentsDependentConfigurationLoader(project), - testRootDisposable - ) - - configureScriptFile("idea/testData/script/definition/loading/async/") scriptConfigurationManager = ServiceManager.getService(project, ScriptConfigurationManager::class.java) } @@ -59,6 +50,17 @@ abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurat currentLoadingScriptConfigurationCallback = null } + override fun setUpTestProject() { + addExtensionPointInTest( + DefaultScriptConfigurationManagerExtensions.LOADER, + project, + FileContentsDependentConfigurationLoader(project), + testRootDisposable + ) + + configureScriptFile("idea/testData/script/definition/loading/async/") + } + override fun loadScriptConfigurationSynchronously(script: VirtualFile) { // do nothings } @@ -80,8 +82,12 @@ abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurat } } - protected fun assertAppliedConfiguration(contents: String) { - val secondConfiguration = scriptConfigurationManager.getConfiguration(ktFile)!! + protected fun assertNoBackgroundTasks() { + assertTrue(scriptConfigurationManager.testingBackgroundExecutor.noBackgroundTasks()) + } + + protected fun assertAppliedConfiguration(contents: String, file: KtFile = myFile as KtFile) { + val secondConfiguration = scriptConfigurationManager.getConfiguration(file)!! assertEquals( contents, secondConfiguration.defaultImports.single().let { @@ -91,39 +97,35 @@ abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurat ) } - protected fun makeChanges(contents: String) { + protected fun makeChanges(contents: String, file: KtFile = myFile as KtFile) { changeContents(contents) - scriptConfigurationManager.updater.ensureUpToDatedConfigurationSuggested(ktFile) + scriptConfigurationManager.updater.ensureUpToDatedConfigurationSuggested(file) } - protected fun changeContents(contents: String) { + protected fun changeContents(contents: String, file: PsiFile = myFile) { runWriteAction { - val fileDocumentManager = FileDocumentManager.getInstance() - fileDocumentManager.reloadFiles(virtualFile) - val document = fileDocumentManager.getDocument(virtualFile)!! - document.setText(contents) - fileDocumentManager.saveDocument(document) - psiManager.reloadFromDisk(myFile) - myFile = psiManager.findFile(virtualFile) + VfsUtil.saveText(file.virtualFile, contents) + PsiDocumentManager.getInstance(project).commitAllDocuments() + myFile = psiManager.findFile(file.virtualFile) } } - protected fun assertReports(expected: String) { - val actual = IdeScriptReportSink.getReports(virtualFile).single().message + protected fun assertReports(expected: String, file: KtFile = myFile as KtFile) { + val actual = IdeScriptReportSink.getReports(file.virtualFile).single().message assertEquals(expected, actual) } - protected fun assertSuggestedConfiguration() { - assertTrue(virtualFile.hasSuggestedScriptConfiguration(project)) + protected fun assertSuggestedConfiguration(file: KtFile = myFile as KtFile) { + assertTrue(file.virtualFile.hasSuggestedScriptConfiguration(project)) } - protected fun assertAndApplySuggestedConfiguration() { - assertTrue(virtualFile.applySuggestedScriptConfiguration(project)) + protected fun assertAndApplySuggestedConfiguration(file: KtFile = myFile as KtFile) { + assertTrue(file.virtualFile.applySuggestedScriptConfiguration(project)) } - protected fun assertNoSuggestedConfiguration() { - assertFalse(virtualFile.applySuggestedScriptConfiguration(project)) + protected fun assertNoSuggestedConfiguration(file: KtFile = myFile as KtFile) { + assertFalse(file.virtualFile.applySuggestedScriptConfiguration(project)) } protected fun assertNoLoading() { @@ -136,12 +138,12 @@ abstract class AbstractScriptConfigurationLoadingTest : AbstractScriptConfigurat occurredLoadings = 0 } - protected fun assertAndLoadInitialConfiguration() { - assertNull(scriptConfigurationManager.getConfiguration(ktFile)) + protected fun assertAndLoadInitialConfiguration(file: KtFile = myFile as KtFile) { + assertNull(scriptConfigurationManager.getConfiguration(file)) assertAndDoAllBackgroundTasks() assertSingleLoading() - assertAppliedConfiguration("initial") + assertAppliedConfiguration(file.text, file) - checkHighlighting() + checkHighlighting(file) } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt index 54e67e43774..35939f91c5d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt @@ -19,6 +19,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.testFramework.PlatformTestCase import com.intellij.testFramework.PsiTestUtil import com.intellij.util.ui.UIUtil +import org.jdom.Element import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink @@ -26,6 +27,7 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companio import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager import org.jetbrains.kotlin.idea.core.script.isScriptChangesNotifierDisabled +import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.util.application.runWriteAction @@ -86,6 +88,10 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { protected fun configureScriptFile(path: String) { val mainScriptFile = findMainScript(path) + configureScriptFile(path, mainScriptFile) + } + + protected fun configureScriptFile(path: String, mainScriptFile: File) { val environment = createScriptEnvironment(mainScriptFile) registerScriptTemplateProvider(environment) @@ -137,9 +143,23 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath") + private var settings: Element? = null + override fun setUp() { super.setUp() ApplicationManager.getApplication().isScriptChangesNotifierDisabled = true + + settings = KotlinScriptingSettings.getInstance(project).state + + ScriptDefinitionsManager.getInstance(project).getAllDefinitions().forEach { + KotlinScriptingSettings.getInstance(project).setEnabled(it, false) + } + + setUpTestProject() + } + + open fun setUpTestProject() { + } override fun tearDown() { @@ -147,6 +167,10 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { System.setProperty("kotlin.script.classpath", oldScripClasspath ?: "") + settings?.let { + KotlinScriptingSettings.getInstance(project).loadState(it) + } + super.tearDown() } @@ -250,7 +274,7 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { } } - private fun createFileAndSyncDependencies(scriptFile: File) { + protected fun createFileAndSyncDependencies(scriptFile: File) { var script: VirtualFile? = null if (module != null) { script = module.moduleFile?.parent?.findChild(scriptFile.name) @@ -278,14 +302,13 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() { checkHighlighting() } - protected fun checkHighlighting() { - val ktFile = myFile as KtFile - val reports = IdeScriptReportSink.getReports(ktFile) + protected fun checkHighlighting(file: KtFile = myFile as KtFile) { + val reports = IdeScriptReportSink.getReports(file) val isFatalErrorPresent = reports.any { it.severity == ScriptDiagnostic.Severity.FATAL } - assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(myFile)) { - "Highlighting is switched off for ${myFile.virtualFile.path}\n" + + assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(file)) { + "Highlighting is switched off for ${file.virtualFile.path}\n" + "reports=$reports\n" + - "scriptDefinition=${ktFile.findScriptDefinition()}" + "scriptDefinition=${file.findScriptDefinition()}" } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationLoadingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationLoadingTest.kt index 434bce6f0da..ec8934da78f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationLoadingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/script/ScriptConfigurationLoadingTest.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.idea.script +import org.jetbrains.kotlin.psi.KtFile + class ScriptConfigurationLoadingTest : AbstractScriptConfigurationLoadingTest() { fun testSimple() { assertAndLoadInitialConfiguration() @@ -158,10 +160,10 @@ class ScriptConfigurationLoadingTest : AbstractScriptConfigurationLoadingTest() fun testLoadingForUsagesSearch() { assertAndLoadInitialConfiguration() - assertTrue(scriptConfigurationManager.updater.ensureConfigurationUpToDate(listOf(ktFile))) + assertTrue(scriptConfigurationManager.updater.ensureConfigurationUpToDate(listOf(myFile as KtFile))) changeContents("A") - assertFalse(scriptConfigurationManager.updater.ensureConfigurationUpToDate(listOf(ktFile))) + assertFalse(scriptConfigurationManager.updater.ensureConfigurationUpToDate(listOf(myFile as KtFile))) } fun testReportsOnAutoApply() {