Skip to content

Commit

Permalink
Move JUnit functionality to junit plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Virtlink committed Jul 19, 2024
1 parent 15c4fa3 commit d1a5aa3
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 12 deletions.
4 changes: 4 additions & 0 deletions convention-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ gradlePlugin {
id = "org.metaborg.convention.java"
implementationClass = "org.metaborg.convention.JavaConventionPlugin"
}
create("convention.junit") {
id = "org.metaborg.convention.junit"
implementationClass = "org.metaborg.convention.JUnitConventionPlugin"
}
create("convention.maven-publish") {
id = "org.metaborg.convention.maven-publish"
implementationClass = "org.metaborg.convention.MavenPublishConventionPlugin"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.metaborg.convention

import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject

/** Configuration for the JUnit convention. */
open class JUnitConventionExtension @Inject constructor(
/** The Gradle object factory. */
objects: ObjectFactory,
) {
/** The name of the version catalog to use for the JUnit dependency. */
val versionCatalogName: Property<String> = objects.property(String::class.java)
.convention("libs")
/** The alias of the JUnit dependency in the version catalog. */
val jUnitAlias: Property<String> = objects.property(String::class.java)
.convention("junit")
/** The default JUnit dependency to use if the version catalog dependency cannot be found. */
val jUnitDependency: Property<Any> = objects.property(Any::class.java)
.convention("org.junit.jupiter:junit-jupiter:5.10.3")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.metaborg.convention

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.withType

/**
* Configures a Gradle project to use JUnit 5 for testing.
*
* Also applies the `java-base` plugin to the project. Apply the `java` or `java-library` plugin manually.
*/
class JUnitConventionPlugin: Plugin<Project> {

override fun apply(project: Project): Unit = with(project) {
// Add the configuration extension
val extension = extensions.create<JUnitConventionExtension>("junitConvention")

// Apply the Java base plugin
plugins.apply("java-base")

// Add the JUnit 5 dependency (org.junit.jupiter:junit-jupiter)
// - org.junit.jupiter:junit-jupiter-api
// - org.junit.jupiter:junit-jupiter-engine
// - org.junit.jupiter:junit-jupiter-params
// - org.junit.platform:junit-platform-commons
// - org.junit.platform:junit-platform-engine
dependencies {
versionCatalog()
add("testImplementation",
with(extension.versionCatalogName, extension.jUnitAlias, extension.jUnitDependency) { versionCatalogName, junitAlias, jUnitDependency ->
getLibrary(junitAlias, jUnitDependency, versionCatalogName)
}
)
}

plugins.withType<JavaPlugin> {
tasks.withType<Test> {
// Support any JUnit 5 compatible test runner
useJUnitPlatform()

// Configure the logging
testLogging {
// Default (lifecycle) logging level: only show test failures
lifecycle {
events(TestLogEvent.FAILED)
showExceptions = true
showCauses = true
showStackTraces = true
exceptionFormat = TestExceptionFormat.FULL
}
// Info logging level: show failed and skipped tests, and standard output/error
info {
events(
TestLogEvent.FAILED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT,
TestLogEvent.STANDARD_ERROR
)
showExceptions = true
showCauses = true
showStackTraces = true
exceptionFormat = TestExceptionFormat.FULL
}
// Debug logging level: show all tests, and standard output/error
debug {
events(
TestLogEvent.STARTED,
TestLogEvent.PASSED,
TestLogEvent.FAILED,
TestLogEvent.SKIPPED,
TestLogEvent.STANDARD_OUT,
TestLogEvent.STANDARD_ERROR
)
showExceptions = true
showCauses = true
showStackTraces = true
exceptionFormat = TestExceptionFormat.FULL
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ class JavaConventionPlugin: Plugin<Project> {
plugins.apply("java-base")

plugins.withType<JavaPlugin> {
tasks.withType<Test> {
// Support any JUnit 5 compatible test runner
useJUnitPlatform()
}

configure<JavaPluginExtension> {
// Compile to a specific Java version
toolchain.languageVersion.set(extension.javaVersion)
Expand Down
50 changes: 43 additions & 7 deletions convention-plugin/src/main/kotlin/org/metaborg/convention/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,33 +1,69 @@
package org.metaborg.convention

import org.gradle.api.Project
import org.gradle.api.UnknownDomainObjectException
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.provider.Provider
import org.gradle.kotlin.dsl.getByType

private fun <A0, A1> zipId(p0: Provider<A0>, p1: Provider<A1>): Provider<Pair<A0, A1>> =
p0.zip(p1) { a0, a1 -> Pair(a0, a1) }

// Utility functions that map multiple Providers to a single Provider through a transformer function.

@JvmName("alsoWith")
fun <A0, A1, R : Any> Provider<A0>.with(p1: Provider<A1>, transformer: (A0, A1) -> R): Provider<R> =
internal fun <A0, A1, R : Any> Provider<A0>.with(p1: Provider<A1>, transformer: (A0, A1) -> R): Provider<R> =
with(this, p1, transformer)

@JvmName("alsoWith")
fun <A0, A1, A2, R : Any> Provider<A0>.with(p1: Provider<A1>, p2: Provider<A2>, transformer: (A0, A1, A2) -> R): Provider<R> =
internal fun <A0, A1, A2, R : Any> Provider<A0>.with(p1: Provider<A1>, p2: Provider<A2>, transformer: (A0, A1, A2) -> R): Provider<R> =
with(this, p1, p2, transformer)

@JvmName("alsoWith")
fun <A0, A1, A2, A3, R : Any> Provider<A0>.with(p1: Provider<A1>, p2: Provider<A2>, p3: Provider<A3>, transformer: (A0, A1, A2, A3) -> R): Provider<R> =
internal fun <A0, A1, A2, A3, R : Any> Provider<A0>.with(p1: Provider<A1>, p2: Provider<A2>, p3: Provider<A3>, transformer: (A0, A1, A2, A3) -> R): Provider<R> =
with(this, p1, p2, p3, transformer)


fun <A0, R : Any> with(p0: Provider<A0>, transformer: (A0) -> R): Provider<R> =
internal fun <A0, R : Any> with(p0: Provider<A0>, transformer: (A0) -> R): Provider<R> =
p0.map { a0 -> transformer(a0) }

fun <A0, A1, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, transformer: (A0, A1) -> R): Provider<R> =
internal fun <A0, A1, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, transformer: (A0, A1) -> R): Provider<R> =
p0.zip(p1) { a0, a1 -> transformer(a0, a1) }

fun <A0, A1, A2, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, p2: Provider<A2>, transformer: (A0, A1, A2) -> R): Provider<R> =
internal fun <A0, A1, A2, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, p2: Provider<A2>, transformer: (A0, A1, A2) -> R): Provider<R> =
p0.zip(zipId(p1, p2)) { a0, (a1, a2) -> transformer(a0, a1, a2) }

fun <A0, A1, A2, A3, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, p2: Provider<A2>, p3: Provider<A3>, transformer: (A0, A1, A2, A3) -> R): Provider<R> =
internal fun <A0, A1, A2, A3, R : Any> with(p0: Provider<A0>, p1: Provider<A1>, p2: Provider<A2>, p3: Provider<A3>, transformer: (A0, A1, A2, A3) -> R): Provider<R> =
p0.zip(zipId(p1, zipId(p2, p3))) { a0, (a1, a23) -> val (a2, a3) = a23; transformer(a0, a1, a2, a3) }


/**
* Gets the project's version catalog with the specified name, if it exists.
*
* @param catalogName The name of the version catalog.
* @return The [VersionCatalog]; or `null` if not found.
*/
internal fun Project.versionCatalog(catalogName: String = "libs"): VersionCatalog? = try {
extensions.getByType<VersionCatalogsExtension>().named(catalogName)
} catch (e: UnknownDomainObjectException) {
null
}

/**
* Gets a library with the specified alias from the specified version catalog of the project,
* or if not found, returns the provided dependency notation instead.
*
* For example, to get the JUnit dependency, call this function as:
*
* ```kotlin
* getLibrary("junit", "org.junit.jupiter:junit-jupiter-api:5.7.0")
* ```
*
* Either this finds a dependency with alias `"junit"` in the version catalog,
* or returns the given dependency notation if not found. Note that a platform or
* explicit version restriction may still modify the dependency.
*/
internal fun Project.getLibrary(alias: String, dependencyNotation: Any, catalog: String = "libs"): Any {
return versionCatalog(catalog)?.findLibrary(alias)?.map { it.getOrNull() }?.orElse(null) ?: dependencyNotation
}
1 change: 1 addition & 0 deletions example/java-example/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
`java-library`
id("org.metaborg.convention.java")
id("org.metaborg.convention.junit")
id("org.metaborg.convention.maven-publish")
}

Expand Down

0 comments on commit d1a5aa3

Please sign in to comment.