import com.intellij.openapi.project.ProjectManager
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import com.intellij.testFramework.Assertions.assertThat
+import com.intellij.testFramework.InitInspectionRule
import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.RuleChain
import com.intellij.testFramework.TemporaryDirectory
-import com.intellij.testFramework.runInInitMode
import com.intellij.util.delete
import com.intellij.util.readText
import com.intellij.util.write
val tempDirManager = TemporaryDirectory()
- private val ruleChain = RuleChain(tempDirManager)
- @Rule fun getChain() = ruleChain
+ @Rule
+ @JvmField
+ val ruleChain = RuleChain(tempDirManager, InitInspectionRule())
@Test fun `component`() {
loadAndUseProject(tempDirManager, {
}
}
+ @Test fun `do not save default project profile`() {
+ loadAndUseProject(tempDirManager, {
+ it.path
+ }) { project ->
+ val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
+ val profileFile = inspectionDir.resolve("Project_Default.xml")
+ assertThat(profileFile).doesNotExist()
+
+ val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
+ assertThat(projectInspectionProfileManager.state).isEmpty()
+
+ projectInspectionProfileManager.currentProfile
+
+ assertThat(projectInspectionProfileManager.state).isEmpty()
+
+ project.saveStore()
+
+ assertThat(profileFile).doesNotExist()
+ }
+ }
+
@Test fun `profiles`() {
- runInInitMode {
- loadAndUseProject(tempDirManager, {
- it.path
- }) { project ->
- val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
- projectInspectionProfileManager.forceLoadSchemes()
-
- assertThat(projectInspectionProfileManager.state).isEmpty()
-
- // cause to use app profile
- runInInitMode {
- val currentProfile = projectInspectionProfileManager.currentProfile
- assertThat(currentProfile.isProjectLevel).isTrue()
- currentProfile.disableTool("Convert2Diamond", project)
- }
-
- project.saveStore()
-
- val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
- val file = inspectionDir.resolve("profiles_settings.xml")
-
- assertThat(file).doesNotExist()
- val profileFile = inspectionDir.resolve("Project_Default.xml")
- assertThat(profileFile.readText()).isEqualTo("""
+ loadAndUseProject(tempDirManager, {
+ it.path
+ }) { project ->
+ val projectInspectionProfileManager = ProjectInspectionProfileManager.getInstanceImpl(project)
+ projectInspectionProfileManager.forceLoadSchemes()
+
+ assertThat(projectInspectionProfileManager.state).isEmpty()
+
+ // cause to use app profile
+ val currentProfile = projectInspectionProfileManager.currentProfile
+ assertThat(currentProfile.isProjectLevel).isTrue()
+ currentProfile.disableTool("Convert2Diamond", project)
+
+ project.saveStore()
+
+ val inspectionDir = Paths.get(project.stateStore.stateStorageManager.expandMacros(PROJECT_CONFIG_DIR), "inspectionProfiles")
+ val file = inspectionDir.resolve("profiles_settings.xml")
+
+ assertThat(file).doesNotExist()
+ val profileFile = inspectionDir.resolve("Project_Default.xml")
+ assertThat(profileFile.readText()).isEqualTo("""
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
</component>""".trimIndent())
- profileFile.write("""
+ profileFile.write("""
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
</component>""".trimIndent())
- project.baseDir.refresh(false, true)
- (ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedAlarm()
- runInInitMode {
- assertThat(projectInspectionProfileManager.currentProfile.getToolDefaultState("Convert2Diamond", project).level).isEqualTo(HighlightDisplayLevel.ERROR)
- }
- }
+ project.baseDir.refresh(false, true)
+ (ProjectManager.getInstance() as StoreAwareProjectManager).flushChangedAlarm()
+ assertThat(projectInspectionProfileManager.currentProfile.getToolDefaultState("Convert2Diamond", project).level).isEqualTo(HighlightDisplayLevel.ERROR)
}
}
}
\ No newline at end of file
}
abstract class InspectionProfileProcessor : LazySchemeProcessor<InspectionProfileImpl, InspectionProfileImpl>() {
- override final fun getState(scheme: InspectionProfileImpl): SchemeState {
- if (scheme is InspectionProfileImpl) {
- return if (scheme.wasInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED
- }
- else {
- return SchemeState.NON_PERSISTENT
- }
+ override fun getState(scheme: InspectionProfileImpl): SchemeState {
+ return if (scheme.wasInitialized()) SchemeState.POSSIBLY_CHANGED else SchemeState.UNCHANGED
}
}
\ No newline at end of file
import com.intellij.codeInspection.ex.InspectionToolRegistrar
import com.intellij.concurrency.runAsync
import com.intellij.configurationStore.SchemeDataHolder
+import com.intellij.configurationStore.digest
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.profile.Profile
import com.intellij.psi.search.scope.packageSet.NamedScopeManager
import com.intellij.psi.search.scope.packageSet.NamedScopesHolder
+import com.intellij.util.loadElement
import com.intellij.util.ui.UIUtil
import com.intellij.util.xmlb.Accessor
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters
import com.intellij.util.xmlb.XmlSerializer
import org.jdom.Element
import org.jetbrains.annotations.TestOnly
+import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.function.Function
private const val NAME = "name"
private const val PROJECT_DEFAULT_PROFILE_NAME = "Project Default"
+private val defaultSchemeDigest = loadElement("""<component name="InspectionProjectProfileManager">
+ <profile version="1.0">
+ <option name="myName" value="Project Default" />
+ </profile>
+</component>""").digest()
+
@State(name = "InspectionProjectProfileManager", storages = arrayOf(Storage(value = "inspectionProfiles/profiles_settings.xml", exclusive = true)))
class ProjectInspectionProfileManager(val project: Project,
private val applicationProfileManager: InspectionProfileManager,
override fun isSchemeFile(name: CharSequence) = !StringUtil.equals(name, "profiles_settings.xml")
+ override fun isSchemeDefault(scheme: InspectionProfileImpl, digest: ByteArray): Boolean {
+ return scheme.name == PROJECT_DEFAULT_PROFILE_NAME && Arrays.equals(digest, defaultSchemeDigest)
+ }
+
override fun onSchemeDeleted(scheme: InspectionProfileImpl) {
schemeRemoved(scheme)
}
import java.io.File
import java.io.IOException
import java.io.InputStream
-import java.io.OutputStream
import java.nio.file.Path
-import java.security.MessageDigest
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
return
}
+ // we must check it only here to avoid delete old scheme just because it is empty (old idea save -> new idea delete on open)
+ if (processor is LazySchemeProcessor && processor.isSchemeDefault(scheme, newDigest)) {
+ externalInfo?.scheduleDelete()
+ return
+ }
+
val fileName = fileNameWithoutExtension!! + schemeExtension
// file will be overwritten, so, we don't need to delete it
filesToDelete.remove(fileName)
return parent.findChild(fileName) ?: runWriteAction { parent.createChildData(requestor, fileName) }
}
-class DigestOutputStream(val digest: MessageDigest) : OutputStream() {
- override fun write(b: Int) {
- digest.update(b.toByte())
- }
-
- override fun write(b: ByteArray, off: Int, len: Int) {
- digest.update(b, off, len)
- }
-
- override fun toString(): String {
- return "[Digest Output Stream] " + digest.toString()
- }
-}
-
-fun Element.digest(): ByteArray {
- // sha-1 is enough, sha-256 is slower, see https://www.nayuki.io/page/native-hash-functions-for-java
- val digest = MessageDigest.getInstance("SHA-1")
- serializeElementToBinary(this, DigestOutputStream(digest))
- return digest.digest()
-}
-
private inline fun catchAndLog(fileName: String, runnable: (fileName: String) -> Unit) {
try {
runnable(fileName)
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.intellij.openapi.util.Ref;
import com.intellij.util.ArrayUtil;
import com.intellij.util.EventDispatcher;
+import com.intellij.util.ExceptionUtil;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Stack;
import com.intellij.util.ui.UIUtil;
+import io.netty.util.internal.SystemPropertyUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
invokeLater(runnable1, modalityState);
semaphore.waitFor();
if (!exception.isNull()) {
- // wrap everything to keep the current thread stacktrace
- // also TC ComparisonFailure feature depends on this
- throw new RuntimeException(exception.get());
+ Throwable cause = exception.get();
+ if (SystemPropertyUtil.getBoolean("invoke.later.wrap.error", true)) {
+ // wrap everything to keep the current thread stacktrace
+ // also TC ComparisonFailure feature depends on this
+ throw new RuntimeException(cause);
+ }
+ else {
+ ExceptionUtil.rethrow(cause);
+ }
}
}
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package com.intellij.configurationStore
import org.jdom.*
import com.intellij.openapi.options.Scheme
import com.intellij.openapi.options.SchemeProcessor
import org.jdom.Element
+import java.io.OutputStream
+import java.security.MessageDigest
import java.util.function.Function
interface SchemeDataHolder<in MUTABLE_SCHEME : Scheme> {
override final fun writeScheme(scheme: MUTABLE_SCHEME) = (scheme as SerializableScheme).writeScheme()
open fun isSchemeFile(name: CharSequence) = true
+
+ open fun isSchemeDefault(scheme: MUTABLE_SCHEME, digest: ByteArray) = false
+}
+
+class DigestOutputStream(val digest: MessageDigest) : OutputStream() {
+ override fun write(b: Int) {
+ digest.update(b.toByte())
+ }
+
+ override fun write(b: ByteArray, off: Int, len: Int) {
+ digest.update(b, off, len)
+ }
+
+ override fun toString(): String {
+ return "[Digest Output Stream] " + digest.toString()
+ }
+}
+
+fun Element.digest(): ByteArray {
+ // sha-1 is enough, sha-256 is slower, see https://www.nayuki.io/page/native-hash-functions-for-java
+ val digest = MessageDigest.getInstance("SHA-1")
+ serializeElementToBinary(this, DigestOutputStream(digest))
+ return digest.digest()
}
\ No newline at end of file
}
}
+class InitInspectionRule : TestRule {
+ override fun apply(base: Statement, description: Description) = statement { runInInitMode { base.evaluate() } }
+}
+
private inline fun statement(crossinline runnable: () -> Unit) = object : Statement() {
override fun evaluate() {
runnable()
import com.intellij.openapi.util.JDOMUtil
import com.intellij.util.isEmpty
+import com.intellij.util.readText
+import com.intellij.util.size
import org.assertj.core.api.AbstractAssert
+import org.assertj.core.api.PathAssert
import org.assertj.core.internal.Objects
import org.jdom.Element
+import java.nio.file.Files
+import java.nio.file.LinkOption
+import java.nio.file.Path
class JdomAssert(actual: Element?) : AbstractAssert<JdomAssert, Element?>(actual, JdomAssert::class.java) {
fun isEmpty(): JdomAssert {
Objects.instance().assertEqual(info, JDOMUtil.writeElement(actual!!), expected.trimIndent())
return this
}
+}
+
+class PathAssertEx(actual: Path?) : PathAssert(actual) {
+ override fun doesNotExist(): PathAssert {
+ isNotNull
+
+ if (Files.exists(actual, LinkOption.NOFOLLOW_LINKS)) {
+ var error = "Expecting path:\n\t${actual}\nnot to exist"
+ if (actual.size() < 16 * 1024) {
+ error += ", content:\n\n${actual.readText()}\n"
+ }
+ failWithMessage(error)
+ }
+ return this
+ }
}
\ No newline at end of file
*/
package com.intellij.testFramework;
+import org.assertj.core.api.AbstractPathAssert;
import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.nio.file.Path;
+
public final class Assertions extends org.assertj.core.api.Assertions {
+ @NotNull
public static JdomAssert assertThat(@Nullable Element element) {
return new JdomAssert(element);
}
+
+ @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
+ @NotNull
+ public static AbstractPathAssert<?> assertThat(@Nullable Path actual) {
+ return new PathAssertEx(actual);
+ }
}
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
+ public static void rethrow(@Nullable Throwable throwable) {
+ if (throwable instanceof Error) {
+ throw (Error)throwable;
+ }
+ else if (throwable instanceof RuntimeException) {
+ throw (RuntimeException)throwable;
+ }
+ else {
+ throw new RuntimeException(throwable);
+ }
+ }
+
public static void rethrowAllAsUnchecked(@Nullable Throwable t) {
if (t != null) {
rethrowUnchecked(t);
/*
- * Copyright 2000-2015 JetBrains s.r.o.
+ * Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import com.intellij.util.Consumer;
import com.intellij.util.EmptyConsumer;
+import com.intellij.util.ExceptionUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
}
if (throwables.size() == 1) {
- Throwable throwable = throwables.get(0);
- if (throwable instanceof Error) {
- throw (Error)throwable;
- }
- else if (throwable instanceof RuntimeException) {
- throw (RuntimeException)throwable;
- }
- else {
- throw new RuntimeException(throwable);
- }
+ ExceptionUtil.rethrow(throwables.get(0));
}
else {
throw new CompoundRuntimeException(throwables);
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
<orderEntry type="library" scope="TEST" name="picocontainer" level="project" />
<orderEntry type="library" scope="TEST" name="jna" level="project" />
- <orderEntry type="module" module-name="platform-impl" scope="TEST" />
+ <orderEntry type="module" module-name="core-api" scope="TEST" />
</component>
</module>
\ No newline at end of file
<orderEntry type="library" name="batik" level="project" />
<orderEntry type="library" name="xmlgraphics-commons" level="project" />
<orderEntry type="library" name="xml-apis-ext" level="project" />
+ <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
<component name="copyright">
<Base>