Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 7 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -469,19 +469,19 @@ intellijPlatform {
// experimental API is acceptable with a reason; a deprecated one is not acceptable at all, because
// it has an announced removal date and the plugin has to keep working across the IDE range.
//
// MISSING_DEPENDENCIES is here for a reason found the hard way, and it is the most load-bearing entry
// in this list: a mandatory `<depends>` that the target IDE cannot satisfy means **the plugin does not
// load at all** — not a degraded feature, not a warning, nothing. The verifier detects it perfectly
// (pointed at 253.28294.334 it says "1 missing mandatory dependency" in as many words) and, without
// this line, still finished with BUILD SUCCESSFUL. A gate that finds the fault and passes anyway is
// worse than no gate: it is a green tick over a plugin that cannot start.
// MISSING_DEPENDENCIES is deliberately NOT here either. A mandatory `<depends>` the target IDE cannot
// satisfy means the plugin does not load at all, and the verifier does detect it — but the Gradle plugin
// (2.16.0, and 2.18.1 alike) parses the verifier's stdout by the "Missing dependencies" heading and
// cannot tell `(optional): Unavailable` from a mandatory gap, so with that level on, every PyCharm
// target fails on the optional com.intellij.modules.java dependency that PyCharm lacks by design.
// The protection that level gave lives in PluginDependenciesContractTest instead: every non-optional
// <depends> must be a platform module every IntelliJ-based IDE ships.
failureLevel =
listOf(
VerifyPluginTask.FailureLevel.COMPATIBILITY_PROBLEMS,
VerifyPluginTask.FailureLevel.INTERNAL_API_USAGES,
VerifyPluginTask.FailureLevel.OVERRIDE_ONLY_API_USAGES,
VerifyPluginTask.FailureLevel.DEPRECATED_API_USAGES,
VerifyPluginTask.FailureLevel.MISSING_DEPENDENCIES,
)
ides {
// No hardcoded path in the repo: a developer can point the verifier at local IDE installs to skip the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package dev.lain.claudejb

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.io.File

class PluginDependenciesContractTest {

private val metaInf = File("src/main/resources/META-INF")
private val descriptor = metaInf.resolve("plugin.xml").readText()
private val depends = DEPENDS.findAll(descriptor).map { Dependency(it.groupValues[1], it.groupValues[2].trim()) }.toList()

@Test
fun `every mandatory dependency is a platform module that every IntelliJ-based IDE ships`() {
val mandatory = depends.filterNot { it.optional }.map { it.id }
assertEquals(
emptyList<String>(),
mandatory - MODULES_IN_EVERY_IDE,
"A mandatory <depends> the target IDE cannot satisfy means the plugin does not load at all, and the " +
"verifier no longer fails the build over missing dependencies because it cannot tell an optional " +
"one from a mandatory one. Make it optional with a config-file, or add it here with the proof that " +
"every IDE in the verified range ships it.",
)
}

@Test
fun `every optional dependency names a config-file that exists`() {
depends.filter { it.optional }.forEach { dependency ->
val configFile = dependency.configFile
assertTrue(configFile != null, "${dependency.id} is optional but names no config-file")
assertTrue(
metaInf.resolve(configFile!!).isFile,
"META-INF/$configFile is referenced by plugin.xml for ${dependency.id} but does not exist",
)
}
}

@Test
fun `the verifier is not asked to fail on missing dependencies, because it fails the optional ones too`() {
val failureLevels = Regex("""failureLevel\s*=\s*listOf\(([^)]*)\)""").find(File("build.gradle.kts").readText())
assertTrue(failureLevels != null, "No failureLevel list found in build.gradle.kts")
assertFalse(
"MISSING_DEPENDENCIES" in failureLevels!!.groupValues[1],
"FailureLevel.MISSING_DEPENDENCIES turns every optional dependency a target IDE lacks into a failed " +
"verification: PyCharm has no com.intellij.modules.java. The mandatory case is guarded by this test.",
)
}

private class Dependency(attributes: String, val id: String) {
val optional = OPTIONAL.containsMatchIn(attributes)
val configFile = CONFIG_FILE.find(attributes)?.groupValues?.get(1)
}

private companion object {
val DEPENDS = Regex("""<depends([^>]*)>([^<]+)</depends>""")
val OPTIONAL = Regex("""optional\s*=\s*"true"""")
val CONFIG_FILE = Regex("""config-file\s*=\s*"([^"]+)"""")
val MODULES_IN_EVERY_IDE = setOf("com.intellij.modules.platform", "com.intellij.modules.jcef")
}
}