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)
This commit is contained in:
Anatoly Nikitin
2019-09-27 14:01:02 +03:00
committed by Ilya Chernikov
parent 81e2e119e2
commit c7e9a6c5d9
12 changed files with 535 additions and 0 deletions
@@ -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 <T> 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<List<File>> {
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")
}
}
@@ -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 <T> assertIsFailure(r: ResultWithDiagnostics<T>) {
contract {
returns() implies (r is ResultWithDiagnostics.Failure)
}
Assert.assertTrue(r is ResultWithDiagnostics.Failure)
}
@ExperimentalContracts
fun <T> assertIsSuccess(r: ResultWithDiagnostics<T>) {
contract {
returns() implies (r is ResultWithDiagnostics.Success<T>)
}
TestCase.assertTrue(r is ResultWithDiagnostics.Success<T>)
}
@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)
}
}