* Fix deprecation warning in KAPT > Task :kotlin-power-assert-plugin:kaptTestKotlin Annotation processors discovery from compile classpath is deprecated. Set 'kapt.include.compile.classpath=false' to disable discovery. * Fix kotlinc warning in InfixFunctionTest > Task :kotlin-power-assert-plugin:compileTestKotlin w: P:\projects\contrib\github-kotlin-power-assert\kotlin-power-assert-plugin\src\test\kotlin\com\bnorm\power\InfixFunctionTest.kt: (357, 9): Name shadowed: main * Fix kotlinc warning in PowerAssertCallTransformer > Task :kotlin-power-assert-plugin:compileKotlin w: P:\projects\contrib\github-kotlin-power-assert\kotlin-power-assert-plugin\src\main\kotlin\com\bnorm\power\PowerAssertCallTransformer.kt: (174, 77): Name shadowed: newVariables * Fix Gradle 8 breaking change, add dependency between tasks. > Task :kotlin-power-assert-gradle:publishPluginJar Execution optimizations have been disabled for task ':kotlin-power-assert-gradle:publishPluginJar' to ensure correctness due to the following reasons: - Gradle detected a problem with the following location: 'P:\projects\contrib\github-kotlin-power-assert\kotlin-power-assert-gradle\build\generated\source\buildConfig\main\main'. Reason: Task ':kotlin-power-assert-gradle:publishPluginJar' uses this output of task ':kotlin-power-assert-gradle:generateBuildConfig' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.4.2/userguide/validation_problems.html#implicit_dependency for more details about this problem. Gradle detected a problem with the following location: 'P:\projects\contrib\github-kotlin-power-assert\kotlin-power-assert-gradle\build\generated\source\buildConfig\main\main'. Reason: Task ':kotlin-power-assert-gradle:publishPluginJar' uses this output of task ':kotlin-power-assert-gradle:generateBuildConfig' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Please refer to https://docs.gradle.org/7.4.2/userguide/validation_problems.html#implicit_dependency for more details about this problem. This behaviour has been deprecated and is scheduled to be removed in Gradle 8.0. Execution optimizations are disabled to ensure correctness. See https://docs.gradle.org/7.4.2/userguide/more_about_tasks.html#sec:up_to_date_checks for more details. * Fix https://github.com/gmazzo/gradle-buildconfig-plugin/issues/38 by using the latest version. * Update Gradle wrapper to 7.x latest 7.6.1 * Build URI using built-in function instead of string manipulation. * Reduce code duplication and be a bit more explicit. * Upgrade plugin-publish to latest 1.1.0 Action "Deprecate the entire `pluginBundle` block when using Gradle version 7.6 or later, mark for removal in version 8.0" from https://plugins.gradle.org/plugin/com.gradle.plugin-publish/1.0.0-rc-3 Gradle publishing has to execute publishPlugins directly, because the new version already applies the Maven Publish Plugin, that creates the task "publish", which publishes to registered repositories only. * Add test repository to all modules (enabled by plugin-publish 1.0) * Bump Gradle to latest 8.x stable 8.0.2 * Downgrade Gradle in sample project because of removed outdated API (details below), assuming Kotlin 1.7 to 1.8 bump is not wanted yet. * What went wrong: Failed to notify task execution graph listener. > org.gradle.api.reporting.DirectoryReport.setEnabled(Z)V Caused by: java.lang.NoSuchMethodError: org.gradle.api.reporting.DirectoryReport.setEnabled(Z)V at org.jetbrains.kotlin.gradle.testing.internal.KotlinTestReport.disableTestReporting(KotlinTestReport.kt:242) https://github.com/JetBrains/kotlin/blob/v1.7.0/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/testing/internal/KotlinTestReport.kt#L242 Gradle 8.0 removed that deprecated property: https://docs.gradle.org/current/userguide/upgrading_version_7.html#report_and_testreport_api_cleanup Fixed in Kotlin 1.8.0: https://github.com/JetBrains/kotlin/commit/e902ae7c09639559ac734842ebe5c383ec6e6e14 Deprecation warnings in sample project are all related to this, run: `sample$ gradlew build --warning-mode=all` for more info.
kotlin-power-assert
Kotlin Compiler Plugin which high-jacks Kotlin assert function calls and transforms them similar to Groovy's Power Assert feature. This plugin uses the IR backend for the Kotlin compiler and supports all platforms: JVM, JS, and Native!
Example
Given following code:
val hello = "Hello"
assert(hello.length == "World".substring(1, 4).length)
Normally the assertion message would look like:
java.lang.AssertionError: Assertion failed
at <stacktrace>
A custom assertion message can be provided:
val hello = "Hello"
assert(hello.length == "World".substring(1, 4).length) { "Incorrect length" }
But this just replaces the message:
java.lang.AssertionError: Incorrect length
at <stacktrace>
With kotlin-power-assert included, the error message for the previous example
will be transformed:
java.lang.AssertionError: Incorrect length
assert(hello.length == "World".substring(1, 4).length)
| | | | |
| | | | 3
| | | orl
| | false
| 5
Hello
at <stacktrace>
Complex, multi-line, boolean expression are also supported:
Assertion failed
assert(
(text != null && text.toLowerCase() == text) ||
| |
| false
null
text == "Hello"
| |
| false
null
)
Beyond Assert
The plugin by default will transform assert function calls but can also
transform other functions like require, check, assertTrue, and many, many
more.
Functions which can be transformed have specific requirements. A function must
have a form which allows taking a String or () -> String value as the last
parameter. This can either be as an overload or the original function.
For example, the assert function has 2 definitions:
fun assert(value: Boolean)fun assert(value: Boolean, lazyMessage: () -> Any)
If the first function definition is called, it will be transformed into calling the second definition with the diagram message supplied as the last parameter. If the second definition is called, it will be transformed into calling the same function but with the diagram message appended to the last parameter.
This transformed function call doesn't need to throw an exception either. See Advanced Usage for some examples.
Gradle Plugin
Builds of the Gradle plugin are available through the Gradle Plugin Portal.
plugins {
kotlin("multiplatform") version "1.7.0"
id("com.bnorm.power.kotlin-power-assert") version "0.12.0"
}
The Gradle plugin allows configuring the functions which should be transformed with a list of fully-qualified function names.
// Kotlin DSL
configure<com.bnorm.power.PowerAssertGradleExtension> {
functions = listOf("kotlin.assert", "kotlin.test.assertTrue")
}
// Groovy
kotlinPowerAssert {
functions = ["kotlin.assert", "kotlin.test.assertTrue"]
}
You can also exclude Gradle source sets from being transformed by the plugin, where those source sets can be specified by name.
// Kotlin DSL
configure<com.bnorm.power.PowerAssertGradleExtension> {
excludedSourceSets = listOf(
"commonMain",
"jvmMain",
"jsMain",
"nativeMain"
)
}
// Groovy
kotlinPowerAssert {
excludedSourceSets = [
"commonMain",
"jvmMain",
"jsMain",
"nativeMain"
]
}
Compatibility
The Kotlin compiler plugin API is unstable and each new version of Kotlin can bring breaking changes to the APIs used by this compiler plugin. Make sure you are using the correct version of this plugin for whatever version of Kotlin used. Check the table below to find when support for a particular version of Kotlin was first introduced. If a version of Kotlin or this plugin is not listed it can be assumed to maintain compatibility with the next oldest version listed.
| Kotlin Version | Plugin Version |
|---|---|
| 1.3.60 | 0.1.0 |
| 1.3.70 | 0.3.0 |
| 1.4.0 | 0.4.0 |
| 1.4.20 | 0.6.0 |
| 1.4.30 | 0.7.0 |
| 1.5.0 | 0.8.0 |
| 1.5.10 | 0.9.0 |
| 1.5.20 | 0.10.0 |
| 1.6.0 | 0.11.0 |
| 1.7.0 | 0.12.0 |
Kotlin IR
This plugin supports all IR based compiler backends: JVM, JS, and Native! Only Kotlin/JS still uses the legacy compiler backend by default, use the following to make sure IR is enabled.
target {
js(IR) {
}
}
Advanced Usage
Function Call Tracing
Similar to Rust's dbg! macro, functions which take arbitrary parameters can
be transformed. For example:
fun <T> dbg(value: T): T = value
fun <T> dbg(value: T, msg: String): T {
println(msg)
return value
}
fun main() {
println(dbg(1 + 2 + 3))
}
Prints the following:
dbg(1 + 2 + 3)
| |
| 6
3
6
Soft Assertion
To achieve soft assertion, the following template can be implemented:
typealias LazyMessage = () -> Any
interface AssertScope {
fun assert(assertion: Boolean, lazyMessage: LazyMessage? = null)
}
fun <R> assertSoftly(block: AssertScope.() -> R): R = TODO("implement")
You can then use the template as follows:
val jane: Person = TODO()
assertSoftly {
assert(jane.firstName == "Jane")
assert(jane.lastName == "Doe")
}
A working example is available in this repository in the sample directory.