Skip to content

Introduce ConversionService in junit-platform-commons #4219

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

scordio
Copy link
Contributor

@scordio scordio commented Dec 23, 2024

Overview


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

@scordio
Copy link
Contributor Author

scordio commented Dec 23, 2024

There is plenty of work to do 🙃

The current highlights:

Any feedback would be highly appreciated!

@scordio scordio force-pushed the conversion-service branch 3 times, most recently from 3304ad5 to c886b7a Compare December 31, 2024 11:26
@marcphilipp
Copy link
Member

Thanks for the draft! 👍

The tests are failing due to:

org.junit.platform.commons.support.conversion.ConversionService: module org.junit.platform.commons does not declare uses

That's because junit-platform-commons/src/module/org.junit.platform.commons/module-info.java is missing

uses org.junit.platform.commons.support.conversion.ConversionService;

Copy link
Member

@marcphilipp marcphilipp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very promising! 👍


import org.junit.platform.commons.support.conversion.TypedConversionService;

// FIXME delete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make a good test case, though. We have existing tests that register services for tests using an extra class loader:

private static void withTestServices(Runnable runnable) {
var current = Thread.currentThread().getContextClassLoader();
var url = LauncherFactoryTests.class.getClassLoader().getResource("testservices/");
try (var classLoader = new URLClassLoader(new URL[] { url }, current)) {
Thread.currentThread().setContextClassLoader(classLoader);
runnable.run();
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
finally {
Thread.currentThread().setContextClassLoader(current);
}
}

We could generalize and move that method to a test utility class (e.g. in junit-jupiter-api/src/testFixtures) so it can be reused here.

@marcphilipp
Copy link
Member

That's because junit-platform-commons/src/module/org.junit.platform.commons/module-info.java is missing

uses org.junit.platform.commons.support.conversion.ConversionService;

When you add that, you'll also have to add it to platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt to adjust the integration test.

@scordio scordio force-pushed the conversion-service branch 2 times, most recently from 2e17c2b to 0c2faa7 Compare January 2, 2025 16:35
@scordio
Copy link
Contributor Author

scordio commented Jan 15, 2025

I've been lagging behind with this one but I should be able to spend time on it in the upcoming weekend.

@scordio
Copy link
Contributor Author

scordio commented Apr 21, 2025

Would you like to include this in 5.13 too? I should have enough time in the upcoming days to finalize it.

@scordio scordio force-pushed the conversion-service branch 3 times, most recently from 0a36152 to e380b8f Compare April 27, 2025 11:33
@scordio scordio force-pushed the conversion-service branch from e380b8f to 45effd9 Compare April 28, 2025 08:18
@marcphilipp
Copy link
Member

@scordio Sorry for the late reply! I was out for a few days. If you still have time to finish it in the next two weeks, we could still include it in 5.13.0-RC1. WDYT?

@scordio
Copy link
Contributor Author

scordio commented Apr 28, 2025

Sounds good!

@sbrannen
Copy link
Member

sbrannen commented May 5, 2025

Sorry, for chiming in so late in the game, but...

Instead of ConversionService, I would prefer that we call this new public SPI something more like Converter.

And I'm also wondering if it should be parameterized.

FYI: I'm taking inspiration from the conversion APIs in the Spring Framework, which have been evolved over the years.

For example, I see our Converter as basically a combination of the Converter and ConditionalConverter APIs from Spring.

Thoughts?

@marcphilipp
Copy link
Member

marcphilipp commented May 5, 2025

I'd be okay with Converter instead of ConversionService as well.

I'm not sure about parameterizing it. Where we use it (for example, to dynamically resolve method parameters), we won't be able to benefit from the type arguments. It might be a bit nicer for ConversionService/Converter authors to implement but we could also provide a convenience base class similar to TypedArgumentConverter.

One thing we should consider is using a parameter object (like Spring's TypeDescriptor) instead of the raw Class. This could, for example, be used to later add support for nested classes with runtime enclosing types. Thoughts?

@sbrannen
Copy link
Member

sbrannen commented May 5, 2025

I'd be okay with Converter instead of ConversionService as well.

Then let's change it to Converter. 👍

I'm not sure about parameterizing it. Where we use it (for example, to dynamically resolve method parameters), we won't be able to benefit from the type arguments. It might be a bit nicer for ConversionService/Converter authors to implement but we could also provide a convenience base class similar to TypedArgumentConverter.

Although we (the framework itself) might not benefit from generics at the moment, I would rather introduce them now than later discover that we actually need them ourselves to support additional (more dynamic) use cases.

In other words, what downside would there be to supporting generics from the start?

One thing we should consider is using a parameter object (like Spring's TypeDescriptor) instead of the raw Class. This could, for example, be used to later add support for nested classes with runtime enclosing types. Thoughts?

Yes, as you know... I am a big fan of "parameter context objects" in APIs that need to stand the test of time.

As I mentioned before, the conversion subsystem in Spring Framework has evolved organically over time, and most modern implementations actually implement for GenericConverter and ConditionalConverter, which both deal with TypeDescriptor instead of Class (except for the direct Class usage in ConvertiblePair).

In fact, there's even a dedicated ConditionalGenericConverter interface which combines those two, which I implemented in the latest converter that I implemented: OptionalToObjectConverter.

I am of course not suggesting that we introduce mechanisms as complex as those in Spring; however, I do hope that we can design in the API in such a manner that we don't prevent ourselves from being able to evolve the APIs and feature set over time as necessary.

And... supporting generics and something like a TypeDescriptor parameter object would be a good step in that direction.

@scordio

This comment was marked as outdated.

@scordio
Copy link
Contributor Author

scordio commented May 5, 2025

I've hidden my last comment as it's mostly outdated after @sbrannen's one, and

supporting generics and something like a TypeDescriptor parameter object would be a good step in that direction.

If you'd like to go this way, I'm happy to adjust my work in this direction!

@marcphilipp
Copy link
Member

I agree with @sbrannen. Let's go with the generic version and the parameter object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce generic ConversionService SPI
3 participants