mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-10 13:17:09 +07:00
[threading] IJPL-179707: Minor bug fix and refactoring of Analyzer
GitOrigin-RevId: ca372c621f208ccba68219f8bf91e43c2958bc63
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6c81db53bc
commit
ae7ce93178
@@ -13,8 +13,6 @@
|
||||
<module name="intellij.platform.jewel.ideLafBridge"/>
|
||||
<module name="intellij.libraries.compose.foundation.desktop"/>
|
||||
<module name="intellij.libraries.skiko"/>
|
||||
<module name="intellij.platform.jewel.markdown.core"/>
|
||||
<module name="intellij.platform.jewel.markdown.ideLafBridgeStyling"/>
|
||||
</dependencies>
|
||||
|
||||
<resource-bundle>messages.DevKitBundle</resource-bundle>
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.threadingModelHelper
|
||||
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
|
||||
|
||||
class LockReqsAnalyzer(private val detector: LockReqsDetector = LockReqsDetector()) {
|
||||
|
||||
private data class TraversalContext(
|
||||
val config: AnalysisConfig,
|
||||
val currentPath: MutableList<MethodCall> = mutableListOf(),
|
||||
val visited: MutableSet<MethodSignature> = mutableSetOf(),
|
||||
val paths: MutableSet<ExecutionPath> = mutableSetOf(),
|
||||
val messageBusTopics: MutableSet<String> = mutableSetOf(),
|
||||
val swingComponents: MutableSet<MethodSignature> = mutableSetOf(),
|
||||
)
|
||||
|
||||
fun analyzeMethod(method: PsiMethod, config: AnalysisConfig = AnalysisConfig.forProject(method.project)): AnalysisResult {
|
||||
val context = TraversalContext(config)
|
||||
traverseMethod(method, context)
|
||||
return AnalysisResult(method, context.paths, context.messageBusTopics, context.swingComponents)
|
||||
}
|
||||
|
||||
private fun traverseMethod(method: PsiMethod, context: TraversalContext, isPolymorphic: Boolean = false) {
|
||||
if (context.currentPath.size >= context.config.maxDepth) return
|
||||
val signature = MethodSignature.fromMethod(method)
|
||||
if (signature in context.visited) return
|
||||
context.visited.add(signature)
|
||||
context.currentPath.add(MethodCall(method, isPolymorphic))
|
||||
|
||||
val annotationRequirements = detector.findAnnotationRequirements(method)
|
||||
annotationRequirements.forEach { context.paths.add(ExecutionPath(context.currentPath.toList(), it)) }
|
||||
processMethodBody(method, context)
|
||||
context.currentPath.removeLast()
|
||||
}
|
||||
|
||||
|
||||
private fun processMethodBody(method: PsiMethod, context: TraversalContext) {
|
||||
val body = method.body ?: return
|
||||
val localRequirements = mutableListOf<LockRequirement>()
|
||||
|
||||
body.accept(object : JavaRecursiveElementVisitor() {
|
||||
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
|
||||
super.visitMethodCallExpression(expression)
|
||||
|
||||
val resolvedMethod = expression.resolveMethod() ?: return
|
||||
localRequirements += detector.findBodyRequirements(resolvedMethod)
|
||||
if (localRequirements.any { it.requirementReason == RequirementReason.SWING_COMPONENT }) {
|
||||
context.swingComponents.add(MethodSignature.fromMethod(resolvedMethod))
|
||||
}
|
||||
handleMethodCall(resolvedMethod, expression, context)
|
||||
}
|
||||
|
||||
override fun visitMethodReferenceExpression(expression: PsiMethodReferenceExpression) {
|
||||
super.visitMethodReferenceExpression(expression)
|
||||
(expression.resolve() as? PsiMethod)?.let { resolvedMethod ->
|
||||
localRequirements += detector.findBodyRequirements(resolvedMethod)
|
||||
if (!detector.isAsyncBoundary(resolvedMethod)) traverseMethod(resolvedMethod, context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitNewExpression(expression: PsiNewExpression) {
|
||||
super.visitNewExpression(expression)
|
||||
expression.resolveMethod()?.let { resolvedMethod ->
|
||||
if (!detector.isAsyncBoundary(resolvedMethod)) traverseMethod(resolvedMethod, context)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
localRequirements.forEach { requirement ->
|
||||
val path = ExecutionPath(context.currentPath.toList(), requirement,
|
||||
context.currentPath.any { it.isPolymorphic || it.isMessageBusCall })
|
||||
context.paths.add(path)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMethodCall(method: PsiMethod, expression: PsiMethodCallExpression, context: TraversalContext) {
|
||||
when {
|
||||
context.config.includeMessageBus && detector.isMessageBusCall(expression) -> handleMessageBusCall(method, expression, context)
|
||||
context.config.includePolymorphic && detector.isPolymorphicCall(method) -> handlePolymorphicCall(method, context)
|
||||
else -> traverseMethod(method, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMessageBusCall(method: PsiMethod, expression: PsiMethodCallExpression, context: TraversalContext) {
|
||||
detector.extractMessageBusTopic(expression)?.let { context.messageBusTopics.add(it) }
|
||||
if (method.name != "syncPublisher") return
|
||||
|
||||
val topicType = method.returnType as? PsiClassType ?: return
|
||||
val topicInterface = topicType.resolve() ?: return
|
||||
if (!topicInterface.isInterface) return
|
||||
|
||||
findTopicListeners(topicInterface, context.config).forEach { listener ->
|
||||
listener.methods.forEach { method -> traverseMethod(method, context) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun handlePolymorphicCall(method: PsiMethod, context: TraversalContext) {
|
||||
val implementations = findImplementations(method, context.config)
|
||||
|
||||
if (implementations.size > context.config.maxImplementations) {
|
||||
val requirement = LockRequirement(method, LockType.READ, RequirementReason.IMPLICIT)
|
||||
context.currentPath.add(MethodCall(method, isPolymorphic = true))
|
||||
context.paths.add(ExecutionPath(context.currentPath.toList(), requirement, true))
|
||||
return
|
||||
}
|
||||
implementations.forEach { traverseMethod(it, context) }
|
||||
}
|
||||
|
||||
private fun findImplementations(method: PsiMethod, config: AnalysisConfig): List<PsiMethod> {
|
||||
val implementations = mutableListOf<PsiMethod>()
|
||||
if (method.body != null) implementations.add(method)
|
||||
|
||||
if (method.hasModifierProperty(PsiModifier.ABSTRACT) || method.containingClass?.isInterface == true) {
|
||||
val query = OverridingMethodsSearch.search(method, config.scope, true)
|
||||
implementations.addAll(query.findAll().take(config.maxImplementations))
|
||||
}
|
||||
return implementations
|
||||
}
|
||||
|
||||
|
||||
private fun findTopicListeners(topicInterface: PsiClass, config: AnalysisConfig): List<PsiClass> {
|
||||
val query = ClassInheritorsSearch.search(topicInterface, config.scope, true)
|
||||
return query.findAll().take(config.maxImplementations).toList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.threadingModelHelper
|
||||
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.JavaRecursiveElementVisitor
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiMethodCallExpression
|
||||
import com.intellij.psi.PsiMethodReferenceExpression
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.PsiNewExpression
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.util.Processor
|
||||
|
||||
|
||||
class LockReqsAnalyzerDFS(private val detector: LockReqsDetector = LockReqsDetector()) {
|
||||
|
||||
private data class TraversalContext(
|
||||
val config: AnalysisConfig,
|
||||
val currentPath: MutableList<MethodCall> = mutableListOf(),
|
||||
val visited: MutableSet<MethodSignature> = mutableSetOf(),
|
||||
val paths: MutableSet<ExecutionPath> = mutableSetOf(),
|
||||
val messageBusTopics: MutableSet<PsiClass> = mutableSetOf(),
|
||||
val swingComponents: MutableSet<MethodSignature> = mutableSetOf(),
|
||||
)
|
||||
|
||||
fun analyzeMethod(method: PsiMethod, config: AnalysisConfig = AnalysisConfig.forProject(method.project)): AnalysisResult {
|
||||
val context = TraversalContext(config)
|
||||
traverseMethod(method, context)
|
||||
return AnalysisResult(method, context.paths, context.messageBusTopics, context.swingComponents)
|
||||
}
|
||||
|
||||
private fun traverseMethod(method: PsiMethod, context: TraversalContext) {
|
||||
println("Traversing ${method.containingClass?.qualifiedName}.${method.name}")
|
||||
val signature = MethodSignature.fromMethod(method)
|
||||
if (context.currentPath.size >= context.config.maxDepth || signature in context.visited) return
|
||||
context.visited.add(signature)
|
||||
context.currentPath.add(MethodCall(method))
|
||||
|
||||
val annotationRequirement = detector.findAnnotationRequirements(method)
|
||||
annotationRequirement.forEach { context.paths.add(ExecutionPath(context.currentPath.toList(), it)) }
|
||||
getMethodCallees(method).forEach { processCallee(it, context) }
|
||||
|
||||
context.currentPath.removeLast()
|
||||
}
|
||||
|
||||
private fun getMethodCallees(method: PsiMethod): List<PsiMethod> {
|
||||
val callees = mutableListOf<PsiMethod>()
|
||||
|
||||
method.body?.accept(object : JavaRecursiveElementVisitor() {
|
||||
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
|
||||
super.visitMethodCallExpression(expression)
|
||||
expression.resolveMethod()?.let { callees.add(it) }
|
||||
}
|
||||
|
||||
override fun visitMethodReferenceExpression(expression: PsiMethodReferenceExpression) {
|
||||
super.visitMethodReferenceExpression(expression)
|
||||
(expression.resolve() as? PsiMethod)?.let { callees.add(it) }
|
||||
}
|
||||
|
||||
override fun visitNewExpression(expression: PsiNewExpression) {
|
||||
super.visitNewExpression(expression)
|
||||
expression.resolveMethod()?.let { callees.add(it) }
|
||||
}
|
||||
})
|
||||
|
||||
return callees
|
||||
}
|
||||
|
||||
private fun processCallee(callee: PsiMethod, context: TraversalContext) {
|
||||
detector.findBodyRequirements(callee).forEach { requirement ->
|
||||
context.paths.add(ExecutionPath(context.currentPath.toList(), requirement))
|
||||
}
|
||||
if (!detector.isAsyncDispatch(callee)) {
|
||||
when {
|
||||
detector.isMessageBusCall(callee) -> handleMessageBusCall(callee, context)
|
||||
detector.isPolymorphicCall(callee) -> handlePolymorphic(callee, context)
|
||||
else -> traverseMethod(callee, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePolymorphic(method: PsiMethod, context: TraversalContext) {
|
||||
val implementations = findImplementations(method, context.config)
|
||||
implementations.forEach { traverseMethod(it, context) }
|
||||
}
|
||||
|
||||
private fun findImplementations(method: PsiMethod, config: AnalysisConfig): List<PsiMethod> {
|
||||
val implementations = mutableListOf<PsiMethod>()
|
||||
if (method.body != null) {
|
||||
implementations.add(method)
|
||||
}
|
||||
if (method.hasModifierProperty(PsiModifier.ABSTRACT) || method.containingClass?.isInterface == true) {
|
||||
val query = OverridingMethodsSearch.search(method, config.scope, true)
|
||||
query.forEach(Processor<PsiMethod> {
|
||||
if (implementations.size >= config.maxImplementations) return@Processor false
|
||||
implementations.add(it)
|
||||
})
|
||||
}
|
||||
return implementations
|
||||
}
|
||||
|
||||
private fun handleMessageBusCall(method: PsiMethod, context: TraversalContext) {
|
||||
detector.extractMessageBusTopic(method)?.let { context.messageBusTopics.add(it) }
|
||||
|
||||
val topicType = method.returnType as? PsiClassType ?: return
|
||||
val topicInterface = topicType.resolve() ?: return
|
||||
if (!topicInterface.isInterface) return
|
||||
|
||||
detector.findTopicListeners(topicInterface, context.config).forEach { listener ->
|
||||
listener.methods.forEach { method -> traverseMethod(method, context) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.idea.devkit.threadingModelHelper
|
||||
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiClassType
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiMethodCallExpression
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||
import com.intellij.psi.util.InheritanceUtil
|
||||
import com.intellij.util.Processor
|
||||
|
||||
class LockReqsDetector(private val patterns: LockReqsPatterns = DefaultLockReqsPatterns()) {
|
||||
|
||||
@@ -27,15 +29,12 @@ class LockReqsDetector(private val patterns: LockReqsPatterns = DefaultLockReqsP
|
||||
patterns.assertionMethods[className]?.get(methodName)?.let { lockType ->
|
||||
requirements.add(LockRequirement(method, lockType, RequirementReason.ASSERTION))
|
||||
}
|
||||
if (isSwingMethod(method)) requirements.add(LockRequirement(method, LockType.EDT, RequirementReason.SWING_COMPONENT))
|
||||
if (isSwingMethod(method)) {
|
||||
requirements.add(LockRequirement(method, LockType.EDT, RequirementReason.SWING_COMPONENT))
|
||||
}
|
||||
return requirements
|
||||
}
|
||||
|
||||
private fun isSwingComponent(className: String): Boolean {
|
||||
return patterns.edtRequiredClasses.contains(className) ||
|
||||
patterns.edtRequiredPackages.any { className.startsWith("$it.") }
|
||||
}
|
||||
|
||||
private fun isSwingMethod(method: PsiMethod): Boolean {
|
||||
val containingClass = method.containingClass ?: return false
|
||||
val className = containingClass.qualifiedName ?: return false
|
||||
@@ -45,24 +44,13 @@ class LockReqsDetector(private val patterns: LockReqsPatterns = DefaultLockReqsP
|
||||
}
|
||||
}
|
||||
|
||||
fun isAsyncBoundary(method: PsiMethod): Boolean {
|
||||
return method.name in patterns.asyncMethods || method.containingClass?.qualifiedName in patterns.asyncClasses
|
||||
private fun isSwingComponent(className: String): Boolean {
|
||||
return patterns.edtRequiredClasses.contains(className) ||
|
||||
patterns.edtRequiredPackages.any { className.startsWith("$it.") }
|
||||
}
|
||||
|
||||
fun isMessageBusCall(expression: PsiMethodCallExpression): Boolean {
|
||||
val method = expression.resolveMethod() ?: return false
|
||||
val containingClass = method.containingClass?.qualifiedName ?: return false
|
||||
return patterns.messageBusClasses.contains(containingClass) && method.name in patterns.messageBusMethods
|
||||
}
|
||||
|
||||
fun extractMessageBusTopic(expression: PsiMethodCallExpression): String? {
|
||||
val method = expression.resolveMethod() ?: return null
|
||||
if (method.name == "syncPublisher") {
|
||||
val returnType = method.returnType as? PsiClassType ?: return null
|
||||
val resolvedTopicInterface = returnType.resolve() ?: return null
|
||||
return resolvedTopicInterface.qualifiedName
|
||||
}
|
||||
return null
|
||||
fun isAsyncDispatch(method: PsiMethod): Boolean {
|
||||
return method.name in patterns.asyncMethods && method.containingClass?.qualifiedName in patterns.asyncClasses
|
||||
}
|
||||
|
||||
fun isPolymorphicCall(method: PsiMethod): Boolean {
|
||||
@@ -72,4 +60,28 @@ class LockReqsDetector(private val patterns: LockReqsPatterns = DefaultLockReqsP
|
||||
val containingClass = method.containingClass ?: return false
|
||||
return !containingClass.hasModifierProperty(PsiModifier.FINAL)
|
||||
}
|
||||
|
||||
fun isMessageBusCall(method: PsiMethod): Boolean {
|
||||
val containingClass = method.containingClass?.qualifiedName ?: return false
|
||||
return patterns.messageBusClasses.contains(containingClass) && method.name in patterns.messageBusSyncMethods
|
||||
}
|
||||
|
||||
fun extractMessageBusTopic(method: PsiMethod): PsiClass? {
|
||||
if (method.name in patterns.messageBusSyncMethods) {
|
||||
val returnType = method.returnType as? PsiClassType ?: return null
|
||||
val resolvedTopicInterface = returnType.resolve() ?: return null
|
||||
return resolvedTopicInterface
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun findTopicListeners(topicInterface: PsiClass, config: AnalysisConfig): List<PsiClass> {
|
||||
val listeners = mutableListOf<PsiClass>()
|
||||
val query = ClassInheritorsSearch.search(topicInterface, config.scope, true)
|
||||
query.forEach(Processor {
|
||||
if (listeners.size > config.maxImplementations) return@Processor false
|
||||
listeners.add(it)
|
||||
})
|
||||
return listeners
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
package org.jetbrains.idea.devkit.threadingModelHelper
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
@@ -47,7 +48,7 @@ data class ExecutionPath(
|
||||
data class AnalysisResult(
|
||||
val method: PsiMethod,
|
||||
val paths: Set<ExecutionPath>,
|
||||
val messageBusTopics: Set<String> = emptySet(),
|
||||
val messageBusTopics: Set<PsiClass> = emptySet(),
|
||||
val swingComponents: Set<MethodSignature> = emptySet(),
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ interface LockReqsPatterns {
|
||||
val asyncClasses: Set<String>
|
||||
val asyncMethods: Set<String>
|
||||
val messageBusClasses: Set<String>
|
||||
val messageBusMethods: Set<String>
|
||||
val messageBusSyncMethods: Set<String>
|
||||
val safeSwingMethods: Set<String>
|
||||
}
|
||||
|
||||
@@ -66,12 +66,11 @@ class DefaultLockReqsPatterns : LockReqsPatterns {
|
||||
)
|
||||
|
||||
override val messageBusClasses: Set<String> = setOf(
|
||||
"com.intellij.util.messages.MessageBus",
|
||||
"com.intellij.util.messages.MessageBusConnection"
|
||||
"com.intellij.util.messages.MessageBus"
|
||||
)
|
||||
|
||||
override val messageBusMethods: Set<String> = setOf(
|
||||
"syncPublisher", "connect", "simpleConnect"
|
||||
override val messageBusSyncMethods: Set<String> = setOf(
|
||||
"syncPublisher"
|
||||
)
|
||||
|
||||
override val safeSwingMethods: Set<String> = setOf()
|
||||
|
||||
@@ -20,7 +20,7 @@ class LockReqsService(private val project: Project) {
|
||||
var onResultsUpdated: ((AnalysisResult?) -> Unit)? = null
|
||||
|
||||
fun updateResults(method: PsiMethod) {
|
||||
val analyzer = LockReqsAnalyzer()
|
||||
val analyzer = LockReqsAnalyzerDFS()
|
||||
_currentResult = analyzer.analyzeMethod(method)
|
||||
onResultsUpdated?.invoke(_currentResult)
|
||||
|
||||
|
||||
+2
-3
@@ -11,14 +11,13 @@ class DifferentClassesMethods {
|
||||
void helperMethod() {
|
||||
Service service = new Service();
|
||||
service.serviceMethod();
|
||||
ThreadingAssertions.assertWriteAccess();
|
||||
}
|
||||
}
|
||||
|
||||
class Service {
|
||||
@RequiresEdt
|
||||
void serviceMethod() {
|
||||
ThreadingAssertions.assertWriteAccess();
|
||||
}
|
||||
void serviceMethod() {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import com.intellij.util.concurrency.ThreadingAssertions;
|
||||
|
||||
public class SubtypingPolymorphism {
|
||||
public void testMethod() {
|
||||
Service[] services = {new FileService(), new NetworkService(), new DatabaseService()};
|
||||
Service[] services = {new FileService(), new UIService(), new DatabaseService()};
|
||||
for (Service service : services) {
|
||||
service.execute();
|
||||
}
|
||||
|
||||
+18
-14
@@ -10,11 +10,11 @@ import org.jetbrains.idea.devkit.DevkitJavaTestsUtil
|
||||
@TestDataPath($$"$CONTENT_ROOT/testData/threadingModelHelper/")
|
||||
class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
|
||||
private lateinit var analyzer: LockReqsAnalyzer
|
||||
private lateinit var analyzer: LockReqsAnalyzerDFS
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
analyzer = LockReqsAnalyzer()
|
||||
analyzer = LockReqsAnalyzerDFS()
|
||||
myFixture.addFileToProject("com/intellij/util/concurrency/annotations.java", """
|
||||
package com.intellij.util.concurrency.annotations;
|
||||
public @interface RequiresReadLock {}
|
||||
@@ -44,20 +44,20 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
override fun getBasePath() = DevkitJavaTestsUtil.TESTDATA_PATH + "threadingModelHelper/"
|
||||
|
||||
fun testNoLockRequirements() {
|
||||
val result = doTest("NoLockRequirements", "testMethod")
|
||||
val result = doTest("NoLockRequirements")
|
||||
val actualPaths = formatResult(result)
|
||||
assertTrue(actualPaths.isEmpty())
|
||||
}
|
||||
|
||||
fun testAssertionInNestedBlock() {
|
||||
val result = doTest("AssertionInNestedBlock", "testMethod")
|
||||
val result = doTest("AssertionInNestedBlock")
|
||||
val expectedPaths = listOf("AssertionInNestedBlock.testMethod => READ.ASSERTION")
|
||||
val actualPaths = formatResult(result)
|
||||
assertEquals(expectedPaths.sorted(), actualPaths.sorted())
|
||||
}
|
||||
|
||||
fun testAnnotationInChain() {
|
||||
val result = doTest("AnnotationInChain", "testMethod")
|
||||
val result = doTest("AnnotationInChain")
|
||||
val expectedPaths = listOf("AnnotationInChain.testMethod -> AnnotationInChain.intermediateMethod" +
|
||||
" -> AnnotationInChain.targetMethod => READ.ANNOTATION")
|
||||
val actualPaths = formatResult(result)
|
||||
@@ -65,7 +65,7 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
}
|
||||
|
||||
fun testBothAnnotationAndAssertion() {
|
||||
val result = doTest("BothAnnotationAndAssertion", "testMethod")
|
||||
val result = doTest("BothAnnotationAndAssertion")
|
||||
val expectedPaths = listOf("BothAnnotationAndAssertion.testMethod => WRITE.ANNOTATION",
|
||||
"BothAnnotationAndAssertion.testMethod => BGT.ASSERTION")
|
||||
val actualPaths = formatResult(result)
|
||||
@@ -73,22 +73,22 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
}
|
||||
|
||||
fun testCyclicRecursiveCalls() {
|
||||
val result = doTest("CyclicRecursiveCalls", "testMethod")
|
||||
val result = doTest("CyclicRecursiveCalls")
|
||||
val expectedPaths = listOf("CyclicRecursiveCalls.testMethod -> CyclicRecursiveCalls.methodB => READ.ANNOTATION")
|
||||
val actualPaths = formatResult(result)
|
||||
assertEquals(expectedPaths.sorted(), actualPaths.sorted())
|
||||
}
|
||||
|
||||
fun testDifferentClassesMethods() {
|
||||
val result = doTest("DifferentClassesMethods", "testMethod")
|
||||
val result = doTest("DifferentClassesMethods")
|
||||
val expectedPaths = listOf("DifferentClassesMethods.testMethod -> Helper.helperMethod -> Service.serviceMethod => EDT.ANNOTATION",
|
||||
"DifferentClassesMethods.testMethod -> Helper.helperMethod -> Service.serviceMethod => WRITE.ASSERTION")
|
||||
"DifferentClassesMethods.testMethod -> Helper.helperMethod => WRITE.ASSERTION")
|
||||
val actualPaths = formatResult(result)
|
||||
assertEquals(expectedPaths.sorted(), actualPaths.sorted())
|
||||
}
|
||||
|
||||
fun testMultipleAssertionsInMethod() {
|
||||
val result = doTest("MultipleAssertionsInMethod", "testMethod")
|
||||
val result = doTest("MultipleAssertionsInMethod")
|
||||
val expectedPaths = listOf("MultipleAssertionsInMethod.testMethod => READ.ASSERTION")
|
||||
val actualPaths = formatResult(result)
|
||||
assertEquals(1, actualPaths.size)
|
||||
@@ -97,7 +97,7 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
|
||||
|
||||
fun testLambdaWithMethodReference() {
|
||||
val result = doTest("LambdaWithMethodReference", "testMethod")
|
||||
val result = doTest("LambdaWithMethodReference")
|
||||
val expectedPaths = listOf("LambdaWithMethodReference.testMethod -> LambdaWithMethodReference.processItem => READ.ASSERTION")
|
||||
val actualPaths = formatResult(result)
|
||||
assertEquals(expectedPaths.sorted(), actualPaths.sorted())
|
||||
@@ -105,7 +105,7 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
}
|
||||
|
||||
fun testSubtypingPolymorphism() {
|
||||
val result = doTest("SubtypingPolymorphism", "testMethod")
|
||||
val result = doTest("SubtypingPolymorphism")
|
||||
val expectedPaths = listOf(
|
||||
"SubtypingPolymorphism.testMethod -> FileService.execute => READ.ANNOTATION",
|
||||
"SubtypingPolymorphism.testMethod -> UIService.execute => EDT.ASSERTION",
|
||||
@@ -115,11 +115,11 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
assertEquals(expectedPaths.sorted(), actualPaths.sorted())
|
||||
}
|
||||
|
||||
private fun doTest(className: String, methodName: String): AnalysisResult {
|
||||
private fun doTest(className: String): AnalysisResult {
|
||||
val fileName = "${getTestName(false)}.java"
|
||||
val psiJavaFile = myFixture.configureByFile(fileName) as PsiJavaFile
|
||||
val targetClass = psiJavaFile.classes.find { it.name == className } ?: error("Could not find class $className")
|
||||
val targetMethod = targetClass.methods.find { it.name == methodName } ?: error("Could not find method $methodName")
|
||||
val targetMethod = targetClass.methods.find { it.name == testMethodName } ?: error("Could not find method $testMethodName")
|
||||
return analyzer.analyzeMethod(targetMethod)
|
||||
}
|
||||
|
||||
@@ -131,4 +131,8 @@ class LockReqsUnitTest : BasePlatformTestCase() {
|
||||
}
|
||||
return actualPaths
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val testMethodName = "testMethod"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user