Gradle_used_by_scope not correctly generated when the java-test-fixtures plugin is used

Gradle 6.8.3, Buildship 3.1.5, Eclipse 4.7.3a.

I’ve been using java-test-fixtures for sharing text fixtures between projects in a multi-project setup.

However, when I use this it appears the gradle_used_by_scope setting in .classpath is not correctly generated.

I create a simple test:

$ tree com.simpletest/
com.simpletest/
├── bin
│   ├── default
│   └── main
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   └── java
    ├── test
    │   └── java
    └── testFixtures
        └── java

If build.gradle is:

plugins {
  id "java"
}

Then it all works fine. .classpath's main entry is:

...
<classpathentry kind="src" output="bin/main" path="src/main/java">
	<attributes>
		<attribute name="gradle_scope" value="main"/>
		<attribute name="gradle_used_by_scope" value="main,test"/>
	</attributes>
</classpathentry>
...

However if it’s:

plugins {
  id "java"
  id "java-test-fixtures"
}

...
<classpathentry kind="src" output="bin/main" path="src/main/java">
	<attributes>
		<attribute name="gradle_scope" value="main"/>
		<attribute name="gradle_used_by_scope" value="main"/>
	</attributes>
</classpathentry>
...

This means, when I run unit tests in src/test/java, I get NoClassDefFoundErrors when referencing classes in main.

The tests run fine when run via gradlew on the command line.

I also noticed if I don’t actually have the testFixtures source folder, the entry is generated ok.

Is this expected?

Workaround
If I add this to build.gradle I can get the .classpath to generate correctly (as I see it!):

eclipse {
    classpath {
        file {
            whenMerged {
                entries.findAll { it instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder }.each {
                    if (it.path == 'src/main/java') {
                        it.entryAttributes['gradle_used_by_scope'] = "main,test"
                    }
                }
            }
        }
    }
}

Additionally, I’ve noticed the testFixtures classpathentry doesn’t have its gradle_used_by_scope set either.

This appears to make tests in other projects fail, which are supposed to be able to see the output of testFixtures on the classpath. With test not set, the bin/testFixtures is not included in the classpath of the test.

If you land here then you might also want to look at Enabling java-test-fixture plugin breaks eclipse junit runner · Issue #11845 · gradle/gradle · GitHub which appears to be the same issue, reported earlier. The workaround might also be better - it supports multi module projects.

Oh, so I also realised the tests inside the same project are not dependent on testFixtures.

So the workaround I’m using in my root project becomes:

subprojects {
    apply plugin: 'eclipse'
    eclipse.classpath.file.whenMerged {
        def main = entries.find { it.path == 'src/main/java' }
        if (main) {
            main.entryAttributes['gradle_used_by_scope'] = 'main,test'
        }
        def testFixtures = entries.find { it.path == 'src/testFixtures/java' }
        if (testFixtures) {
            testFixtures.entryAttributes['gradle_used_by_scope'] = 'test'
        }
    }   
}