Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running Junit5 tests with module-info fails #1812

Open
xazap opened this issue Nov 25, 2024 · 8 comments
Open

Running Junit5 tests with module-info fails #1812

xazap opened this issue Nov 25, 2024 · 8 comments
Labels
bug Something isn't working

Comments

@xazap
Copy link

xazap commented Nov 25, 2024

Given

  • Maven project A has production code and contains a module-info (module A)
  • Maven project B has test code (Junit 5) for project A and contains a module-info ( module B requires module A)
  • After importing project A and B in Eclipse, project B has a project reference to project A
  • The standalone Maven build executes the unit tests without failures given this maven-surefire-plugin configuration:
<plugin> 
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<version>3.5.2</version>
	<configuration>
		<useModulePath>true</useModulePath>
		<useSystemClassLoader>false</useSystemClassLoader>
		<useManifestOnlyJar>false</useManifestOnlyJar>
		<forkCount>1</forkCount>
		<reuseForks>false</reuseForks>
		<argLine>-javaagent:${org.mockito:mockito-core:jar}</argLine>
	</configuration>
</plugin>

Expectation

I expected Eclipse to run the junit tests successfully since the Maven build executes correctly, proving that dependencies and and module references are setup correctly.

Findings

  • When running the unit tests in project B in Eclipse a popup titled Problem Occurred with description: An internal error occurred during: "Launching java (6)". Index 0 out of bounds for length 0
  • The stacktrace in de Eclipse's log is:
!MESSAGE An internal error occurred during: "Launching java (6)".
!STACK 0
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate.enumerateTypesInPackage(JUnitLaunchConfigurationDelegate.java:543)
	at org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate.collectExecutionArguments(JUnitLaunchConfigurationDelegate.java:473)
	at org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate.getVMRunnerConfiguration(JUnitLaunchConfigurationDelegate.java:182)
	at org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate.launch(JUnitLaunchConfigurationDelegate.java:275)
	at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:805)
	at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:716)
	at org.eclipse.debug.internal.ui.DebugUIPlugin.buildAndLaunch(DebugUIPlugin.java:1054)
	at org.eclipse.debug.internal.ui.DebugUIPlugin$1.run(DebugUIPlugin.java:1257)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)
  • If I remove the module-info.java file from test project B, Eclipse starts executing unit tests. However, this is an undesirable workaround since it causes test failures when the code under test uses the ServiceLoader mechanism and needs to find provides interface with implementation clauses in the module descriptor of the test project.

Tested under this environment:

  • OS & version
    Linux 6.8.0-49-generic 49-Ubuntu SMP PREEMPT_DYNAMIC Mon Nov 4 02:06:24 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
  • Eclipse IDE/Platform version:
    Version: 2024-09 (4.33.0)
    Build id: 20240905-0614
    No additional plugins installed
  • JDK:
    java version "21.0.4" 2024-07-16 LTS
    Java(TM) SE Runtime Environment (build 21.0.4+8-LTS-274)
    Java HotSpot(TM) 64-Bit Server VM (build 21.0.4+8-LTS-274, mixed mode, sharing)
  • Maven:
    Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
@jukzi
Copy link
Contributor

jukzi commented Nov 26, 2024

Currently ECJ does not allow javacode in module-info.java but that might change. See for example eclipse-jdt/eclipse.jdt.core#3347
Please propose a workaround that ignores this exception for "module-info" but does not hard code to exclude "module-info".

@jukzi jukzi added the bug Something isn't working label Nov 26, 2024
@srikanth-sankaran
Copy link

Currently ECJ does not allow javacode in module-info.java but that might change. See for example eclipse-jdt/eclipse.jdt.core#3347 Please propose a workaround that ignores this exception for "module-info" but does not hard code to exclude "module-info".

Hmm. Is there any evidence here that the "problematic" module-info.java contains any java code other than module description ?? I am wondering if there is really a connection to eclipse-jdt/eclipse.jdt.core#3347

I think this should be investigated by JDT-UI folk and I will forward it there.

@srikanth-sankaran srikanth-sankaran transferred this issue from eclipse-jdt/eclipse.jdt.core Nov 26, 2024
@xazap
Copy link
Author

xazap commented Nov 26, 2024

Currently ECJ does not allow javacode in module-info.java but that might change. See for example eclipse-jdt/eclipse.jdt.core#3347 Please propose a workaround that ignores this exception for "module-info" but does not hard code to exclude "module-info".

Please note that I have no java code inside the module-info.java files, eclipse-jdt/eclipse.jdt.core#3347 is likely not the issue.

@xazap
Copy link
Author

xazap commented Nov 26, 2024

Since I really would like to get this resolved, I have created a minimal Maven project that reproduces the issue. Please clone this repo and find that:

  • Running with Maven on the command line the unit test executes fine.
  • Running the unit test in Eclipse reproduces the exception.

@jukzi
Copy link
Contributor

jukzi commented Nov 26, 2024

I really would like to get this resolve

Please contribute a PR that fixes the issue.

@gzsombor
Copy link
Contributor

I've tried to run your project @xazap - I found a couple of issues, but they are different than you mentioned.
First, mvn verify fails with:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:testCompile (default-testCompile) on project project-test: Execution default-testCompile of goal 
org.apache.maven.plugins:maven-compiler-plugin:3.13.0:testCompile failed: 
Can't compile test sources when main sources are missing a module descriptor -> [Help 1]

I'm using Maven 3.9.9.

Second, running the test from Eclipse fails with:

java.lang.IllegalAccessError: class org.junit.platform.launcher.core.LauncherFactory (in unnamed module @0x13deb50e) cannot access class org.junit.platform.commons.util.Preconditions (in module org.junit.platform.commons) because module org.junit.platform.commons does not export org.junit.platform.commons.util to unnamed module @0x13deb50e
	at org.junit.platform.launcher.core.LauncherFactory.create(LauncherFactory.java:128)
	at org.junit.platform.launcher.core.LauncherFactory.create(LauncherFactory.java:112)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.<init>(JUnit5TestLoader.java:37)
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
	at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:304)
	at java.base/java.lang.Class.newInstance(Class.java:725)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.createRawTestLoader(RemoteTestRunner.java:372)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.createLoader(RemoteTestRunner.java:367)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.defaultInit(RemoteTestRunner.java:311)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.init(RemoteTestRunner.java:226)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

It seems that in the freshly launched VM, those classes are not accessible to the launcher classes, they are not opened up.

I could hack this, with using

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter</artifactId>
			<version>5.11.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-suite-commons</artifactId>
			<scope>test</scope>
			<version>1.10.5</version>
		</dependency>

in the pom.xml
and

open module project.test {
	requires project;
	requires org.junit.platform.launcher;
	requires org.assertj.core;
	requires org.junit.jupiter;
	
	provides project.spi.SomeProviderFactory with test.support.TestProviderFactory;
}

in the module file, but I couldn't find a solution, with both Eclipse and Maven are happy.

@xazap
Copy link
Author

xazap commented Nov 28, 2024

I've tried to run your project @xazap - I found a couple of issues, but they are different than you mentioned. First, mvn verify fails with:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:testCompile (default-testCompile) on project project-test: Execution default-testCompile of goal 
org.apache.maven.plugins:maven-compiler-plugin:3.13.0:testCompile failed: 
Can't compile test sources when main sources are missing a module descriptor -> [Help 1]

Yes, I get that too sometimes. This is not an issue with my sample project, but rather a side effect of using the project in Eclipse before running it from the command line. Eclipse appears to be opinionated about missing src/main/java and src/main/resources. Eclipse adds them without user consent thereby breaking the command line build.

If you remove the erroneously added src/main/java from the project-test module it should compile fine from the command line.

Second, running the test from Eclipse fails with:

java.lang.IllegalAccessError: class org.junit.platform.launcher.core.LauncherFactory (in unnamed module @0x13deb50e) cannot access class org.junit.platform.commons.util.Preconditions (in module org.junit.platform.commons) because module org.junit.platform.commons does not export org.junit.platform.commons.util to unnamed module @0x13deb50e
	at org.junit.platform.launcher.core.LauncherFactory.create(LauncherFactory.java:128)
	at org.junit.platform.launcher.core.LauncherFactory.create(LauncherFactory.java:112)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.<init>(JUnit5TestLoader.java:37)
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
	at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:304)
	at java.base/java.lang.Class.newInstance(Class.java:725)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.createRawTestLoader(RemoteTestRunner.java:372)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.createLoader(RemoteTestRunner.java:367)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.defaultInit(RemoteTestRunner.java:311)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.init(RemoteTestRunner.java:226)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

Interesting, with some experimentation I could also get this same stacktrace. It appears that different stacktraces appears depending on you what you select before performing Run as -> JUnit Test:

  • If I select src/test/java in the Project Explorer I see java.lang.ArrayIndexOutOfBoundsException: ...
  • If I select SomeServiceTest.java in the Project Explorer I also get java.lang.IllegalAccessError: ...

It seems that in the freshly launched VM, those classes are not accessible to the launcher classes, they are not opened up.

I could hack this, with using

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter</artifactId>
			<version>5.11.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-suite-commons</artifactId>
			<scope>test</scope>
			<version>1.10.5</version>
		</dependency>

in the pom.xml and

open module project.test {
	requires project;
	requires org.junit.platform.launcher;
	requires org.assertj.core;
	requires org.junit.jupiter;
	
	provides project.spi.SomeProviderFactory with test.support.TestProviderFactory;
}

Thanks, This fixes some things:

  • ✅ Select src/test/java source root and run JUnit test results in success
  • ✅ Select test.project package and run JUnit test results in success
  • ✅ Select SomeServiceTest java file and run JUnit test results in success
  • ❌ Select project-test project root and run JUnit test results in success

... but I couldn't find a solution, with both Eclipse and Maven are happy.
If I remove the empty src/main/java from project-test then both Maven and Eclipse are happy.

@xazap
Copy link
Author

xazap commented Nov 28, 2024

We arrive at three issues:

  1. Eclipse adds empty src/main/java and src/main/resources breaking standalone Maven builds. I should probably file a separate issue for this.
  2. Eclipse's JUnit runner handles modules differently than Maven's Surefire Plugin. Although this can be worked around (as demonstrated by gzsombor), this does not make a good developer experience.
  3. Even with workaround, Eclipse's JUnit runner cannot be started by selecting the project root.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants