Skip to content

Commit

Permalink
Use separate local Maven repos when file system type is NTFS (#4259)
Browse files Browse the repository at this point in the history
Prior to this commit, Maven builds triggered from integration tests 
sometimes failed because another concurrent build was writing to the 
same file in the temporary local Maven repo.
  • Loading branch information
marcphilipp authored Jan 23, 2025
1 parent 544c3b2 commit 3583e01
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
*/
class JavaVersionsTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@TempDir
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,47 @@

package platform.tooling.support.tests;

import static platform.tooling.support.tests.ManagedResource.Scope.GLOBAL;
import static platform.tooling.support.tests.ManagedResource.Scope.PER_CONTEXT;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;

import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

@ManagedResource.Scoped(LocalMavenRepo.ScopeProvider.class)
public class LocalMavenRepo implements AutoCloseable {

public static class ScopeProvider implements ManagedResource.Scoped.Provider {

private static final Namespace NAMESPACE = Namespace.create(LocalMavenRepo.class);

@Override
public ManagedResource.Scope determineScope(ExtensionContext extensionContext) {
var store = extensionContext.getRoot().getStore(NAMESPACE);
var fileSystemType = store.getOrComputeIfAbsent("tempFileSystemType", key -> {
var type = getFileSystemType(Path.of(System.getProperty("java.io.tmpdir")));
extensionContext.getRoot().publishReportEntry("tempFileSystemType", type);
return type;
}, String.class);
// Writing to the same file from multiple Maven processes may fail the build on Windows
return "NTFS".equalsIgnoreCase(fileSystemType) ? PER_CONTEXT : GLOBAL;
}

private static String getFileSystemType(Path path) {
try {
return Files.getFileStore(path).type();
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

private final Path tempDir;

public LocalMavenRepo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,30 @@

@Target({ ElementType.PARAMETER, ElementType.FIELD })
@Retention(RUNTIME)
@ExtendWith(GlobalResource.Extension.class)
public @interface GlobalResource {
@ExtendWith(ManagedResource.Extension.class)
public @interface ManagedResource {

@Target(ElementType.TYPE)
@Retention(RUNTIME)
@interface Scoped {

Class<? extends Provider> value();

interface Provider {
Scope determineScope(ExtensionContext extensionContext);
}
}

enum Scope {
GLOBAL, PER_CONTEXT
}

class Extension implements ParameterResolver, TestInstancePostProcessor {

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return parameterContext.isAnnotated(GlobalResource.class);
return parameterContext.isAnnotated(ManagedResource.class);
}

@Override
Expand All @@ -50,9 +65,14 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
return getOrCreateResource(extensionContext, type).get();
}

@Override
public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {
return ExtensionContextScope.TEST_METHOD;
}

@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext) {
streamFields(testInstance.getClass(), field -> AnnotationSupport.isAnnotated(field, GlobalResource.class),
streamFields(testInstance.getClass(), field -> AnnotationSupport.isAnnotated(field, ManagedResource.class),
HierarchyTraversalMode.BOTTOM_UP) //
.forEach(field -> {
try {
Expand All @@ -66,7 +86,16 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext extens

@SuppressWarnings("unchecked")
private <T> Resource<T> getOrCreateResource(ExtensionContext extensionContext, Class<T> type) {
return extensionContext.getRoot().getStore(Namespace.GLOBAL) //
var scope = AnnotationSupport.findAnnotation(type, Scoped.class) //
.map(Scoped::value) //
.map(ReflectionSupport::newInstance) //
.map(provider -> provider.determineScope(extensionContext)) //
.orElse(Scope.GLOBAL);
var storingContext = switch (scope) {
case GLOBAL -> extensionContext.getRoot();
case PER_CONTEXT -> extensionContext;
};
return storingContext.getStore(Namespace.GLOBAL) //
.getOrComputeIfAbsent(type, Resource::new, Resource.class);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
*/
class MavenStarterTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@GlobalResource
@ManagedResource
MavenRepoProxy mavenRepoProxy;

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
*/
class MavenSurefireCompatibilityTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
*/
class MultiReleaseJarTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@GlobalResource
@ManagedResource
MavenRepoProxy mavenRepoProxy;

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
*/
class UnalignedClasspathTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@GlobalResource
@ManagedResource
MavenRepoProxy mavenRepoProxy;

@ParameterizedTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

class VintageMavenIntegrationTests {

@GlobalResource
@ManagedResource
LocalMavenRepo localMavenRepo;

@TempDir
Expand Down

0 comments on commit 3583e01

Please sign in to comment.