Check also drop-box content in multi-module line marker tests

Related to KT-26957
This commit is contained in:
Mikhail Glukhikh
2018-10-25 13:04:47 +03:00
parent f4c77ec66f
commit e39953c4a0
10 changed files with 163 additions and 76 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.highlighter.markers
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
import com.intellij.ide.util.DefaultPsiElementCellRenderer
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analyzer.ModuleInfo
@@ -64,19 +63,26 @@ fun getPlatformActualTooltip(declaration: KtDeclaration): String? {
}
}
fun navigateToPlatformActual(e: MouseEvent?, declaration: KtDeclaration) {
val actualDeclarations =
declaration.actualsForExpected() + declaration.findMarkerBoundDeclarations().flatMap { it.actualsForExpected() }
if (actualDeclarations.isEmpty()) return
fun KtDeclaration.allNavigatableActualDeclarations(): Set<KtDeclaration> =
actualsForExpected() + findMarkerBoundDeclarations().flatMap { it.actualsForExpected() }
val renderer = object : DefaultPsiElementCellRenderer() {
override fun getContainerText(element: PsiElement?, name: String?) = ""
class ActualExpectedPsiElementCellRenderer : DefaultPsiElementCellRenderer() {
override fun getContainerText(element: PsiElement?, name: String?) = ""
}
fun KtDeclaration.navigateToActualTitle() = "Choose actual for $name"
fun KtDeclaration.navigateToActualUsagesTitle() = "Actuals for $name"
fun buildNavigateToActualDeclarationsPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
return element?.markerDeclaration?.let {
val navigatableActualDeclarations = it.allNavigatableActualDeclarations()
if (navigatableActualDeclarations.isEmpty()) return null
return NavigationPopupDescriptor(
navigatableActualDeclarations,
it.navigateToActualTitle(),
it.navigateToActualUsagesTitle(),
ActualExpectedPsiElementCellRenderer()
)
}
PsiElementListNavigator.openTargets(
e,
actualDeclarations.toTypedArray(),
"Choose actual for ${declaration.name}",
"Actuals for ${declaration.name}",
renderer
)
}
@@ -16,8 +16,6 @@
package org.jetbrains.kotlin.idea.highlighter.markers
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
import com.intellij.ide.util.DefaultPsiElementCellRenderer
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
@@ -38,20 +36,22 @@ fun getExpectedDeclarationTooltip(declaration: KtDeclaration): String? {
return "Has declaration in common module"
}
fun navigateToExpectedDeclaration(e: MouseEvent?, declaration: KtDeclaration) {
val expectedDeclarations =
listOfNotNull(declaration.expectedDeclarationIfAny()) +
declaration.findMarkerBoundDeclarations().mapNotNull { it.expectedDeclarationIfAny() }
if (expectedDeclarations.isEmpty()) return
fun KtDeclaration.allNavigatableExpectedDeclarations(): List<KtDeclaration> =
listOfNotNull(expectedDeclarationIfAny()) + findMarkerBoundDeclarations().mapNotNull { it.expectedDeclarationIfAny() }
val renderer = object : DefaultPsiElementCellRenderer() {
override fun getContainerText(element: PsiElement?, name: String?) = ""
fun KtDeclaration.navigateToExpectedTitle() = "Choose expected for $name"
fun KtDeclaration.navigateToExpectedUsagesTitle() = "Expected for $name"
fun buildNavigateToExpectedDeclarationsPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
return element?.markerDeclaration?.let {
val navigatableExpectedDeclarations = it.allNavigatableExpectedDeclarations()
if (navigatableExpectedDeclarations.isEmpty()) return null
return NavigationPopupDescriptor(
navigatableExpectedDeclarations,
it.navigateToExpectedTitle(),
it.navigateToExpectedUsagesTitle(),
ActualExpectedPsiElementCellRenderer()
)
}
PsiElementListNavigator.openTargets(
e,
expectedDeclarations.toTypedArray(),
"Choose expected for ${declaration.name}",
"Expected for ${declaration.name}",
renderer
)
}
@@ -198,28 +198,52 @@ private val OVERRIDDEN_PROPERTY = object : MarkerType(
}
}
private val PsiElement.markerDeclaration
val PsiElement.markerDeclaration
get() = (this as? KtDeclaration) ?: (parent as? KtDeclaration)
private val PLATFORM_ACTUAL = MarkerType(
private val PLATFORM_ACTUAL = object : MarkerType(
"PLATFORM_ACTUAL",
{ element -> element?.markerDeclaration?.let { getPlatformActualTooltip(it) } },
object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) {
element?.markerDeclaration?.let { navigateToPlatformActual(e, it) }
buildNavigateToActualDeclarationsPopup(e, element)?.showPopup(e)
}
}) {
override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
val superHandler = super.getNavigationHandler()
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
superHandler.navigate(e, elt)
}
override fun getTargetsPopupDescriptor(element: PsiElement?): NavigationPopupDescriptor? {
return buildNavigateToActualDeclarationsPopup(null, element)
}
}
}
)
}
private val EXPECTED_DECLARATION = MarkerType(
private val EXPECTED_DECLARATION = object : MarkerType(
"EXPECTED_DECLARATION",
{ element -> element?.markerDeclaration?.let { getExpectedDeclarationTooltip(it) } },
object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) {
element?.markerDeclaration?.let { navigateToExpectedDeclaration(e, it) }
buildNavigateToExpectedDeclarationsPopup(e, element)?.showPopup(e)
}
}) {
override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
val superHandler = super.getNavigationHandler()
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
superHandler.navigate(e, elt)
}
override fun getTargetsPopupDescriptor(element: PsiElement?): NavigationPopupDescriptor? {
return buildNavigateToExpectedDeclarationsPopup(null, element)
}
}
}
)
}
private fun isImplementsAndNotOverrides(
descriptor: CallableMemberDescriptor,
@@ -1 +1,11 @@
actual class <lineMarker>WithConstructor</lineMarker> actual constructor(actual val x: Int, actual val s: String)
actual class <lineMarker descr="Has declaration in common module">WithConstructor</lineMarker> actual constructor(actual val x: Int, actual val s: String)
/*
LINEMARKER: Has declaration in common module
TARGETS:
common.kt
expect class <1>WithConstructor(x: Int, s: String) {
val <3>x: Int
val <2>s: String
*/
@@ -1,3 +1,10 @@
package test
actual enum class <lineMarker>Enum</lineMarker> { A, B, C }
actual enum class <lineMarker descr="Has declaration in common module">Enum</lineMarker> { A, B, C }
/*
LINEMARKER: Has declaration in common module
TARGETS:
common.kt
expect enum class <4>Enum { <1>A, <2>B, <3>C }
*/
@@ -1,3 +1,10 @@
package test
expect enum class <lineMarker>Enum</lineMarker> { A, B, C, D }
expect enum class <lineMarker descr="Has actuals in JVM">Enum</lineMarker> { A, B, C, D }
/*
LINEMARKER: Has actuals in JVM
TARGETS:
jvm.kt
actual enum class <5>Enum { <1>A, <2>B, <3>C, <4>D }
*/
@@ -1,3 +1,12 @@
interface <lineMarker>I</lineMarker> {
suspend fun <lineMarker descr="<html><body>Is implemented in <br>&nbsp;&nbsp;&nbsp;&nbsp;KJs<br>&nbsp;&nbsp;&nbsp;&nbsp;KJvm</body></html>">foo</lineMarker>(s: String)
}
}
/*
LINEMARKER: <html><body>Is implemented in <br>&nbsp;&nbsp;&nbsp;&nbsp;KJs<br>&nbsp;&nbsp;&nbsp;&nbsp;KJvm</body></html>
TARGETS:
js.kt
suspend override fun <1>foo(s: String) {
jvm.kt
suspend override fun <2>foo(s: String) {
*/
@@ -16,16 +16,30 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.codeInsight.AbstractLineMarkersTest
import org.jetbrains.kotlin.idea.multiplatform.setupMppProjectFromDirStructure
import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.allJavaFiles
import org.jetbrains.kotlin.idea.test.allKotlinFiles
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() {
protected open fun checkLineMarkersInProject(
findFiles: () -> List<PsiFile> = { project.allKotlinFiles().excludeByDirective() }
) {
checkFiles(findFiles) {
checkHighlighting(myEditor, true, false)
val markers = DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(file), project)
AbstractLineMarkersTest.assertNavigationElements(project, myFile as KtFile, markers)
}
}
protected open fun checkHighlightingInProject(
findFiles: () -> List<PsiFile> = { project.allKotlinFiles().excludeByDirective() }
) {
@@ -32,6 +32,6 @@ abstract class AbstractMultiModuleLineMarkerTest : AbstractMultiModuleHighlighti
protected fun doTest(path: String) {
setupMppProjectFromDirStructure(File(path))
checkHighlightingInProject()
checkLineMarkersInProject()
}
}
@@ -9,6 +9,7 @@ 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.rt.execution.junit.FileComparisonFailure
@@ -93,7 +94,7 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
val markers = doAndCheckHighlighting(document, data, File(path))
assertNavigationElements(markers)
assertNavigationElements(myFixture.project, myFixture.file as KtFile, markers)
additionalCheck()
} catch (exc: Exception) {
throw RuntimeException(exc)
@@ -104,45 +105,53 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
}
private fun assertNavigationElements(markers: List<LineMarkerInfo<*>>) {
val navigationDataComments = KotlinTestUtils.getLastCommentsInFile(
myFixture.file as KtFile, KotlinTestUtils.CommentType.BLOCK_COMMENT, false)
if (navigationDataComments.isEmpty()) return
for (navigationComment in navigationDataComments) {
val description = getLineMarkerDescription(navigationComment)
val navigateMarker = markers.find { it.lineMarkerTooltip?.startsWith(description) == true }!!
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(myFixture.project, navigateElements)
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)
}
else {
Assert.fail("Only SuperDeclarationMarkerNavigationHandler are supported in navigate check")
}
}
}
companion object {
private val LINE_MARKER_PREFIX = "LINEMARKER:"
private val TARGETS_PREFIX = "TARGETS"
@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 (navigationComment in navigationDataComments) {
val description = getLineMarkerDescription(navigationComment)
val navigateMarker = markers.find { it.lineMarkerTooltip?.startsWith(description) == true }!!
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)
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))
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)
@@ -155,8 +164,9 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
var expectedNavigationText = navigationComment.substring(firstLineEnd + 1)
TestCase.assertTrue(
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
expectedNavigationText.startsWith(TARGETS_PREFIX))
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
expectedNavigationText.startsWith(TARGETS_PREFIX)
)
expectedNavigationText = expectedNavigationText.substring(expectedNavigationText.indexOf("\n") + 1)