Gradle 8.13 not compiling test classes

Oracle OpenJDK 23.0.2
Gradle 8.13

So I’ve been working on a project and wanted to add tests.
If I try to run gradle test no tests are executed and I get > Task :test NO-SOURCE. I also tried filtered tests with ‘test --tests “com.toxicstoxm.LEDSuite.GeneralTest”’ but that just gives me an error:

No matching tests found in any candidate test task.
    Requested tests:
        Test pattern com.toxicstoxm.LEDSuite.GeneralTest in task :test

I’ve tried adding various things to my build.gradle.kts like includes for the tests and printing the test classpath to verify if the test classes actually exist. For reference this is my current build.gradle.kts:

import org.gradle.api.tasks.testing.logging.TestLogEvent

plugins {
    id("java")
    id("application")
    id("io.github.jwharm.flatpak-gradle-generator") version "1.5.0"
    id("io.github.crimix.replace-placeholders") version "2.0"
}

if ("@ID@".endsWith("@")) {
    group = "com.toxicstoxm.LEDSuite"
    version = "1.0.0-rc3"
} else {
    group = "@ID@"
    version = "@VERSION@"
}

repositories {
    mavenCentral()
    mavenLocal()
    maven {
        url = uri("./offline-repository")
    }
}

dependencies {
    implementation("io.github.jwharm.javagi:adw:0.12.0")

    implementation("org.yaml:snakeyaml:2.4")

    implementation("jakarta.websocket:jakarta.websocket-client-api:2.2.0")
    implementation("org.glassfish.tyrus:tyrus-client:2.2.0")
    implementation("org.glassfish.tyrus:tyrus-container-grizzly-client:2.2.0")

    compileOnly("org.jetbrains:annotations:26.0.2")
    testCompileOnly("org.jetbrains:annotations:26.0.2")
    annotationProcessor("org.jetbrains:annotations:26.0.2")
    testAnnotationProcessor("org.jetbrains:annotations:26.0.2")

    implementation("com.toxicstoxm.YAJSI:YAJSI:2.1.5")
    implementation("com.toxicstoxm.YAJL:YAJL:2.0.6")

    compileOnly("org.projectlombok:lombok:1.18.38")
    testCompileOnly("org.projectlombok:lombok:1.18.38")
    annotationProcessor("org.projectlombok:lombok:1.18.38")
    testAnnotationProcessor("org.projectlombok:lombok:1.18.38")

    implementation("io.github.classgraph:classgraph:4.8.179")

    testImplementation("org.junit.jupiter:junit-jupiter:5.12.2")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.12.2")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(23)
    }
}

tasks.named<Test>("test") {
    useJUnitPlatform()

    maxHeapSize = "1G"

    testLogging {
        events = setOf(TestLogEvent.PASSED)
    }

    doFirst {
        println("Running test classes from classpath: ${classpath.asPath}")
        println("Test include patterns: $includes")
    }
}

sourceSets {
    test {
        java {
            srcDirs.add(file("src/test/java"))
        }
    }
}

tasks.register<Exec>("glibCompileResources") {
    workingDir = file("src/main/resources")
    commandLine = listOf("glib-compile-resources", "LEDSuite.gresource.xml")
}

tasks.named("classes") {
    dependsOn("glibCompileResources")
}

application {
    mainClass = "com.toxicstoxm.LEDSuite.Main"
    mainModule = "com.toxicstoxm.LEDSuite"
}

tasks.named<JavaExec>("run") {
    jvmArgs("--enable-native-access=ALL-UNNAMED")
}

tasks.flatpakGradleGenerator {
    outputFile.set(
        file("flatpak-sources.json")
    )

    downloadDirectory.set(
        "./offline-repository"
    )
}

replaceSourcePlaceholders {
    enabled(true)
    filesToExpand("**/LEDSuiteApplication.java")
    extraProperties("version")
}

tasks.jar {
    dependsOn("fatJar")
    manifest {
        attributes["Main-Class"] = "com.toxicstoxm.LEDSuite.Main"
    }
}

tasks.clean {
    // Removes the .gresource file to prevent caching
    delete("src/main/resources/LEDSuite.gresource")
}

tasks.register<Jar>("fatJar") {
    manifest {
        attributes["Main-Class"] = "com.toxicstoxm.LEDSuite.Main"
    }

    archiveBaseName = "${rootProject.name}-fat"
    duplicatesStrategy = DuplicatesStrategy.INCLUDE

    from(configurations.compileClasspath.get().filter { it.isDirectory || it.isFile }.map {
        if (it.isDirectory) it else zipTree(it)
    })

    with(tasks.jar.get())
}

settings.gradle.kts:

pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenLocal()
        // DO NOT REMOVE, NEEDED FOR FLATHUB OFFLINE BUILD (!= to mavenLocal)
        maven {
            url = uri("./offline-repository")
        }
    }
}

rootProject.name = "LEDSuite"

My project structure:

src/
├── main
│   ├── java
│   │   ├── com
│   │   │   └── toxicstoxm
│   │   │       └── LEDSuite
│   │   │           └── <main source code>
│   │   └── module-info.java
│   └── resources
│       └── <resources>
└── test
    └── java
        └── com
            └── toxicstoxm
                └── LEDSuite
                    └── GeneralTests.java

as far as I know this should be correct.
Now after investigating some more i discovered that the build/classes/test directory only contains the compiled classes from src/main/java but not src/test/java. Could this be the problem? Now I tried to get gradle to compile my test classes but I just can’t figure out why it isn’t compiling them. The gradle problems report tells me that test sources were found but no tests were executed and that i should check my test configuration.
This is the output from gradle clean test --info:

The client will now receive all logging from the daemon (pid: 335). The daemon log file: /var/home/dominik/.gradle/daemon/8.13/daemon-335.out.log
Starting 50th build in daemon [uptime: 1 hrs 14 mins 8.688 secs, performance: 98%, GC rate: 0.00/s, heap usage: 3% of 512 MiB, non-heap usage: 48% of 384 MiB]
Using 16 worker leases.
Not watching /var/home/dominik/IdeaProjects/LEDSuite since the file system is not supported
Watching the file system is configured to be enabled if available
File system watching is active
Starting Build
Settings evaluated using settings file '/var/home/dominik/IdeaProjects/LEDSuite/settings.gradle.kts'.
Projects loaded. Root project using build file '/var/home/dominik/IdeaProjects/LEDSuite/build.gradle.kts'.
Included projects: [root project 'LEDSuite']
> Configure project :
Evaluating root project 'LEDSuite' using build file '/var/home/dominik/IdeaProjects/LEDSuite/build.gradle.kts'.
Resolved plugin [id: 'java']
Resolved plugin [id: 'application']
Resolved plugin [id: 'io.github.jwharm.flatpak-gradle-generator', version: '1.5.0']
Resolved plugin [id: 'io.github.crimix.replace-placeholders', version: '2.0']
All projects evaluated.
Task name matched 'clean'
Task name matched 'test'
Selected primary task 'clean' from project :
Selected primary task 'test' from project :
Tasks to be executed: [task ':clean', task ':processSources', task ':compileJava', task ':glibCompileResources', task ':processResources', task ':classes', task ':compileTestJava', task ':processTestResources', task ':testClasses', task ':test']
Tasks that were excluded: []
Resolve mutations for :clean (Thread[#4088,Execution worker,5,main]) started.
:clean (Thread[#4088,Execution worker,5,main]) started.
destroyer locations for task group 0 (Thread[#4089,Execution worker Thread 2,5,main]) started.
> Task :clean
Caching disabled for task ':clean' because:
  Build cache is disabled
Task ':clean' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
Resolve mutations for :processSources (Thread[#4088,Execution worker,5,main]) started.
:processSources (Thread[#4088,Execution worker,5,main]) started.
> Task :processSources
Caching disabled for task ':processSources' because:
  Build cache is disabled
  Not worth caching
Task ':processSources' is not up-to-date because:
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/main/src has been removed.
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com has been removed.
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com/toxicstoxm has been removed.
  and more...
Resolve mutations for :compileJava (Thread[#4088,Execution worker,5,main]) started.
:compileJava (Thread[#4088,Execution worker,5,main]) started.
> Task :compileJava
Caching disabled for task ':compileJava' because:
  Build cache is disabled
Task ':compileJava' is not up-to-date because:
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/main has been removed.
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/main/com has been removed.
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/main/com/toxicstoxm has been removed.
  and more...
The input changes require a full rebuild for incremental task ':compileJava'.
Compilation mode: in-process compilation
Full recompilation is required because no incremental change information is available. This is usually caused by clean builds or changing compiler arguments.
Compiling with toolchain '/var/home/dominik/.jdks/openjdk-23.0.2'.
Compiling with JDK Java compiler API.
/var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com/toxicstoxm/LEDSuite/communication/packet_management/packets/replys/menu_reply/animation_menu/widgets/templates/AnimationMenuWidget.java:69: warning: [removal] cpy() in Widget has been deprecated and marked for removal
    public AnimationMenuWidget<?> cpy() {
                                  ^
/var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com/toxicstoxm/LEDSuite/communication/packet_management/packets/replys/menu_reply/animation_menu/AnimationMenuManager.java:279: warning: [removal] cpy() in Widget has been deprecated and marked for removal
                            get(deserializableWidget.widgetType()).cpy().deserialize(deserializableWidget)
                                                                  ^
2 warnings
Class dependency analysis for incremental compilation took 0.01 secs.
Created classpath snapshot for incremental compilation in 0.008 secs.
Resolve mutations for :glibCompileResources (Thread[#4088,Execution worker,5,main]) started.
:glibCompileResources (Thread[#4088,Execution worker,5,main]) started.
> Task :glibCompileResources
Caching disabled for task ':glibCompileResources' because:
  Build cache is disabled
Task ':glibCompileResources' is not up-to-date because:
  Task has not declared any outputs despite executing actions.
Starting process 'command 'glib-compile-resources''. Working directory: /var/home/dominik/IdeaProjects/LEDSuite/src/main/resources Command: glib-compile-resources LEDSuite.gresource.xml
Successfully started process 'command 'glib-compile-resources''
Resolve mutations for :processResources (Thread[#4088,Execution worker,5,main]) started.
:processResources (Thread[#4088,Execution worker,5,main]) started.
> Task :processResources
Caching disabled for task ':processResources' because:
  Build cache is disabled
  Not worth caching
Task ':processResources' is not up-to-date because:
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/resources/main has been removed.
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/resources/main/com.toxicstoxm.LEDSuite.metainfo.xml has been removed.
  Output property 'destinationDir' file /var/home/dominik/IdeaProjects/LEDSuite/build/resources/main/com.toxicstoxm.LEDSuite.metainfo.xml.in.in has been removed.
  and more...
Resolve mutations for :classes (Thread[#4088,Execution worker,5,main]) started.
:classes (Thread[#4088,Execution worker,5,main]) started.
> Task :classes
Skipping task ':classes' as it has no actions.
Resolve mutations for :compileTestJava (Thread[#4088,Execution worker,5,main]) started.
:compileTestJava (Thread[#4088,Execution worker,5,main]) started.
> Task :compileTestJava
Caching disabled for task ':compileTestJava' because:
  Build cache is disabled
Task ':compileTestJava' is not up-to-date because:
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/test has been removed.
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/test/com has been removed.
  Output property 'destinationDirectory' file /var/home/dominik/IdeaProjects/LEDSuite/build/classes/java/test/com/toxicstoxm has been removed.
  and more...
The input changes require a full rebuild for incremental task ':compileTestJava'.
Compilation mode: in-process compilation
Full recompilation is required because no incremental change information is available. This is usually caused by clean builds or changing compiler arguments.
Compiling with toolchain '/var/home/dominik/.jdks/openjdk-23.0.2'.
Compiling with JDK Java compiler API.
/var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com/toxicstoxm/LEDSuite/communication/packet_management/packets/replys/menu_reply/animation_menu/widgets/templates/AnimationMenuWidget.java:69: warning: [removal] cpy() in Widget has been deprecated and marked for removal
    public AnimationMenuWidget<?> cpy() {
                                  ^
/var/home/dominik/IdeaProjects/LEDSuite/build/main/src/com/toxicstoxm/LEDSuite/communication/packet_management/packets/replys/menu_reply/animation_menu/AnimationMenuManager.java:279: warning: [removal] cpy() in Widget has been deprecated and marked for removal
                            get(deserializableWidget.widgetType()).cpy().deserialize(deserializableWidget)
                                                                  ^
2 warnings
Class dependency analysis for incremental compilation took 0.007 secs.
Created classpath snapshot for incremental compilation in 0.004 secs.
Resolve mutations for :processTestResources (Thread[#4088,Execution worker,5,main]) started.
:processTestResources (Thread[#4088,Execution worker,5,main]) started.
> Task :processTestResources NO-SOURCE
Skipping task ':processTestResources' as it has no source files and no previous output files.
Resolve mutations for :testClasses (Thread[#4088,Execution worker,5,main]) started.
:testClasses (Thread[#4088,Execution worker,5,main]) started.
> Task :testClasses
Skipping task ':testClasses' as it has no actions.
Resolve mutations for :test (Thread[#4088,Execution worker,5,main]) started.
:test (Thread[#4088,Execution worker,5,main]) started.
> Task :test NO-SOURCE
Skipping task ':test' as it has no source files and no previous output files.
[Incubating] Problems report is available at: file:///var/home/dominik/IdeaProjects/LEDSuite/build/reports/problems/problems-report.html
BUILD SUCCESSFUL in 3s
6 actionable tasks: 6 executed
Some of the file system contents retained in the virtual file system are on file systems that Gradle doesn't support watching. The relevant state was discarded to ensure changes to these locations are properly detected. You can override this by explicitly enabling file system watching.
9:24:43 AM: Execution finished 'clean test --info'.

I tested the same build.gradle.kts on another project and there it worked perfectly fine. So I really don’t know what the issue is.

Thanks in advance for any helpful information you can give me.
Best regards
Dominik

Besides that you should not generally use mavenLocal() except for exceptional cases temporarily and optimally always with a repository content filter as it is broken by design in Maven already and can lead to slower builds and flaky builds that behave really strange. Find more information about this at Declaring repositories,
your build looks fine from a cursory look, except that you reclare src/test/java as test source directory which already is the default, but I guess you added this in desperation to fix your issue, but it is just redeclaring the default.

Now after investigating some more i discovered that the build/classes/test directory only contains the compiled classes from src/main/java but not src/test/java . Could this be the problem?

Definitely.
The question is why this is happening, this is extremely strange.
build/classes/test should not contain the classes from src/main/java at all.
Tasks must not have overlapping outputs or you end up in devil’s kitchen.
compileJava compiles src/main/java to build/classes/main,
compileTestJava compiles src/test/java to build/classes/test. (Given the defaults)
Your not up-to-date because messages also confirm that this should be the case.

But as you can see from the compiler warning during compileTestJava, it complains about warnings in files inside build/main/src/, so it seems something in your build is putting files to build/main/src and reconfigures the test source set to use that as srcDir instead of what is configured.

The problem is a majorly f***ed up and misbehaving plugin you apply, the io.github.crimix.replace-placeholders plugin.
It is full of very bad practices and additionally full of bugs.
What bites you concretely here is, that it takes the sources from src/main/java, syncs them over to build/main/src replacing the placeholders, and then configures each and every JavaCompile task in your project (including compileTestJava) to use exclusively those files from build/main/src.

Hey Björn, Thanks a ton! This resolved the issue.
I just had to remove the io.github.crimix.replace-placeholders plugin and it worked after a gradle clean build.
I’m feeling kind of stupid now for not catching on to this earlier. My problem was that I already tried removing the plugins individually because I suspected one of them tinkering with the sources or something similar. And when I did that with this specific plugin the tests still failed. In hindsight I probably just forgot to gradle clean before running the tests, but it was like 3 am at the time of me debugging this. And yes the mavenLocal() was just for testing one of my libraries before publishing to central. Anyways thanks for your quick and super helpful response. Have a nice day.

Actually, clean should usually never be necessary, this is Gradle, not Maven. :smiley:

With Maven you should always use clean to get halfway reliable and reproducible results. With Gradle you mainly disable one of the biggest strengths of Gradle, which is avoiding unnecessary work.

If you need to use clean to get correct or reliable results, this usually means there is a build bug somewhere.

1 Like