Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify that com.intellij.languageBundle EP is internal and must be used by JetBrains only #1130

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import com.jetbrains.plugin.structure.intellij.beans.ProductDescriptorBean
import com.jetbrains.plugin.structure.intellij.extractor.PluginBeanExtractor
import com.jetbrains.plugin.structure.intellij.problems.*
import com.jetbrains.plugin.structure.intellij.resources.ResourceResolver
import com.jetbrains.plugin.structure.intellij.verifiers.LanguageBundleExtensionPointVerifier
import com.jetbrains.plugin.structure.intellij.verifiers.PluginIdVerifier
import com.jetbrains.plugin.structure.intellij.verifiers.PluginUntilBuildVerifier
import com.jetbrains.plugin.structure.intellij.verifiers.ReusedDescriptorVerifier
Expand Down Expand Up @@ -682,6 +683,7 @@ internal class PluginCreator private constructor(

ServiceExtensionPointPreloadVerifier().verify(plugin, ::registerProblem)
StatusBarWidgetFactoryExtensionPointVerifier().verify(plugin, ::registerProblem)
LanguageBundleExtensionPointVerifier().verify(plugin, ::registerProblem)
}

private fun resolveDocumentAndValidateBean(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.jetbrains.plugin.structure.intellij.problems

import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.base.problems.InvalidDescriptorProblem
import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.base.problems.ProblemSolutionHint
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginContentDescriptor

Expand Down Expand Up @@ -56,6 +56,18 @@ class StatusBarWidgetFactoryExtensionPointIdMissing(private val implementationCl
"value returned from the getId() method of the $implementationClassFqn implementation."
}

class LanguageBundleExtensionPointIsInternal : PluginProblem() {
private val extensionPointName = "com.intellij.languageBundle"

override val level
get() = Level.UNACCEPTABLE_WARNING

override val message
get() = "The extension point in the <${extensionPointName}> element is internal " +
novotnyr marked this conversation as resolved.
Show resolved Hide resolved
"and must be used by JetBrains only."
}


class NoDependencies(descriptorPath: String) : InvalidDescriptorProblem(
descriptorPath = descriptorPath,
detailedMessage = "Plugin has no dependencies. Please check the documentation: https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.jetbrains.plugin.structure.intellij.verifiers
import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginContentDescriptor
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginContentDescriptor.ServiceDescriptor
import com.jetbrains.plugin.structure.intellij.plugin.PluginVendors.isDevelopedByJetBrains
import com.jetbrains.plugin.structure.intellij.problems.LanguageBundleExtensionPointIsInternal
import com.jetbrains.plugin.structure.intellij.problems.ServiceExtensionPointPreloadNotSupported
import com.jetbrains.plugin.structure.intellij.problems.StatusBarWidgetFactoryExtensionPointIdMissing

Expand Down Expand Up @@ -46,4 +48,20 @@ class StatusBarWidgetFactoryExtensionPointVerifier {
}
}
}
}

/**
* Rule: EP `com.intellij.languageBundle` is internal and must be used by JetBrains only.
*/
class LanguageBundleExtensionPointVerifier {
private val extensionPointName = "com.intellij.languageBundle"

fun verify(plugin: IdePlugin, problemRegistrar: ProblemRegistrar) {
if (!isDevelopedByJetBrains(plugin)) {
val languageBundles = plugin.extensions[extensionPointName] ?: emptyList()
if (languageBundles.isNotEmpty()) {
problemRegistrar.registerProblem(LanguageBundleExtensionPointIsInternal())
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jetbrains.plugin.structure.intellij.verifiers

import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.mocks.SimpleProblemRegistrar
import org.junit.Before

internal const val PLUGIN_ID = "com.example.thirdparty"
internal const val PLUGIN_VENDOR = "PluginIndustries s.r.o."
internal const val JETBRAINS_PLUGIN_VENDOR = "JetBrains"

abstract class BaseExtensionPointTest<V : Any>(val verifier: V) {

protected val problemRegistrar = SimpleProblemRegistrar()

protected val problems: List<PluginProblem>
get() = problemRegistrar.problems

@Before
fun setUp() {
problemRegistrar.reset()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.jetbrains.plugin.structure.intellij.verifiers

import com.jetbrains.plugin.structure.intellij.plugin.IdePluginImpl
import com.jetbrains.plugin.structure.mocks.MockExtension
import org.junit.Assert.assertEquals
import org.junit.Test

private const val MESSAGE_TEMPLATE = "The extension point in the <com.intellij.languageBundle> element is internal and must be used by JetBrains only."

class LanguageBundleEpVerifierTest : BaseExtensionPointTest<LanguageBundleExtensionPointVerifier>(LanguageBundleExtensionPointVerifier()) {

@Test
fun `plugin is not allowed to use languageBundle EP`() {
val extension = MockExtension.from("languageBundle", "locale" to "en-US")

val idePlugin = IdePluginImpl().apply {
pluginId = PLUGIN_ID
vendor = PLUGIN_VENDOR
extension.apply(this)
}
verifier.verify(idePlugin, problemRegistrar)
assertEquals(1, problems.size)
val problem = problems[0]
assertEquals(MESSAGE_TEMPLATE, problem.message)
}

@Test
fun `JetBrains plugin is allowed to use languageBundle EP`() {
val extension = MockExtension.from("languageBundle", "locale" to "en-US")

val idePlugin = IdePluginImpl().apply {
pluginId = PLUGIN_ID
vendor = JETBRAINS_PLUGIN_VENDOR
extension.apply(this)
}
verifier.verify(idePlugin, problemRegistrar)
assertEquals(0, problems.size)
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
package com.jetbrains.plugin.structure.intellij.verifiers

import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginContentDescriptor.*
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginImpl
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test


private const val PLUGIN_ID = "com.example.thirdparty"
private const val PLUGIN_VENDOR = "PluginIndustries s.r.o."
private const val MESSAGE_TEMPLATE = "Service preloading is deprecated in the <%s> element. Remove the 'preload' " +
"attribute and migrate to listeners, see https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html."

class ServiceExtensionPointPreloadVerifierTest {
private lateinit var verifier: ServiceExtensionPointPreloadVerifier

private lateinit var problems: MutableList<PluginProblem>

private val problemRegistrar = ProblemRegistrar {
problems += it
}

@Before
fun setUp() {
verifier = ServiceExtensionPointPreloadVerifier()
problems = mutableListOf()
}

class ServiceExtensionPointPreloadVerifierTest :
BaseExtensionPointTest<ServiceExtensionPointPreloadVerifier>(ServiceExtensionPointPreloadVerifier()) {

@Test
fun `has a single project service that is preloaded`() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
package com.jetbrains.plugin.structure.intellij.verifiers

import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.intellij.plugin.IdePluginImpl
import org.jdom2.Element
import org.junit.Assert
import org.junit.Before
import org.junit.Test

private const val PLUGIN_ID = "com.example.thirdparty"
private const val PLUGIN_VENDOR = "PluginIndustries s.r.o."
private const val EP_IMPLEMENTATION = "com.example.MyStatusBarWidgetFactory"

private const val MESSAGE_TEMPLATE = "The extension point in the <com.intellij.statusBarWidgetFactory> element must have " +
"'id' attribute set with the same value returned from the getId() method of the $EP_IMPLEMENTATION implementation."


class StatusBarWidgetFactoryExtensionPointVerifierTest {
private lateinit var verifier: StatusBarWidgetFactoryExtensionPointVerifier

private lateinit var problems: MutableList<PluginProblem>

private val problemRegistrar = ProblemRegistrar {
problems += it
}

@Before
fun setUp() {
verifier = StatusBarWidgetFactoryExtensionPointVerifier()
problems = mutableListOf()
}

class StatusBarWidgetFactoryExtensionPointVerifierTest :
BaseExtensionPointTest<StatusBarWidgetFactoryExtensionPointVerifier>(StatusBarWidgetFactoryExtensionPointVerifier()) {

@Test
fun `status bar widget factory extension does not declare ID`() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.jetbrains.plugin.structure.mocks

import com.jetbrains.plugin.structure.intellij.plugin.IdePluginImpl
import org.jdom2.Element

class MockExtension(private val fullyQualifiedName: String, private val elements: List<Element>) {
fun apply(plugin: IdePluginImpl) {
plugin.extensions[fullyQualifiedName] = elements.toMutableList()
}

companion object {
fun from(extensionLocalName: String, vararg attributes: Pair<String, String>): MockExtension {
novotnyr marked this conversation as resolved.
Show resolved Hide resolved
val extensionFqn = "com.intellij.$extensionLocalName"
val element = Element(extensionFqn).apply {
for ((attrName, attrValue) in attributes) {
setAttribute(attrName, attrValue)
}
}
return MockExtension(extensionFqn, listOf(element))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.jetbrains.plugin.structure.mocks

import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.intellij.verifiers.ProblemRegistrar

class SimpleProblemRegistrar : ProblemRegistrar {
private val _problems = mutableListOf<PluginProblem>()

val problems: List<PluginProblem>
get() = _problems

override fun registerProblem(problem: PluginProblem) {
_problems += problem
}

fun reset() {
_problems.clear()
}
}
Loading