How do I publish a second library from another sourceset in gradle? - gradle

I have this tlib sourceset
sourceSets {
val main by getting
val tlib by creating {
compileClasspath += main.output
runtimeClasspath += main.output
}
val test by getting {
compileClasspath += tlib.output
runtimeClasspath += tlib.output
}
}
configurations {
val tlibCompile by getting {
extendsFrom(configurations["implementation"])
}
}
I am imagining something like this, but this is not complete
publishing {
publications {
val tlibSourcesJar by tasks.registering(Jar::class) {
classifier = "sources"
from(sourceSets["tlib"].allSource)
}
register("mavenTLib", MavenPublication::class) {
from(components["tlib"])
artifact(tlibSourcesJar.get())
}
}
}
but I get
Could not create domain object 'mavenTLib' (MavenPublication)
> SoftwareComponentInternal with name 'tlib' not found.
How can I publish my test lib separately from my main lib?

this works to an extent but is probably not the best way to do it
sourceSets {
val main by getting
val tlib by creating {
compileClasspath += main.output
runtimeClasspath += main.output
}
val test by getting {
compileClasspath += tlib.output
runtimeClasspath += tlib.output
}
}
configurations {
val tlibCompile by getting {
extendsFrom(configurations["implementation"])
}
}
publishing {
publications {
val tlibJar by tasks.registering(Jar::class) {
from(sourceSets["tlib"].output)
}
val tlibSourcesJar by tasks.registering(Jar::class) {
archiveClassifier.set("sources")
from(sourceSets["tlib"].allSource)
}
register("mavenTLib", MavenPublication::class) {
artifactId = "phg-entity-tlib"
artifact(tlibJar.get())
artifact(tlibSourcesJar.get())
}
}
}

I have an example here, unfortunately it is written in Groovy, and I am not yet familiar with the Kotlin way of doing it.
Maybe it still helps:
https://github.com/thokari/gradle-workshop/blob/master/examples/09-multiple-artifacts/build.gradle
The most relevant part is probably this:
outputArchives.each { outputArchive ->
String logicalName = outputArchive.camelCase()
// Add archiving tasks.
// These could be anything with type AbstractArchiveTask (e.g. War, Zip).
task("${logicalName}Jar", type: Jar) { from configurations."${logicalName}Compile" }
task("${logicalName}SourceJar", type: Jar) { from sourceSets."${logicalName}".java }
// Configure the publishing extension added by the 'maven-publish' plugin.
// For every combination of publication and repository, a task with name
// publish<publicationName>PublicationTo<repositoryName>Repository is created.
// The task 'publish' is a shortcut, depending on each one of them.
publishing {
publications {
// Create a publication by calling its name and type.
"${logicalName}"(MavenPublication) {
// Override the artifact id, which defaults to the project name.
artifactId = outputArchive.dashSeparated()
// Publish the artifacts created by the archiving tasks.
artifact tasks."${logicalName}Jar"
artifact(tasks."${logicalName}SourceJar") { classifier 'source' }
}
}
}
}
I also never figured out how to make use of this SoftwareComponent concept. I solved this by calling the artifact method on the archiving tasks that I created, instead of using from(component), I would think this could also be done in Kotlin.

Related

Signing fails when trying to publish to Maven Central using Sonatype nexus (gradle-nexus.publish-plugin)

I'm trying to publish my library to Maven Central. I already set it up over at Sonatype Nexus, and now I'm trying to hook up Gradle to it (this library depends on Minecraft Forge, so using Maven isn't an option).
I saw that there was a plugin created called Gradle Nexus Publish Plugin and thought I could use it to make publishing easier.
Using the publishAllPublicationsToSonatypeRepository Gradle task, I could publish a repo to Nexus;
But once I call closeSonatypeStagingRepository, it fails with the following error:
Execution failed for task ':signMavenPublication'.
> Could not read PGP secret key
Cause: premature end of stream in PartialInputStream
Now, I added a println to see what key Gradle reads from signingKey, and turns out that no matter what I put there, it always retrieves the same key, even if I put in an invalid key (this is an altered key):
lQWGBGByDfwBDADB5pWxgBT04U0bDIv/Pv0zDEYqKXt2Dubj3RcvCDAveel3eYFt
(The password is being updated though)
Why is this happening?
This is my build.gradle.kts:
import net.minecraftforge.gradle.common.util.ModConfig
import net.minecraftforge.gradle.common.util.RunConfig
import net.minecraftforge.gradle.userdev.UserDevExtension
import java.time.Instant
import java.time.format.DateTimeFormatter
// BuildScript
buildscript {
repositories {
maven(url = "https://maven.minecraftforge.net/")
jcenter()
mavenCentral()
}
dependencies {
classpath(group = "net.minecraftforge.gradle", name = "ForgeGradle", version = "4.1.+")
}
}
plugins {
idea
id("io.github.gradle-nexus.publish-plugin") version "1.0.0"
`java-library`
`maven-publish`
signing
kotlin("jvm") version "1.5.0"
}
apply(plugin = "net.minecraftforge.gradle")
// Config -> Minecraft
val forgeVersion: String by extra
val minecraftVersion: String by extra
val kffVersion: String by extra
val modVersion: String by extra
// JVM Info
println(
"""
Java: ${System.getProperty("java.version")}
JVM: ${System.getProperty("java.vm.version")} (${System.getProperty("java.vendor")})
Arch: ${System.getProperty("os.arch")}
""".trimIndent()
)
// Minecraft
configure<UserDevExtension> {
mappings("official", minecraftVersion)
#Suppress("SpellCheckingInspection")
accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg"))
runs(closureOf<NamedDomainObjectContainer<RunConfig>> {
create("client") {
workingDirectory(file("run"))
taskName = "client"
// Recommended logging data for a userdev environment
property("forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP")
// Recommended logging level for the console
property("forge.logging.console.level", "debug")
mods(closureOf<NamedDomainObjectContainer<ModConfig>> {
create("loottables") {
source(sourceSets["main"])
}
})
}
create("server") {
workingDirectory(file("run"))
taskName = "server"
// Recommended logging data for a userdev environment
property("forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP")
// Recommended logging level for the console
property("forge.logging.console.level", "debug")
mods(closureOf<NamedDomainObjectContainer<ModConfig>> {
create("loottables") {
source(sourceSets["main"])
}
})
}
create("data") {
workingDirectory(file("run"))
taskName = "datagen"
// Recommended logging data for a userdev environment
property("forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP")
// Recommended logging level for the console
property("forge.logging.console.level", "debug")
// Specify the mod id for data generation, where to output the resulting resource, and where to look for existing resources.
args(
"--mod",
"loottables",
"--all",
"--output",
file("src/generated/resources/"),
"--existing",
file("src/main/resources/")
)
mods(closureOf<NamedDomainObjectContainer<ModConfig>> {
create("loottables") {
source(sourceSets["main"])
}
})
}
})
}
// Minecraft Dependency
// Note: Due to the way kotlin gradle works we need to define the minecraft dependency after we configure Minecraft
dependencies {
"minecraft"(group = "net.minecraftforge", name = "forge", version = "$minecraftVersion-$forgeVersion")
implementation(group = "thedarkcolour", name = "kotlinforforge", version = "latest.release")
testImplementation(
group = "org.jetbrains.kotlin",
name = "kotlin-test-junit5",
version = kotlin.coreLibrariesVersion
)
}
repositories {
maven {
name = "kotlinforforge"
url = uri("https://thedarkcolour.github.io/KotlinForForge/")
}
}
// Setup
project.group = "com.theonlytails"
project.version = modVersion
base.archivesBaseName = "loottables"
// Sets the toolchain to compile against OpenJDK 8
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
vendor.set(JvmVendorSpec.ADOPTOPENJDK)
}
}
// Finalize the jar by re-obfuscating
tasks.named<Jar>("jar") {
// Manifest
manifest {
attributes(
"Specification-Title" to "LootTables",
"Specification-Vendor" to "TheOnlyTails",
"Specification-Version" to "1",
"Implementation-Title" to "LootTables",
"Implementation-Version" to project.version,
"Implementation-Vendor" to "TheOnlyTails",
"Implementation-Timestamp" to DateTimeFormatter.ISO_INSTANT.format(Instant.now()),
"FMLModType" to "LIBRARY"
)
}
#Suppress("SpellCheckingInspection")
finalizedBy("reobfJar")
}
// Publishing to maven central
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "com.theonlytails"
artifactId = "loottables"
version = modVersion
from(components["java"])
pom {
name.set("LootTables")
description.set("A Kotlin DSL for creating loot tables in Minecraft Forge mods")
url.set("https://github.com/theonlytails/loottables")
properties.set(mapOf("project.build.sourceEncoding" to "UTF-8"))
licenses {
license {
name.set("MIT License")
url.set("https://www.opensource.org/licenses/mit-license.php")
distribution.set("repo")
}
}
developers {
developer {
id.set("theonlytails")
name.set("TheOnlyTails")
url.set("https://theonlytails.com/")
}
}
issueManagement {
url.set("https://github.com/theonlytails/loottables/issues")
system.set("GitHub Issues")
}
scm {
connection.set("scm:git:git:github.com/theonlytails/loottables.git")
developerConnection.set("scm:git:git#github.com:theonlytails/loottables.git")
url.set("https://github.com/theonlytails/loottables")
}
}
}
}
}
java {
withJavadocJar()
withSourcesJar()
}
signing {
val signingPassword: String? by extra
val signingKey: String? by extra
println(signingKey) // this is for debbuging purposes only, I'm obviously going to remove this once done
println(signingPassword)
useInMemoryPgpKeys(
signingKey ?: throw GradleException("Couldn't find a signing password"),
signingPassword ?: throw GradleException("Couldn't find a signing key")
)
sign(publishing.publications["maven"])
}
nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
username.set(System.getenv("MAVEN_USERNAME"))
password.set(System.getenv("MAVEN_PASSWORD"))
}
}
}
// Testing
tasks.withType<Test> {
useJUnitPlatform()
}

Publish Kotlin MPP metadata with Gradle Kotlin DSL

I have created a Kotlin MPP to share Json utilities between JVM and JS. All the code lies in the common source set and I have configured the necessary targets with their respective dependencies. Without further configuration I'm able to use the utilities from both JVM and JS but not from the common source set of another MPP, which has to do with the way Gradle handles metadata.
I already found the solution (taken from https://medium.com/xorum-io/crafting-and-publishing-kotlin-multiplatform-library-to-bintray-cbc00a4f770)
afterEvaluate {
project.publishing.publications.all {
groupId = group
if (it.name.contains('metadata')) {
artifactId = "$libraryName"
} else {
artifactId = "$libraryName-$name"
}
}
}
and I also got it to work with the Gradle Kotlin DSL:
afterEvaluate {
publishing.publications.all {
this as MavenPublication
artifactId = project.name + "-$name".takeUnless { "metadata" in name }.orEmpty()
}
}
However, this doesn't feel quite right yet.
There is no such code snippet in the official documentation.
The documentation advertises that a single dependency from the common source set should suffice to automatically resolve target specific dependencies: https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#metadata-publishing. I had to add the dependency for each target, respectively, for it to work.
this as MavenPublication is necessary because Publication has no field artifactId.
I use project.name instead of libraryName.
Is this even remotely the right way to do things or am I missing some other option which would make the whole process trivial?
Right now I'm using Kotlin 1.3.72 and Gradle 5.2.1 with enableFeaturePreview("GRADLE_METADATA") in settings.gradle.kts. I also tried it with Gradle 6.5.1 (latest) but it behaves exactly the same.
For now I'm glad that it's working at all but I suspect there is a cleaner way to do this. I'd really appreciate if someone with a bit more Gradle expertise could clear things up for me or point me into the right direction.
Edit:
gradle.build.kts for completeness. Although there isn't much going on here.
group = "org.example"
version = "1.0-SNAPSHOT"
plugins {
kotlin("multiplatform") version "1.3.72"
`maven-publish`
}
repositories {
mavenCentral()
}
kotlin {
jvm()
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib"))
}
}
}
}
There wasn't really a problem after all. The solution is to simply add enableFeaturePreview("GRADLE_METADATA") to the consuming project too.
According to https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#metadata-publishing this shouldn't be necessary:
In earlier Gradle versions starting from 5.3, the module metadata is
used during dependency resolution, but publications don't include any
module metadata by default. To enable module metadata publishing, add
enableFeaturePreview("GRADLE_METADATA") to the root project's
settings.gradle file.
Weirdly it only works when both publishing project and consuming project have metadata enabled, even when both use the latest Gradle version.
enableFeaturePreview("GRADLE_METADATA") is enabled by default in latest gradle.
According to this you need to substitute "kotlinMultiplatform" by "" an empty string.
I finally managed to accomplish publishing to bintray with maven-publish plugin only, without outdated bintray library.
Here is my full maven.publish.gradle.kts:
import java.io.FileInputStream
import java.util.*
import org.gradle.api.publish.PublishingExtension
apply(plugin = "maven-publish")
val fis = FileInputStream("local.properties")
val properties = Properties().apply {
load(fis)
}
val bintrayUser = properties.getProperty("bintray.user")
val bintrayApiKey = properties.getProperty("bintray.apikey")
val bintrayPassword = properties.getProperty("bintray.gpg.password")
val libraryVersion: String by project
val publishedGroupId: String by project
val artifact: String by project
val bintrayRepo: String by project
val libraryName: String by project
val bintrayName: String by project
val libraryDescription: String by project
val siteUrl: String by project
val gitUrl: String by project
val licenseName: String by project
val licenseUrl: String by project
val developerOrg: String by project
val developerName: String by project
val developerEmail: String by project
val developerId: String by project
project.group = publishedGroupId
project.version = libraryVersion
afterEvaluate {
configure<PublishingExtension> {
publications.all {
val mavenPublication = this as? MavenPublication
mavenPublication?.artifactId =
"${project.name}${"-$name".takeUnless { "kotlinMultiplatform" in name }.orEmpty()}"
}
}
}
configure<PublishingExtension> {
publications {
withType<MavenPublication> {
groupId = publishedGroupId
artifactId = artifact
version = libraryVersion
pom {
name.set(libraryName)
description.set(libraryDescription)
url.set(siteUrl)
licenses {
license {
name.set(licenseName)
url.set(licenseUrl)
}
}
developers {
developer {
id.set(developerId)
name.set(developerName)
email.set(developerEmail)
}
}
organization {
name.set(developerOrg)
}
scm {
connection.set(gitUrl)
developerConnection.set(gitUrl)
url.set(siteUrl)
}
}
}
}
repositories {
maven("https://api.bintray.com/maven/${developerOrg}/${bintrayRepo}/${artifact}/;publish=1") {
credentials {
username = bintrayUser
password = bintrayApiKey
}
}
}
}
And build.gradle.kts:
plugins {
id("kotlin-multiplatform")
}
kotlin {
sourceSets {
jvm()
js() {
browser()
nodejs()
}
linuxX64()
linuxArm64()
mingwX64()
macosX64()
iosArm64()
iosX64()
val commonMain by getting {
dependencies {
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val jsMain by getting {
dependencies {
}
}
val jsTest by getting {
dependencies {
implementation(kotlin("test-js"))
}
}
val jvmMain by getting {
dependencies {
}
}
val jvmTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-junit"))
}
}
val nativeMain by creating {
dependsOn(commonMain)
dependencies {
}
}
val linuxX64Main by getting {
dependsOn(nativeMain)
}
val linuxArm64Main by getting {
dependsOn(nativeMain)
}
val mingwX64Main by getting {
dependsOn(nativeMain)
}
val macosX64Main by getting {
dependsOn(nativeMain)
}
val iosArm64Main by getting {
dependsOn(nativeMain)
}
val iosX64Main by getting {
dependsOn(nativeMain)
}
}
}
apply(from = "maven.publish.gradle.kts")
Please note, there are bintray.user and bintray.apikey properties in local.properties file.
Also gradle.properties contains rest listed properties above:
libraryVersion = 0.5.22
libraryName = MultiplatformCommon
libraryDescription = Kotlin multiplatform extensions
publishedGroupId = com.olekdia
artifact = multiplatform-common
bintrayRepo = olekdia
bintrayName = multiplatform-common
siteUrl = https://gitlab.com/olekdia/common/libraries/multiplatform-common
gitUrl = https://gitlab.com/olekdia/common/libraries/multiplatform-common.git
.........
kotlin.mpp.enableGranularSourceSetsMetadata = true
systemProp.org.gradle.internal.publish.checksums.insecure = true
If you haven't created organisation in bintray you need to change in this url:
https://api.bintray.com/maven/${developerOrg}/${bintrayRepo}/${artifact}/;publish=1
developerOrg by bintrayUser, where the last one is your user name at bintray.com

Could not find method intTestImplementation() for arguments [org.hibernate:hibernate:5.3.7.FINAL] on object of type DefaultDependencyHandler

I am trying to configure the Integration tests in Gradle 5.0.
I am seeing error as
Could not find method intTestImplementation() for arguments [org.hibernate:hibernate:5.3.7.FINAL] on object of type DefaultDependencyHandler.
I can see the intTestImplementation when I say gradle dependencies
I am trying to add one dependency
intTestImplementation 'org.hibernate:hibernate:5.3.7.FINAL'
task integrationTest(type: Test) {
description = 'Runs integration tests.'
group = 'verification'
testClassesDirs = sourceSets.intTest.output.classesDirs
classpath = sourceSets.intTest.runtimeClasspath
shouldRunAfter test
}
sourceSets {
intTest {
java.srcDir file('src/integrationTest/java')
resources.srcDir file('src/integrationTest/resources') // to add the resources
}
}
You could try one of these two options and see if some of them works for you:
a) your custom sourceSets declared in this way:
sourceSets {
intTest {
java {
srcDirs = ['src/integrationTest/java']
}
resources {
srcDirs = ['src/integrationTest/resources']
}
compileClasspath += sourceSets.main.output + sourceSets.main.compileClasspath + sourceSets.test.compileClasspath
runtimeClasspath += sourceSets.main.output + sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath
}
}
In this case you would be declaring your dependencies for integration tests in the same way as the dependencies for tests. Just like this:
testImplementation 'org.hibernate:hibernate:5.3.7.FINAL'
b) intTestImplementation must be declared after your custom sourceSets:
sourceSets {
intTest {
java {
srcDirs = ['src/integrationTest/java']
}
resources {
srcDirs = ['src/integrationTest/resources']
}
}
}
dependencies {
intTestImplementation 'org.hibernate:hibernate:5.3.7.FINAL'
}
I got similar message because I had my sourceSets defined after dependencies

Integration tests with Gradle Kotlin DSL

I'm using this blog post to configure integration tests for a Spring Boot project, but I'm pretty stuck on declaring the source sets. I also found this post on StackOverflow, but I think I'm a bit further already.
My project structure is
project
|_ src
|_ main
| |_ kotlin
| |_ resources
|_ testIntegration
| |_ kotlin
| |_ resources
|_ test
| |_ kotlin
| |_ resources
|_ build.gradle.kts
|_ ... other files
And build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
idea
kotlin("jvm")
id("org.springframework.boot") version "2.0.5.RELEASE"
id("org.jetbrains.kotlin.plugin.spring") version "1.2.71"
}
fun DependencyHandlerScope.springBoot(module: String) = this.compile("org.springframework.boot:spring-boot-$module:2.0.5.RELEASE")
fun DependencyHandlerScope.springBootStarter(module: String) = this.springBoot("starter-$module")
dependencies {
springBoot("devtools")
springBootStarter("batch")
springBootStarter("... spring boot dependencies")
compile("... more dependencies")
testCompile("... more test dependencies")
}
val test by tasks.getting(Test::class) {
useJUnitPlatform { }
}
kotlin {
sourceSets {
val integrationTest by creating {
kotlin.srcDir("src/testIntegration/kotlin")
resources.srcDir("src/testIntegration/resources")
}
}
}
val integrationTestCompile by configurations.creating {
extendsFrom(configurations["testCompile"])
}
val integrationTestRuntime by configurations.creating {
extendsFrom(configurations["testRuntime"])
}
val testIntegration by tasks.creating(Test::class) {
group = "verification"
testClassesDirs = kotlin.sourceSets["integrationTest"].kotlin
}
idea {
module {
testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].kotlin.srcDirs)
testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].resources.srcDirs)
}
}
I think I'm pretty much in the right direction. At least it doesn't throw an exception any more :)
When I run the testIntegration task, I get the following output:
Testing started at 12:08 ...
12:08:49: Executing task 'testIntegration'...
> Task :project:compileKotlin UP-TO-DATE
> Task :project:compileJava NO-SOURCE
> Task :project:processResources UP-TO-DATE
> Task :project:classes UP-TO-DATE
> Task :project:compileTestKotlin UP-TO-DATE
> Task :project:compileTestJava NO-SOURCE
> Task :project:processTestResources UP-TO-DATE
> Task :project:testClasses UP-TO-DATE
> Task :project:testIntegration
BUILD SUCCESSFUL in 2s
5 actionable tasks: 1 executed, 4 up-to-date
12:08:51: Task execution finished 'testIntegration'.
Also, IntelliJ doesn't recognise the testIntegration directories as Kotlin packages.
I was finally able to figure it out thanks to some help on the Kotlin Slack channel. First of all I had to upgrade to Gradle version 4.10.2.
For more info have a look at these two pages from Gradle:
https://docs.gradle.org/release-nightly/userguide/organizing_gradle_projects.html#sec:separate_test_type_source_files
https://docs.gradle.org/release-nightly/userguide/organizing_gradle_projects.html#sec:separate_test_type_source_files
Then I just had to create the sourceSets for the integrationTests
sourceSets {
create("integrationTest") {
kotlin.srcDir("src/integrationTest/kotlin")
resources.srcDir("src/integrationTest/resources")
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
}
}
This would work just fine for Java, but since I'm working with Kotlin I had to add an extra withConvention wrapper
sourceSets {
create("integrationTest") {
withConvention(KotlinSourceSet::class) {
kotlin.srcDir("src/integrationTest/kotlin")
resources.srcDir("src/integrationTest/resources")
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
}
}
}
In the docs they only put runtimeClasspath += output + compileClasspath, but I added sourceSets["test"].runtimeClasspath so I can directly use the test dependencies instead of declaring new dependencies for the integrationTest task.
Once the sourceSets were created it was a matter of declaring a new task
task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
After this the tests still didn't run, but that was because I'm using JUnit4. So I just had to add useJUnitPlatform() which makes this the final code
task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
useJUnitPlatform()
}
I didnt like the use of withConvention and how the kotlin src dir was set. So after check out both gradle docs here and here, I came up with this:
sourceSets {
create("integrationTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val integrationTest = task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
tasks.check {
dependsOn(integrationTest)
}
I preferr the less verbose style when using kotlin { and the use of variable for the new integrationTestTask.
As of Gradle 5.2.1 see https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
sourceSets {
create("intTest") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
}
val intTestImplementation by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
configurations["intTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())
dependencies {
intTestImplementation("junit:junit:4.12")
}
val integrationTest = task<Test>("integrationTest") {
description = "Runs integration tests."
group = "verification"
testClassesDirs = sourceSets["intTest"].output.classesDirs
classpath = sourceSets["intTest"].runtimeClasspath
shouldRunAfter("test")
}
tasks.check { dependsOn(integrationTest) }
Here is git repo that you can refer to: enter link description here
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
application
kotlin("jvm") version "1.3.72"
id("com.diffplug.gradle.spotless") version "3.24.2"
id("org.jmailen.kotlinter") version "1.26.0"
checkstyle
}
version = "1.0.2"
group = "org.sample"
application {
mainClass.set("org.sample.MainKt")
}
repositories {
mavenCentral()
jcenter()
}
tasks.checkstyleMain { group = "verification" }
tasks.checkstyleTest { group = "verification" }
spotless {
kotlin {
ktlint()
}
kotlinGradle {
target(fileTree(projectDir).apply {
include("*.gradle.kts")
} + fileTree("src").apply {
include("**/*.gradle.kts")
})
ktlint()
}
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
lifecycle {
events = mutableSetOf(TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
showStandardStreams = true
}
info.events = lifecycle.events
info.exceptionFormat = lifecycle.exceptionFormat
}
val failedTests = mutableListOf<TestDescriptor>()
val skippedTests = mutableListOf<TestDescriptor>()
addTestListener(object : TestListener {
override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
when (result.resultType) {
TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor)
TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor)
else -> Unit
}
}
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent == null) { // root suite
logger.lifecycle("----")
logger.lifecycle("Test result: ${result.resultType}")
logger.lifecycle(
"Test summary: ${result.testCount} tests, " +
"${result.successfulTestCount} succeeded, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped")
failedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tFailed Tests")
skippedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tSkipped Tests:")
}
}
private infix fun List<TestDescriptor>.prefixedSummary(subject: String) {
logger.lifecycle(subject)
forEach { test -> logger.lifecycle("\t\t${test.displayName()}") }
}
private fun TestDescriptor.displayName() = parent?.let { "${it.name} - $name" } ?: "$name"
})
}
dependencies {
implementation(kotlin("stdlib"))
implementation("com.sparkjava:spark-core:2.5.4")
implementation("org.slf4j:slf4j-simple:1.7.30")
testImplementation("com.squareup.okhttp:okhttp:2.5.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:4.0.5")
testImplementation("io.kotest:kotest-assertions-core-jvm:4.0.5") // for kotest core jvm assertions
testImplementation("io.kotest:kotest-property-jvm:4.0.5")
}
sourceSets {
create("integTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val integTest = task<Test>("integTest") {
description = "Runs the integTest tests"
group = "verification"
testClassesDirs = sourceSets["integTest"].output.classesDirs
classpath = sourceSets["integTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
tasks.check {
dependsOn(integTest)
}
sourceSets {
create("journeyTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val journeyTest = task<Test>("journeyTest") {
description = "Runs the JourneyTest tests"
group = "verification"
testClassesDirs = sourceSets["journeyTest"].output.classesDirs
classpath = sourceSets["journeyTest"].runtimeClasspath
mustRunAfter(tasks["integTest"])
}
tasks.check {
dependsOn(journeyTest)
}
I hope this helps. :)
There is a dedicated Gradle feature called Declarative Test Suite that supports this case:
testing {
suites {
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}
register("integrationTest", JvmTestSuite::class) {
dependencies {
implementation(project())
}
targets {
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
More:
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests

Idea Gradle integration add classes as a test source tree, but don't include in test sourceSet

I would like to mark the classes of my systest sourceSet as unit test classes. I tried to mark them with the following code:
sourceSets {
main {
groovy {
srcDirs = [
'src/main/masks'
}
resources {
srcDirs += 'src/main/journaltemplates'
}
}
/* This brings up systest in the test resources */
test.java.srcDir 'src/systest/java'
test.resources.srcDir 'src/systest/resources'
systest {
java {
srcDirs = ['src/systest/java']
}
resources {
srcDirs = ['src/systest/resources']
}
}
}
With this solution the sourceset got marked as unit test class, but was additionally added to the test sourceSet which is not desired. I want to keep the classes in the systest sourceSet and specify that the systest sourceSet, is a unit test sourceSet. I want the same behaviour for the systest sourceSet as for the test sourceSet, but they should be distinct sourceSets.
The second solution i tried was using the idea plugin for gradle and modify the module setting, as seen in this SO post:
idea {
module {
testSourceDirs += file('src/systest')
}
}
The problem with this solution is that the systest sources are added to the test sourceSet too.
Hopefully this is clear enough, otherwise please comment. Thank you.
There is a dedicated Gradle feature called Declarative Test Suite that supports this case:
testing {
suites {
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}
register("integrationTest", JvmTestSuite::class) {
dependencies {
implementation(project())
}
targets {
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
More:
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
Please try this configuration:
apply plugin: "idea"
sourceSets {
systest {
java {
compileClasspath = test.output + main.output
runtimeClasspath = output + compileClasspath
}
}
}
idea {
module {
testSourceDirs = sourceSets.systest.allSource.srcDirs
}
}

Resources