eclipse / buildship

The Eclipse Plug-ins for Gradle project.
528 stars 167 forks source link

Test classes and resources of dependency are in classpath even with without_test_code=true #1270

Closed Tiller closed 10 months ago

Tiller commented 10 months ago

Expected Behavior

Given the following project structure:

/projectA
    src/
        test/
            java/
                MyClass.java
            resources/
                myResource.txt
    build.gradle
/projectB
    src/
        test/
            java/
                MyOtherClass.java
    build.gradle
build.gradle
settings.gradle

With projectB containing:

dependencies {
    implementation project(':projectA')
}

MyClass and myResource.txt should not be in the test classpath of projectB. MyOtherClass shouldn't be able to access MyClass or myResource.txt

Current Behavior

projectA/bin/test is in the classpath of projectB when running tests contained in MyOtherClass.

Eclipse's generated command line is :

/usr/lib/jvm/java-8-openjdk-amd64/bin/java
-ea
-Dfile.encoding=UTF-8
-Dstdout.encoding=UTF-8
-Dstderr.encoding=UTF-8
-classpath /rootPath/projectB/bin/main
    :/rootPath/projectB/bin/test
    :/projectB/bin/default
    :/projectA/bin/default
    :/rootPath/projectA/bin/main
    :<...>/junit-4.13.jar
    :<...>/hamcrest-core-1.3.jar
    :/rootPath/projectA/bin/test
    :<...>/eclipse-2023-09/configuration/org.eclipse.osgi/244/0/.cp
    :<...>/eclipse-2023-09/configuration/org.eclipse.osgi/243/0/.cp
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner
-version 3
-port 45949
-testLoaderClass org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader
-loaderpluginname org.eclipse.jdt.junit4.runtime
-test de.topobyte.test.TestResources:test

even though .classpath contains:

    <classpathentry kind="src" path="/projectA">
        <attributes>
            <attribute name="without_test_code" value="true"/>
        </attributes>
    </classpathentry>

Context

I'm having issues with some tests in projectB that does some reflection / classpath scanning that ends-up with test classes of projectA

Steps to Reproduce

User sebkur opened a strictly identical issue years ago (#1009) and provided a perfectly good example of the issue : https://github.com/sebkur/test-gradle-eclipse

In eclipse, test TestResources.test() fails because it sees a resource that is in the test classpath of a dependency.

Your Environment

OS: Ubuntu 22.04 Gradle: tested with 7.4.2, 7.6.2 and 8.2.1 Eclipse: tested with 2023-03 and 2023-09, with buildship 3.1.7 (and the version that was shipped with 2023-03)

oleosterhagen commented 10 months ago

Your projectB/.classpath file seems not be created by Buildship. There should not exists a classpath entry with path /projectA:

<classpathentry kind="src" path="/projectA">
    <attributes>
        <attribute name="without_test_code" value="true"/>
    </attributes>
</classpathentry>

In Buildship, references to other projects and external dependencies are handled with the Gradle Classpath Container which is automatically added to the .classpath file when importing or refreshing the Gradle project.

<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>

There should be no direct entry for projectA in the file projectB/.classpath.

The actual entries of the classpath container can be inspected in the Package or Project Explorer under the folder Project and External Dependencies:

grafik

With theses steps you can ensure that the projects will be correctly imported:

  1. Remove the projects from the Eclipse workspace
  2. Delete all .classpath and .project files and all .settings folders
  3. Import the Gradle project again in Eclipse
Tiller commented 10 months ago

Hello, Indeed you're right, it works fine when importing the project as a gradle project.

However, it does not when using gradlew eclipse + Import existing project. Do you know who we might reach for such issue? Is it an issue on gradle that generates a wrong classpath? Is it an issue on eclipse, not interpreting the without_test correctly? Is it an issue on buildship because, even if the project is not imported as gradle project, my understanding was that buildship was still doing some magic?

For the last option, I've had this understanding because I had to tweak some classpath attributes to make other things work, such as gradle_used_by_scope. And I mean, I don't think eclipse natively supports this attribute, does it? I've always believed that it was buildship that read these attributes and tweaked the run conf accordingly?

oleosterhagen commented 10 months ago

projectA/build.gradle

plugins {
    id 'java-library'
    id 'eclipse'
}

projectB/build.gradle

plugins {
    id 'java-library'
    id 'eclipse'
}

dependencies {
    implementation project(':projectA')
}

After invoking ./gradlew eclipse these files are created:

projectA/.classpath

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="output" path="bin/default"/>
    <classpathentry output="bin/test" kind="src" path="src/test/java">
        <attributes>
            <attribute name="gradle_scope" value="test"/>
            <attribute name="gradle_used_by_scope" value="test"/>
            <attribute name="test" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
</classpath>

projectB/.classpath

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="output" path="bin/default"/>
    <classpathentry output="bin/test" kind="src" path="src/test/java">
        <attributes>
            <attribute name="gradle_scope" value="test"/>
            <attribute name="gradle_used_by_scope" value="test"/>
            <attribute name="test" value="true"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17/"/>
    <classpathentry kind="src" path="/projectA">
        <attributes>
            <attribute name="without_test_code" value="true"/>
        </attributes>
    </classpathentry>
</classpath>

That looks correct and also importing these as normal projects (and not via Buildship) shows the expected behavior, e.g. the properties for projectB:

grafik

MyClass from projectA cannot be imported from MyOtherClass in projectB.

Could you provide a minimal reproducible example for your concrete case?

Tiller commented 10 months ago

Thanks for checking this out.

The best way to reproduce the issue would be to clone https://github.com/sebkur/test-gradle-eclipse

You can tweak the gradle version in gradle-wrapper.properties if you want, but I tested with both 7.4.2, 7.6.2 and 8.2.1. (with a more recent version of gradle, you'll need to remove the task sourcesJar and artifacts in build.gradle)

Then you run ./gradlew eclipse and import project in eclipse as a regular project. In the properties of project "util", you'll see that it depends on core without test code.

image

But then, if you try to run test TestResources in util, it will fail at :

List<URL> testing = Resources.getAll("testing.txt");
Assert.assertEquals(1, testing.size());

image

Because it'll find both util/src/test/resources/testing.txt and core/src/test/resources/testing.txt. Where it should not have the second one in its classpath.

If you look at the generated command line, you can see the test classpath of core: image

oleosterhagen commented 10 months ago

Thank you for the detailed explanations.

The problem is independent of Gradle or Buildship and has to be solved in JDT Debug. I have created Issue #327 and provided pull request #328. I hope it will be accepted.

Since Buildship already handles the attribute Without test code correctly, this issue should be closed.

Tiller commented 10 months ago

Thanks again for checking this out and for dispatching it (and fixing it!) to the correct project!

oleosterhagen commented 8 months ago

@Tiller The pull request #328 has been integrated and will be part of the upcoming Eclipse 2024-03 (release date 2024-03-13). Integration builds with this fix are already available.