From c7e9a6c5d9046afc4074548d3d0f1496c4194efa Mon Sep 17 00:00:00 2001 From: Anatoly Nikitin Date: Fri, 27 Sep 2019 14:01:02 +0300 Subject: [PATCH] Add new script dependency resolvers Changes in new API for dependency resolvers (GenericDependenciesResolver) comparing to an old one (GenericResolver: - Add ability to fast check whether artifact or repository is suitable for current resolver to distinguish between unsuitable resolvers and resolution failures - Return all artifact resolution failures in ResultWithDiagnostics - Use single string for artifact coordinates - Add compound resolver that combines several resolvers - Merge Direct and FlatLib resolver into single FileSystemDependencyResolver - Mark resolve() method as suspend to indicate long operation - Add credentials support for maven resolver (https://youtrack.jetbrains.com/issue/KT-27701) --- build.gradle.kts | 2 + .../dependencies-maven/build.gradle.kts | 32 +++++ .../maven/MavenDependenciesResolver.kt | 87 ++++++++++++ .../experimental/test/MavenResolverTest.kt | 25 ++++ .../scripting/dependencies/build.gradle.kts | 29 ++++ .../CompoundDependenciesResolver.kt | 49 +++++++ .../ExternalDependenciesResolver.kt | 38 ++++++ .../FileSystemDependenciesResolver.kt | 57 ++++++++ .../dependencies/impl/resolverUtils.kt | 24 ++++ .../script/experimental/test/ResolversTest.kt | 126 ++++++++++++++++++ .../experimental/test/ResolversTestBase.kt | 62 +++++++++ settings.gradle | 4 + 12 files changed, 535 insertions(+) create mode 100644 libraries/scripting/dependencies-maven/build.gradle.kts create mode 100644 libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt create mode 100644 libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt create mode 100644 libraries/scripting/dependencies/build.gradle.kts create mode 100644 libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/CompoundDependenciesResolver.kt create mode 100644 libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/ExternalDependenciesResolver.kt create mode 100644 libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/FileSystemDependenciesResolver.kt create mode 100644 libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverUtils.kt create mode 100644 libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTest.kt create mode 100644 libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTestBase.kt diff --git a/build.gradle.kts b/build.gradle.kts index 63c72d50733..1047a79fd41 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -535,6 +535,8 @@ tasks { dependsOn(":kotlin-scripting-common:test") dependsOn(":kotlin-scripting-jvm:test") dependsOn(":kotlin-scripting-jvm-host-test:test") + dependsOn(":kotlin-scripting-dependencies:test") + dependsOn(":kotlin-scripting-dependencies-maven:test") dependsOn(":kotlin-scripting-jsr223-test:test") // see comments on the task in kotlin-scripting-jvm-host-test // dependsOn(":kotlin-scripting-jvm-host-test:embeddableTest") diff --git a/libraries/scripting/dependencies-maven/build.gradle.kts b/libraries/scripting/dependencies-maven/build.gradle.kts new file mode 100644 index 00000000000..2de7325c734 --- /dev/null +++ b/libraries/scripting/dependencies-maven/build.gradle.kts @@ -0,0 +1,32 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +jvmTarget = "1.6" + +dependencies { + compile(kotlinStdlib()) + compile(project(":kotlin-scripting-dependencies")) + compile("org.jetbrains.kotlin:jcabi-aether:1.0-dev-3") + compile("org.sonatype.aether:aether-api:1.13.1") + testCompile("org.apache.maven:maven-settings-builder:3.0.5") + testCompile(projectTests(":kotlin-scripting-dependencies")) + testCompile(commonDep("junit")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +tasks.withType> { + kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package" +} + +publish() + +runtimeJar() +sourcesJar() +javadocJar() diff --git a/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt new file mode 100644 index 00000000000..9f40045c2b1 --- /dev/null +++ b/libraries/scripting/dependencies-maven/src/kotlin/script/experimental/dependencies/maven/MavenDependenciesResolver.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.dependencies.maven + +import com.jcabi.aether.Aether +import org.sonatype.aether.repository.Authentication +import org.sonatype.aether.repository.RemoteRepository +import org.sonatype.aether.resolution.DependencyResolutionException +import org.sonatype.aether.util.artifact.DefaultArtifact +import org.sonatype.aether.util.artifact.JavaScopes +import java.io.File +import java.util.* +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.dependencies.ExternalDependenciesResolver +import kotlin.script.experimental.dependencies.RepositoryCoordinates +import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult +import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull + +val mavenCentral = RemoteRepository("maven-central", "default", "https://repo.maven.apache.org/maven2/") + +class MavenRepositoryCoordinates( + url: String, + val username: String?, + val password: String?, + val privateKeyFile: String?, + val passPhrase: String? +) : RepositoryCoordinates(url) + +class MavenDependenciesResolver : ExternalDependenciesResolver { + + override fun acceptsArtifact(artifactCoordinates: String): Boolean = + artifactCoordinates.toMavenArtifact() != null + + override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean { + return repositoryCoordinates.toRepositoryUrlOrNull() != null + } + + // TODO: make robust + val localRepo = File(File(System.getProperty("user.home")!!, ".m2"), "repository") + + val repos: ArrayList = arrayListOf() + + private fun remoteRepositories() = if (repos.isEmpty()) arrayListOf(mavenCentral) else repos + + private fun allRepositories() = remoteRepositories().map { it.url!!.toString() } + localRepo.toString() + + private fun String.toMavenArtifact(): DefaultArtifact? = + if (this.isNotBlank() && this.count { it == ':' } == 2) DefaultArtifact(this) + else null + + override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics> { + + val artifactId = artifactCoordinates.toMavenArtifact()!! + + try { + val deps = Aether(remoteRepositories(), localRepo).resolve(artifactId, JavaScopes.RUNTIME) + if (deps != null) + return ResultWithDiagnostics.Success(deps.map { it.file }) + } catch (e: DependencyResolutionException) { + + } + return makeResolveFailureResult(allRepositories().map { "$it: $artifactId not found" }) + } + + private fun tryResolveEnvironmentVariable(str: String) = + if (str.startsWith("$")) System.getenv(str.substring(1)) ?: str + else str + + override fun addRepository(repositoryCoordinates: RepositoryCoordinates) { + val url = repositoryCoordinates.toRepositoryUrlOrNull() + ?: throw IllegalArgumentException("Invalid Maven repository URL: ${repositoryCoordinates}") + val repo = RemoteRepository( + repositoryCoordinates.string, + "default", + url.toString() + ) + if (repositoryCoordinates is MavenRepositoryCoordinates) { + val username = repositoryCoordinates.username?.let(::tryResolveEnvironmentVariable) + val password = repositoryCoordinates.password?.let(::tryResolveEnvironmentVariable) + repo.authentication = Authentication(username, password, repositoryCoordinates.privateKeyFile, repositoryCoordinates.passPhrase) + } + repos.add(repo) + } +} \ No newline at end of file diff --git a/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt new file mode 100644 index 00000000000..d147b432513 --- /dev/null +++ b/libraries/scripting/dependencies-maven/test/kotlin/script/experimental/test/MavenResolverTest.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.test + +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import kotlin.contracts.ExperimentalContracts +import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.valueOrThrow + +@ExperimentalContracts +class MavenResolverTest : ResolversTestBase() { + + fun testResolve() { + val resolver = MavenDependenciesResolver() + val result = runBlocking { resolver.resolve("org.jetbrains.kotlin:kotlin-annotations-jvm:1.3.50") } + Assert.assertTrue(result is ResultWithDiagnostics.Success) + val files = result.valueOrThrow() + files.forEach { it.delete() } + } +} diff --git a/libraries/scripting/dependencies/build.gradle.kts b/libraries/scripting/dependencies/build.gradle.kts new file mode 100644 index 00000000000..b889f05eb85 --- /dev/null +++ b/libraries/scripting/dependencies/build.gradle.kts @@ -0,0 +1,29 @@ + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +jvmTarget = "1.6" + +dependencies { + compile(kotlinStdlib()) + compile(project(":kotlin-scripting-common")) + testCompile(commonDep("junit")) +} + +sourceSets { + "main" { projectDefault() } + "test" { projectDefault() } +} + +tasks.withType> { + kotlinOptions.freeCompilerArgs += "-Xallow-kotlin-package" +} + +publish() + +runtimeJar() +sourcesJar() +javadocJar() +testsJar() diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/CompoundDependenciesResolver.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/CompoundDependenciesResolver.kt new file mode 100644 index 00000000000..6dcebc197d2 --- /dev/null +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/CompoundDependenciesResolver.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.dependencies + +import java.io.File +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.ScriptDiagnostic +import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult + +class CompoundDependenciesResolver(private val resolvers: List) : ExternalDependenciesResolver { + + constructor(vararg resolvers: ExternalDependenciesResolver) : this(resolvers.toList()) + + override fun acceptsArtifact(artifactCoordinates: String): Boolean { + return resolvers.any { it.acceptsArtifact(artifactCoordinates) } + } + + override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean { + return resolvers.any { it.acceptsRepository(repositoryCoordinates) } + } + + override fun addRepository(repositoryCoordinates: RepositoryCoordinates) { + if (resolvers.count { it.tryAddRepository(repositoryCoordinates) } == 0) + throw Exception("Failed to detect repository type: $repositoryCoordinates") + } + + override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics> { + + val reports = mutableListOf() + + for (resolver in resolvers) { + if (resolver.acceptsArtifact(artifactCoordinates)) { + when (val resolveResult = resolver.resolve(artifactCoordinates)) { + is ResultWithDiagnostics.Failure -> reports.addAll(resolveResult.reports) + else -> return resolveResult + } + } + } + return if (reports.count() == 0) { + makeResolveFailureResult("No suitable dependency resolver found for artifact '$artifactCoordinates'") + } else { + ResultWithDiagnostics.Failure(reports) + } + } + +} \ No newline at end of file diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/ExternalDependenciesResolver.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/ExternalDependenciesResolver.kt new file mode 100644 index 00000000000..e314766ed98 --- /dev/null +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/ExternalDependenciesResolver.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.dependencies + +import java.io.File +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.valueOrNull + +open class RepositoryCoordinates(val string: String) + +interface ExternalDependenciesResolver { + + fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean + fun acceptsArtifact(artifactCoordinates: String): Boolean + + suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics> + fun addRepository(repositoryCoordinates: RepositoryCoordinates) +} + +fun ExternalDependenciesResolver.acceptsRepository(repositoryCoordinates: String): Boolean = + acceptsRepository(RepositoryCoordinates(repositoryCoordinates)) + +fun ExternalDependenciesResolver.addRepository(repositoryCoordinates: String) = addRepository(RepositoryCoordinates(repositoryCoordinates)) + +suspend fun ExternalDependenciesResolver.tryResolve(artifactCoordinates: String): List? = + if (acceptsArtifact(artifactCoordinates)) resolve(artifactCoordinates).valueOrNull() else null + +fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: String) = + tryAddRepository(RepositoryCoordinates(repositoryCoordinates)) + +fun ExternalDependenciesResolver.tryAddRepository(repositoryCoordinates: RepositoryCoordinates) = + if (acceptsRepository(repositoryCoordinates)) { + addRepository(repositoryCoordinates) + true + } else false \ No newline at end of file diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/FileSystemDependenciesResolver.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/FileSystemDependenciesResolver.kt new file mode 100644 index 00000000000..a0d56ce56f3 --- /dev/null +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/FileSystemDependenciesResolver.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.dependencies + +import java.io.File +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult +import kotlin.script.experimental.dependencies.impl.toRepositoryUrlOrNull + +class FileSystemDependenciesResolver(vararg paths: File) : ExternalDependenciesResolver { + + private fun String.toRepositoryFileOrNull(): File? = + File(this).takeIf { it.exists() && it.isDirectory } + + private fun RepositoryCoordinates.toFilePath() = + (this.toRepositoryUrlOrNull()?.takeIf { it.protocol == "file" }?.path ?: string).toRepositoryFileOrNull() + + override fun addRepository(repositoryCoordinates: RepositoryCoordinates) { + val repoDir = repositoryCoordinates.toFilePath() + ?: throw IllegalArgumentException("Invalid repository location: '${repositoryCoordinates}'") + localRepos.add(repoDir) + } + + override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics> { + if (!acceptsArtifact(artifactCoordinates)) throw IllegalArgumentException("Path is invalid") + + val messages = mutableListOf() + + for (repo in localRepos) { + // TODO: add coordinates and wildcard matching + val file = if (repo == null) File(artifactCoordinates) else File(repo, artifactCoordinates) + when { + !file.exists() -> messages.add("File '$file' does not exists") + !file.isFile && !file.isDirectory -> messages.add("Path '$file' is neither file nor directory") + else -> return ResultWithDiagnostics.Success(listOf(file)) + } + } + return makeResolveFailureResult(messages) + } + + override fun acceptsArtifact(artifactCoordinates: String) = + !artifactCoordinates.isBlank() // TODO: make check stronger, e.g. using NIO's Path + + override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean = repositoryCoordinates.toFilePath() != null + + private val localRepos = arrayListOf(null) + + init { + for (path in paths) { + require(path.exists() && path.isDirectory) { "Invalid flat lib directory repository path '$path'" } + } + localRepos.addAll(paths) + } +} \ No newline at end of file diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverUtils.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverUtils.kt new file mode 100644 index 00000000000..effd28d74e4 --- /dev/null +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/resolverUtils.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.dependencies.impl + +import java.net.MalformedURLException +import java.net.URL +import kotlin.script.experimental.dependencies.RepositoryCoordinates +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.ScriptDiagnostic + +fun makeResolveFailureResult(message: String) = makeResolveFailureResult(listOf(message)) + +fun makeResolveFailureResult(messages: Iterable) = + ResultWithDiagnostics.Failure(messages.map { ScriptDiagnostic(it, ScriptDiagnostic.Severity.WARNING) }) + +fun RepositoryCoordinates.toRepositoryUrlOrNull(): URL? = + try { + URL(string) + } catch (_: MalformedURLException) { + null + } diff --git a/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTest.kt b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTest.kt new file mode 100644 index 00000000000..17f5fc7905e --- /dev/null +++ b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTest.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.test + +import java.io.File +import kotlin.contracts.ExperimentalContracts +import kotlin.script.experimental.dependencies.* +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.dependencies.impl.makeResolveFailureResult + +@ExperimentalContracts +class ResolversTest : ResolversTestBase() { + + private fun withTempFile(body: (file: File) -> T): T { + createTempFile() + val file = createTempFile() + file.deleteOnExit() + try { + return body(file) + } finally { + file.delete() + } + } + + private fun getNonExistingFile() = withTempFile { it }.also { assertFalse(it.exists()) } + + fun testFileSystemResolver() { + withTempFile { file -> + + val resolver = FileSystemDependenciesResolver() + + resolver.assertNotResolve(1, file.name) + resolver.assertResolve(file, file.canonicalPath) + + val dir = file.parent!! + resolver.assertAcceptsRepository(dir) + resolver.addRepository(dir) + + resolver.assertResolve(file, file.name) + } + } + + fun testFileSystemResolverFail() { + val file = getNonExistingFile() + + val resolver = FileSystemDependenciesResolver() + resolver.assertAcceptsArtifact(file.path) + resolver.assertNotResolve(1, file.path) + + resolver.addRepository(file.parent) + + resolver.assertNotResolve(2, file.name) + resolver.assertNotResolve(2, file.absolutePath) + + resolver.addRepository(file.parentFile.parent) + resolver.assertNotResolve(3, file.path) + } + + fun testFileSystemAcceptsLinuxPath() { + val resolver = FileSystemDependenciesResolver() + resolver.assertAcceptsArtifact("/usr/local/bin/temp") + } + + fun testFileSystemAcceptsWindowsPath() { + val resolver = FileSystemDependenciesResolver() + resolver.assertAcceptsArtifact("C:\\temp\\myfile") + } + + fun testFileSystemNotAcceptsMavenPath() { + val resolver = FileSystemDependenciesResolver() + resolver.assertNotAcceptsArtifact(" ") + } + + class TestDependenciesResolver( + val acceptsArt: (String) -> Boolean, + val doResolve: (String) -> File?, + val acceptsRepo: (String) -> Boolean, + val addRepo: (String) -> Unit + ) : ExternalDependenciesResolver { + + override fun acceptsArtifact(artifactCoordinates: String): Boolean = acceptsArt(artifactCoordinates) + + override suspend fun resolve(artifactCoordinates: String): ResultWithDiagnostics> { + if (!acceptsArtifact(artifactCoordinates)) throw Exception("Path is invalid") + val file = doResolve(artifactCoordinates) ?: return makeResolveFailureResult("Failed to resolve '$artifactCoordinates'") + return ResultWithDiagnostics.Success(listOf(file)) + } + + override fun addRepository(repositoryCoordinates: RepositoryCoordinates) { + addRepo(repositoryCoordinates.string) + } + + override fun acceptsRepository(repositoryCoordinates: RepositoryCoordinates): Boolean { + return acceptsRepo(repositoryCoordinates.string) + } + } + + fun testCompoundResolver() { + val file1 = getNonExistingFile() + val file2 = getNonExistingFile() + val resolver1 = TestDependenciesResolver(acceptsArt = { it.startsWith("file") }, + doResolve = { if (it == "file1") file1 else null }, + acceptsRepo = { false }, + addRepo = {}) + + var prefix: String? = null + val resolver2 = TestDependenciesResolver(acceptsArt = { a -> prefix?.let { a.startsWith(it) } ?: false }, + doResolve = { if (it.contains(".")) file2 else null }, + acceptsRepo = { true }, + addRepo = { prefix = it }) + + val resolver = CompoundDependenciesResolver(resolver1, resolver2) + + resolver.assertNotResolve(1, "abc") + resolver.assertNotResolve(1, "file2") + resolver.assertResolve(file1, "file1") + resolver.addRepository("fil") + resolver.assertNotResolve(2, "file2") + resolver.assertResolve(file2, "file2.txt") + resolver.assertResolve(file1, "file1") + resolver.assertResolve(file2, "fil1.txt") + } +} diff --git a/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTestBase.kt b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTestBase.kt new file mode 100644 index 00000000000..8707173a0ac --- /dev/null +++ b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolversTestBase.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.script.experimental.test + +import junit.framework.TestCase +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import java.io.File +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract +import kotlin.script.experimental.dependencies.ExternalDependenciesResolver +import kotlin.script.experimental.dependencies.acceptsRepository +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.valueOrThrow + +@ExperimentalContracts +fun assertIsFailure(r: ResultWithDiagnostics) { + contract { + returns() implies (r is ResultWithDiagnostics.Failure) + } + + Assert.assertTrue(r is ResultWithDiagnostics.Failure) +} + +@ExperimentalContracts +fun assertIsSuccess(r: ResultWithDiagnostics) { + contract { + returns() implies (r is ResultWithDiagnostics.Success) + } + + TestCase.assertTrue(r is ResultWithDiagnostics.Success) +} + +@ExperimentalContracts +abstract class ResolversTestBase : TestCase() { + fun ExternalDependenciesResolver.assertNotResolve(expectedReportsCount: Int, path: String) { + val result = runBlocking { resolve(path) } + assertIsFailure(result) + assertEquals(expectedReportsCount, result.reports.count()) + } + + fun ExternalDependenciesResolver.assertAcceptsArtifact(path: String) = assertTrue(acceptsArtifact(path)) + + fun ExternalDependenciesResolver.assertNotAcceptsArtifact(path: String) = assertFalse(acceptsArtifact(path)) + + fun ExternalDependenciesResolver.assertAcceptsRepository(path: String) = assertTrue(acceptsRepository(path)) + + fun ExternalDependenciesResolver.assertResolve(expected: File, path: String) { + + assertTrue(acceptsArtifact(path)) + + val result = runBlocking { resolve(path) } + assertIsSuccess(result) + + val value = result.valueOrThrow() + assertEquals(1, value.count()) + assertEquals(expected.canonicalPath, value.first().canonicalPath) + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 78584e2679b..055530ebb4d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -211,6 +211,8 @@ include ":kotlin-build-common", ":kotlin-scripting-compiler-embeddable", ":kotlin-scripting-compiler-impl", ":kotlin-scripting-compiler-impl-embeddable", + ":kotlin-scripting-dependencies", + ":kotlin-scripting-dependencies-maven", ":kotlin-scripting-jsr223", ":kotlin-scripting-jsr223-test", ":kotlin-scripting-jsr223-embeddable", @@ -398,6 +400,8 @@ project(':kotlin-scripting-jvm').projectDir = "$rootDir/libraries/scripting/jvm" project(':kotlin-scripting-jvm-host').projectDir = "$rootDir/libraries/scripting/jvm-host" as File project(':kotlin-scripting-jvm-host-test').projectDir = "$rootDir/libraries/scripting/jvm-host-test" as File project(':kotlin-scripting-jvm-host-embeddable').projectDir = "$rootDir/libraries/scripting/jvm-host-embeddable" as File +project(':kotlin-scripting-dependencies').projectDir = "$rootDir/libraries/scripting/dependencies" as File +project(':kotlin-scripting-dependencies-maven').projectDir = "$rootDir/libraries/scripting/dependencies-maven" as File project(':kotlin-scripting-jsr223').projectDir = "$rootDir/libraries/scripting/jsr223" as File project(':kotlin-scripting-jsr223-test').projectDir = "$rootDir/libraries/scripting/jsr223-test" as File project(':kotlin-scripting-jsr223-embeddable').projectDir = "$rootDir/libraries/scripting/jsr223-embeddable" as File