Test task classpath 'frozen' and not reflecting sourceSet changes

Hi. I have a multi-module build, and a new task, integrationTest, with the following configuration in the main build.gradle:

subprojects {
      sourceSets {
        sharedTest {
            compileClasspath = main.output + compileClasspath
            runtimeClasspath = output + compileClasspath + runtimeClasspath
        }
          [test, integrationTest].each {
            it.compileClasspath = sharedTest.output + sharedTest.compileClasspath + it.compileClasspath
            it.runtimeClasspath = it.output + it.compileClasspath + it.runtimeClasspath
        }
    }
      configurations {
        sharedTestCompile {
            extendsFrom compile
        }
        sharedTestRuntime {
            extendsFrom runtime
        }
          testCompile {
            extendsFrom sharedTestCompile
        }
        testRuntime {
            extendsFrom sharedTestRuntime
        }
          integrationTestCompile {
            extendsFrom compile, sharedTestCompile
        }
        integrationTestRuntime {
            extendsFrom runtime, sharedTestRuntime
        }
    }
      task integrationTest(type: Test) {
        dependsOn integrationTestClasses
        classpath = sourceSets.integrationTest.runtimeClasspath
        testClassesDir = sourceSets.integrationTest.output.classesDir
        testReportDir = new File(project.reporting.baseDir, 'integrationTests')
        testResultsDir = new File(project.buildDir, 'integrationTest-results')
        testSrcDirs = (sourceSets.integrationTest.java.srcDirs + sourceSets.integrationTest.groovy.srcDirs) as List
    }

Note the classpath of the integrationTest task: sourceSets.integrationTest.runtimeClasspath.

Than, in one module, I change the integrationTest sourceSet / configuration:

sourceSets {
    testUtils {
        compileClasspath = main.output + compileClasspath
        runtimeClasspath = output + compileClasspath + runtimeClasspath
    }
      integrationTest {
        compileClasspath = testUtils.output + compileClasspath
        runtimeClasspath = output + compileClasspath + runtimeClasspath
    }
}
  configurations {
    testUtilsCompile {
        extendsFrom compile
    }
    testUtilsRuntime {
        extendsFrom runtime
    }
    testUtils
      integrationTestCompile {
        extendsFrom testUtilsCompile
    }
    integrationTestRuntime {
        extendsFrom testUtilsRuntime
    }
}

And the integrationTests are compiled Ok, but there is a NoClassDefFoundError at runtime. I checked and the classpath for the task was not reflecting the changes I did. It is as if gradle was creating the task in the main build.gradle with the sourceSet it finds there, and then process the module’s build file, but the task’s classpath does not reflect the changes in the sourceSet. So, I did this:

integrationTest {
    classpath = sourceSets.integrationTest.runtimeClasspath
}

and it works.

The question: I though Gradle was evaluating the sourceSets and all dynamically, so I think this last bit (integrationTest classpath in the module) could be skipped? I must be doing something wrong with the configuation - could you give me some pointers? Or am I expecting too much and it is supposed to work this way?

wujek

It’s true that file collections are lazy, but you are assigning a new file collection. There is no way for the test task to pick up this new object automatically.

What is the different with this new file collection and the ones that are already there (I guess you mean test, compile and son on?) Is it because they are defined as lazy closures somewhere in Gradle’s guts?

There is no difference. The Java plugin sets the source set’s class paths once, and then they are referred to from many places. If you reassign them, you’ll also have to update the referrers. No magic here.

Ahhh now I understand. Thats because I do:

runtimeClasspath = output + compileClasspath + runtimeClasspath

instead of extending it. Ok, that’s clear now, thank you for your guidance.