diff --git a/intellij-plugin-structure/structure-ide/src/main/java/com/jetbrains/plugin/structure/ide/ProductInfoBasedIdeManager.kt b/intellij-plugin-structure/structure-ide/src/main/java/com/jetbrains/plugin/structure/ide/ProductInfoBasedIdeManager.kt index 0b30bee666..daa7529d69 100644 --- a/intellij-plugin-structure/structure-ide/src/main/java/com/jetbrains/plugin/structure/ide/ProductInfoBasedIdeManager.kt +++ b/intellij-plugin-structure/structure-ide/src/main/java/com/jetbrains/plugin/structure/ide/ProductInfoBasedIdeManager.kt @@ -131,7 +131,7 @@ class ProductInfoBasedIdeManager : IdeManager() { ): PluginWithArtifactPathResult { return IdePluginManager .createManager(pathResolver) - .createBundledModule(pluginArtifactPath, ideVersion, descriptorName) + .createBundledModule(pluginArtifactPath, ideVersion, descriptorName, bundledPluginCreationResultResolver) .withPath(pluginArtifactPath) } 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 d9c22f80e0..2d76b2f266 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 @@ -792,9 +792,11 @@ internal class PluginCreator private constructor( return false } val invalidPlugin = newInvalidPlugin(bean, document) - return problemResolver.classify(invalidPlugin, problems).any { - it.level == ERROR - } + return problemResolver + .classify(invalidPlugin, problems) + .filter { + it.level == ERROR + }.notEmpty(context = bean) } private fun validateId(plugin: PluginBean) { @@ -948,6 +950,19 @@ internal class PluginCreator private constructor( private val PluginCreationSuccess.problems: List get() = warnings + unacceptableWarnings + + private fun List.notEmpty(context: PluginBean): Boolean { + return if (isEmpty()) { + false + } else { + if (LOG.isDebugEnabled) { + val errorMsg = joinToString() + LOG.debug("Plugin '${context.id}' has $size error(s): $errorMsg") + } + true + } + } + } private fun PluginCreationResult.add(telemetry: PluginTelemetry): PluginCreationResult { diff --git a/intellij-plugin-verifier/verifier-intellij/src/main/java/com/jetbrains/pluginverifier/dependencies/resolution/RepositoryDependencyFinder.kt b/intellij-plugin-verifier/verifier-intellij/src/main/java/com/jetbrains/pluginverifier/dependencies/resolution/RepositoryDependencyFinder.kt index df0df4a58f..db515ff14d 100644 --- a/intellij-plugin-verifier/verifier-intellij/src/main/java/com/jetbrains/pluginverifier/dependencies/resolution/RepositoryDependencyFinder.kt +++ b/intellij-plugin-verifier/verifier-intellij/src/main/java/com/jetbrains/pluginverifier/dependencies/resolution/RepositoryDependencyFinder.kt @@ -7,6 +7,7 @@ package com.jetbrains.pluginverifier.dependencies.resolution import com.jetbrains.pluginverifier.misc.retry import com.jetbrains.pluginverifier.plugin.PluginDetailsCache import com.jetbrains.pluginverifier.repository.PluginRepository +import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginRepository /** * [DependencyFinder] that searches for the dependency in the [PluginRepository]. @@ -14,11 +15,13 @@ import com.jetbrains.pluginverifier.repository.PluginRepository * if multiple versions are available. */ class RepositoryDependencyFinder( - private val pluginRepository: PluginRepository, + pluginRepository: PluginRepository, private val pluginVersionSelector: PluginVersionSelector, private val pluginDetailsCache: PluginDetailsCache ) : DependencyFinder { + private val pluginRepository = DependencyPluginRepository(pluginRepository) + override val presentableName get() = pluginRepository.toString() diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/AbstractPluginDetailsProvider.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/AbstractPluginDetailsProvider.kt index fde622726f..58fd53c177 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/AbstractPluginDetailsProvider.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/AbstractPluginDetailsProvider.kt @@ -24,7 +24,7 @@ import java.nio.file.Path * uses the [extractDirectory] for extracting `.zip`-ped plugins. */ abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path) : PluginDetailsProvider { - private val idePluginManager = IdePluginManager.createManager(extractDirectory) + protected val idePluginManager = IdePluginManager.createManager(extractDirectory) private val IdePlugin.problems: List get() = if (this is StructurallyValidated) this.problems else emptyList() @@ -33,7 +33,7 @@ abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path) override fun providePluginDetails(pluginInfo: PluginInfo, pluginFileLock: FileLock) = pluginFileLock.closeOnException { - with(idePluginManager.createPlugin(pluginFileLock.file)) { + with(createPlugin(pluginInfo, pluginFileLock)) { when (this) { is PluginCreationSuccess -> { readPluginClasses( @@ -84,4 +84,7 @@ abstract class AbstractPluginDetailsProvider(private val extractDirectory: Path) PluginDetailsProvider.Result.InvalidPlugin(pluginInfo, listOf(UnableToReadPluginFile(message))) } } + + protected open fun createPlugin(pluginInfo: PluginInfo, pluginFileLock: FileLock) = + idePluginManager.createPlugin(pluginFileLock.file) } \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/DefaultPluginDetailsProvider.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/DefaultPluginDetailsProvider.kt index d4d586345d..ecb535a519 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/DefaultPluginDetailsProvider.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/DefaultPluginDetailsProvider.kt @@ -1,11 +1,17 @@ package com.jetbrains.pluginverifier.plugin +import com.jetbrains.plugin.structure.base.plugin.PluginCreationResult import com.jetbrains.plugin.structure.intellij.classes.locator.CompileServerExtensionKey import com.jetbrains.plugin.structure.intellij.classes.plugin.BundledPluginClassesFinder import com.jetbrains.plugin.structure.intellij.classes.plugin.IdePluginClassesLocations import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin +import com.jetbrains.plugin.structure.intellij.problems.IntelliJPluginCreationResultResolver +import com.jetbrains.plugin.structure.intellij.problems.JetBrainsPluginCreationResultResolver +import com.jetbrains.plugin.structure.intellij.problems.PluginCreationResultResolver import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.files.FileLock import com.jetbrains.pluginverifier.repository.repositories.bundled.BundledPluginInfo +import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginInfo import java.nio.file.Path /** @@ -16,6 +22,9 @@ import java.nio.file.Path class DefaultPluginDetailsProvider(extractDirectory: Path) : AbstractPluginDetailsProvider(extractDirectory) { private val nonBundledPluginDetailsProvider: PluginDetailsProviderImpl = PluginDetailsProviderImpl(extractDirectory) + private val dependencyProblemResolver: PluginCreationResultResolver = + JetBrainsPluginCreationResultResolver.fromClassPathJson(IntelliJPluginCreationResultResolver()) + override fun readPluginClasses(pluginInfo: PluginInfo, idePlugin: IdePlugin): IdePluginClassesLocations { return if (pluginInfo is BundledPluginInfo) { BundledPluginClassesFinder.findPluginClasses(idePlugin, additionalKeys = listOf(CompileServerExtensionKey)) @@ -23,4 +32,12 @@ class DefaultPluginDetailsProvider(extractDirectory: Path) : AbstractPluginDetai nonBundledPluginDetailsProvider.readPluginClasses(pluginInfo, idePlugin) } } + + override fun createPlugin(pluginInfo: PluginInfo, pluginFileLock: FileLock): PluginCreationResult { + return if (pluginInfo is DependencyPluginInfo) { + idePluginManager.createPlugin(pluginFileLock.file, validateDescriptor = false, problemResolver = dependencyProblemResolver) + } else { + super.createPlugin(pluginInfo, pluginFileLock) + } + } } \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/SizeLimitedPluginDetailsCache.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/SizeLimitedPluginDetailsCache.kt index 78657368a2..225de1e78b 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/SizeLimitedPluginDetailsCache.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/plugin/SizeLimitedPluginDetailsCache.kt @@ -5,6 +5,7 @@ package com.jetbrains.pluginverifier.plugin import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.WithIdePlugin import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntry import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntryResult import com.jetbrains.pluginverifier.repository.cache.createSizeLimitedResourceCache @@ -12,6 +13,7 @@ import com.jetbrains.pluginverifier.repository.cleanup.SizeWeight import com.jetbrains.pluginverifier.repository.provider.ProvideResult import com.jetbrains.pluginverifier.repository.provider.ResourceProvider import com.jetbrains.pluginverifier.repository.repositories.bundled.BundledPluginInfo +import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginInfo import com.jetbrains.pluginverifier.repository.repositories.local.LocalPluginInfo /** @@ -23,8 +25,8 @@ import com.jetbrains.pluginverifier.repository.repositories.local.LocalPluginInf */ class SizeLimitedPluginDetailsCache( cacheSize: Int, - val pluginFileProvider: PluginFileProvider, - val pluginDetailsProvider: PluginDetailsProvider + pluginFileProvider: PluginFileProvider, + pluginDetailsProvider: PluginDetailsProvider ) : PluginDetailsCache { private val internalCache = createSizeLimitedResourceCache( @@ -75,19 +77,42 @@ private class PluginDetailsResourceProvider( override fun provide(key: PluginInfo) = when (key) { is LocalPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin)) is BundledPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin)) + is DependencyPluginInfo -> provideDependencyDetails(key) else -> provideFileAndDetails(key) } private fun provideFileAndDetails(pluginInfo: PluginInfo): ProvideResult { - return with(pluginFileProvider.getPluginFile(pluginInfo)) { - when (this) { - is PluginFileProvider.Result.Found -> { - val pluginDetailsResult = pluginDetailsProvider.providePluginDetails(pluginInfo, pluginFileLock) - ProvideResult.Provided(pluginDetailsResult) - } - is PluginFileProvider.Result.NotFound -> ProvideResult.NotFound(reason) - is PluginFileProvider.Result.Failed -> ProvideResult.Failed(reason, error) + return pluginFileProvider + .getPluginFile(pluginInfo) + .provideDetails(pluginInfo) + } + + private fun provideDependencyDetails(dependency: DependencyPluginInfo): ProvideResult { + val unwrappedPluginInfo = dependency.pluginInfo + val unwrappedPlugin = (unwrappedPluginInfo as? WithIdePlugin)?.idePlugin + return if (unwrappedPlugin != null) { + pluginDetailsProvider.providePluginDetails(unwrappedPluginInfo, unwrappedPlugin).provided + } else { + pluginFileProvider + .getPluginFile(unwrappedPluginInfo) + .provideDetails(dependency) + } + } + + private fun PluginFileProvider.Result.provideDetails(pluginInfo: PluginInfo): ProvideResult { + return when (this) { + is PluginFileProvider.Result.Found -> { + pluginDetailsProvider + .providePluginDetails(pluginInfo, pluginFileLock) + .provided } + + is PluginFileProvider.Result.NotFound -> ProvideResult.NotFound(reason) + is PluginFileProvider.Result.Failed -> ProvideResult.Failed(reason, error) } } -} \ No newline at end of file + + private val PluginDetailsProvider.Result.provided + get() = ProvideResult.Provided(this) + +} diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/WithIdePlugin.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/WithIdePlugin.kt new file mode 100644 index 0000000000..9895c190b2 --- /dev/null +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/WithIdePlugin.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package com.jetbrains.pluginverifier.repository + +import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin + +/** + * Indicates a [plugin info][PluginInfo] that contains a resolved [IDE Plugin][IdePlugin]. + */ +interface WithIdePlugin { + val idePlugin: IdePlugin +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/bundled/BundledPluginInfo.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/bundled/BundledPluginInfo.kt index ef0379f7b6..9edb2f5a3c 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/bundled/BundledPluginInfo.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/bundled/BundledPluginInfo.kt @@ -7,6 +7,7 @@ package com.jetbrains.pluginverifier.repository.repositories.bundled import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin import com.jetbrains.plugin.structure.intellij.version.IdeVersion import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.WithIdePlugin import java.io.ObjectInputStream import java.util.* @@ -15,7 +16,7 @@ import java.util.* */ class BundledPluginInfo( val ideVersion: IdeVersion, - val idePlugin: IdePlugin + override val idePlugin: IdePlugin ) : PluginInfo( idePlugin.pluginId!!, idePlugin.pluginName ?: idePlugin.pluginId!!, @@ -23,7 +24,7 @@ class BundledPluginInfo( idePlugin.sinceBuild, idePlugin.untilBuild, idePlugin.vendor -) { +), WithIdePlugin { private fun writeReplace(): Any = throw UnsupportedOperationException("Bundled plugins cannot be serialized") diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginInfo.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginInfo.kt new file mode 100644 index 0000000000..986552acb2 --- /dev/null +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginInfo.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ +package com.jetbrains.pluginverifier.repository.repositories.dependency + +import com.jetbrains.pluginverifier.repository.PluginInfo + +/** + * Plugin information that is resolved as a plugin dependency. + */ +class DependencyPluginInfo(val pluginInfo: PluginInfo) : PluginInfo( + pluginInfo.pluginId, + pluginInfo.pluginName, + pluginInfo.version, + pluginInfo.sinceBuild, + pluginInfo.untilBuild, + pluginInfo.vendor +) \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginRepository.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginRepository.kt new file mode 100644 index 0000000000..63b49fc0d4 --- /dev/null +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/dependency/DependencyPluginRepository.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2024 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + */ + +package com.jetbrains.pluginverifier.repository.repositories.dependency + +import com.jetbrains.plugin.structure.intellij.version.IdeVersion +import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.PluginRepository + +/** + * Repository wrapping another repository while mapping plugins into [dependencies][DependencyPluginInfo]. + */ +class DependencyPluginRepository(private val delegateRepository: PluginRepository) : PluginRepository { + override val presentableName: String = "${delegateRepository.presentableName} (used for plugin dependencies)" + + override fun getLastCompatiblePlugins(ideVersion: IdeVersion): List = + delegateRepository.getLastCompatiblePlugins(ideVersion).asDependencies() + + override fun getLastCompatibleVersionOfPlugin(ideVersion: IdeVersion, pluginId: String): PluginInfo? = + delegateRepository.getLastCompatibleVersionOfPlugin(ideVersion, pluginId).asDependency() + + override fun getAllVersionsOfPlugin(pluginId: String): List = + delegateRepository.getAllVersionsOfPlugin(pluginId).asDependencies() + + override fun getPluginsDeclaringModule(moduleId: String, ideVersion: IdeVersion?): List = + delegateRepository.getPluginsDeclaringModule(moduleId, ideVersion).asDependencies() + + private fun List.asDependencies(): List = map { DependencyPluginInfo(it) } + + private fun PluginInfo?.asDependency(): PluginInfo? = this?.let { DependencyPluginInfo(it) } + +} + + diff --git a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/local/LocalPluginInfo.kt b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/local/LocalPluginInfo.kt index 18ef8b83b0..d2eafb9768 100644 --- a/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/local/LocalPluginInfo.kt +++ b/intellij-plugin-verifier/verifier-repository/src/main/java/com/jetbrains/pluginverifier/repository/repositories/local/LocalPluginInfo.kt @@ -6,6 +6,7 @@ package com.jetbrains.pluginverifier.repository.repositories.local import com.jetbrains.plugin.structure.intellij.plugin.IdePlugin import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.WithIdePlugin import java.io.ObjectInputStream /** @@ -14,7 +15,7 @@ import java.io.ObjectInputStream * @see [LocalPluginRepository] */ class LocalPluginInfo( - val idePlugin: IdePlugin + override val idePlugin: IdePlugin ) : PluginInfo( idePlugin.pluginId!!, idePlugin.pluginName ?: idePlugin.pluginId!!, @@ -22,7 +23,7 @@ class LocalPluginInfo( idePlugin.sinceBuild, idePlugin.untilBuild, idePlugin.vendor -) { +), WithIdePlugin { val definedModules: Set get() = idePlugin.definedModules diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/DependencyDiscoveryTest.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/DependencyDiscoveryTest.kt new file mode 100644 index 0000000000..04b38f0bc5 --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/DependencyDiscoveryTest.kt @@ -0,0 +1,118 @@ +package com.jetbrains.pluginverifier.plugin.resolution + +import com.jetbrains.plugin.structure.base.utils.contentBuilder.ContentBuilder +import com.jetbrains.plugin.structure.base.utils.contentBuilder.buildZipFile +import com.jetbrains.plugin.structure.intellij.problems.NoModuleDependencies +import com.jetbrains.plugin.structure.intellij.problems.ReleaseDateInFuture +import com.jetbrains.plugin.structure.jar.META_INF +import com.jetbrains.plugin.structure.jar.PLUGIN_XML +import com.jetbrains.pluginverifier.dependencies.resolution.DependencyFinder +import com.jetbrains.pluginverifier.dependencies.resolution.LastVersionSelector +import com.jetbrains.pluginverifier.dependencies.resolution.RepositoryDependencyFinder +import com.jetbrains.pluginverifier.plugin.DefaultPluginDetailsProvider +import com.jetbrains.pluginverifier.plugin.PluginDetailsCache +import com.jetbrains.pluginverifier.plugin.SizeLimitedPluginDetailsCache +import com.jetbrains.pluginverifier.repository.PluginInfo +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.nio.file.Path + +class DependencyDiscoveryTest { + + @Rule + @JvmField + val temporaryFolder = TemporaryFolder() + + @Test + fun `plugin dependencies are resolved`() { + val pluginV1 = PluginInfo("somePlugin", "Some Plugin", "1.0.0") + val pluginV2 = PluginInfo("somePlugin", "Some Plugin", "2.0.0") + val dependency = PluginInfo("aDependency", "Dependency", "1.0.0") + + val pluginV1JarPath = temporaryFolder.createJarPath(pluginV1).also { + buildPluginWithDescriptor(it) { + pluginV1.getPluginXml(v1Dependencies = listOf("aDependency")) + } + } + + val pluginV2JarPath = temporaryFolder.createJarPath(pluginV2).also { + buildPluginWithDescriptor(it) { + pluginV2.getPluginXml(v1Dependencies = listOf("aDependency")) { + append(""" + + """.trimIndent()) + } + } + } + + val dependencyPath = temporaryFolder.createJarPath(dependency).also { + buildPluginWithDescriptor(it) { + dependency.getPluginXml() + } + } + + val repository = InMemoryPluginRepository.create(pluginV1, pluginV2) + val versionSelector = LastVersionSelector() + val fileProvider = InMemoryPluginFileProvider().apply { + this[pluginV1] = pluginV1JarPath + this[pluginV2] = pluginV2JarPath + this[dependency] = dependencyPath + } + val detailsProvider = DefaultPluginDetailsProvider(temporaryFolder.newFolder("plugin-cache").toPath()) + val detailsCache = SizeLimitedPluginDetailsCache(Int.MAX_VALUE, fileProvider, detailsProvider) + + val dependencyFinder = RepositoryDependencyFinder(repository, versionSelector, detailsCache) + + val result = dependencyFinder.findPluginDependency("somePlugin", isModule = false) + assertTrue(result is DependencyFinder.Result.DetailsProvided) + val detailsProvided = result as DependencyFinder.Result.DetailsProvided + assertTrue(detailsProvided.pluginDetailsCacheResult is PluginDetailsCache.Result.Provided) + val cacheResult = detailsProvided.pluginDetailsCacheResult as PluginDetailsCache.Result.Provided + with(cacheResult.pluginDetails) { + with(pluginWarnings) { + assertEquals(2, size) + assertTrue(hasUnwrappedProblem()) + assertTrue(hasUnwrappedProblem()) + } + with(idePlugin.dependencies) { + assertEquals(1, size) + assertEquals("aDependency", first().id) + } + } + } + + private fun buildPluginWithDescriptor(pluginArtifactPath: Path, pluginXmlContent: ContentBuilder.() -> String): Path = + buildZipFile(pluginArtifactPath) { + dir(META_INF) { + file(PLUGIN_XML) { + pluginXmlContent() + } + } + } + + private fun PluginInfo.getPluginXml( + v1Dependencies: List = emptyList(), + additionalContents: StringBuilder.() -> Unit = {} + ): String { + val v1DependenciesString = v1Dependencies.joinToString("\n") { "$it" } + val additionalString = StringBuilder().apply { + additionalContents() + } + return """ + + $pluginId + $pluginName + $version + $vendor + $v1DependenciesString + $additionalString + + """.trimIndent() + } +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProvider.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProvider.kt new file mode 100644 index 0000000000..85864594e0 --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProvider.kt @@ -0,0 +1,36 @@ +package com.jetbrains.pluginverifier.plugin.resolution + +import com.jetbrains.pluginverifier.plugin.PluginFileProvider +import com.jetbrains.pluginverifier.plugin.PluginFileProvider.Result.Found +import com.jetbrains.pluginverifier.plugin.PluginFileProvider.Result.NotFound +import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.files.IdleFileLock +import java.nio.file.Path +import java.util.* + +class InMemoryPluginFileProvider : PluginFileProvider { + private val mapping = TreeMap(pluginInfoComparator) + + override fun getPluginFile(pluginInfo: PluginInfo) = + mapping[pluginInfo] + ?.let { Found(IdleFileLock(it)) } + ?: NotFound("Not found ${pluginInfo.fqn()}.") + + operator fun set(pluginInfo: PluginInfo, path: Path) { + mapping[pluginInfo] = path + } + + private val pluginInfoComparator: Comparator + get() { + return Comparator { info1, info2 -> + val i1 = info1.fqn() + val i2 = info2.fqn() + i1.compareTo(i2) + } + } + + private fun PluginInfo?.fqn(): String { + if (this == null) return "" + return "${pluginId}:${version}" + } +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProviderTest.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProviderTest.kt new file mode 100644 index 0000000000..ddb157a58e --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginFileProviderTest.kt @@ -0,0 +1,43 @@ +package com.jetbrains.pluginverifier.plugin.resolution + +import com.jetbrains.pluginverifier.plugin.PluginFileProvider.Result.Found +import com.jetbrains.pluginverifier.plugin.PluginFileProvider.Result.NotFound +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class InMemoryPluginFileProviderTest { + @Rule + @JvmField + val temporaryFolder = TemporaryFolder() + + private val plugin = PluginInfo("somePlugin", "Some Plugin", "1.0.0") + private val dependency = PluginInfo("aDependency", "Dependency", "1.0.0") + + private lateinit var provider: InMemoryPluginFileProvider + + @Before + fun setUp() { + provider = InMemoryPluginFileProvider() + provider[plugin] = temporaryFolder.createJarPath(plugin) + provider[dependency] = temporaryFolder.createJarPath(dependency) + } + + @Test + fun `existing plugins are resolved`() { + val pluginResult = provider.getPluginFile(plugin) + assert(pluginResult is Found) + + val dependencyResult = provider.getPluginFile(dependency) + assert(dependencyResult is Found) + } + + @Test + fun `unknown plugin is resolved as not found`() { + val randomPlugin = PluginInfo("randomPlugin", "Random Plugin", "1.0.0") + + val pluginResult = provider.getPluginFile(randomPlugin) + assert(pluginResult is NotFound) + } +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginRepository.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginRepository.kt new file mode 100644 index 0000000000..f6843801cd --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/InMemoryPluginRepository.kt @@ -0,0 +1,41 @@ +package com.jetbrains.pluginverifier.plugin.resolution + +import com.jetbrains.plugin.structure.intellij.version.IdeVersion +import com.jetbrains.pluginverifier.repository.PluginInfo +import com.jetbrains.pluginverifier.repository.PluginRepository + +class InMemoryPluginRepository : PluginRepository{ + override val presentableName: String = "In-Memory Plugin Repository" + + private val plugins = mutableListOf() + + override fun getLastCompatiblePlugins(ideVersion: IdeVersion): List { + return plugins.toList() + } + + override fun getLastCompatibleVersionOfPlugin(ideVersion: IdeVersion, pluginId: String): PluginInfo? { + return plugins.lastOrNull { it.pluginId == pluginId } + } + + override fun getAllVersionsOfPlugin(pluginId: String): List { + return plugins.filter { it.pluginId == pluginId } + } + + override fun getPluginsDeclaringModule(moduleId: String, ideVersion: IdeVersion?): List { + throw UnsupportedOperationException("Not implemented") + } + + operator fun plusAssign(pluginInfo: PluginInfo) { + plugins += pluginInfo + } + + companion object { + fun create(vararg plugins: PluginInfo): InMemoryPluginRepository { + return InMemoryPluginRepository().apply { + plugins.forEach { + this += it + } + } + } + } +} \ No newline at end of file diff --git a/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/Resolutions.kt b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/Resolutions.kt new file mode 100644 index 0000000000..1f72d4e8ae --- /dev/null +++ b/intellij-plugin-verifier/verifier-test/src/test/java/com/jetbrains/pluginverifier/plugin/resolution/Resolutions.kt @@ -0,0 +1,27 @@ +package com.jetbrains.pluginverifier.plugin.resolution + +import com.jetbrains.plugin.structure.base.problems.PluginProblem +import com.jetbrains.plugin.structure.base.problems.unwrapped +import com.jetbrains.pluginverifier.repository.PluginInfo +import org.junit.rules.TemporaryFolder +import java.nio.file.Path + +internal fun TemporaryFolder.createJarPath(plugin: PluginInfo): Path { + val jarFileName = with(plugin) { + "${pluginId}-${version}.jar" + } + return newFile(jarFileName).toPath() +} + +@Suppress("TestFunctionName") +internal fun PluginInfo(id: String, name: String, version: String, vendor: String = "JetBrains"): PluginInfo { + return object : PluginInfo(id, name, version, sinceBuild = null, untilBuild = null, vendor = vendor) { + // intentionally blank + } +} + +internal inline fun List.hasUnwrappedProblem(): Boolean = + map { + it.unwrapped + }.filterIsInstance() + .isNotEmpty() \ No newline at end of file