From 337bb02bebddda4058715942d5a0b3d974bbd1ec Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 11:14:15 +0200 Subject: [PATCH 1/7] Remove warning-based methods They are not used anywhere --- .../pluginverifier/plugin/PluginDetailsProvider.kt | 5 ----- .../pluginverifier/plugin/PluginDetailsProviderImpl.kt | 7 ------- 2 files changed, 12 deletions(-) diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProvider.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProvider.kt index b56296903..abc65e96f 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProvider.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProvider.kt @@ -28,11 +28,6 @@ interface PluginDetailsProvider { */ fun providePluginDetails(pluginInfo: PluginInfo, pluginFileLock: FileLock): Result - /** - * Creates [PluginDetails] for existing plugin with warnings of the plugin structure. - */ - fun providePluginDetails(pluginInfo: PluginInfo, idePlugin: IdePlugin, warnings: List): Result - /** * Represents possible results of [providing] [providePluginDetails] the [PluginDetails]. */ diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt index 6d807d6e0..f4d57e4df 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt @@ -52,13 +52,6 @@ class PluginDetailsProviderImpl(private val extractDirectory: Path) : PluginDeta idePlugin: IdePlugin ) = readPluginClasses(pluginInfo, idePlugin, emptyList(), null) - override fun providePluginDetails( - pluginInfo: PluginInfo, - idePlugin: IdePlugin, - warnings: List - ) = readPluginClasses(pluginInfo, idePlugin, warnings, null) - - private fun readPluginClasses( pluginInfo: PluginInfo, idePlugin: IdePlugin, From 512912dbb49ce9ccd6976d48fca9e11b169ca335 Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 11:18:13 +0200 Subject: [PATCH 2/7] Make structurally validated plugin carry warnings and unacceptable warnings --- .../plugin/structure/intellij/plugin/IdePluginImpl.kt | 5 ++++- .../structure/intellij/plugin/StructurallyValidated.kt | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/StructurallyValidated.kt diff --git a/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/IdePluginImpl.kt b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/IdePluginImpl.kt index 29fac08e8..0371fc0cf 100644 --- a/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/IdePluginImpl.kt +++ b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/IdePluginImpl.kt @@ -6,12 +6,13 @@ package com.jetbrains.plugin.structure.intellij.plugin import com.jetbrains.plugin.structure.base.plugin.PluginIcon import com.jetbrains.plugin.structure.base.plugin.ThirdPartyDependency +import com.jetbrains.plugin.structure.base.problems.PluginProblem import com.jetbrains.plugin.structure.intellij.version.IdeVersion import org.jdom2.Document import org.jdom2.Element import java.nio.file.Path -class IdePluginImpl : IdePlugin { +class IdePluginImpl : IdePlugin, StructurallyValidated { override var pluginId: String? = null override var pluginName: String? = null @@ -77,6 +78,8 @@ class IdePluginImpl : IdePlugin { override fun isCompatibleWithIde(ideVersion: IdeVersion) = (sinceBuild == null || sinceBuild!! <= ideVersion) && (untilBuild == null || ideVersion <= untilBuild!!) + override val problems: MutableList = mutableListOf() + override fun toString(): String = (pluginId ?: pluginName ?: "") + (pluginVersion?.let { ":$it" } ?: "") } diff --git a/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/StructurallyValidated.kt b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/StructurallyValidated.kt new file mode 100644 index 000000000..332f6b9d7 --- /dev/null +++ b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/StructurallyValidated.kt @@ -0,0 +1,7 @@ +package com.jetbrains.plugin.structure.intellij.plugin + +import com.jetbrains.plugin.structure.base.problems.PluginProblem + +interface StructurallyValidated { + val problems: List +} \ No newline at end of file From 42946408f00ff7795037856649330960738822d2 Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 11:46:50 +0200 Subject: [PATCH 3/7] Delegate problems to structurally validated plugin implementation --- .../plugin/structure/intellij/plugin/PluginCreator.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/PluginCreator.kt b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/PluginCreator.kt index 9c43014ac..2a7fcb2cf 100644 --- a/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/PluginCreator.kt +++ b/intellij-plugin-structure/structure-intellij/src/main/java/com/jetbrains/plugin/structure/intellij/plugin/PluginCreator.kt @@ -129,7 +129,8 @@ internal class PluginCreator private constructor( val contentModules = arrayListOf() private val plugin = IdePluginImpl() - private val problems = arrayListOf() + private val problems: MutableList + get() = plugin.problems val pluginId: String? get() = plugin.pluginId ?: parentPlugin?.pluginId From 6f3426dd6659715674b45f353876bb31fcd505c0 Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 11:47:31 +0200 Subject: [PATCH 4/7] Propagate structural problems to plugin details --- .../pluginverifier/plugin/PluginDetailsProviderImpl.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt index f4d57e4df..817a7fd21 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/PluginDetailsProviderImpl.kt @@ -14,6 +14,7 @@ import com.jetbrains.plugin.structure.intellij.classes.locator.CompileServerExte import com.jetbrains.plugin.structure.intellij.classes.plugin.IdePluginClassesFinder import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin import com.jetbrains.plugin.structure.intellij.plugin.IdePluginManager +import com.jetbrains.plugin.structure.intellij.plugin.StructurallyValidated import com.jetbrains.plugin.structure.intellij.problems.UnableToReadPluginFile import com.jetbrains.pluginverifier.repository.PluginInfo import com.jetbrains.pluginverifier.repository.files.FileLock @@ -34,7 +35,7 @@ class PluginDetailsProviderImpl(private val extractDirectory: Path) : PluginDeta readPluginClasses( pluginInfo, plugin, - warnings, + plugin.problems, pluginFileLock ) } @@ -50,7 +51,9 @@ class PluginDetailsProviderImpl(private val extractDirectory: Path) : PluginDeta override fun providePluginDetails( pluginInfo: PluginInfo, idePlugin: IdePlugin - ) = readPluginClasses(pluginInfo, idePlugin, emptyList(), null) + ): PluginDetailsProvider.Result { + return readPluginClasses(pluginInfo, idePlugin, idePlugin.problems, null) + } private fun readPluginClasses( pluginInfo: PluginInfo, @@ -78,4 +81,7 @@ class PluginDetailsProviderImpl(private val extractDirectory: Path) : PluginDeta ) } + private val IdePlugin.problems: List + get() = if (this is StructurallyValidated) this.problems else emptyList() + } From d8be02f686c196f3336396615b16735f1391f501 Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 11:52:28 +0200 Subject: [PATCH 5/7] Improve HTML presentation of structural problems --- .../pluginverifier/output/html/HtmlResultPrinter.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/intellij-plugin-verifier/verifier-cli/src/main/java/com/jetbrains/pluginverifier/output/html/HtmlResultPrinter.kt b/intellij-plugin-verifier/verifier-cli/src/main/java/com/jetbrains/pluginverifier/output/html/HtmlResultPrinter.kt index d648be38c..ba996b125 100644 --- a/intellij-plugin-verifier/verifier-cli/src/main/java/com/jetbrains/pluginverifier/output/html/HtmlResultPrinter.kt +++ b/intellij-plugin-verifier/verifier-cli/src/main/java/com/jetbrains/pluginverifier/output/html/HtmlResultPrinter.kt @@ -128,8 +128,12 @@ class HtmlResultPrinter( printShortAndFullDescriptionItems("Override-only API usages", overrideOnlyMethodUsages) { it.shortDescription to it.fullDescription } if (pluginStructureWarnings.isNotEmpty()) { printShortAndFullDescription("Plugin structure defects") { - pluginStructureWarnings.forEach { - +it.message + ul { + pluginStructureWarnings.forEach { + li { + +it.message + } + } } } } From 3b6760ad6cab48548ab6f256aa04fe82407c46e8 Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Wed, 3 Apr 2024 15:04:33 +0200 Subject: [PATCH 6/7] Remove method that is no longer necessary --- .../tests/dependencies/IdeDependencyFinderTest.kt | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/tests/dependencies/IdeDependencyFinderTest.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/tests/dependencies/IdeDependencyFinderTest.kt index 3e84a6e93..f4af20b93 100644 --- a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/tests/dependencies/IdeDependencyFinderTest.kt +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/tests/dependencies/IdeDependencyFinderTest.kt @@ -1,6 +1,5 @@ package com.jetbrains.pluginverifier.tests.dependencies -import com.jetbrains.plugin.structure.base.problems.PluginProblem import com.jetbrains.plugin.structure.ide.Ide import com.jetbrains.plugin.structure.intellij.classes.plugin.IdePluginClassesLocations import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin @@ -145,20 +144,6 @@ class IdeDependencyFinderTest { null ) ) - - override fun providePluginDetails(pluginInfo: PluginInfo, idePlugin: IdePlugin, warnings: List) = PluginDetailsProvider.Result.Provided( - PluginDetails( - pluginInfo, - idePlugin, - warnings, - IdePluginClassesLocations( - idePlugin, - Closeable { }, - emptyMap() - ), - null - ) - ) } val pluginDetailsCache = SizeLimitedPluginDetailsCache(10, pluginFileProvider, pluginDetailsProvider) From e0779cf2e5acc12c1270802954f04aae826578cd Mon Sep 17 00:00:00 2001 From: Robert Novotny Date: Thu, 4 Apr 2024 12:32:55 +0200 Subject: [PATCH 7/7] Add stream output test --- .../pluginverifier/output/BaseOutputTest.kt | 237 ++++++++++++++++++ .../output/stream/StreamOutputTest.kt | 164 ++++++++++++ 2 files changed, 401 insertions(+) create mode 100644 intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/BaseOutputTest.kt create mode 100644 intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/stream/StreamOutputTest.kt diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/BaseOutputTest.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/BaseOutputTest.kt new file mode 100644 index 000000000..f4aacfe83 --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/BaseOutputTest.kt @@ -0,0 +1,237 @@ +package com.jetbrains.pluginverifier.output + +import com.jetbrains.plugin.structure.classes.resolvers.FileOrigin +import com.jetbrains.plugin.structure.intellij.plugin.IdePluginManager +import com.jetbrains.plugin.structure.intellij.plugin.PluginDependencyImpl +import com.jetbrains.plugin.structure.intellij.problems.ForbiddenPluginIdPrefix +import com.jetbrains.plugin.structure.intellij.problems.NoModuleDependencies +import com.jetbrains.plugin.structure.intellij.version.IdeVersion +import com.jetbrains.pluginverifier.PluginVerificationResult +import com.jetbrains.pluginverifier.PluginVerificationTarget +import com.jetbrains.pluginverifier.dependencies.DependenciesGraph +import com.jetbrains.pluginverifier.dependencies.DependencyNode +import com.jetbrains.pluginverifier.dependencies.MissingDependency +import com.jetbrains.pluginverifier.dymamic.DynamicPluginStatus +import com.jetbrains.pluginverifier.jdk.JdkVersion +import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.results.hierarchy.ClassHierarchy +import com.jetbrains.pluginverifier.results.instruction.Instruction +import com.jetbrains.pluginverifier.results.location.ClassLocation +import com.jetbrains.pluginverifier.results.location.MethodLocation +import com.jetbrains.pluginverifier.results.location.toReference +import com.jetbrains.pluginverifier.results.modifiers.Modifiers +import com.jetbrains.pluginverifier.results.problems.CompatibilityProblem +import com.jetbrains.pluginverifier.results.problems.MethodNotFoundProblem +import com.jetbrains.pluginverifier.results.problems.SuperInterfaceBecameClassProblem +import com.jetbrains.pluginverifier.results.reference.ClassReference +import com.jetbrains.pluginverifier.results.reference.MethodReference +import com.jetbrains.pluginverifier.tasks.InvalidPluginFile +import com.jetbrains.pluginverifier.usages.experimental.ExperimentalClassUsage +import com.jetbrains.pluginverifier.usages.internal.InternalClassUsage +import com.jetbrains.pluginverifier.usages.nonExtendable.NonExtendableTypeInherited +import com.jetbrains.pluginverifier.warnings.PluginStructureWarning +import org.junit.Assert.assertEquals +import java.io.StringWriter +import kotlin.io.path.Path + +const val PLUGIN_ID = "pluginId" +const val PLUGIN_VERSION = "1.0" + +typealias VerifiedPluginHandler = (PluginVerificationResult.Verified) -> Unit + +open class BaseOutputTest { + private val pluginInfo = mockPluginInfo() + private val verificationTarget = PluginVerificationTarget.IDE(IdeVersion.createIdeVersion("232"), JdkVersion("11", null)) + + protected lateinit var out: StringWriter + protected lateinit var resultPrinter: T + + fun interface VerifiedPluginWithPrinterRunner { + fun run(resultPrinter: T, result: R) + } + + open fun setUp() { + out = StringWriter() + } + + private val dependenciesGraph: DependenciesGraph = DependenciesGraph( + verifiedPlugin = DependencyNode(PLUGIN_ID, PLUGIN_VERSION), + vertices = emptyList(), + edges = emptyList(), + missingDependencies = emptyMap()) + + open fun `when plugin is compatible`(testRunner: VerifiedPluginHandler) { + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph)) + } + + open fun `when plugin has compatibility warnings`(testRunner: VerifiedPluginHandler) { + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, mockCompatibilityProblems())) + } + + open fun `when plugin has structural problems`(testRunner: VerifiedPluginHandler) { + val structureWarnings = setOf( + PluginStructureWarning(NoModuleDependencies(IdePluginManager.PLUGIN_XML)) + ) + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, pluginStructureWarnings = structureWarnings)) + } + + open fun `when plugin has internal API usage problems`(testRunner: VerifiedPluginHandler) { + val internalApiUsages = setOf( + InternalClassUsage(ClassReference("com.jetbrains.InternalClass"), internalApiClassLocation, mockMethodLocationInSampleStuffFactory) + ) + + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, internalApiUsages = internalApiUsages)) + } + + open fun `when plugin has non-extendable API usages problems`(testRunner: VerifiedPluginHandler) { + val nonExtendableClass = ClassLocation("NonExtendableClass", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + val extendingClass = ClassLocation("ExtendingClass", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + + val nonExtendableApiUsages = setOf( + NonExtendableTypeInherited(nonExtendableClass, extendingClass) + ) + + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, nonExtendableApiUsages = nonExtendableApiUsages)) + } + + open fun `when plugin has experimental API usage problems`(testRunner: VerifiedPluginHandler) { + val experimentalClass = ClassLocation("ExperimentalClass", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + val extendingClass = ClassLocation("ExtendingClass", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + val usageLocation = MethodLocation( + extendingClass, + "someMethod", + "()V", + emptyList(), + null, + Modifiers.of(Modifiers.Modifier.PUBLIC) + ) + + val experimentalApiUsages = setOf( + ExperimentalClassUsage(experimentalClass.toReference(), experimentalClass, usageLocation) + ) + + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, experimentalApiUsages = experimentalApiUsages)) + } + + open fun `when plugin has missing dependencies`(testRunner: VerifiedPluginHandler) { + val pluginDependency = DependencyNode(PLUGIN_ID, PLUGIN_VERSION) + val expectedDependency = MissingDependency(PluginDependencyImpl("MissingPlugin", true, false), "Dependency MissingPlugin is not found among the bundled plugins of IU-211.500") + + val dependenciesGraph = DependenciesGraph( + verifiedPlugin = pluginDependency, + vertices = emptyList(), + edges = emptyList(), + missingDependencies = mapOf(pluginDependency to setOf(expectedDependency)) + ) + + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph)) + } + + open fun `when plugin is dynamic`(testRunner: VerifiedPluginHandler) { + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, dynamicPluginStatus = DynamicPluginStatus.MaybeDynamic)) + } + + open fun `when plugin is dynamic and has structural warnings`(testRunner: VerifiedPluginHandler) { + val structureWarnings = setOf( + PluginStructureWarning(NoModuleDependencies(IdePluginManager.PLUGIN_XML)) + ) + testRunner.runTest(PluginVerificationResult.Verified(pluginInfo, verificationTarget, dependenciesGraph, + dynamicPluginStatus = DynamicPluginStatus.MaybeDynamic, + pluginStructureWarnings = structureWarnings + )) + } + + open fun `when plugin has structural problems with invalid plugin ID`(testRunner: VerifiedPluginWithPrinterRunner>) { + val pluginId = "com.example.intellij" + val prefix = "com.example" + val invalidPluginFiles = listOf( + InvalidPluginFile(Path("plugin.zip"), listOf(ForbiddenPluginIdPrefix(pluginId, prefix))) + ) + + testRunner.run(resultPrinter, invalidPluginFiles) + } + + fun output() = out.buffer.toString() + + private fun printResults(verificationResult: PluginVerificationResult) { + resultPrinter.printResults(listOf(verificationResult)) + } + + fun assertOutput(expected: String) { + assertEquals(expected, output()) + } + + private fun VerifiedPluginHandler.runTest(verificationResult: PluginVerificationResult.Verified) { + printResults(verificationResult) + this(verificationResult) + } +} + +private fun mockPluginInfo(): PluginInfo = + object : PluginInfo(PLUGIN_ID, PLUGIN_ID, PLUGIN_VERSION, null, null, null) {} + +private fun mockCompatibilityProblems(): Set = + setOf(superInterfaceBecameClassProblem(), superInterfaceBecameClassProblemInOtherLocation(), methodNotFoundProblem(), methodNotFoundProblemInSampleStuffFactoryClass()) + +private fun superInterfaceBecameClassProblem(): SuperInterfaceBecameClassProblem { + val child = ClassLocation("com.jetbrains.plugin.Child", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + val clazz = ClassLocation("com.jetbrains.plugin.Parent", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + return SuperInterfaceBecameClassProblem(child, clazz) +} + +private fun superInterfaceBecameClassProblemInOtherLocation(): SuperInterfaceBecameClassProblem { + val child = ClassLocation("com.jetbrains.plugin.pkg.Child", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + val clazz = ClassLocation("com.jetbrains.plugin.pkg.Parent", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + return SuperInterfaceBecameClassProblem(child, clazz) +} + +private val javaLangObjectClassHierarchy = ClassHierarchy( + "java/lang/Object", + false, + null, + emptyList() +) + +private val sampleStuffFactoryLocation = ClassLocation("SampleStuffFactory", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) +private val internalApiClassLocation = ClassLocation("InternalApiRegistrar", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin) + +private val mockMethodLocationInSampleStuffFactory = MethodLocation( + sampleStuffFactoryLocation, + "produceStuff", + "()V", + emptyList(), + null, + Modifiers.of(Modifiers.Modifier.PUBLIC) +) + +private fun methodNotFoundProblem(): MethodNotFoundProblem { + val deletedClassRef = ClassReference("org/some/deleted/Class") + val referencingMethodLocation = MethodLocation( + ClassLocation("SomeClassUsingDeletedClass", null, Modifiers.of(Modifiers.Modifier.PUBLIC), SomeFileOrigin), + "someMethodReferencingDeletedClass", + "()V", + emptyList(), + null, + Modifiers.of(Modifiers.Modifier.PUBLIC) + ) + return MethodNotFoundProblem( + MethodReference(deletedClassRef, "foo", "()V"), + referencingMethodLocation, + Instruction.INVOKE_VIRTUAL, + javaLangObjectClassHierarchy + ) +} + +private fun methodNotFoundProblemInSampleStuffFactoryClass(): MethodNotFoundProblem { + val deletedClassRef = ClassReference("org/some/deleted/Class") + return MethodNotFoundProblem( + MethodReference(deletedClassRef, "foo", "()V"), + mockMethodLocationInSampleStuffFactory, + Instruction.INVOKE_VIRTUAL, + javaLangObjectClassHierarchy + ) +} + +private object SomeFileOrigin : FileOrigin { + override val parent: FileOrigin? = null +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/stream/StreamOutputTest.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/stream/StreamOutputTest.kt new file mode 100644 index 000000000..263ab3f4d --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/output/stream/StreamOutputTest.kt @@ -0,0 +1,164 @@ +package com.jetbrains.pluginverifier.output.stream + +import com.jetbrains.pluginverifier.output.BaseOutputTest +import org.junit.Before +import org.junit.Test +import java.io.PrintWriter + +class StreamOutputTest : BaseOutputTest() { + @Before + override fun setUp() { + super.setUp() + resultPrinter = WriterResultPrinter(PrintWriter(out)) + } + + @Test + fun `plugin is compatible`() { + `when plugin is compatible` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has compatibility warnings`() { + `when plugin has compatibility warnings` { + val expected = """ + Plugin pluginId 1.0 against 232.0: 4 compatibility problems + Compatibility problems (4): + #Incompatible change of super interface com.jetbrains.plugin.Parent to class + Class com.jetbrains.plugin.Child has a *super interface* com.jetbrains.plugin.Parent which is actually a *class*. This can lead to **IncompatibleClassChangeError** exception at runtime. + #Incompatible change of super interface com.jetbrains.plugin.pkg.Parent to class + Class com.jetbrains.plugin.pkg.Child has a *super interface* com.jetbrains.plugin.pkg.Parent which is actually a *class*. This can lead to **IncompatibleClassChangeError** exception at runtime. + #Invocation of unresolved method org.some.deleted.Class.foo() : void + Method SomeClassUsingDeletedClass.someMethodReferencingDeletedClass() : void contains an *invokevirtual* instruction referencing an unresolved method org.some.deleted.Class.foo() : void. This can lead to **NoSuchMethodError** exception at runtime. + Method SampleStuffFactory.produceStuff() : void contains an *invokevirtual* instruction referencing an unresolved method org.some.deleted.Class.foo() : void. This can lead to **NoSuchMethodError** exception at runtime. + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has structural problems`() { + `when plugin has structural problems` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible. 1 plugin configuration defect + Plugin structure warnings (1): + Invalid plugin descriptor 'plugin.xml'. The plugin configuration file does not include any module dependency tags. So, the plugin is assumed to be a legacy plugin and is loaded only in IntelliJ IDEA. Please note that plugins should declare a dependency on `com.intellij.modules.platform` to indicate dependence on shared functionality. + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has internal API usage problems`() { + `when plugin has internal API usage problems` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible. 1 usage of internal API + Internal API usages (1): + #Internal class InternalApiRegistrar reference + Internal class InternalApiRegistrar is referenced in SampleStuffFactory.produceStuff() : void. This class is marked with @org.jetbrains.annotations.ApiStatus.Internal annotation or @com.intellij.openapi.util.IntellijInternalApi annotation and indicates that the class is not supposed to be used in client code. + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has non-extendable API usages problems`() { + `when plugin has non-extendable API usages problems` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible. 1 non-extendable API usage violation + Non-extendable API usages (1): + #Non-extendable class NonExtendableClass is extended + Non-extendable class NonExtendableClass is extended by ExtendingClass. This class is marked with @org.jetbrains.annotations.ApiStatus.NonExtendable, which indicates that the class is not supposed to be extended. See documentation of the @ApiStatus.NonExtendable for more info. + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has experimental API usage problems`() { + `when plugin has experimental API usage problems` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible. 1 usage of experimental API + Experimental API usages (1): + #Experimental API class ExperimentalClass reference + Experimental API class ExperimentalClass is referenced in ExtendingClass.someMethod() : void. This class can be changed in a future release leading to incompatibilities + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has missing dependencies`() { + `when plugin has missing dependencies` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible + Missing dependencies: + MissingPlugin (optional): Dependency MissingPlugin is not found among the bundled plugins of IU-211.500 + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin is dynamic`() { + `when plugin is dynamic` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible + Dynamic Plugin Eligibility: + Plugin can probably be enabled or disabled without IDE restart + + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin has structural problems with invalid plugin ID`() { + `when plugin has structural problems with invalid plugin ID` { resultPrinter, result -> + resultPrinter.printInvalidPluginFiles(result) + + val expected = """ + The following files specified for the verification are not valid plugins: + plugin.zip + Invalid plugin descriptor 'id'. The plugin ID 'com.example.intellij' has a prefix 'com.example' that is not allowed. + + """.trimIndent() + assertOutput(expected) + } + } + + @Test + fun `plugin is dynamic and has structural warnings`() { + `when plugin is dynamic and has structural warnings` { + val expected = """ + Plugin pluginId 1.0 against 232.0: Compatible. 1 plugin configuration defect + Plugin structure warnings (1): + Invalid plugin descriptor 'plugin.xml'. The plugin configuration file does not include any module dependency tags. So, the plugin is assumed to be a legacy plugin and is loaded only in IntelliJ IDEA. Please note that plugins should declare a dependency on `com.intellij.modules.platform` to indicate dependence on shared functionality. + Dynamic Plugin Eligibility: + Plugin can probably be enabled or disabled without IDE restart + + + """.trimIndent() + assertOutput(expected) + } + } +} \ No newline at end of file