Actually one thing I need to figure out, that if y...
# kotest-contributors
s
Actually one thing I need to figure out, that if you wanted to do that, would be helpful: • work out how to detect if a dependency is on the classpath for a gradle
module
before the plugin is applied (only want to apply the gradle plugin to modules that have kotest on the classpath)
a
work out how to detect if a dependency is on the classpath for a gradle module before the plugin is applied
this is essentially impossible
s
Oh is it
a
the other way round is more normal: if the Kotest Gradle plugin is applied to a subproject then automatically add Kotest dependency
s
I guess that would work, if the dependency is added manually too, it still works ?
a
yeah, it'll work
Gradle will de-dupe the requested dependencies before actually resolving them
s
ok
I was just trying to avoid adding kotest as a task to every module in the build, even if you don't have kotest on that module, but I guess that doesn't matter too much
a
hmmm is the plugin using
allprojects {}
or
subprojects {}
to configure all projects?
I hope it is not :)
s
hmmmm 🙂 I'll check, I don't know a lot about plugins
I'm just learning really, please feel free to tell me how bad this is:
Copy code
project.afterEvaluate {
         plugins.withId("org.jetbrains.kotlin.jvm") {
            this@afterEvaluate.allprojects.forEach { subproject ->
               subproject.tasks.register("kotest", KotestTask::class.java) {
                  description = DESCRIPTION
                  group = JavaBasePlugin.VERIFICATION_GROUP
                  dependsOn(subproject.tasks.getByName(JavaPlugin.TEST_CLASSES_TASK_NAME))
               }
            }
         }
      }
a
Gradle is absolutely full of footguns, about half of the API is bugged/soon-to-be-deprecated/broken
avoid using
afterEvaluate {}
, it leads to a race-to-the-bottom between plugins trying to evaluate after each other. The replacement is exactly what you have,
plugins.withId()  { /* lazy config */ }
, so just remove
afterEvaluate {}
s
yes I think its the worst API I've ever used in the 10 years I've been using it
ok, I added afterEvaluate to get dependsOn working
I'll try it with just the withId
a
allprojects {}
(and
subprojects {}
) are also something to avoid. They're really old, and make Gradle behave like Maven, so it's hierarchical. But it makes Gradle slow and fragile, because now the root project has to configure all the subprojects. Gradle will remove them soon https://docs.gradle.org/current/userguide/isolated_projects.html
instead the Kotest plugin should just configure the current project, and if someone wants to apply Kotest to multiple subprojects, then they'll have to apply the plugin in each subproject. It's repetitive but it's declarative.
👍🏻 1
very nice 1
s
Isolated Projects is a pre-alpha Gradle feature that extends the configuration cache to further improve performance, particularly the performance of Android Studio and IDEA sync Anything that makes the sync faster is a god send
ok I can do that if that's best practice
a
but what you've written is fine for now, if it helps you develop. I can help out later when it's more stable :)
s
I've made the changes you suggested
and yes there will no doubt be 1000 ways you can improve this 🙂
I've got the new gradle plugin working with intellij, so we can launch individual tests from intellij through the new plugin, and then all the things that are broken atm (like navigating to individual tests) work
since the gradle test task makes so many assumptions about tests being methods in a class
a
sounds amazing
e
Was looking into this as well.. It gets hard when/if we decide to support adding Kotest for individual multiplatform targets..
I think something like this would just add the dependency, based on a single target project:
Copy code
project
   .configurations
   .named("testImplementation")
   .configure {
      dependencies.add(project.dependencies.create("io.kotest:kotest-runner-junit5:$kotestVersion"))
   }
I guess we could just add Kotest for each target being declared.. looking at how that could be done.
Copy code
> Configure project :pcb:pe-sync
Detected target: metadata
Skipping unsupported target: metadata
Detected target: jvm
Creating task kotestJvm
Detected target: js
Creating task kotestJs
By changing the
KotestPlugin#apply
to do
Copy code
project.extensions.configure<KotlinMultiplatformExtension> {
         targets.configureEach {
            println("Detected target: $name")
            if (name in unsupportedTargets) {
               println("Skipping unsupported target: $name")
            } else {
               println("Creating task kotest${name.replaceFirstChar { it.uppercase() }}")
               project.tasks.register("kotest${name.replaceFirstChar { it.uppercase() }}", KotestTask::class.java) {
                  description = DESCRIPTION
                  group = JavaBasePlugin.VERIFICATION_GROUP
                  // TODO: Setup dependency on the compile task for this target's test source set
               }
            }
         }
      }
s
I wonder if it's possible to get the version of the plugin itself, and use that for the kotest dependency
e
I think you can just declare a constant that's used as a default in the
KotestExtension
, which needs to be set by some gradle property during release of the plugin
plus1 1
s
so have the release process set the version like we do for the the other plugin
e
And that could allow the user to downgrade kotest version as well by doing something like
Copy code
kotest {
  version = "5.9.1"
}
Will the gradle plugin depend on the kotest version being used?
s
It's part of the main repo so they will be released together - it's probably going to be best to keep the versions aligned although we probably won't change the contract
although 6.x is not going to work with 5.x, as I am making some breaking changes to support things
e
@Adam S I think wiring dependencies by inputs/outputs is supposed to be "better" than just explicitly adding dependsOn, but I'm not sure why. Do you know? And does it make a difference in this case?
a
there are pro/cons to both. It's kind of like explicit imports
import com.foo.lib.Type
vs wildcard imports
import com.foo.lib.*
I prefer avoiding
dependsOn()
because then you can be confident that the task inputs/outputs are wired up correctly, and Gradle will be able to infer the dependencies.
👍 1
e
if you get a chance to look at the PR I'm grateful for any feedback. As you said, Gradle API is a fucking nightmare.. seeing as I don't work with it daily I probably triggered a couple of those footguns you mentioned.. :)
s
I like this direction
we should be able to make native tests run through the new plugin very easily, so we can have per test run icons for native. JS will take me a bit of time.