Scripting loading tests and extension points
This commit is contained in:
committed by
Natalia Selezneva
parent
ca71b2fe90
commit
0fb58ed94c
+16
-5
@@ -14,13 +14,10 @@ import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.HyperlinkLabel
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.awt.event.ComponentListener
|
||||
import javax.swing.JPanel
|
||||
|
||||
fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
||||
withSelectedEditor(project) { manager ->
|
||||
@@ -53,6 +50,20 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun VirtualFile.hasSuggestedScriptConfiguration(project: Project): Boolean {
|
||||
return FileEditorManager.getInstance(project).getSelectedEditor(this)?.notificationPanel != null
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun VirtualFile.applySuggestedScriptConfiguration(project: Project): Boolean {
|
||||
val notificationPanel = FileEditorManager.getInstance(project).getSelectedEditor(this)?.notificationPanel
|
||||
?: return false
|
||||
notificationPanel.onClick.invoke()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun VirtualFile.withSelectedEditor(project: Project, f: FileEditor.(FileEditorManager) -> Unit) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
if (project.isDisposed) return@invokeLater
|
||||
@@ -67,7 +78,7 @@ private fun VirtualFile.withSelectedEditor(project: Project, f: FileEditor.(File
|
||||
private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel"))
|
||||
|
||||
private class NewScriptDependenciesNotificationPanel(
|
||||
onClick: () -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
val compilationConfigurationResult: ScriptCompilationConfigurationWrapper,
|
||||
project: Project
|
||||
) : EditorNotificationPanel() {
|
||||
|
||||
+35
-13
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.idea.core.script.configuration
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
@@ -15,6 +17,7 @@ import com.intellij.ui.EditorNotifications
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.kotlin.idea.core.script.*
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptConfigurationManagerExtensions.LOADER
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationFileAttributeCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationMemoryCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.ScriptConfigurationSnapshot
|
||||
@@ -27,6 +30,9 @@ import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigur
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoadingContext
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptOutsiderFileConfigurationLoader
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.BackgroundExecutor
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.DefaultBackgroundExecutor
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.TestingBackgroundExecutor
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.isUnitTestModeWithoutScriptLoadingNotification
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -96,21 +102,23 @@ import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
*/
|
||||
internal class DefaultScriptConfigurationManager(project: Project) :
|
||||
AbstractScriptConfigurationManager(project) {
|
||||
private val backgroundExecutor = BackgroundExecutor(project, rootsIndexer)
|
||||
|
||||
internal val backgroundExecutor: BackgroundExecutor =
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) TestingBackgroundExecutor(rootsIndexer)
|
||||
else DefaultBackgroundExecutor(project, rootsIndexer)
|
||||
|
||||
private val fileAttributeCache = ScriptConfigurationFileAttributeCache(project)
|
||||
|
||||
private val loaders: List<ScriptConfigurationLoader> = listOf(
|
||||
ScriptOutsiderFileConfigurationLoader(project),
|
||||
fileAttributeCache,
|
||||
GradleScriptConfigurationLoader(project),
|
||||
DefaultScriptConfigurationLoader(project)
|
||||
)
|
||||
private val loaders: List<ScriptConfigurationLoader>
|
||||
get() = listOf(
|
||||
ScriptOutsiderFileConfigurationLoader(project),
|
||||
ScriptConfigurationFileAttributeCache(project)
|
||||
) + project[LOADER] + listOf(
|
||||
DefaultScriptConfigurationLoader(project)
|
||||
)
|
||||
|
||||
private val listeners: List<ScriptChangeListener> = listOf(
|
||||
GradleScriptListener(),
|
||||
DefaultScriptChangeListener()
|
||||
)
|
||||
private val listeners: List<ScriptChangeListener>
|
||||
get() = project[DefaultScriptConfigurationManagerExtensions.LISTENER] + listOf(DefaultScriptChangeListener())
|
||||
|
||||
private val notifier = ScriptChangesNotifier(project, updater, listeners)
|
||||
|
||||
@@ -123,6 +131,9 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun <T> Project.get(epName: ExtensionPointName<T>): List<T> =
|
||||
Extensions.getArea(this).getExtensionPoint(epName).extensionList
|
||||
|
||||
/**
|
||||
* Will be called on [cache] miss to initiate loading of [file]'s script configuration.
|
||||
*
|
||||
@@ -248,7 +259,7 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
||||
val autoReload = skipNotification
|
||||
|| oldConfiguration == null
|
||||
|| KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled
|
||||
|| ApplicationManager.getApplication().isUnitTestMode
|
||||
|| ApplicationManager.getApplication().isUnitTestModeWithoutScriptLoadingNotification
|
||||
|
||||
if (autoReload) {
|
||||
if (oldConfiguration != null) {
|
||||
@@ -303,4 +314,15 @@ internal class DefaultScriptConfigurationManager(project: Project) :
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object DefaultScriptConfigurationManagerExtensions {
|
||||
val LOADER: ExtensionPointName<ScriptConfigurationLoader> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.scripting.idea.loader")
|
||||
|
||||
val LISTENER: ExtensionPointName<ScriptChangeListener> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.scripting.idea.listener")
|
||||
}
|
||||
|
||||
val ScriptConfigurationManager.testingBackgroundExecutor
|
||||
get() = (this as DefaultScriptConfigurationManager).backgroundExecutor as TestingBackgroundExecutor
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.core.script.configuration.loader
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.cache.CachedConfigurationInputs
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
open class FileContentsDependentConfigurationLoader(project: Project) : DefaultScriptConfigurationLoader(project) {
|
||||
override fun getInputsStamp(virtualFile: VirtualFile, file: KtFile): CachedConfigurationInputs {
|
||||
return CachedConfigurationInputs.SourceContentsStamp.get(project, virtualFile, file)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
*
|
||||
* @see [org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptConfigurationManager] for more details.
|
||||
*/
|
||||
internal interface ScriptConfigurationLoader {
|
||||
interface ScriptConfigurationLoader {
|
||||
fun shouldRunInBackground(scriptDefinition: ScriptDefinition): Boolean = false
|
||||
|
||||
/**
|
||||
|
||||
+2
-230
@@ -5,236 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.script.configuration.utils
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.progress.util.BackgroundTaskUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.Alarm
|
||||
import com.intellij.util.containers.HashSetQueue
|
||||
import org.jetbrains.kotlin.idea.core.script.debug
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Sequentially loads script configuration in background.
|
||||
* Loading tasks scheduled by calling [ensureScheduled].
|
||||
*
|
||||
* Progress indicator will be shown after [PROGRESS_INDICATOR_DELAY] ms or if
|
||||
* more then [PROGRESS_INDICATOR_MIN_QUEUE] tasks scheduled.
|
||||
*
|
||||
* States:
|
||||
* silentWorker underProgressWorker
|
||||
* - sleep
|
||||
* - silent x
|
||||
* - silent and under progress x x
|
||||
* - under progress x
|
||||
*/
|
||||
internal class BackgroundExecutor(
|
||||
val project: Project,
|
||||
val rootsManager: ScriptClassRootsIndexer
|
||||
) {
|
||||
companion object {
|
||||
const val PROGRESS_INDICATOR_DELAY = 1000
|
||||
const val PROGRESS_INDICATOR_MIN_QUEUE = 3
|
||||
}
|
||||
|
||||
private val work = Any()
|
||||
private val queue: Queue<LoadTask> = HashSetQueue()
|
||||
|
||||
/**
|
||||
* Let's fix queue size when progress bar displayed.
|
||||
* Progress for rest items will be counted from zero
|
||||
*/
|
||||
private var currentProgressSize: Int = 0
|
||||
private var currentProgressDone: Int = 0
|
||||
|
||||
private var silentWorker: SilentWorker? = null
|
||||
private var underProgressWorker: UnderProgressWorker? = null
|
||||
private val longRunningAlarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, project)
|
||||
private var longRunningAlaramRequested = false
|
||||
|
||||
private var inTransaction: Boolean = false
|
||||
private var currentFile: VirtualFile? = null
|
||||
|
||||
class LoadTask(val key: VirtualFile, val actions: () -> Unit) {
|
||||
override fun equals(other: Any?) =
|
||||
this === other || (other is LoadTask && key == other.key)
|
||||
|
||||
override fun hashCode() = key.hashCode()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun ensureScheduled(key: VirtualFile, actions: () -> Unit) {
|
||||
val task = LoadTask(key, actions)
|
||||
|
||||
if (queue.add(task)) {
|
||||
debug(task.key) { "added to update queue" }
|
||||
|
||||
// If the queue is longer than PROGRESS_INDICATOR_MIN_QUEUE, show progress and cancel button
|
||||
if (queue.size > PROGRESS_INDICATOR_MIN_QUEUE) {
|
||||
requireUnderProgressWorker()
|
||||
} else {
|
||||
requireSilentWorker()
|
||||
|
||||
if (!longRunningAlaramRequested) {
|
||||
longRunningAlaramRequested = true
|
||||
longRunningAlarm.addRequest(
|
||||
{
|
||||
longRunningAlaramRequested = false
|
||||
requireUnderProgressWorker()
|
||||
},
|
||||
PROGRESS_INDICATOR_DELAY
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requireSilentWorker() {
|
||||
if (silentWorker == null && underProgressWorker == null) {
|
||||
silentWorker = SilentWorker().also { it.start() }
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requireUnderProgressWorker() {
|
||||
if (queue.isEmpty() && silentWorker == null) return
|
||||
|
||||
silentWorker?.stopGracefully()
|
||||
if (underProgressWorker == null) {
|
||||
underProgressWorker = UnderProgressWorker().also { it.start() }
|
||||
restartProgressBar()
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun restartProgressBar() {
|
||||
currentProgressSize = queue.size
|
||||
currentProgressDone = 0
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateProgress() {
|
||||
underProgressWorker?.progressIndicator?.let {
|
||||
it.text2 = currentFile?.path ?: ""
|
||||
if (queue.size == 0) {
|
||||
// last file
|
||||
it.isIndeterminate = true
|
||||
} else {
|
||||
it.isIndeterminate = false
|
||||
if (currentProgressDone > currentProgressSize) {
|
||||
restartProgressBar()
|
||||
}
|
||||
it.fraction = currentProgressDone.toDouble() / currentProgressSize.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun ensureInTransaction() {
|
||||
if (inTransaction) return
|
||||
inTransaction = true
|
||||
rootsManager.startTransaction()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun endBatch() {
|
||||
check(inTransaction)
|
||||
rootsManager.commit()
|
||||
inTransaction = false
|
||||
}
|
||||
|
||||
private abstract inner class Worker {
|
||||
private var shouldStop = false
|
||||
|
||||
open fun start() {
|
||||
ensureInTransaction()
|
||||
}
|
||||
|
||||
fun stopGracefully() {
|
||||
shouldStop = true
|
||||
}
|
||||
|
||||
protected open fun checkCancelled() = false
|
||||
protected abstract fun close()
|
||||
|
||||
protected fun run() {
|
||||
try {
|
||||
while (true) {
|
||||
// prevent parallel work in both silent and under progress
|
||||
synchronized(work) {
|
||||
val next = synchronized(this@BackgroundExecutor) {
|
||||
if (shouldStop) return
|
||||
|
||||
if (checkCancelled()) {
|
||||
queue.clear()
|
||||
endBatch()
|
||||
return
|
||||
} else if (queue.isEmpty()) {
|
||||
endBatch()
|
||||
return
|
||||
}
|
||||
|
||||
queue.poll()?.also {
|
||||
currentFile = it.key
|
||||
currentProgressDone++
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
|
||||
next?.actions?.invoke()
|
||||
|
||||
synchronized(work) {
|
||||
currentFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class UnderProgressWorker : Worker() {
|
||||
var progressIndicator: ProgressIndicator? = null
|
||||
|
||||
override fun start() {
|
||||
super.start()
|
||||
|
||||
object : Task.Backgroundable(project, "Kotlin: Loading script dependencies...", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
progressIndicator = indicator
|
||||
updateProgress()
|
||||
run()
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
|
||||
override fun checkCancelled(): Boolean = progressIndicator?.isCanceled == true
|
||||
|
||||
override fun close() {
|
||||
synchronized(this@BackgroundExecutor) {
|
||||
underProgressWorker = null
|
||||
progressIndicator = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class SilentWorker : Worker() {
|
||||
override fun start() {
|
||||
super.start()
|
||||
|
||||
BackgroundTaskUtil.executeOnPooledThread(project, Runnable {
|
||||
run()
|
||||
})
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
synchronized(this@BackgroundExecutor) {
|
||||
silentWorker = null
|
||||
}
|
||||
}
|
||||
}
|
||||
interface BackgroundExecutor {
|
||||
fun ensureScheduled(key: VirtualFile, actions: () -> Unit)
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.core.script.configuration.utils
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.progress.util.BackgroundTaskUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.Alarm
|
||||
import com.intellij.util.containers.HashSetQueue
|
||||
import org.jetbrains.kotlin.idea.core.script.debug
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Sequentially loads script configuration in background.
|
||||
* Loading tasks scheduled by calling [ensureScheduled].
|
||||
*
|
||||
* Progress indicator will be shown after [PROGRESS_INDICATOR_DELAY] ms or if
|
||||
* more then [PROGRESS_INDICATOR_MIN_QUEUE] tasks scheduled.
|
||||
*
|
||||
* States:
|
||||
* silentWorker underProgressWorker
|
||||
* - sleep
|
||||
* - silent x
|
||||
* - silent and under progress x x
|
||||
* - under progress x
|
||||
*/
|
||||
internal class DefaultBackgroundExecutor(
|
||||
val project: Project,
|
||||
val rootsManager: ScriptClassRootsIndexer
|
||||
) : BackgroundExecutor {
|
||||
companion object {
|
||||
const val PROGRESS_INDICATOR_DELAY = 1000
|
||||
const val PROGRESS_INDICATOR_MIN_QUEUE = 3
|
||||
}
|
||||
|
||||
private val work = Any()
|
||||
private val queue: Queue<LoadTask> = HashSetQueue()
|
||||
|
||||
/**
|
||||
* Let's fix queue size when progress bar displayed.
|
||||
* Progress for rest items will be counted from zero
|
||||
*/
|
||||
private var currentProgressSize: Int = 0
|
||||
private var currentProgressDone: Int = 0
|
||||
|
||||
private var silentWorker: SilentWorker? = null
|
||||
private var underProgressWorker: UnderProgressWorker? = null
|
||||
private val longRunningAlarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, project)
|
||||
private var longRunningAlaramRequested = false
|
||||
|
||||
private var inTransaction: Boolean = false
|
||||
private var currentFile: VirtualFile? = null
|
||||
|
||||
class LoadTask(val key: VirtualFile, val actions: () -> Unit) {
|
||||
override fun equals(other: Any?) =
|
||||
this === other || (other is LoadTask && key == other.key)
|
||||
|
||||
override fun hashCode() = key.hashCode()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun ensureScheduled(key: VirtualFile, actions: () -> Unit) {
|
||||
val task = LoadTask(key, actions)
|
||||
|
||||
if (queue.add(task)) {
|
||||
debug(task.key) { "added to update queue" }
|
||||
|
||||
// If the queue is longer than PROGRESS_INDICATOR_MIN_QUEUE, show progress and cancel button
|
||||
if (queue.size > PROGRESS_INDICATOR_MIN_QUEUE) {
|
||||
requireUnderProgressWorker()
|
||||
} else {
|
||||
requireSilentWorker()
|
||||
|
||||
if (!longRunningAlaramRequested) {
|
||||
longRunningAlaramRequested = true
|
||||
longRunningAlarm.addRequest(
|
||||
{
|
||||
longRunningAlaramRequested = false
|
||||
requireUnderProgressWorker()
|
||||
},
|
||||
PROGRESS_INDICATOR_DELAY
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requireSilentWorker() {
|
||||
if (silentWorker == null && underProgressWorker == null) {
|
||||
silentWorker = SilentWorker().also { it.start() }
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun requireUnderProgressWorker() {
|
||||
if (queue.isEmpty() && silentWorker == null) return
|
||||
|
||||
silentWorker?.stopGracefully()
|
||||
if (underProgressWorker == null) {
|
||||
underProgressWorker = UnderProgressWorker().also { it.start() }
|
||||
restartProgressBar()
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun restartProgressBar() {
|
||||
currentProgressSize = queue.size
|
||||
currentProgressDone = 0
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateProgress() {
|
||||
underProgressWorker?.progressIndicator?.let {
|
||||
it.text2 = currentFile?.path ?: ""
|
||||
if (queue.size == 0) {
|
||||
// last file
|
||||
it.isIndeterminate = true
|
||||
} else {
|
||||
it.isIndeterminate = false
|
||||
if (currentProgressDone > currentProgressSize) {
|
||||
restartProgressBar()
|
||||
}
|
||||
it.fraction = currentProgressDone.toDouble() / currentProgressSize.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun ensureInTransaction() {
|
||||
if (inTransaction) return
|
||||
inTransaction = true
|
||||
rootsManager.startTransaction()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun endBatch() {
|
||||
check(inTransaction)
|
||||
rootsManager.commit()
|
||||
inTransaction = false
|
||||
}
|
||||
|
||||
private abstract inner class Worker {
|
||||
private var shouldStop = false
|
||||
|
||||
open fun start() {
|
||||
ensureInTransaction()
|
||||
}
|
||||
|
||||
fun stopGracefully() {
|
||||
shouldStop = true
|
||||
}
|
||||
|
||||
protected open fun checkCancelled() = false
|
||||
protected abstract fun close()
|
||||
|
||||
protected fun run() {
|
||||
try {
|
||||
while (true) {
|
||||
// prevent parallel work in both silent and under progress
|
||||
synchronized(work) {
|
||||
val next = synchronized(this@DefaultBackgroundExecutor) {
|
||||
if (shouldStop) return
|
||||
|
||||
if (checkCancelled()) {
|
||||
queue.clear()
|
||||
endBatch()
|
||||
return
|
||||
} else if (queue.isEmpty()) {
|
||||
endBatch()
|
||||
return
|
||||
}
|
||||
|
||||
queue.poll()?.also {
|
||||
currentFile = it.key
|
||||
currentProgressDone++
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
|
||||
next?.actions?.invoke()
|
||||
|
||||
synchronized(work) {
|
||||
currentFile = null
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class UnderProgressWorker : Worker() {
|
||||
var progressIndicator: ProgressIndicator? = null
|
||||
|
||||
override fun start() {
|
||||
super.start()
|
||||
|
||||
object : Task.Backgroundable(project, "Kotlin: Loading script dependencies...", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
progressIndicator = indicator
|
||||
updateProgress()
|
||||
run()
|
||||
}
|
||||
}.queue()
|
||||
}
|
||||
|
||||
override fun checkCancelled(): Boolean = progressIndicator?.isCanceled == true
|
||||
|
||||
override fun close() {
|
||||
synchronized(this@DefaultBackgroundExecutor) {
|
||||
underProgressWorker = null
|
||||
progressIndicator = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class SilentWorker : Worker() {
|
||||
override fun start() {
|
||||
super.start()
|
||||
|
||||
BackgroundTaskUtil.executeOnPooledThread(project, Runnable {
|
||||
run()
|
||||
})
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
synchronized(this@DefaultBackgroundExecutor) {
|
||||
silentWorker = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.core.script.configuration.utils
|
||||
|
||||
import com.intellij.openapi.application.impl.LaterInvocator
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.containers.HashSetQueue
|
||||
|
||||
class TestingBackgroundExecutor internal constructor(
|
||||
private val rootsManager: ScriptClassRootsIndexer
|
||||
) : BackgroundExecutor {
|
||||
val backgroundQueue = HashSetQueue<BackgroundTask>()
|
||||
|
||||
class BackgroundTask(val file: VirtualFile, val actions: () -> Unit) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BackgroundTask
|
||||
|
||||
if (file != other.file) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return file.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun ensureScheduled(key: VirtualFile, actions: () -> Unit) {
|
||||
backgroundQueue.add(BackgroundTask(key, actions))
|
||||
}
|
||||
|
||||
fun doAllBackgroundTaskWith(actions: () -> Unit): Boolean {
|
||||
val copy = backgroundQueue.toList()
|
||||
backgroundQueue.clear()
|
||||
|
||||
actions()
|
||||
|
||||
rootsManager.transaction {
|
||||
copy.forEach {
|
||||
it.actions()
|
||||
}
|
||||
}
|
||||
|
||||
LaterInvocator.ensureFlushRequested()
|
||||
LaterInvocator.dispatchPendingFlushes()
|
||||
|
||||
return copy.isNotEmpty()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.core.script.configuration.utils
|
||||
|
||||
import com.intellij.openapi.application.Application
|
||||
|
||||
var testScriptConfigurationNotification: Boolean = false
|
||||
|
||||
val Application.isUnitTestModeWithoutScriptLoadingNotification: Boolean
|
||||
get() = isUnitTestMode && !testScriptConfigurationNotification
|
||||
@@ -59,6 +59,14 @@
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDiagnosticFixProvider"
|
||||
interface="org.jetbrains.kotlin.idea.script.ScriptDiagnosticFixProvider"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.loader"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoader"
|
||||
area="IDEA_PROJECT"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.listener"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener"
|
||||
area="IDEA_PROJECT"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
|
||||
@@ -67,6 +67,14 @@
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDiagnosticFixProvider"
|
||||
interface="org.jetbrains.kotlin.idea.script.ScriptDiagnosticFixProvider"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.loader"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoader"
|
||||
area="IDEA_PROJECT"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.listener"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener"
|
||||
area="IDEA_PROJECT"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
|
||||
@@ -59,6 +59,14 @@
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scriptDiagnosticFixProvider"
|
||||
interface="org.jetbrains.kotlin.idea.script.ScriptDiagnosticFixProvider"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.loader"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoader"
|
||||
area="IDEA_PROJECT"/>
|
||||
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.listener"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptChangeListener"
|
||||
area="IDEA_PROJECT"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
initial
|
||||
@@ -0,0 +1,42 @@
|
||||
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.ScriptConfigurationLoadingTest")
|
||||
.methods.single { it.name == "loadingScriptConfigurationCallback" }
|
||||
.invoke(null)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ScriptExpectedLocations([ScriptExpectedLocation.Everywhere])
|
||||
@ScriptTemplateDefinition(TestDependenciesResolver::class, scriptFilePattern = "script.kts")
|
||||
class Template : Base()
|
||||
|
||||
open class Base {
|
||||
val i = 3
|
||||
val str = ""
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.script
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.core.script.applySuggestedScriptConfiguration
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptConfigurationManagerExtensions
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.FileContentsDependentConfigurationLoader
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.testingBackgroundExecutor
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.testScriptConfigurationNotification
|
||||
import org.jetbrains.kotlin.idea.core.script.hasSuggestedScriptConfiguration
|
||||
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 {
|
||||
private var occurredLoadings = 0
|
||||
private var currentLoadingScriptConfigurationCallback: (() -> Unit)? = null
|
||||
@JvmStatic
|
||||
@Suppress("unused")
|
||||
fun loadingScriptConfigurationCallback() {
|
||||
// this method is called from testData/script/definition/loading/async/template/template.kt
|
||||
currentLoadingScriptConfigurationCallback?.invoke()
|
||||
occurredLoadings++
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
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)
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
super.tearDown()
|
||||
testScriptConfigurationNotification = false
|
||||
occurredLoadings = 0
|
||||
currentLoadingScriptConfigurationCallback = null
|
||||
}
|
||||
|
||||
override fun loadScriptConfigurationSynchronously(script: VirtualFile) {
|
||||
// do nothings
|
||||
}
|
||||
|
||||
protected fun assertAndDoAllBackgroundTasks() {
|
||||
assertDoAllBackgroundTaskAndDoWhileLoading { }
|
||||
}
|
||||
|
||||
protected fun assertDoAllBackgroundTaskAndDoWhileLoading(actions: () -> Unit) {
|
||||
assertTrue(doAllBackgroundTasksWith(actions))
|
||||
}
|
||||
|
||||
protected fun doAllBackgroundTasksWith(actions: () -> Unit): Boolean {
|
||||
return scriptConfigurationManager.testingBackgroundExecutor.doAllBackgroundTaskWith {
|
||||
currentLoadingScriptConfigurationCallback = {
|
||||
actions()
|
||||
currentLoadingScriptConfigurationCallback = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun assertAppliedConfiguration(contents: String) {
|
||||
val secondConfiguration = scriptConfigurationManager.getConfiguration(ktFile)!!
|
||||
assertEquals(
|
||||
contents,
|
||||
secondConfiguration.defaultImports.single().let {
|
||||
check(it.startsWith("x_"))
|
||||
it.removePrefix("x_")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
protected fun makeChanges(contents: String) {
|
||||
changeContents(contents)
|
||||
|
||||
scriptConfigurationManager.updater.ensureUpToDatedConfigurationSuggested(ktFile)
|
||||
}
|
||||
|
||||
protected fun changeContents(contents: String) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun assertReports(expected: String) {
|
||||
val actual = IdeScriptReportSink.getReports(virtualFile).single().message
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
protected fun assertSuggestedConfiguration() {
|
||||
assertTrue(virtualFile.hasSuggestedScriptConfiguration(project))
|
||||
}
|
||||
|
||||
protected fun assertAndApplySuggestedConfiguration() {
|
||||
assertTrue(virtualFile.applySuggestedScriptConfiguration(project))
|
||||
}
|
||||
|
||||
protected fun assertNoSuggestedConfiguration() {
|
||||
assertFalse(virtualFile.applySuggestedScriptConfiguration(project))
|
||||
}
|
||||
|
||||
protected fun assertNoLoading() {
|
||||
assertEquals(0, occurredLoadings)
|
||||
occurredLoadings = 0
|
||||
}
|
||||
|
||||
protected fun assertSingleLoading() {
|
||||
assertEquals(1, occurredLoadings)
|
||||
occurredLoadings = 0
|
||||
}
|
||||
|
||||
protected fun assertAndLoadInitialConfiguration() {
|
||||
assertNull(scriptConfigurationManager.getConfiguration(ktFile))
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAppliedConfiguration("initial")
|
||||
}
|
||||
}
|
||||
@@ -265,6 +265,10 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
if (script == null) error("Test file with script couldn't be found in test project")
|
||||
|
||||
configureByExistingFile(script)
|
||||
loadScriptConfigurationSynchronously(script)
|
||||
}
|
||||
|
||||
protected open fun loadScriptConfigurationSynchronously(script: VirtualFile) {
|
||||
updateScriptDependenciesSynchronously(myFile, project)
|
||||
|
||||
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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.script
|
||||
|
||||
class ScriptConfigurationLoadingTest : AbstractScriptConfigurationLoadingTest() {
|
||||
fun testSimple() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileInQueue() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
makeChanges("B")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("B")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileInQueueABA() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
makeChanges("B")
|
||||
makeChanges("A")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileInQueueABA2() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
makeChanges("initial")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertNoLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileAnotherLoadInProgress() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertDoAllBackgroundTaskAndDoWhileLoading {
|
||||
makeChanges("B")
|
||||
}
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("B")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileAnotherLoadInProgressABA() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertDoAllBackgroundTaskAndDoWhileLoading {
|
||||
makeChanges("B")
|
||||
makeChanges("A")
|
||||
}
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertNoLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
}
|
||||
|
||||
fun mutedTestConcurrentLoadingWhileAnotherLoadInProgressABA2() { // todo: fix and unmute
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertDoAllBackgroundTaskAndDoWhileLoading {
|
||||
makeChanges("initial")
|
||||
}
|
||||
assertSingleLoading()
|
||||
assertSuggestedConfiguration()
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertNoLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileNotApplied() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAppliedConfiguration("initial")
|
||||
|
||||
// we have loaded and not applied configuration for A
|
||||
// let's invalidate file again and check that loading will occur
|
||||
|
||||
makeChanges("B")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("B")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileNotAppliedABA() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAppliedConfiguration("initial")
|
||||
|
||||
// we have loaded and not applied configuration for A
|
||||
// let's invalidate file and change it back
|
||||
// and check that loading will NOT occur
|
||||
|
||||
makeChanges("B")
|
||||
makeChanges("A")
|
||||
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertNoLoading()
|
||||
assertAppliedConfiguration("initial")
|
||||
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
}
|
||||
|
||||
fun testConcurrentLoadingWhileNotAppliedABA2() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertSuggestedConfiguration()
|
||||
|
||||
makeChanges("initial")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertNoLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
}
|
||||
|
||||
fun testLoadingForUsagesSearch() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
changeContents("A")
|
||||
assertNoLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
assertFalse(doAllBackgroundTasksWith {})
|
||||
scriptConfigurationManager.updater.ensureConfigurationUpToDate(listOf(ktFile))
|
||||
}
|
||||
|
||||
fun testReportsOnAutoApply() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
assertReports("initial")
|
||||
}
|
||||
|
||||
fun testReportsOnFailedConfiguration() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("#BAD:A")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
assertReports("#BAD:A")
|
||||
}
|
||||
|
||||
fun testReportsOnSameConfiguration() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("initial#IGNORE_IN_CONFIGURATION")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertNoSuggestedConfiguration()
|
||||
assertAppliedConfiguration("initial")
|
||||
assertReports("initial#IGNORE_IN_CONFIGURATION")
|
||||
}
|
||||
|
||||
fun testReportsAfterApply() {
|
||||
assertAndLoadInitialConfiguration()
|
||||
|
||||
makeChanges("A")
|
||||
assertAndDoAllBackgroundTasks()
|
||||
assertSingleLoading()
|
||||
assertAndApplySuggestedConfiguration()
|
||||
assertAppliedConfiguration("A")
|
||||
assertReports("A")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user