Skip to content

Conversation

@chrisrueger
Copy link
Contributor

Closes #6651
Copied from fork PR chrisrueger#1 which was created via Copilot.

JUnit Platform 1.13+ Support - Fix NamespacedHierarchicalStore Error

Overview

Addressing the "No NamespacedHierarchicalStore" error when using JUnit Platform 1.13+ with the BundleEngine. The issue occurs because JUnit Platform 1.13+ changed how execution context is propagated to child test engines.

Implementation Status

  • Understand the problem: ExecutionRequest context not properly propagated in JUnit Platform 1.13+
  • Modify BundleEngine.execute() to pass the full ExecutionRequest to child execution methods
  • Update BundleDescriptor.executeChild() signature to accept ExecutionRequest instead of just listener and params
  • Implement proper context propagation using ExecutionRequestFactory with reflection
  • Update version constraint in biz.aQute.tester.junit-platform/bnd.bnd to allow JUnit Platform 1.13+
  • Address all code review feedback
  • Fix test setup to include ExecutionRequestFactory class
  • Update ExecutionRequestFactory to handle JUnit Platform 1.13.4 constructor signature changes
  • Update to JUnit Jupiter 5.13.4 / Platform 1.13.4
  • Fix test classes for JUnit 5.13+ @BeforeAll static requirement

Changes Made

  1. BundleEngine.java - Pass ExecutionRequest through to child execution methods
  2. BundleDescriptor.java - Use ExecutionRequestFactory to create child requests with proper context
  3. ExecutionRequestFactory.java - Factory with reflection-based context propagation
    • Detects both 4-param (pre-1.13) and 5-param (1.13+) constructor signatures
    • Handles OutputDirectoryProvider parameter added in 1.13+
    • Properly propagates NamespacedHierarchicalStore to child engines
  4. bnd.bnd - Updated Import-Package to allow JUnit Platform 1.13+
  5. BundleEngineTest.java - Updated test setup to include ExecutionRequestFactory class
  6. cnf/ext/junit.bnd - Updated to junit.jupiter.version=5.13.4 and junit.platform.version=1.13.4
  7. JUnit5ContainerFailure.java & JUnit5ContainerError.java - Made @BeforeAll methods static for JUnit 5.13+ compatibility

Technical Details - JUnit Platform 1.13 Changes

In JUnit Platform 1.13+, the ExecutionRequest private constructor signature changed:

  • Pre-1.13: ExecutionRequest(TestDescriptor, EngineExecutionListener, ConfigurationParameters, NamespacedHierarchicalStore)
  • 1.13+: ExecutionRequest(TestDescriptor, EngineExecutionListener, ConfigurationParameters, OutputDirectoryProvider, NamespacedHierarchicalStore)

The ExecutionRequestFactory now:

  1. Detects which constructor is available at class load time (4-param or 5-param)
  2. At runtime, checks the parameter count to determine which constructor to use
  3. For 5-param constructor (1.13+), retrieves the OutputDirectoryProvider from the parent ExecutionRequest using getOutputDirectoryProvider()
  4. Properly propagates both the OutputDirectoryProvider and NamespacedHierarchicalStore to child engines

Additionally, JUnit 5.13+ enforces that @BeforeAll methods must be static (unless using @TestInstance(Lifecycle.PER_CLASS)). Updated test classes to comply with this requirement.

Current Status

Build: ✅ Succeeds with JUnit Platform 1.13.4
ExecutionRequestFactory Logic: ✅ Correctly handles both pre-1.13 and 1.13+ constructor signatures
Version Updated: ✅ JUnit Jupiter 5.13.4 / Platform 1.13.4
Test Compatibility: ✅ Test classes updated for JUnit 5.13+ validation rules

The implementation properly handles the ExecutionRequest context propagation for JUnit Platform 1.13+, addressing the "No NamespacedHierarchicalStore" error by using reflection to access internal APIs and propagate the execution context store to child test engines.

Original prompt

Please create a new PR based on #6999 to addresse the remaining Full JUnit Platform 1.13+ support and specifically the "No NamespacedHierarchicalStore" error in the last comment.

The "No NamespacedHierarchicalStore" error reveals that JUnit Platform 1.13+ has additional breaking changes beyond the deprecated API. The issue is in how nested test engines execute - the BundleEngine creates ExecutionRequests for child engines, but 1.13+ requires a different approach for propagating execution context.

This is a separate architectural issue beyond fixing the deprecated getMethodParameterTypes() API.

Full JUnit Platform 1.13+ support requires investigating and redesigning how the BundleEngine handles nested test engine execution contexts, which should be addressed in a separate issue.

@chrisrueger chrisrueger changed the title Copilot/fix no namespaced hierarchical store Fix Junit Platform 1.13+ Support Dec 14, 2025
@chrisrueger chrisrueger marked this pull request as ready for review December 14, 2025 18:55
@chrisrueger
Copy link
Contributor Author

Hi @kriegfrj , could you have a look at this PR if this contains something useful for #6651 ? I have tried using Copilot for it as a learning experience for me, since this whole interaction between bnd and JUnit is unfamiliar to me.

And it came up with this + two minor commits by me. (A first attempt in https://github.com/bndtools/bnd/pull/6999/changes led to a dead end I guess)

So let me know what you think, and if it at least contains some useful clues.

@kriegfrj
Copy link
Contributor

Hi @kriegfrj , could you have a look at this PR if this contains something useful for #6651 ? I have tried using Copilot for it as a learning experience for me, since this whole interaction between bnd and JUnit is unfamiliar to me.

And it came up with this + two minor commits by me. (A first attempt in https://github.com/bndtools/bnd/pull/6999/changes led to a dead end I guess)

So let me know what you think, and if it at least contains some useful clues.

Thanks for trying, @chrisrueger - I'll see if I can have a look tomorrow.

@chrisrueger chrisrueger force-pushed the copilot/fix-no-namespaced-hierarchical-store branch from babd508 to f170708 Compare December 28, 2025 21:49
@chrisrueger chrisrueger changed the title Fix Junit Platform 1.13+ Support Fix Junit Platform 1.13+ Support (up to JUnit Jupiter and Platform to 5.14.1/1.14.1) Dec 28, 2025
@chrisrueger
Copy link
Contributor Author

chrisrueger commented Dec 28, 2025

@kriegfrj I did some additional work to support up to JUnit Jupiter and Platform to 5.14.1/1.14.1 (which is the current latest version from that 5.x / 1.x range and the version Eclipse uses eclipse-platform/eclipse.platform.releng.aggregator#3579 (comment))

The recent commit f170708 changes Copilots suggestion from using the ExecutionRequest constructor to using the static create() factory methods, which I hope is a little bit more future proof than the constructor.

https://github.com/junit-team/junit-framework/blob/4133d24bc557d676f94806ac669c35e0ae8b6fd0/junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java#L105

Also interesting:

@kriegfrj
Copy link
Contributor

Sorry @chrisrueger, Christmas is a very busy time for me. I'll try and look when I can. Thank you so much for being willing to have a look at this, it looks promising! The static create() method didn't exist when I first wrote this, I think.

@chrisrueger chrisrueger force-pushed the copilot/fix-no-namespaced-hierarchical-store branch from 20de661 to c89ceb8 Compare December 29, 2025 12:04
@chrisrueger
Copy link
Contributor Author

Thanks @kriegfrj based on the discussion in #7024 (comment) I think this fix would be really appreciated by all downstream consumers like Eclipse / AssertJ who currently have trouble with the narrow version range (1.13) introduced in 7.2.0 (because they are on >= 1.13 already.

Since my debugging session yesterday I now have a better understanding how this Junit Tester works and the ExecutionRequestFactory introduced by Copilot made it possible for me to have a good location to set a debugger breakpoint and run the tests and see whats going on. At least the reflection part I now understand.

@chrisrueger chrisrueger force-pushed the copilot/fix-no-namespaced-hierarchical-store branch from c89ceb8 to 7c6ad38 Compare December 29, 2025 20:16
@kriegfrj
Copy link
Contributor

Thanks @kriegfrj based on the discussion in #7024 (comment) I think this fix would be really appreciated by all downstream consumers like Eclipse / AssertJ who currently have trouble with the narrow version range (1.13) introduced in 7.2.0 (because they are on >= 1.13 already.

Since my debugging session yesterday I now have a better understanding how this Junit Tester works and the ExecutionRequestFactory introduced by Copilot made it possible for me to have a good location to set a debugger breakpoint and run the tests and see whats going on. At least the reflection part I now understand.

Just to fill you in a little on the background of why the tester is written the way that it is written: It mostly came down to how the class/method resolvers would work. The built-in JUnit platform resolvers for method and class selectors would always try and use the system classloader to resolve them. This obviously caused issues. Initially I thought it would be sufficient to resolve the classes by loading them from the corrent bundle classloader, and then hand off the resolved classes to the JUnit Platform resolving/execution infrastructure for method resolution. However then I discovered that we had to do more due to the difference between class initialisation and class loading: the classloader is lazy and won't load all of the dependency classes (eg, method parameters) until you use the class for the first time ("initialisation"). So it was not sufficient to merely load the class with the correct classloader, but you also had to ensure that it got initialised from within the correct classloader context too or else the dependencies would fail to load. So I had to fully resolve all of the method selectors as well as the class selectors. The SubExecutionRequest was the bridge between the OSGi/classloader-aware BundleEngine, and the non-classloader-aware JUnit Platorm and the test engines.

I raised a number of bugs/feature requests with the JUnit team as a result of this work so that the built-in resolvers could be made more class aware (one example: the method resolver will now use the class's classloader rather than the system classloader to resolve the method parameters). So it is possible that a lot of the workarounds I had to put in place could be bypassed now.

I'll try and have a look at your implementation in the coming days.

…1/1.14.1)

- Modified BundleEngine.executeBundle() to accept and pass ExecutionRequest parameter
- Updated BundleDescriptor.executeChild() to accept ExecutionRequest instead of separate listener and params

Add ExecutionRequestFactory to propagate execution context for JUnit Platform 1.13+

Implemented ExecutionRequestFactory that uses reflection to properly propagate the internal NamespacedHierarchicalStore from parent ExecutionRequest to child requests. This is necessary for JUnit Platform 1.13+ compatibility.

The factory:
- Detects if reflection is available and needed
- Uses internal Store API if available (JUnit Platform 1.13+)
- Falls back to public constructor for older versions
- Handles failures gracefully

Updated BundleDescriptor to use the factory instead of directly creating ExecutionRequest.

- Add detailed comment explaining why internal JUnit Platform API is accessed

Updated BundleEngineTest to include ExecutionRequestFactory class in the engine bundle for tests. This class was added in the JUnit Platform 1.13+ compatibility changes but wasn't included in the test bundle setup.

All tests now pass successfully.

Update JUnit Jupiter/Platform versions to 5.11.4/1.11.4

Updated junit.jupiter.version and junit.platform.version to 5.11.4/1.11.4 to test with newer versions. These versions are compatible and all 136 tests pass.

Fix JUnit5 test classes to use static @BeforeAll methods for 5.13+ compatibility

Made @BeforeAll methods static in JUnit5ContainerFailure and JUnit5ContainerError test classes.

In JUnit 5.13+, non-static @BeforeAll methods are now detected as discovery errors (causing DiscoveryIssueException) rather than execution failures. This was causing the exitCode_countsJupiterContainerErrorsAndFailures() test to fail - expecting exit code 2 (two container failures) but getting exit code 1 (discovery error).

Making the @BeforeAll methods static ensures they properly execute and fail as intended by the test, maintaining compatibility with JUnit Platform 1.13+.

fix ConfigVersionsTest

replace deprecated method getParameterTypeNames

Replaces calls to getMethodParameterTypes with getParameterTypeNames when creating and resolving MethodSelectors. This aligns with updated API usage and ensures correct parameter type handling.

Refactor ExecutionRequest to use static create() methods

Replaces use of internal constructors with reflection-based invocation of static create() methods for ExecutionRequest, improving compatibility with JUnit Platform 1.13+ and future versions. This change also updates the logic to detect and use the appropriate static method signatures, and removes reliance on direct constructor access.

Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>

Update JUnit Jupiter and Platform to 5.14.1/1.14.1

Bump JUnit Jupiter and Platform versions to 5.14.1 and 1.14.1 in cnf/ext/junit.bnd, Gradle, and Maven plugin parent POM to keep dependencies up to date and consistent across the project.

This is the latest version in the 5.x / 1.x range and also the version used by Eclipse

Signed-off-by: Christoph Rueger <chrisrueger@gmail.com>
Co-Authored-By: chrisrueger <188422+chrisrueger@users.noreply.github.com>
@chrisrueger chrisrueger force-pushed the copilot/fix-no-namespaced-hierarchical-store branch from 7c6ad38 to 3f562ba Compare January 5, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[junit-platform] Tester incompatible with JUnit Platform 1.13.x

2 participants