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

Add factory SPI for @TempDir #2958

Merged
merged 18 commits into from
Apr 26, 2023
Merged

Conversation

scordio
Copy link
Contributor

@scordio scordio commented Jul 2, 2022

TL;DR

This could solve:

Overview

This adds a factory SPI to @TempDir, allowing to define how the temporary directory is created.

My initial target was to support a custom file system (#2400), but it may also be a solution for the issues mentioned above.

The intended usage is to extend TempDirFactory and provide the class to the new factory attribute of @TempDir:

...
import java.nio.file.Files;
...

class CustomFactory implements TempDirFactory {

    @Override
    public Path createTempDirectory(ExtensionContext context) throws IOException {
        return Files.createTempDirectory("junit");
    }

}
@Test
void test(@TempDir(factory = CustomFactory.class) tempDir) {
    ...
}

Example with Custom Parent Directory

...
import java.nio.file.Files;
...

class CustomParentFactory implements TempDirFactory {

    @Override
    public Path createTempDirectory(ExtensionContext context) throws IOException {
        return Files.createTempDirectory(Paths.get("/tmp-dir-root"), "junit");
    }

}

Example with Custom File System

Using marschall/memoryfilesystem:

...
import java.nio.file.Files;
...

class MemoryFileSystemFactory implements TempDirFactory {

    private FileSystem fileSystem;

    @Override
    public Path createTempDirectory(ExtensionContext context) throws IOException {
        fileSystem = MemoryFileSystemBuilder.newEmpty().build();
        return Files.createTempDirectory(fileSystem.getPath("/"), "junit");
    }

    @Override
    public void close() throws IOException {
        fileSystem.close();
    }
}

Using google/jimfs:

...
import java.nio.file.Files;
...

class JimfsFactory implements TempDirFactory {

    private FileSystem fileSystem;

    @Override
    public Path createTempDirectory(ExtensionContext context) throws IOException {
        FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
        return Files.createTempDirectory(fileSystem.getPath("/"), "junit");
    }

    @Override
    public void close() throws IOException {
        fileSystem.close();
    }
}

I would be happy to receive any feedback on the direction I have taken.

If this feature gets accepted, I would like to add a global configuration parameter like junit.jupiter.tempdir.factory.default in a separate PR.

Open points

  • Behavior in case of junit.jupiter.tempdir.scope=PER_CONTEXT: the extension fails if a custom factory is set
  • Both in-memory file system libraries currently fail due to an unsupported Path::toFile call:
java.lang.UnsupportedOperationException: memory file system does not support #toFile()
	at com.github.marschall.memoryfilesystem.AbstractPath.toFile(AbstractPath.java:74)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.resetPermissions(TempDirectory.java:353)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.deleteAllFilesAndDirectories(TempDirectory.java:282)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.close(TempDirectory.java:268)
java.lang.UnsupportedOperationException
	at com.google.common.jimfs.JimfsPath.toFile(JimfsPath.java:387)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.resetPermissions(TempDirectory.java:353)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.deleteAllFilesAndDirectories(TempDirectory.java:282)
	at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.close(TempDirectory.java:268)

To solve this, I catched the UnsupportedOperationException in TempDirectory.CloseablePath::resetPermissions and ignored it (b284505). In support of this, the Path::toFile javadoc mentions that the exception can be thrown if the Path is not associated with the default file system, which can be a potential use case once this SPI is offered.


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


Definition of Done

@scordio scordio force-pushed the 2400_temp_dir_factory branch 2 times, most recently from 6fe1a40 to 9b5da1b Compare July 22, 2022 20:36
@scordio scordio marked this pull request as draft August 22, 2022 20:21
@lacasseio
Copy link

lacasseio commented Sep 10, 2022

I like what you did with this PR. I wonder if passing prefix is necessary given that all new examples ignore it and the standard implementation always uses the same prefix.

I also see this PR as a good way to customize the behaviour of the temporary directory, such as including a space. We have a custom test directory provider that namespace the directory per-class/method, i.e. test-files/Foo/myTestMethod, and the idea in this PR could help with that. However, we would need access to some information from the ExecutionContext, mainly the test method and/or test class. I won't say to include this feature in this PR, but I think it adds additional use cases to the TempDirFactory design. I don't think passing the ExecutionContext instead of the prefix is wise, but maybe something like TempDirContext could double for the prefix (if required). What do you think?

Finally, I think having a way to pass a global TempDirFactory implementation via system property would be useful. Users could still override the factory per @TempDir declaration. Maybe something like junit.jupiter.tempdir.factory=com.example.MyTempDirFactory.

I hope this PR gets a bit more traction because it provides the last bit of customization that TempDir needs.

@scordio
Copy link
Contributor Author

scordio commented Sep 10, 2022

I like what you did with this PR.

@lacasseio thanks for the feedback! I have to admit I've put this topic a bit aside lately, there are still some points to clarify and test coverage to improve... I'll get it finalized sooner or later 🙂

I wonder if passing prefix is necessary given that all new examples ignore it and the standard implementation always uses the same prefix.

Indeed, the prefix doesn't seem so useful right now but I'd let the JUnit team judge what to do there.

I also see this PR as a good way to customize the behaviour of the temporary directory, such as including a space. We have a custom test directory provider that namespace the directory per-class/method, i.e. test-files/Foo/myTestMethod, and the idea in this PR could help with that. However, we would need access to some information from the ExecutionContext, mainly the test method and/or test class. I won't say to include this feature in this PR, but I think it adds additional use cases to the TempDirFactory design. I don't think passing the ExecutionContext instead of the prefix is wise, but maybe something like TempDirContext could double for the prefix (if required). What do you think?

I agree TempDirFactory could have more input/control but I didn't feel the silver bullet yet, so I preferred starting with the smallest scope possible and for sure it could be extended with concrete use cases like yours. Especially here, I'd wait for some feedback from the core team before introducing something more complex.

Finally, I think having a way to pass a global TempDirFactory implementation via system property would be useful. Users could still override the factory per @TempDir declaration. Maybe something like junit.jupiter.tempdir.factory=com.example.MyTempDirFactory.

That was exactly my idea, I've mentioned it in the middle of the description:

If this feature gets accepted, I would like to add a global configuration parameter like junit.jupiter.tempdir.factory in a separate PR.

@stale
Copy link

stale bot commented Dec 13, 2022

This pull request has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be closed if no further activity occurs. If you intend to work on this pull request, please reopen the PR. Thank you for your contributions.

@lacasseio
Copy link

Looking at implementing something similar as part of my current "TempDir" extension, I think it would be important to ensure Factory can be AutoCloseable as, for example, Jimfs are AutoCloseable which signals that the file system should be closed once created (potentially after a test).

@scordio
Copy link
Contributor Author

scordio commented Mar 2, 2023

Thanks for your feedback, @lacasseio! I need to refresh this topic and will check how to apply what you mentioned, as what the factory produces is already wrapped in a CloseablePath.

@lacasseio
Copy link

Great, I just noticed the example didn't close the FileSystem object and though about mentioning it for completeness. You are right that it won't change the current architecture.

@marcphilipp
Copy link
Member

Team Decision: Check whether TempDirFactory implements AutoCloseable and, if so, close it in CloseablePath after deleting the temp dir.

@olamy
Copy link

olamy commented Mar 8, 2023

very good idea! This would be fantastic to have this available soon.
A global configuration would be good as well such junit.jupiter.execution.tempdir.factory or any similar name.

@scordio scordio force-pushed the 2400_temp_dir_factory branch 2 times, most recently from 9223b97 to 40a297b Compare April 11, 2023 16:58
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! Could you please add an integration test using jimfs or memoryfilesystem by introducing an additional test dependency?

@marcphilipp
Copy link
Member

@scordio This PR is a great next step but I don't think it solves #1786, does it?

@scordio
Copy link
Contributor Author

scordio commented Apr 24, 2023

Thanks, @marcphilipp for all your feedback. I think only documentation and changelog are left, I plan to work on them in the evening and I'll also raise a separate PR for the global configuration property.

@scordio
Copy link
Contributor Author

scordio commented Apr 24, 2023

@scordio This PR is a great next step but I don't think it solves #1786, does it?

You're right, I guess I had something else in mind when I mentioned it. I take it out from the description.

@scordio
Copy link
Contributor Author

scordio commented Apr 25, 2023

@marcphilipp I think I'm done with my changes, all the open points should be covered. Please, let me know if there is anything to adjust.

@scordio
Copy link
Contributor Author

scordio commented Apr 25, 2023

If this feature gets accepted, I would like to add a global configuration parameter like junit.jupiter.tempdir.factory.default in a separate PR.

Initial draft at scordio/junit5@2400_temp_dir_factory...scordio:junit5:gh-2400_temp_dir_factory_config_property.

I'll raise a PR once this one is merged.

documentation/documentation.gradle.kts Outdated Show resolved Hide resolved
@marcphilipp marcphilipp merged commit 3781a67 into junit-team:main Apr 26, 2023
@marcphilipp
Copy link
Member

@scordio Thanks a lot for the productive collaboration on this one! 🎉

@scordio scordio deleted the 2400_temp_dir_factory branch April 26, 2023 07:49
@sormuras sormuras removed their request for review April 26, 2023 08:04
@marcphilipp marcphilipp removed this from the 5.10.0-M1 milestone Apr 28, 2023
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.

Support custom file system for @TempDir Provide support for setting a custom parent directory for @TempDir
5 participants