From b4f5479d06f1c5d3629dca3c353658d64bb3a3a8 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Fri, 11 Jan 2019 14:23:17 +0100 Subject: [PATCH] save settings - async flush on disk (part 2 - fix failing tests due to missed call to saveSettingsSavingComponentsAndCommitComponents) --- .../ex/ProjectInspectionManagerTest.kt | 18 ++++---- .../org/jetbrains/ide/BuiltInWebServerTest.kt | 6 ++- .../src/ComponentStoreImpl.kt | 4 +- .../src/ProjectStoreImpl.kt | 2 +- .../testSrc/DefaultProjectStoreTest.kt | 9 ++-- .../testSrc/DoNotSaveDefaults.kt | 2 +- .../testSrc/ProjectStoreTest.kt | 13 ++++-- .../ExternalSystemStorageTest.kt | 5 ++- .../com/intellij/testFramework/FixtureRule.kt | 42 +++++++++++++++---- 9 files changed, 70 insertions(+), 31 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ex/ProjectInspectionManagerTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/ex/ProjectInspectionManagerTest.kt index 19ac64cd5b94..e0ff33ba612f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ex/ProjectInspectionManagerTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ex/ProjectInspectionManagerTest.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInspection.ex import com.intellij.codeHighlighting.HighlightDisplayLevel @@ -12,6 +12,7 @@ import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.util.io.delete import com.intellij.util.io.readText import com.intellij.util.io.write +import kotlinx.coroutines.runBlocking import org.junit.ClassRule import org.junit.Rule import org.junit.Test @@ -30,7 +31,8 @@ class ProjectInspectionManagerTest { @JvmField val ruleChain: RuleChain = RuleChain(tempDirManager, InitInspectionRule()) - @Test fun `component`() { + @Test + fun component() = runBlocking { loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.path }) { project -> @@ -80,7 +82,7 @@ class ProjectInspectionManagerTest { } } - @Test fun `do not save default project profile`() { + @Test fun `do not save default project profile`() = runBlocking { loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.path }) { project -> @@ -101,7 +103,8 @@ class ProjectInspectionManagerTest { } } - @Test fun `profiles`() { + @Test + fun profiles() = runBlocking { loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.path }) { project -> @@ -144,7 +147,8 @@ class ProjectInspectionManagerTest { } } - @Test fun `ipr`() { + @Test + fun ipr() = runBlocking { val emptyProjectFile = """ @@ -162,7 +166,7 @@ class ProjectInspectionManagerTest { currentProfile.setToolEnabled("Convert2Diamond", false) currentProfile.profileChanged() - project.saveStore() + project.stateStore.save() val projectFile = Paths.get((project.stateStore).projectFilePath) assertThat(projectFile.parent.resolve(".inspectionProfiles")).doesNotExist() @@ -182,7 +186,7 @@ class ProjectInspectionManagerTest { currentProfile.disableAllTools() currentProfile.profileChanged() - project.saveStore() + project.stateStore.save() assertThat(projectFile.readText()).isNotEqualTo(expected) assertThat(projectFile.parent.resolve(".inspectionProfiles")).doesNotExist() } diff --git a/platform/built-in-server/testSrc/org/jetbrains/ide/BuiltInWebServerTest.kt b/platform/built-in-server/testSrc/org/jetbrains/ide/BuiltInWebServerTest.kt index 8c2902e97aaa..82796fc71c90 100644 --- a/platform/built-in-server/testSrc/org/jetbrains/ide/BuiltInWebServerTest.kt +++ b/platform/built-in-server/testSrc/org/jetbrains/ide/BuiltInWebServerTest.kt @@ -1,3 +1,4 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.ide import com.google.common.net.UrlEscapers @@ -16,6 +17,7 @@ import com.intellij.util.io.systemIndependentPath import com.intellij.util.io.write import com.intellij.util.io.writeChild import io.netty.handler.codec.http.HttpResponseStatus +import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat import org.junit.ClassRule import org.junit.Rule @@ -79,7 +81,7 @@ internal class HeavyBuiltInWebServerTest { val tempDirManager = TemporaryDirectory() @Test - fun `path outside of project`() { + fun `path outside of project`() = runBlocking { val projectDir = tempDirManager.newPath().resolve("foo/bar") val projectDirPath = projectDir.systemIndependentPath createHeavyProject("$projectDirPath/test.ipr").use { project -> @@ -95,7 +97,7 @@ internal class HeavyBuiltInWebServerTest { } @Test - fun `file in hidden folder`() { + fun `file in hidden folder`() = runBlocking { val projectDir = tempDirManager.newPath().resolve("foo/bar") val projectDirPath = projectDir.systemIndependentPath createHeavyProject("$projectDirPath/test.ipr").use { project -> diff --git a/platform/configuration-store-impl/src/ComponentStoreImpl.kt b/platform/configuration-store-impl/src/ComponentStoreImpl.kt index cd2c6d4a2454..2b3059127a38 100644 --- a/platform/configuration-store-impl/src/ComponentStoreImpl.kt +++ b/platform/configuration-store-impl/src/ComponentStoreImpl.kt @@ -138,10 +138,10 @@ abstract class ComponentStoreImpl : IComponentStore { } internal open suspend fun doSave(errors: MutableList, readonlyFiles: MutableList, isForceSavingAllSettings: Boolean) { - val saveSessionProducerManager = withContext(AppUIExecutor.onUiThread().coroutineDispatchingContext()) { + withContext(AppUIExecutor.onUiThread().coroutineDispatchingContext()) { createSaveSessionManagerAndSaveComponents(isForceSavingAllSettings, errors) } - saveSessionProducerManager.save(readonlyFiles, errors) + .save(readonlyFiles, errors) } @CalledInAwt diff --git a/platform/configuration-store-impl/src/ProjectStoreImpl.kt b/platform/configuration-store-impl/src/ProjectStoreImpl.kt index 0d812818578e..9544af09c998 100644 --- a/platform/configuration-store-impl/src/ProjectStoreImpl.kt +++ b/platform/configuration-store-impl/src/ProjectStoreImpl.kt @@ -338,7 +338,7 @@ private open class ProjectStoreImpl(project: Project, private val pathMacroManag } } launch { - super.doSave(errors, readonlyFiles, isForceSavingAllSettings) + saveSettingsSavingComponentsAndCommitComponents(errors, isForceSavingAllSettings).save(readonlyFiles, errors) } } } diff --git a/platform/configuration-store-impl/testSrc/DefaultProjectStoreTest.kt b/platform/configuration-store-impl/testSrc/DefaultProjectStoreTest.kt index 76fcd15254e0..5310f5bc75ee 100644 --- a/platform/configuration-store-impl/testSrc/DefaultProjectStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/DefaultProjectStoreTest.kt @@ -20,6 +20,7 @@ import com.intellij.util.io.getDirectoryTree import com.intellij.util.io.systemIndependentPath import com.intellij.util.isEmpty import com.intellij.util.loadElement +import kotlinx.coroutines.runBlocking import org.jdom.Element import org.junit.ClassRule import org.junit.Rule @@ -79,7 +80,8 @@ internal class DefaultProjectStoreTest { @Rule fun getChain() = ruleChain - @Test fun `new project from default - file-based storage`() { + @Test + fun `new project from default - file-based storage`() = runBlocking { val externalDependenciesManager = ProjectManager.getInstance().defaultProject.service() externalDependenciesManager.allDependencies = requiredPlugins try { @@ -93,7 +95,7 @@ internal class DefaultProjectStoreTest { } @Test - fun `new project from default - directory-based storage`() { + fun `new project from default - directory-based storage`() = runBlocking { val defaultProject = ProjectManager.getInstance().defaultProject val defaultTestComponent = TestComponent() defaultTestComponent.loadState(JDOMUtil.load(""" @@ -120,7 +122,8 @@ internal class DefaultProjectStoreTest { } } - @Test fun `new project from default - remove workspace component configuration`() { + @Test + fun `new project from default - remove workspace component configuration`() { val testData = Paths.get(PathManagerEx.getCommunityHomePath(), "platform/configuration-store-impl/testData") val element = loadElement(testData.resolve("testData1.xml")) diff --git a/platform/configuration-store-impl/testSrc/DoNotSaveDefaults.kt b/platform/configuration-store-impl/testSrc/DoNotSaveDefaults.kt index c13d138c1155..fda247a3c16a 100644 --- a/platform/configuration-store-impl/testSrc/DoNotSaveDefaults.kt +++ b/platform/configuration-store-impl/testSrc/DoNotSaveDefaults.kt @@ -56,7 +56,7 @@ internal class DoNotSaveDefaultsTest { } @Test - fun testProject() { + fun testProject() = runBlocking { createOrLoadProject(tempDir, directoryBased = false) { project -> doTest(project as ProjectImpl) } diff --git a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt index fdddc6f6600f..904004f2f75e 100644 --- a/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt +++ b/platform/configuration-store-impl/testSrc/ProjectStoreTest.kt @@ -18,6 +18,7 @@ import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.util.PathUtil import com.intellij.util.io.readText import com.intellij.util.io.write +import kotlinx.coroutines.runBlocking import org.intellij.lang.annotations.Language import org.junit.Assume.assumeTrue import org.junit.ClassRule @@ -57,7 +58,8 @@ internal class ProjectStoreTest { data class TestState(var value: String = "default") - @Test fun directoryBasedStorage() { + @Test + fun directoryBasedStorage() = runBlocking { loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.writeChild("${Project.DIRECTORY_STORE_FOLDER}/misc.xml", iprFileContent) it.path @@ -84,7 +86,8 @@ internal class ProjectStoreTest { } } - @Test fun fileBasedStorage() { + @Test + fun fileBasedStorage() = runBlocking { loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.writeChild("test${ProjectFileType.DOT_DEFAULT_EXTENSION}", iprFileContent).path }) { project -> test(project) @@ -92,7 +95,8 @@ internal class ProjectStoreTest { } } - @Test fun saveProjectName() { + @Test + fun saveProjectName() = runBlocking { if (UsefulTestCase.IS_UNDER_TEAMCITY) { assumeTrue("Normal OS is required", !SystemInfo.isWindows) } @@ -128,7 +132,8 @@ internal class ProjectStoreTest { } } - @Test fun `saved project name must be not removed just on open`() { + @Test + fun `saved project name must be not removed just on open`() = runBlocking { val name = "saved project name must be not removed just on open" loadAndUseProjectInLoadComponentStateMode(tempDirManager, { it.writeChild("${Project.DIRECTORY_STORE_FOLDER}/misc.xml", iprFileContent) diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorageTest.kt b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorageTest.kt index a9eb99ab18f6..f186b6cf96cc 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorageTest.kt +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/configurationStore/ExternalSystemStorageTest.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.externalSystem.configurationStore import com.intellij.configurationStore.ESCAPED_MODULE_DIR @@ -18,6 +18,7 @@ import com.intellij.testFramework.assertions.Assertions.assertThat import com.intellij.util.io.delete import com.intellij.util.io.parentSystemIndependentPath import com.intellij.util.io.systemIndependentPath +import kotlinx.coroutines.runBlocking import org.junit.ClassRule import org.junit.Rule import org.junit.Test @@ -40,7 +41,7 @@ class ExternalSystemStorageTest { val ruleChain = RuleChain(tempDirManager, EdtRule()) @Test - fun `must be empty if external system storage`() { + fun `must be empty if external system storage`() = runBlocking { createProjectAndUseInLoadComponentStateMode(tempDirManager, directoryBased = true) { project -> ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(true) diff --git a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt index 95cbddcd39de..f04502898d8a 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt +++ b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt @@ -4,8 +4,12 @@ package com.intellij.testFramework import com.intellij.ide.highlighter.ProjectFileType import com.intellij.idea.IdeaTestApplication import com.intellij.openapi.Disposable +import com.intellij.openapi.application.AppUIExecutor import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.async.coroutineDispatchingContext import com.intellij.openapi.application.runUndoTransparentWriteAction +import com.intellij.openapi.command.impl.UndoManagerImpl +import com.intellij.openapi.command.undo.DocumentReferenceManager import com.intellij.openapi.components.ComponentManager import com.intellij.openapi.components.impl.stores.IProjectStore import com.intellij.openapi.components.stateStore @@ -24,6 +28,7 @@ import com.intellij.project.stateStore import com.intellij.util.containers.forEachGuaranteed import com.intellij.util.io.systemIndependentPath import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import org.junit.rules.ExternalResource import org.junit.rules.TestRule import org.junit.runner.Description @@ -222,16 +227,22 @@ inline fun Project.runInLoadComponentStateMode(task: () -> T): T { } } -fun createHeavyProject(path: String, useDefaultProjectSettings: Boolean = false): Project = ProjectManagerEx.getInstanceEx().newProject(null, path, useDefaultProjectSettings, false)!! +fun createHeavyProject(path: String, useDefaultProjectSettings: Boolean = false): Project { + return ProjectManagerEx.getInstanceEx().newProject(null, path, useDefaultProjectSettings, false)!! +} -fun Project.use(task: (Project) -> Unit) { +suspend fun Project.use(task: suspend (Project) -> Unit) { val projectManager = ProjectManagerEx.getInstanceEx() try { - runInEdtAndWait { projectManager.openTestProject(this) } + withContext(AppUIExecutor.onUiThread().coroutineDispatchingContext()) { + projectManager.openTestProject(this@use) + } task(this) } finally { - runInEdtAndWait { projectManager.forceCloseProject(this, true) } + withContext(AppUIExecutor.onUiThread().coroutineDispatchingContext()) { + projectManager.forceCloseProject(this@use, true) + } } } @@ -277,21 +288,34 @@ class WrapRule(private val before: () -> () -> Unit) : TestRule { } } -fun createProjectAndUseInLoadComponentStateMode(tempDirManager: TemporaryDirectory, directoryBased: Boolean = false, task: (Project) -> Unit) { +suspend fun createProjectAndUseInLoadComponentStateMode(tempDirManager: TemporaryDirectory, directoryBased: Boolean = false, task: suspend (Project) -> Unit) { createOrLoadProject(tempDirManager, task = task, directoryBased = directoryBased, loadComponentState = true) } -fun loadAndUseProjectInLoadComponentStateMode(tempDirManager: TemporaryDirectory, projectCreator: ((VirtualFile) -> String)? = null, task: (Project) -> Unit) { +suspend fun loadAndUseProjectInLoadComponentStateMode(tempDirManager: TemporaryDirectory, projectCreator: (suspend (VirtualFile) -> String)? = null, task: suspend (Project) -> Unit) { createOrLoadProject(tempDirManager, projectCreator, task = task, directoryBased = false, loadComponentState = true) } -fun createOrLoadProject(tempDirManager: TemporaryDirectory, projectCreator: ((VirtualFile) -> String)? = null, directoryBased: Boolean = true, loadComponentState: Boolean = false, task: (Project) -> Unit) { - runInEdtAndWait { +suspend fun runNonUndoableWriteAction(file: VirtualFile, runnable: suspend () -> T): T { + return runUndoTransparentWriteAction { + val result = runBlocking { runnable() } + val documentReference = DocumentReferenceManager.getInstance().create(file) + val undoManager = com.intellij.openapi.command.undo.UndoManager.getGlobalInstance() as UndoManagerImpl + undoManager.nonundoableActionPerformed(documentReference, false) + result + } +} + +suspend fun createOrLoadProject(tempDirManager: TemporaryDirectory, projectCreator: (suspend (VirtualFile) -> String)? = null, directoryBased: Boolean = true, loadComponentState: Boolean = false, task: suspend (Project) -> Unit) { + withContext(AppUIExecutor.onUiThread().coroutineDispatchingContext()) { val filePath = if (projectCreator == null) { tempDirManager.newPath("test${if (directoryBased) "" else ProjectFileType.DOT_DEFAULT_EXTENSION}", refreshVfs = true).systemIndependentPath } else { - runUndoTransparentWriteAction { projectCreator(tempDirManager.newVirtualDirectory()) } + val dir = tempDirManager.newVirtualDirectory() + runNonUndoableWriteAction(dir) { + projectCreator(dir) + } } val project = if (projectCreator == null) createHeavyProject(filePath, true) else ProjectManagerEx.getInstanceEx().loadProject(filePath)!!