202: Fix compilation
This commit is contained in:
committed by
Nikolay Krasko
parent
eb67c4519d
commit
ff7576f8e4
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure
|
||||
import com.intellij.testFramework.ExpectedHighlightingData
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.TestableLineMarkerNavigator
|
||||
import org.jetbrains.kotlin.idea.navigation.NavigationTestUtils
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TagsTestDataUtil
|
||||
import org.jetbrains.kotlin.test.util.renderAsGotoImplementation
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
|
||||
fun doTest(path: String) = doTest(path) {}
|
||||
|
||||
protected fun doAndCheckHighlighting(
|
||||
psiFile: PsiFile,
|
||||
documentToAnalyze: Document,
|
||||
expectedHighlighting: ExpectedHighlightingData,
|
||||
expectedFile: File
|
||||
): List<LineMarkerInfo<*>> {
|
||||
myFixture.doHighlighting()
|
||||
|
||||
return checkHighlighting(psiFile, documentToAnalyze, expectedHighlighting, expectedFile)
|
||||
}
|
||||
|
||||
fun doTest(path: String, additionalCheck: () -> Unit) {
|
||||
val fileText = FileUtil.loadFile(testDataFile())
|
||||
try {
|
||||
ConfigLibraryUtil.configureLibrariesByDirective(myFixture.module, PlatformTestUtil.getCommunityPath(), fileText)
|
||||
if (InTextDirectivesUtils.findStringWithPrefixes(fileText, "METHOD_SEPARATORS") != null) {
|
||||
DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS = true
|
||||
}
|
||||
|
||||
myFixture.configureByFile(fileName())
|
||||
val project = myFixture.project
|
||||
val document = myFixture.editor.document
|
||||
|
||||
val data = ExpectedHighlightingData(document, false, false, false)
|
||||
data.init()
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
val markers = doAndCheckHighlighting(myFixture.file, document, data, testDataFile())
|
||||
|
||||
assertNavigationElements(myFixture.project, myFixture.file as KtFile, markers)
|
||||
additionalCheck()
|
||||
} catch (exc: Exception) {
|
||||
throw RuntimeException(exc)
|
||||
} finally {
|
||||
ConfigLibraryUtil.unconfigureLibrariesByDirective(module, fileText)
|
||||
DaemonCodeAnalyzerSettings.getInstance().SHOW_METHOD_SEPARATORS = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private const val LINE_MARKER_PREFIX = "LINEMARKER:"
|
||||
private const val TARGETS_PREFIX = "TARGETS"
|
||||
|
||||
fun assertNavigationElements(project: Project, file: KtFile, markers: List<LineMarkerInfo<*>>) {
|
||||
val navigationDataComments = KotlinTestUtils.getLastCommentsInFile(
|
||||
file, KotlinTestUtils.CommentType.BLOCK_COMMENT, false
|
||||
)
|
||||
if (navigationDataComments.isEmpty()) return
|
||||
|
||||
for ((navigationCommentIndex, navigationComment) in navigationDataComments.reversed().withIndex()) {
|
||||
val description = getLineMarkerDescription(navigationComment)
|
||||
val navigateMarkers = markers.filter { it.lineMarkerTooltip?.startsWith(description) == true }
|
||||
val navigateMarker = navigateMarkers.singleOrNull() ?: navigateMarkers.getOrNull(navigationCommentIndex)
|
||||
|
||||
TestCase.assertNotNull(
|
||||
String.format("Can't find marker for navigation check with description \"%s\"", description),
|
||||
navigateMarker
|
||||
)
|
||||
|
||||
val handler = navigateMarker!!.navigationHandler
|
||||
if (handler is TestableLineMarkerNavigator) {
|
||||
val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy {
|
||||
it.renderAsGotoImplementation()
|
||||
}
|
||||
val actualNavigationData = NavigationTestUtils.getNavigateElementsText(project, navigateElements)
|
||||
|
||||
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)
|
||||
} else {
|
||||
Assert.fail("Only TestableLineMarkerNavigator are supported in navigate check")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLineMarkerDescription(navigationComment: String): String {
|
||||
val firstLineEnd = navigationComment.indexOf("\n")
|
||||
TestCase.assertTrue(
|
||||
"The first line in block comment must contain description of marker for navigation check", firstLineEnd != -1
|
||||
)
|
||||
|
||||
var navigationMarkerText = navigationComment.substring(0, firstLineEnd)
|
||||
|
||||
TestCase.assertTrue(
|
||||
String.format("Add %s directive in first line of comment", LINE_MARKER_PREFIX),
|
||||
navigationMarkerText.startsWith(LINE_MARKER_PREFIX)
|
||||
)
|
||||
|
||||
navigationMarkerText = navigationMarkerText.substring(LINE_MARKER_PREFIX.length)
|
||||
|
||||
return navigationMarkerText.trim { it <= ' ' }
|
||||
}
|
||||
|
||||
private fun getExpectedNavigationText(navigationComment: String): String {
|
||||
val firstLineEnd = navigationComment.indexOf("\n")
|
||||
|
||||
var expectedNavigationText = navigationComment.substring(firstLineEnd + 1)
|
||||
|
||||
TestCase.assertTrue(
|
||||
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
|
||||
expectedNavigationText.startsWith(TARGETS_PREFIX)
|
||||
)
|
||||
|
||||
expectedNavigationText = expectedNavigationText.substring(expectedNavigationText.indexOf("\n") + 1)
|
||||
|
||||
return expectedNavigationText
|
||||
}
|
||||
|
||||
fun checkHighlighting(
|
||||
psiFile: PsiFile,
|
||||
documentToAnalyze: Document,
|
||||
expectedHighlighting: ExpectedHighlightingData,
|
||||
expectedFile: File
|
||||
): MutableList<LineMarkerInfo<*>> {
|
||||
val markers = DaemonCodeAnalyzerImpl.getLineMarkers(documentToAnalyze, psiFile.project)
|
||||
|
||||
try {
|
||||
expectedHighlighting.checkLineMarkers(psiFile, markers, documentToAnalyze.text)
|
||||
|
||||
// This is a workaround for sad bug in ExpectedHighlightingData:
|
||||
// the latter doesn't throw assertion error when some line markers are expected, but none are present.
|
||||
if (FileUtil.loadFile(expectedFile).contains("<lineMarker") && markers.isEmpty()) {
|
||||
throw AssertionError("Some line markers are expected, but nothing is present at all")
|
||||
}
|
||||
} catch (error: AssertionError) {
|
||||
try {
|
||||
val actualTextWithTestData = TagsTestDataUtil.insertInfoTags(markers, true, documentToAnalyze.text)
|
||||
KotlinTestUtils.assertEqualsToFile(expectedFile, actualTextWithTestData)
|
||||
} catch (failure: FileComparisonFailure) {
|
||||
throw FileComparisonFailure(
|
||||
error.message + "\n" + failure.message,
|
||||
failure.expected,
|
||||
failure.actual,
|
||||
failure.filePath
|
||||
)
|
||||
}
|
||||
}
|
||||
return markers
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.codeInsight
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.testFramework.ExpectedHighlightingData
|
||||
import com.intellij.util.io.createFile
|
||||
import com.intellij.util.io.write
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.SdkAndMockLibraryProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
abstract class AbstractLineMarkersTestInLibrarySources : AbstractLineMarkersTest() {
|
||||
|
||||
private var libraryCleanPath: String? = null
|
||||
|
||||
private var libraryClean: File? = null
|
||||
|
||||
private fun getLibraryCleanPath(): String = libraryCleanPath!!
|
||||
|
||||
private fun getLibraryOriginalPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/codeInsightInLibrary/_library"
|
||||
|
||||
override fun getProjectDescriptor(): SdkAndMockLibraryProjectDescriptor {
|
||||
if (libraryCleanPath == null) {
|
||||
val libraryClean = Files.createTempDirectory("lineMarkers_library")
|
||||
val libraryOriginal = File(getLibraryOriginalPath())
|
||||
libraryCleanPath = libraryClean.toString()
|
||||
|
||||
for (file in libraryOriginal.walkTopDown().filter { !it.isDirectory }) {
|
||||
val text = file.readText().replace("</?lineMarker.*?>".toRegex(), "")
|
||||
val cleanFile = libraryClean.resolve(file.relativeTo(libraryOriginal).path)
|
||||
cleanFile.createFile()
|
||||
cleanFile.write(text)
|
||||
}
|
||||
this.libraryClean = File(libraryCleanPath)
|
||||
}
|
||||
return object : SdkAndMockLibraryProjectDescriptor(getLibraryCleanPath(), false) {
|
||||
override fun configureModule(module: Module, model: ModifiableRootModel) {
|
||||
super.configureModule(module, model)
|
||||
|
||||
val library = model.moduleLibraryTable.getLibraryByName(LIBRARY_NAME)!!
|
||||
val modifiableModel = library.modifiableModel
|
||||
|
||||
modifiableModel.addRoot(LocalFileSystem.getInstance().findFileByIoFile(libraryClean!!)!!, OrderRootType.SOURCES)
|
||||
modifiableModel.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun doTestWithLibrary(path: String) {
|
||||
doTest(path) {
|
||||
val fileSystem = VirtualFileManager.getInstance().getFileSystem("file")
|
||||
val libraryOriginal = File(getLibraryOriginalPath())
|
||||
val project = myFixture.project
|
||||
for (file in libraryOriginal.walkTopDown().filter { !it.isDirectory }) {
|
||||
myFixture.openFileInEditor(fileSystem.findFileByPath(file.absolutePath)!!)
|
||||
val data = ExpectedHighlightingData(myFixture.editor.document, false, false, false)
|
||||
data.init()
|
||||
|
||||
val librarySourceFile = libraryClean!!.resolve(file.relativeTo(libraryOriginal).path)
|
||||
myFixture.openFileInEditor(fileSystem.findFileByPath(librarySourceFile.absolutePath)!!)
|
||||
val document = myFixture.editor.document
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
|
||||
if (!ProjectRootsUtil.isLibrarySourceFile(project, myFixture.file.virtualFile)) {
|
||||
throw AssertionError("File ${myFixture.file.virtualFile.path} should be in library sources!")
|
||||
}
|
||||
|
||||
doAndCheckHighlighting(myFixture.file, document, data, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
libraryClean?.deleteRecursively()
|
||||
ConfigLibraryUtil.removeLibrary(module, SdkAndMockLibraryProjectDescriptor.LIBRARY_NAME)
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.highlighter
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||
import com.intellij.codeInsight.highlighting.HighlightUsagesHandler
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.testFramework.ExpectedHighlightingData
|
||||
import org.jetbrains.kotlin.idea.test.*
|
||||
|
||||
abstract class AbstractUsageHighlightingTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
companion object {
|
||||
// Not standard <caret> to leave it in text after configureByFile and remove manually after collecting highlighting information
|
||||
const val CARET_TAG = "~"
|
||||
}
|
||||
|
||||
protected fun doTest(unused: String) {
|
||||
myFixture.configureByFile(fileName())
|
||||
val document = myFixture.editor.document
|
||||
val data = ExpectedHighlightingData(document, false, false, true, false)
|
||||
data.init()
|
||||
|
||||
val caret = document.extractMarkerOffset(project, CARET_TAG)
|
||||
assert(caret != -1) { "Caret marker '$CARET_TAG' expected" }
|
||||
editor.caretModel.moveToOffset(caret)
|
||||
|
||||
HighlightUsagesHandler.invoke(project, editor, myFixture.file)
|
||||
val highlighters = myFixture.editor.markupModel.allHighlighters
|
||||
|
||||
val infos = highlighters
|
||||
.filter { isUsageHighlighting(it) }
|
||||
.map { highlighter ->
|
||||
var startOffset = highlighter.startOffset
|
||||
var endOffset = highlighter.endOffset
|
||||
|
||||
if (startOffset > caret) startOffset += CARET_TAG.length
|
||||
if (endOffset > caret) endOffset += CARET_TAG.length
|
||||
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION)
|
||||
.range(startOffset, endOffset)
|
||||
.create()
|
||||
}
|
||||
|
||||
data.checkResult(myFixture.file, infos, StringBuilder(document.text).insert(caret, CARET_TAG).toString())
|
||||
}
|
||||
|
||||
private fun isUsageHighlighting(info: RangeHighlighter): Boolean {
|
||||
val globalScheme = EditorColorsManager.getInstance().globalScheme
|
||||
|
||||
val readAttributes: TextAttributes =
|
||||
globalScheme.getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)
|
||||
val writeAttributes: TextAttributes =
|
||||
globalScheme.getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES)
|
||||
|
||||
return info.textAttributes == readAttributes || info.textAttributes == writeAttributes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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.inspections
|
||||
|
||||
import com.intellij.codeInspection.InspectionEP
|
||||
import com.intellij.codeInspection.InspectionProfileEntry
|
||||
import com.intellij.codeInspection.LocalInspectionEP
|
||||
import com.intellij.codeInspection.LocalInspectionTool
|
||||
import com.intellij.codeInspection.ex.InspectionToolRegistrar
|
||||
import com.intellij.codeInspection.ex.InspectionToolWrapper
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import gnu.trove.THashMap
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class InspectionDescriptionTest : LightPlatformTestCase() {
|
||||
|
||||
fun testDescriptionsAndShortNames() {
|
||||
val shortNames = THashMap<String, InspectionToolWrapper<InspectionProfileEntry, InspectionEP>>()
|
||||
|
||||
val inspectionTools = loadKotlinInspections()
|
||||
val errors = StringBuilder()
|
||||
for (toolWrapper in inspectionTools) {
|
||||
val description = toolWrapper.loadDescription()
|
||||
|
||||
if (description == null) {
|
||||
errors.append("description is null for inspection '").append(desc(toolWrapper))
|
||||
}
|
||||
|
||||
val shortName = toolWrapper.shortName
|
||||
val tool = shortNames[shortName]
|
||||
if (tool != null) {
|
||||
errors.append(
|
||||
"Short names must be unique: " + shortName + "\n" +
|
||||
"inspection: '" + desc(tool) + "\n" +
|
||||
" and '" + desc(toolWrapper)
|
||||
)
|
||||
}
|
||||
shortNames.put(shortName, toolWrapper)
|
||||
}
|
||||
|
||||
UsefulTestCase.assertEmpty(errors.toString())
|
||||
}
|
||||
|
||||
private fun loadKotlinInspections(): List<InspectionToolWrapper<InspectionProfileEntry, InspectionEP>> {
|
||||
return InspectionToolRegistrar.getInstance().createTools().filter {
|
||||
it.extension.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
|
||||
} as List<InspectionToolWrapper<InspectionProfileEntry, InspectionEP>>
|
||||
}
|
||||
|
||||
private fun loadKotlinInspectionExtensions() =
|
||||
LocalInspectionEP.LOCAL_INSPECTION.extensions.filter {
|
||||
it.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
|
||||
}
|
||||
|
||||
private fun desc(tool: InspectionToolWrapper<InspectionProfileEntry, InspectionEP>): String {
|
||||
return tool.toString() + " ('" + tool.descriptionContextClass + "') " +
|
||||
"in " + if (tool.extension == null) null else tool.extension.pluginDescriptor
|
||||
}
|
||||
|
||||
fun testExtensionPoints() {
|
||||
val shortNames = THashMap<String, LocalInspectionEP>()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val inspectionEPs = Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION)
|
||||
|
||||
val tools = inspectionEPs.size
|
||||
val errors = StringBuilder()
|
||||
for (ep in inspectionEPs) {
|
||||
val shortName = ep.getShortName()
|
||||
val ep1 = shortNames[shortName]
|
||||
if (ep1 != null) {
|
||||
errors.append(
|
||||
"Short names must be unique: '" + shortName + "':\n" +
|
||||
"inspection: '" + ep1.implementationClass + "' in '" + ep1.pluginDescriptor + "'\n" +
|
||||
"; and '" + ep.implementationClass + "' in '" + ep.pluginDescriptor + "'"
|
||||
)
|
||||
}
|
||||
shortNames.put(shortName, ep)
|
||||
}
|
||||
println("$tools inspection tools total via EP")
|
||||
|
||||
UsefulTestCase.assertEmpty(errors.toString())
|
||||
}
|
||||
|
||||
fun testInspectionMappings() {
|
||||
val toolWrappers = loadKotlinInspections()
|
||||
val errors = StringBuilder()
|
||||
toolWrappers.filter({ toolWrapper -> toolWrapper.extension == null }).forEach { toolWrapper ->
|
||||
errors.append("Please add XML mapping for ").append(toolWrapper.tool::class.java)
|
||||
}
|
||||
|
||||
UsefulTestCase.assertEmpty(errors.toString())
|
||||
}
|
||||
|
||||
fun testMismatchedIds() {
|
||||
val failMessages = mutableListOf<String>()
|
||||
for (ep in loadKotlinInspectionExtensions()) {
|
||||
val toolName = ep.implementationClass
|
||||
val tool = ep.instantiateTool()
|
||||
if (tool is LocalInspectionTool) {
|
||||
checkValue(failMessages, toolName, "suppressId", ep.id, ep.getShortName(), tool.id)
|
||||
checkValue(failMessages, toolName, "alternateId", ep.alternativeId, null, tool.alternativeID)
|
||||
checkValue(failMessages, toolName, "shortName", ep.getShortName(), null, tool.shortName)
|
||||
checkValue(failMessages, toolName, "runForWholeFile", null, "false", tool.runForWholeFile().toString())
|
||||
}
|
||||
}
|
||||
|
||||
UsefulTestCase.assertEmpty(StringUtil.join(failMessages, "\n"), failMessages)
|
||||
}
|
||||
|
||||
fun testNotEmptyToolNames() {
|
||||
val failMessages = mutableListOf<String>()
|
||||
for (ep in LocalInspectionEP.LOCAL_INSPECTION.extensions) {
|
||||
val toolName = ep.implementationClass
|
||||
if (ep.getDisplayName().isNullOrEmpty()) {
|
||||
failMessages.add(toolName + ": toolName is not set, tool won't be available in `run inspection` action")
|
||||
}
|
||||
}
|
||||
UsefulTestCase.assertEmpty(failMessages.joinToString("\n"), failMessages)
|
||||
}
|
||||
|
||||
private fun checkValue(
|
||||
failMessages: MutableCollection<String>,
|
||||
toolName: String,
|
||||
attributeName: String,
|
||||
xmlValue: String?,
|
||||
defaultXmlValue: String?,
|
||||
javaValue: String?
|
||||
) {
|
||||
if (StringUtil.isNotEmpty(xmlValue)) {
|
||||
if (javaValue != xmlValue) {
|
||||
failMessages.add("$toolName: mismatched $attributeName. Xml: $xmlValue; Java: $javaValue")
|
||||
}
|
||||
} else if (StringUtil.isNotEmpty(javaValue)) {
|
||||
if (javaValue != defaultXmlValue) {
|
||||
failMessages.add("$toolName: $attributeName overridden in wrong way, will work in tests only. Please set appropriate $attributeName value in XML ($javaValue)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.intentions
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionActionBean
|
||||
import com.intellij.codeInsight.intention.IntentionManager
|
||||
import com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class IntentionDescriptionTest : LightPlatformTestCase() {
|
||||
|
||||
private val necessaryNormalNames = listOf("description.html", "before.kt.template", "after.kt.template")
|
||||
private val necessaryXmlNames = listOf("description.html", "before.xml.template", "after.xml.template")
|
||||
private val necessaryMavenNames = listOf("description.html")
|
||||
|
||||
fun testDescriptionsAndShortNames() {
|
||||
val intentionTools = loadKotlinIntentions()
|
||||
val errors = StringBuilder()
|
||||
for (tool in intentionTools) {
|
||||
val className = tool.className
|
||||
val shortName = className.substringAfterLast(".").replace("$", "")
|
||||
val directory = File("idea/resources-en/intentionDescriptions/$shortName")
|
||||
if (!directory.exists() || !directory.isDirectory) {
|
||||
if (tool.categories != null) {
|
||||
errors.append("No description directory for intention '").append(className).append("'\n")
|
||||
}
|
||||
} else {
|
||||
val necessaryNames = when {
|
||||
shortName.isMavenIntentionName() -> necessaryMavenNames
|
||||
shortName.isXmlIntentionName() -> necessaryXmlNames
|
||||
else -> necessaryNormalNames
|
||||
}
|
||||
for (fileName in necessaryNames) {
|
||||
val file = directory.resolve(fileName)
|
||||
if (!file.exists() || !file.isFile) {
|
||||
errors.append("No description file $fileName for intention '").append(className).append("'\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UsefulTestCase.assertEmpty(errors.toString())
|
||||
}
|
||||
|
||||
private fun String.isMavenIntentionName() = startsWith("MavenPlugin")
|
||||
|
||||
private fun String.isXmlIntentionName() = startsWith("Add") && endsWith("ToManifest")
|
||||
|
||||
private fun loadKotlinIntentions(): List<IntentionActionBean> {
|
||||
val extensionPoint = Extensions.getRootArea().getExtensionPoint(IntentionManagerImpl.EP_INTENTION_ACTIONS)
|
||||
return extensionPoint.extensions.toList().filter {
|
||||
it.pluginDescriptor.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.analysis.AnalysisScope
|
||||
import com.intellij.ide.projectView.TreeStructureProvider
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructureBase
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil
|
||||
import com.intellij.slicer.DuplicateMap
|
||||
import com.intellij.slicer.SliceAnalysisParams
|
||||
import com.intellij.slicer.SliceNode
|
||||
import com.intellij.slicer.SliceRootNode
|
||||
import com.intellij.usages.TextChunk
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.contains
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.awt.Font
|
||||
|
||||
internal class TestSliceTreeStructure(private val rootNode: SliceNode) : AbstractTreeStructureBase(rootNode.project) {
|
||||
override fun getProviders() = emptyList<TreeStructureProvider>()
|
||||
|
||||
override fun getRootElement() = rootNode
|
||||
|
||||
override fun commit() {
|
||||
}
|
||||
|
||||
override fun hasSomethingToCommit() = false
|
||||
}
|
||||
|
||||
internal fun buildTreeRepresentation(rootNode: SliceNode): String {
|
||||
val project = rootNode.element!!.project!!
|
||||
val projectScope = GlobalSearchScope.projectScope(project)
|
||||
|
||||
fun TextChunk.render(): String {
|
||||
var text = text
|
||||
if (attributes.fontType == Font.BOLD) {
|
||||
text = "<bold>$text</bold>"
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
fun SliceNode.isSliceLeafValueClassNode() = this is HackedSliceLeafValueClassNode
|
||||
|
||||
fun process(node: SliceNode, indent: Int): String {
|
||||
val usage = node.element!!.value
|
||||
|
||||
node.calculateDupNode()
|
||||
val isDuplicated = !node.isSliceLeafValueClassNode() && node.duplicate != null
|
||||
|
||||
return buildString {
|
||||
when {
|
||||
node is SliceRootNode && usage.element is KtFile -> {
|
||||
node.sortedChildren.forEach { append(process(it, indent)) }
|
||||
return@buildString
|
||||
}
|
||||
|
||||
// SliceLeafValueClassNode is package-private
|
||||
node.isSliceLeafValueClassNode() -> append("[${node.nodeText}]\n")
|
||||
|
||||
else -> {
|
||||
val chunks = usage.text
|
||||
if (!PsiSearchScopeUtil.isInScope(projectScope, usage.element!!)) {
|
||||
append("LIB ")
|
||||
} else {
|
||||
append(chunks.first().render() + " ")
|
||||
}
|
||||
|
||||
repeat(indent) { append('\t') }
|
||||
|
||||
if (usage is KotlinSliceDereferenceUsage) {
|
||||
append("DEREFERENCE: ")
|
||||
}
|
||||
|
||||
if (usage is KotlinSliceUsage) {
|
||||
usage.mode.inlineCallStack.forEach {
|
||||
append("(INLINE CALL ${it.function?.name}) ")
|
||||
}
|
||||
usage.mode.behaviourStack.reversed().joinTo(this, separator = "") { it.testPresentationPrefix }
|
||||
}
|
||||
|
||||
if (isDuplicated) {
|
||||
append("DUPLICATE: ")
|
||||
}
|
||||
|
||||
chunks.slice(1 until chunks.size).joinTo(this, separator = "") { it.render() }
|
||||
|
||||
KotlinSliceUsageCellRenderer.containerSuffix(usage)?.let {
|
||||
append(" ($it)")
|
||||
}
|
||||
|
||||
append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDuplicated) {
|
||||
node.sortedChildren.forEach { append(process(it, indent + 1)) }
|
||||
}
|
||||
}.replace(Regex("</bold><bold>"), "")
|
||||
}
|
||||
|
||||
return process(rootNode, 0)
|
||||
}
|
||||
|
||||
private val SliceNode.sortedChildren: List<SliceNode>
|
||||
get() = children.sortedBy { it.value.element?.startOffset ?: -1 }
|
||||
|
||||
internal fun testSliceFromOffset(
|
||||
file: KtFile,
|
||||
offset: Int,
|
||||
doTest: (sliceProvider: KotlinSliceProvider, rootNode: SliceRootNode) -> Unit
|
||||
) {
|
||||
val fileText = file.text
|
||||
val flowKind = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// FLOW: ")
|
||||
val withDereferences = InTextDirectivesUtils.isDirectiveDefined(fileText, "// WITH_DEREFERENCES")
|
||||
val analysisParams = SliceAnalysisParams().apply {
|
||||
dataFlowToThis = when (flowKind) {
|
||||
"IN" -> true
|
||||
"OUT" -> false
|
||||
else -> throw AssertionError("Invalid flow kind: $flowKind")
|
||||
}
|
||||
showInstanceDereferences = withDereferences
|
||||
scope = AnalysisScope(file.project)
|
||||
}
|
||||
|
||||
val elementAtCaret = file.findElementAt(offset)!!
|
||||
val sliceProvider = KotlinSliceProvider()
|
||||
val expression = sliceProvider.getExpressionAtCaret(elementAtCaret, analysisParams.dataFlowToThis)!!
|
||||
val rootUsage = sliceProvider.createRootUsage(expression, analysisParams)
|
||||
val rootNode = SliceRootNode(file.project, DuplicateMap(), rootUsage)
|
||||
doTest(sliceProvider, rootNode)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.stubs
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.impl.cache.CacheManager
|
||||
import com.intellij.psi.impl.source.tree.TreeElement
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.UsageSearchContext
|
||||
import com.intellij.testFramework.ExpectedHighlightingData
|
||||
|
||||
abstract class AbstractMultiHighlightingTest : AbstractMultiModuleTest() {
|
||||
|
||||
protected open val shouldCheckLineMarkers = false
|
||||
|
||||
protected open val shouldCheckResult = true
|
||||
|
||||
override fun checkHighlighting(data: ExpectedHighlightingData): Collection<HighlightInfo> {
|
||||
data.init()
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments()
|
||||
|
||||
//to load text
|
||||
ApplicationManager.getApplication().runWriteAction { TreeUtil.clearCaches(myFile.node as TreeElement) }
|
||||
|
||||
//to initialize caches
|
||||
if (!DumbService.isDumb(project)) {
|
||||
CacheManager.SERVICE.getInstance(myProject)
|
||||
.getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(myProject), true)
|
||||
}
|
||||
|
||||
val infos = doHighlighting()
|
||||
|
||||
val text = myEditor.document.text
|
||||
if (shouldCheckLineMarkers) {
|
||||
data.checkLineMarkers(myFile, DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(file), project), text)
|
||||
}
|
||||
if (shouldCheckResult) {
|
||||
data.checkResult(myFile, infos, text)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user