All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.testing.dsl.org.gradle.api.Project-testMavenProject.kt Maven / Gradle / Ivy

package name.remal.gradle_plugins.testing.dsl

import name.remal.createDirectories
import name.remal.gradle_plugins.dsl.GradleEnumVersion.GRADLE_VERSION_6_0
import name.remal.gradle_plugins.dsl.extensions.compareTo
import name.remal.newTempDir
import org.gradle.api.Project
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.util.GradleVersion
import org.redundent.kotlin.xml.Node
import org.redundent.kotlin.xml.PrintOptions
import org.redundent.kotlin.xml.xml
import java.io.File
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.attribute.FileTime.fromMillis
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.LazyThreadSafetyMode.NONE

val TEST_MAVEN_REPO_DEFAULT_GROUP_ID = "test"
val TEST_MAVEN_REPO_DEFAULT_VERSION = "0"

fun Project.testMavenRepository(init: TestMavenRepository.() -> Unit = {}): MavenArtifactRepository {
    val directory = newTempDir(".maven-repository-", false, projectDir)
    return repositories.mavenLocal().also {
        it.setUrl(directory)
        TestMavenRepository(directory).apply(init)
    }
}

@MavenRepositoryDslMarker
class TestMavenRepository(private val repositoryDir: File) {

    private val components = mutableListOf()

    @Suppress("NestedBlockDepth", "ComplexMethod")
    fun component(groupId: String = TEST_MAVEN_REPO_DEFAULT_GROUP_ID, artifactId: String, version: String = TEST_MAVEN_REPO_DEFAULT_VERSION, init: TestMavenComponent.() -> Unit = {}): TestMavenComponent {
        if (version.endsWith("-SNAPSHOT")) {
            throw IllegalArgumentException("SNAPSHOT versions aren't supported for " + TestMavenRepository::class.java.simpleName)
        }

        val componentDir = File([email protected], "${groupId.replace('.', '/')}/$artifactId/$version")
        componentDir.createDirectories()

        val component = TestMavenComponent(groupId, artifactId, version).apply(init).apply {
            if (jars.isNotEmpty()) {
                pom.rootNode.apply {
                    firstOrNull("packaging")?.let(this::removeNode)
                    element("packaging", "jar")
                }
            }

            File(componentDir, "$artifactId-$version.pom").writeText(pom.rootNode.toString(PrintOptions(singleLineTextElements = true)), UTF_8)

            jars.forEach { classifier, jar ->
                val file = File(componentDir, if (classifier.isEmpty()) "$artifactId-$version.jar" else "$artifactId-$version-$classifier.jar")
                if (jar.entries.isEmpty()) {
                    file.writeBytes(byteArrayOf(80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) // empty zip archive
                    return@forEach
                }

                file.outputStream().use {
                    ZipOutputStream(it, UTF_8).use { out ->
                        jar.entries.forEach { name, content ->
                            out.putNextEntry(ZipEntry(name).apply {
                                creationTime = fromMillis(System.currentTimeMillis())
                                lastModifiedTime = creationTime
                            })
                            out.write(content)
                        }
                    }
                }
            }
        }

        components.add(component)

        if (GradleVersion.current() >= GRADLE_VERSION_6_0) {
            val versions = components.asSequence()
                .filter { it.groupId == component.groupId && it.artifactId == component.artifactId }
                .map(TestMavenComponent::version)
                .toSet()
            val mavenMetadata = xml("metadata", "UTF-8") {
                element("groupId", component.groupId)
                element("artifactId", component.artifactId)
                element("versioning") {
                    element("versions") {
                        versions.forEach {
                            element("version", it)
                        }
                    }
                }
            }
            val mavenMetadataFile = componentDir.resolve("../maven-metadata.xml")
            mavenMetadataFile.writeText(mavenMetadata.toString(PrintOptions(singleLineTextElements = true)), UTF_8)
        }

        return component
    }

}

@MavenRepositoryDslMarker
class TestMavenComponent(
    override val groupId: String,
    override val artifactId: String,
    override val version: String
) : MavenCoordinates {

    internal val pom: TestMavenPom = TestMavenPom(xml("project", "UTF-8") {
        xmlns = "http://maven.apache.org/POM/4.0.0"
        element("modelVersion", "4.0.0")
        element("groupId", groupId)
        element("artifactId", artifactId)
        element("version", [email protected])
        element("packaging", "pom")
    })

    fun pom(init: TestMavenPom.() -> Unit = {}) = apply { init(pom) }


    internal val jars = sortedMapOf()

    fun jar(classifier: String = "", init: TestMavenJar.() -> Unit = {}) = apply {
        jars[classifier] = TestMavenJar().apply(init)
    }

}

@MavenRepositoryDslMarker
class TestMavenPom(internal val rootNode: Node) {

    private val dependenciesNode: Node by lazy(NONE) { rootNode.element("dependencies") }

    fun dependency(groupId: String = TEST_MAVEN_REPO_DEFAULT_GROUP_ID, artifactId: String, version: String = TEST_MAVEN_REPO_DEFAULT_VERSION) = apply {
        dependenciesNode.element("dependency") {
            element("groupId", groupId)
            element("artifactId", artifactId)
            element("version", version)
        }
    }

    fun dependency(coordinates: MavenCoordinates) = dependency(coordinates.groupId, coordinates.artifactId, coordinates.version)


    private val distributionManagementNode: Node by lazy(NONE) { rootNode.element("distributionManagement") }

    fun relocation(groupId: String = TEST_MAVEN_REPO_DEFAULT_GROUP_ID, artifactId: String, version: String = TEST_MAVEN_REPO_DEFAULT_VERSION) = apply {
        distributionManagementNode.removeAllChildren().element("relocation") {
            element("groupId", groupId)
            element("artifactId", artifactId)
            element("version", version)
        }
    }

    fun relocation(coordinates: MavenCoordinates) = relocation(coordinates.groupId, coordinates.artifactId, coordinates.version)

}

@MavenRepositoryDslMarker
class TestMavenJar {

    internal val entries = mutableMapOf()

    fun binary(name: String, content: ByteArray) = apply {
        entries[name] = content
    }

    fun text(name: String, content: String, charset: Charset = UTF_8) = binary(name, content.toByteArray(charset))

}


interface MavenCoordinates {
    val groupId: String
    val artifactId: String
    val version: String
}


@DslMarker
private annotation class MavenRepositoryDslMarker

private fun Node.removeAllChildren() = apply {
    val children = children
    children as ArrayList
    children.clear()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy