diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java
index 81bb8bd75..f8c0ab79d 100644
--- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java
+++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/OutputContainerGroup.java
@@ -26,6 +26,10 @@
*
These can behave as if they are module-oriented, or non-module-oriented.
* It is down to the implementation to mediate access between modules and their files.
*
+ *
Operations on modules should first {@link #getModule(String) get} or
+ * {@link #getOrCreateModule(String) create} the module, and then operate on that sub-container
+ * group. Operations on non-module packages should operate on this container group directly.
+ *
* @author Ashley Scopes
* @since 0.0.1
*/
diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupRepositoryImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupRepositoryImpl.java
index 8ebef892b..1ca23b9df 100644
--- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupRepositoryImpl.java
+++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/ContainerGroupRepositoryImpl.java
@@ -44,7 +44,7 @@
*/
@API(since = "0.0.1", status = Status.STABLE)
@ThreadSafe
-public class ContainerGroupRepositoryImpl {
+public final class ContainerGroupRepositoryImpl implements AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(ContainerGroupRepositoryImpl.class);
@@ -88,14 +88,10 @@ public void addPath(Location location, PathRoot pathRoot) {
}
}
- /**
- * A bulk-style call for {@link #addPath(Location, PathRoot)}.
- *
- * @param location the location to add.
- * @param pathRoots the path roots to register with the location.
- */
- public void addPaths(Location location, Iterable extends PathRoot> pathRoots) {
- pathRoots.forEach(pathRoot -> addPath(location, pathRoot));
+ @Override
+ public void close() {
+ // Nothing to do here. This is a placeholder in case we ever need to allow closing logic
+ // in the future.
}
/**
@@ -158,6 +154,14 @@ public void createEmptyLocation(Location location) {
}
}
+ /**
+ * Perform any flushing operation, if needed.
+ */
+ public void flush() {
+ // Nothing to do here. This is a placeholder for a future implementation if we ever need to
+ // enable flushing.
+ }
+
/**
* Get a container group.
*
@@ -421,5 +425,4 @@ private OutputContainerGroup getOrCreateOutputContainerGroup(Location location)
outputLocation -> new OutputContainerGroupImpl(outputLocation, release)
);
}
-
}
diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java
index 4e2f6711c..9165d4739 100644
--- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java
+++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/containers/impl/OutputContainerGroupImpl.java
@@ -29,12 +29,10 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import javax.annotation.Nullable;
import javax.annotation.WillCloseWhenClosed;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.ThreadSafe;
import javax.tools.JavaFileManager.Location;
-import javax.tools.JavaFileObject.Kind;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
@@ -44,6 +42,10 @@
*
These can contain packages and modules of packages together, and thus
* are slightly more complicated internally as a result.
*
+ *
Operations on modules should first {@link #getModule(String) get} or
+ * {@link #getOrCreateModule(String) create} the module, and then operate on that sub-container
+ * group. Operations on non-module packages should operate on this container group directly.
+ *
* @author Ashley Scopes
* @since 0.0.1
*/
@@ -120,84 +122,6 @@ public PackageContainerGroup getModule(String module) {
.orElse(null);
}
- @Override
- @Nullable
- public PathFileObject getFileForInput(String packageName, String relativeName) {
- var moduleName = extractModuleName(packageName);
-
- if (moduleName != null) {
- var module = getModule(moduleName);
-
- if (module != null) {
- var realPackageName = extractPackageName(packageName);
- var file = module.getFileForInput(realPackageName, relativeName);
-
- if (file != null) {
- return file;
- }
- }
- }
-
- return super.getFileForInput(packageName, relativeName);
- }
-
- @Override
- @Nullable
- public PathFileObject getFileForOutput(String packageName, String relativeName) {
- var moduleName = extractModuleName(packageName);
-
- if (moduleName != null) {
- var module = getOrCreateModule(moduleName);
- var realPackageName = extractPackageName(packageName);
- var file = module.getFileForOutput(realPackageName, relativeName);
-
- if (file != null) {
- return file;
- }
- }
-
- return super.getFileForOutput(packageName, relativeName);
- }
-
- @Override
- @Nullable
- public PathFileObject getJavaFileForInput(String className, Kind kind) {
- var moduleName = extractModuleName(className);
-
- if (moduleName != null) {
- var module = getModule(moduleName);
-
- if (module != null) {
- var realClassName = extractPackageName(className);
- var file = module.getJavaFileForInput(realClassName, kind);
-
- if (file != null) {
- return file;
- }
- }
- }
-
- return super.getJavaFileForInput(className, kind);
- }
-
- @Override
- @Nullable
- public PathFileObject getJavaFileForOutput(String className, Kind kind) {
- var moduleName = extractModuleName(className);
-
- if (moduleName != null) {
- var module = getOrCreateModule(moduleName);
- var realClassName = extractPackageName(className);
- var file = module.getJavaFileForOutput(realClassName, kind);
-
- if (file != null) {
- return file;
- }
- }
-
- return super.getJavaFileForOutput(className, kind);
- }
-
@Override
public Set getLocationsForModules() {
return Set.copyOf(modules.keySet());
@@ -238,19 +162,4 @@ private PackageContainerGroup newPackageGroup(ModuleLocation moduleLocation) {
group.addPackage(pathWrapper);
return group;
}
-
- @Nullable
- private static String extractModuleName(String binaryName) {
- // extractModuleName("foo.bar.Baz") -> null
- // extractModuleName("some.module/foo.bar.Baz") -> "some.module"
- var separatorIndex = binaryName.indexOf('/');
- return separatorIndex == -1 ? null : binaryName.substring(0, separatorIndex);
- }
-
- private static String extractPackageName(String binaryName) {
- // extractPackageName("foo.bar.Baz") -> "foo.bar.Baz"
- // extractPackageName("some.module/foo.bar.Baz") -> "foo.bar.Baz"
- var separatorIndex = binaryName.indexOf('/');
- return separatorIndex == -1 ? binaryName : binaryName.substring(separatorIndex + 1);
- }
}
diff --git a/java-compiler-testing/src/main/java/io/github/ascopes/jct/filemanagers/impl/JctFileManagerImpl.java b/java-compiler-testing/src/main/java/io/github/ascopes/jct/filemanagers/impl/JctFileManagerImpl.java
index 8e3adf281..3e59d1164 100644
--- a/java-compiler-testing/src/main/java/io/github/ascopes/jct/filemanagers/impl/JctFileManagerImpl.java
+++ b/java-compiler-testing/src/main/java/io/github/ascopes/jct/filemanagers/impl/JctFileManagerImpl.java
@@ -69,16 +69,16 @@ public void addPath(Location location, PathRoot pathRoot) {
@Override
public void addPaths(Location location, Collection extends PathRoot> pathRoots) {
- repository.addPaths(location, pathRoots);
+ pathRoots.forEach(pathRoot -> addPath(location, pathRoot));
}
@Override
public void close() {
- // Nothing to close here.
+ repository.close();
}
@Override
- public boolean contains(Location location, FileObject fo) throws IOException {
+ public boolean contains(Location location, FileObject fo) {
if (!(fo instanceof PathFileObject)) {
return false;
}
@@ -99,7 +99,7 @@ public void createEmptyLocation(Location location) {
@Override
public void flush() {
- // Don't do anything else for now.
+ repository.flush();
}
@Nullable
@@ -144,25 +144,23 @@ public FileObject getFileForOutput(
) {
requireOutputLocation(location);
- // If we have a module, we may need to create a brand new location for it.
+ PackageContainerGroup group = null;
+
+ // If we have a module, we may need to create a brand-new location for it.
if (location instanceof ModuleLocation) {
var moduleLocation = ((ModuleLocation) location);
- var group = repository.getOutputContainerGroup(moduleLocation.getParent());
+ var parentGroup = repository.getOutputContainerGroup(moduleLocation.getParent());
- if (group != null) {
- return group
- .getOrCreateModule(moduleLocation.getModuleName())
- .getFileForOutput(packageName, relativeName);
+ if (parentGroup != null) {
+ group = parentGroup.getOrCreateModule(moduleLocation.getModuleName());
}
} else {
- var group = repository.getOutputContainerGroup(location);
-
- if (group != null) {
- return group.getFileForOutput(packageName, relativeName);
- }
+ group = repository.getOutputContainerGroup(location);
}
- return null;
+ return group == null
+ ? null
+ : group.getFileForOutput(packageName, relativeName);
}
@Nullable
@@ -189,37 +187,37 @@ public JavaFileObject getJavaFileForOutput(
) {
requireOutputLocation(location);
- // If we have a module, we may need to create a brand new location for it.
+ PackageContainerGroup group = null;
+
+ // If we have a module, we may need to create a brand-new location for it.
if (location instanceof ModuleLocation) {
var moduleLocation = ((ModuleLocation) location);
- var group = repository.getOutputContainerGroup(moduleLocation.getParent());
+ var parentGroup = repository.getOutputContainerGroup(moduleLocation.getParent());
- if (group != null) {
- return group
- .getOrCreateModule(moduleLocation.getModuleName())
- .getJavaFileForOutput(className, kind);
+ if (parentGroup != null) {
+ group = parentGroup.getOrCreateModule(moduleLocation.getModuleName());
}
} else {
- var group = repository.getOutputContainerGroup(location);
-
- if (group != null) {
- return group.getJavaFileForOutput(className, kind);
- }
+ group = repository.getOutputContainerGroup(location);
}
- return null;
+ return group == null
+ ? null
+ : group.getJavaFileForOutput(className, kind);
}
@Override
- public Location getLocationForModule(Location location, String moduleName) {
- // This checks that the input location is module/output oriented within the constructor,
- // so we don't need to do it here as well.
+ public ModuleLocation getLocationForModule(Location location, String moduleName) {
+ // ModuleLocation will also validate this, but we do this to keep consistent
+ // error messages.
+ requireOutputOrModuleOrientedLocation(location);
+
return new ModuleLocation(location, moduleName);
}
@Nullable
@Override
- public Location getLocationForModule(Location location, JavaFileObject fo) {
+ public ModuleLocation getLocationForModule(Location location, JavaFileObject fo) {
requireOutputOrModuleOrientedLocation(location);
if (fo instanceof PathFileObject) {
@@ -227,7 +225,7 @@ public Location getLocationForModule(Location location, JavaFileObject fo) {
var moduleLocation = pathFileObject.getLocation();
if (moduleLocation instanceof ModuleLocation) {
- return moduleLocation;
+ return (ModuleLocation) moduleLocation;
}
// The expectation is to return null if this is not for a module. Certain frameworks like
@@ -237,7 +235,7 @@ public Location getLocationForModule(Location location, JavaFileObject fo) {
}
throw new IllegalArgumentException(
- "File object " + fo + " does not appear to be registered to a module"
+ "File object " + fo + " is not compatible with this file manager"
);
}
@@ -284,7 +282,7 @@ public ServiceLoader getServiceLoader(Location location, Class service
if (group == null) {
throw new NoSuchElementException(
- "No container group for location " + location.getName() + " exists"
+ "No container group for location " + location.getName() + " exists in this file manager"
);
}
@@ -310,12 +308,12 @@ public String inferBinaryName(Location location, JavaFileObject file) {
if (!(file instanceof PathFileObject)) {
return null;
}
- var pathFileObject = (PathFileObject) file;
+
var group = repository.getPackageOrientedContainerGroup(location);
return group == null
? null
- : group.inferBinaryName(pathFileObject);
+ : group.inferBinaryName((PathFileObject) file);
}
@Nullable
@@ -331,11 +329,7 @@ public String inferModuleName(Location location) {
@Override
public boolean isSameFile(@Nullable FileObject a, @Nullable FileObject b) {
// Some annotation processors provide null values here for some reason.
- if (a == null || b == null) {
- return false;
- }
-
- return Objects.equals(a.toUri(), b.toUri());
+ return a != null && b != null && Objects.equals(a.toUri(), b.toUri());
}
@Override
@@ -367,7 +361,9 @@ public Iterable> listLocationsForModules(Location location) {
@Override
public String toString() {
- return new ToStringBuilder(this).toString();
+ return new ToStringBuilder(this)
+ .attribute("repository", repository)
+ .toString();
}
private static void requireOutputOrModuleOrientedLocation(Location location) {
diff --git a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/helpers/Fixtures.java b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/helpers/Fixtures.java
index f4f5840f4..7fd12763d 100644
--- a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/helpers/Fixtures.java
+++ b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/helpers/Fixtures.java
@@ -31,7 +31,6 @@
import io.github.ascopes.jct.utils.LoomPolyfill;
import io.github.ascopes.jct.workspaces.PathRoot;
import java.io.IOException;
-import java.lang.module.ModuleReference;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
@@ -39,18 +38,14 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.Locale;
import java.util.Objects;
import java.util.Random;
import java.util.UUID;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
import java.util.stream.Stream;
-import javax.annotation.processing.Processor;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileManager.Location;
@@ -200,7 +195,6 @@ public static Diagnostic someDiagnostic() {
*
* @return the mock.
*/
- @SuppressWarnings("deprecation")
public static TraceDiagnostic someTraceDiagnostic() {
var mock = mockRaw(TraceDiagnostic.class)
.>upcastedTo()
@@ -250,61 +244,6 @@ public static List someStackTraceList() {
.build();
}
- /**
- * Get a list of some trace diagnostics.
- *
- * @return some trace diagnostics.
- */
- public static List> someTraceDiagnostics() {
- return Stream
- .generate(Fixtures::someTraceDiagnostic)
- .limit(someInt(3, 8))
- .collect(Collectors.toList());
- }
-
- /**
- * Get some compilation units.
- *
- * @return some compilation units.
- */
- public static List someCompilationUnits() {
- return Stream
- .generate(() -> mock(JavaFileObject.class, withSettings().strictness(Strictness.LENIENT)))
- .peek(mock -> when(mock.getName()).thenReturn(someText()))
- .limit(someInt(3, 8))
- .collect(Collectors.toList());
- }
-
- /**
- * Get some unchecked exception with a stacktrace.
- *
- * @return some exception.
- */
- public static Throwable someUncheckedException() {
- var message = Stream
- .generate(UUID::randomUUID)
- .map(UUID::toString)
- .limit(someInt(1, 4))
- .collect(joining(" blah blah "));
- return new RuntimeException(message)
- .fillInStackTrace();
- }
-
- /**
- * Get some IO exception with a stacktrace.
- *
- * @return some exception.
- */
- public static Throwable someIoException() {
- var message = Stream
- .generate(UUID::randomUUID)
- .map(UUID::toString)
- .limit(someInt(1, 4))
- .collect(joining(" blah blah "));
- return new IOException(message)
- .fillInStackTrace();
- }
-
/**
* Get some charset.
*
@@ -321,28 +260,6 @@ public static Charset someCharset() {
);
}
- /**
- * Get some locale.
- *
- * @return some locale.
- */
- public static Locale someLocale() {
- return oneOf(
- Locale.ROOT,
- Locale.US,
- Locale.UK,
- Locale.ENGLISH,
- Locale.GERMAN,
- Locale.JAPAN,
- Locale.GERMANY,
- Locale.JAPANESE,
- Locale.SIMPLIFIED_CHINESE,
- Locale.TRADITIONAL_CHINESE,
- Locale.CHINESE,
- Locale.CHINA
- );
- }
-
/**
* Get some string release version.
*
@@ -368,6 +285,49 @@ public static String someModuleName() {
.collect(joining("."));
}
+ /**
+ * Get a valid random package name.
+ *
+ * @return the valid package name.
+ */
+ public static String somePackageName() {
+ return Stream
+ .generate(() -> Stream
+ .generate(() -> (char) someInt('a', 'z'))
+ .map(Objects::toString)
+ .limit(someInt(1, 10))
+ .collect(joining()))
+ .limit(someInt(1, 5))
+ .collect(joining("."));
+ }
+
+ /**
+ * Get a valid random class name.
+ *
+ * @return the valid class name.
+ */
+ public static String someClassName() {
+ var firstChar = (char) someInt('A', 'Z');
+ var restOfClassName = Stream
+ .generate(() -> (char) someInt('a', 'z'))
+ .map(Objects::toString)
+ .limit(someInt(1, 10))
+ .collect(joining());
+
+ return somePackageName() + "." + firstChar + restOfClassName;
+ }
+
+ /**
+ * Get some valid binary name. It may or may not be for a module.
+ *
+ * @return the binary name.
+ */
+ public static String someBinaryName() {
+ return someBoolean()
+ ? someModuleName() + "/" + someClassName()
+ : someClassName();
+ }
+
/**
* Get some mock Java file object with a dummy name and some assigned {@link Kind}.
*
@@ -391,7 +351,16 @@ public static JavaFileObject someJavaFileObject() {
* @return some mock location.
*/
public static Location someLocation() {
- return mock(Location.class, "Location-" + someText());
+ var name = "Location-" + someText();
+
+ Location location = mock(withSettings()
+ .strictness(Strictness.LENIENT)
+ .name(name));
+
+ when(location.getName()).thenReturn(name);
+ when(location.isOutputLocation()).thenReturn(someBoolean());
+ when(location.isModuleOrientedLocation()).thenReturn(someBoolean());
+ return location;
}
/**
@@ -443,15 +412,6 @@ public static Path someRelativePath() {
return absolutePath.relativize(relativePath.resolve("some-relative-path"));
}
- /**
- * Get some module reference.
- *
- * @return the module reference.
- */
- public static ModuleReference someModuleReference() {
- return mock(ModuleReference.class);
- }
-
/**
* Get some temporary file system.
*
@@ -463,15 +423,6 @@ public static TempFileSystem someTemporaryFileSystem() {
return new TempFileSystem();
}
- /**
- * Get some annotation processor.
- *
- * @return some annotation processor.
- */
- public static Processor someAnnotationProcessor() {
- return mock(Processor.class, someText() + " processor");
- }
-
/**
* Return one of the given elements.
*
diff --git a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/filemanagers/impl/JctFileManagerImplTest.java b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/filemanagers/impl/JctFileManagerImplTest.java
index 406efafa5..163c041d8 100644
--- a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/filemanagers/impl/JctFileManagerImplTest.java
+++ b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/filemanagers/impl/JctFileManagerImplTest.java
@@ -15,27 +15,81 @@
*/
package io.github.ascopes.jct.tests.unit.filemanagers.impl;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.oneOf;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someAbsolutePath;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someBinaryName;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someBoolean;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someClassName;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someFlags;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someInt;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someJavaFileObject;
import static io.github.ascopes.jct.tests.helpers.Fixtures.someLocation;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someModuleName;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.somePackageName;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.somePathRoot;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someRelativePath;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someText;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.InstanceOfAssertFactories.collection;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import io.github.ascopes.jct.containers.ContainerGroup;
+import io.github.ascopes.jct.containers.ModuleContainerGroup;
+import io.github.ascopes.jct.containers.OutputContainerGroup;
+import io.github.ascopes.jct.containers.PackageContainerGroup;
import io.github.ascopes.jct.containers.impl.ContainerGroupRepositoryImpl;
+import io.github.ascopes.jct.filemanagers.ModuleLocation;
+import io.github.ascopes.jct.filemanagers.PathFileObject;
import io.github.ascopes.jct.filemanagers.impl.JctFileManagerImpl;
-import io.github.ascopes.jct.workspaces.PathRoot;
+import io.github.ascopes.jct.tests.helpers.Fixtures;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardLocation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.InOrder;
import org.mockito.junit.jupiter.MockitoExtension;
+/**
+ * {@link JctFileManagerImpl} tests.
+ *
+ * @author Ashley Scopes
+ */
@DisplayName("JctFileManagerImpl Tests")
@ExtendWith(MockitoExtension.class)
+@SuppressWarnings({"DataFlowIssue", "resource"})
class JctFileManagerImplTest {
JctFileManagerImpl fileManager;
ContainerGroupRepositoryImpl repository;
+ InOrder order;
@BeforeEach
void setUp() {
@@ -44,30 +98,1308 @@ void setUp() {
try (var construction = mockConstruction(ContainerGroupRepositoryImpl.class)) {
fileManager = new JctFileManagerImpl("some-release");
repository = construction.constructed().iterator().next();
+ order = inOrder(repository);
}
}
- @DisplayName("null releases are disallowed")
+ @DisplayName("Constructor disallows null releases")
@SuppressWarnings({"resource", "ConstantConditions"})
@Test
- void testIfNullPointerExceptionThrownIfReleaseNull() {
+ void constructorDisallowsNullReleases() {
// Then
assertThatThrownBy(() -> new JctFileManagerImpl(null))
.isInstanceOf(NullPointerException.class)
.hasMessage("release");
}
- @DisplayName(".addPath(Location, PathRoot) adds the path to the repository")
+ @DisplayName(".addPath(...) delegates to the repository")
@Test
- void addPathAddsThePathToTheRepository() {
+ void addPathDelegatesToRepository() {
// Given
var location = someLocation();
- var pathRoot = mock(PathRoot.class);
+ var pathRoot = somePathRoot();
// When
fileManager.addPath(location, pathRoot);
// Then
verify(repository).addPath(location, pathRoot);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".addPaths(...) delegates multiple calls to the repository")
+ @Test
+ void addPathsDelegatesMultipleCallsToTheRepository() {
+ // Given
+ var location = someLocation();
+ var pathRoots = Stream
+ .generate(Fixtures::somePathRoot)
+ .limit(10)
+ .collect(Collectors.toList());
+
+ // When
+ fileManager.addPaths(location, pathRoots);
+
+ // Then
+ pathRoots.forEach(pathRoot -> order.verify(repository).addPath(location, pathRoot));
+ order.verifyNoMoreInteractions();
+ }
+
+ @DisplayName(".close() closes the repository")
+ @Test
+ void closeDelegatesToTheRepository() {
+ // When
+ fileManager.close();
+
+ // Then
+ verify(repository).close();
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".contains(...) tests")
+ @Nested
+ class ContainsTest {
+
+ @DisplayName(".contains(...) returns false if the file object is not a PathFileObject")
+ @Test
+ void containsReturnsFalseIfFileObjectIsNotPathFileObject() {
+ // Given
+ var location = someLocation();
+ FileObject fileObject = mock();
+
+ // When
+ var result = fileManager.contains(location, fileObject);
+
+ // Then
+ assertThat(result).isFalse();
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".contains(...) returns false if the location is not in the repository")
+ @Test
+ void containsReturnsFalseIfTheLocationIsNotInTheRepository() {
+ var location = someLocation();
+ PathFileObject fileObject = mock();
+
+ when(repository.getContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.contains(location, fileObject);
+
+ // Then
+ verify(repository).getContainerGroup(location);
+ assertThat(result).isFalse();
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".contains(...) checks the expected container group")
+ @ValueSource(booleans = {true, false})
+ @ParameterizedTest(name = "for group.contains(...) = {0}")
+ void containsChecksTheExpectedContainerGroup(boolean contained) {
+ var location = someLocation();
+ PathFileObject fileObject = mock();
+ ContainerGroup containerGroup = mock();
+ when(repository.getContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.contains(any()))
+ .thenReturn(contained);
+
+ // When
+ var result = fileManager.contains(location, fileObject);
+
+ // Then
+ verify(repository).getContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).contains(fileObject);
+ verifyNoMoreInteractions(containerGroup);
+
+ assertThat(result).isEqualTo(contained);
+ }
+ }
+
+ @DisplayName(".copyContainers(...) delegates to the repository")
+ @Test
+ void copyContainersDelegatesToTheRepository() {
+ // Given
+ var from = someLocation();
+ var to = someLocation();
+
+ // When
+ fileManager.copyContainers(from, to);
+
+ // Then
+ verify(repository).copyContainers(from, to);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".createEmptyLocation(...) delegates to the repository")
+ @Test
+ void createEmptyLocationDelegatesToTheRepository() {
+ // Given
+ var location = someLocation();
+
+ // When
+ fileManager.createEmptyLocation(location);
+
+ // Then
+ verify(repository).createEmptyLocation(location);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".flush() flushes the repository")
+ @Test
+ void flushDelegatesToTheRepository() {
+ // When
+ fileManager.flush();
+
+ // Then
+ verify(repository).flush();
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".getClassLoader(...) tests")
+ @Nested
+ class GetClassLoaderTest {
+
+ @DisplayName(".getClassLoader(...) returns null if the group does not exist")
+ @Test
+ void getClassLoaderReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var location = someLocation();
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getClassLoader(location);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".getClassLoader(...) calls .getClassLoader() on the container group")
+ @Test
+ void getClassLoaderDelegatesToTheContainerGroup() {
+ // Given
+ var location = someLocation();
+ PackageContainerGroup containerGroup = mock();
+ ClassLoader classLoader = mock();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.getClassLoader())
+ .thenReturn(classLoader);
+
+ // When
+ var result = fileManager.getClassLoader(location);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).getClassLoader();
+ verifyNoMoreInteractions(containerGroup);
+
+ assertThat(result).isSameAs(classLoader);
+ }
+ }
+
+ @DisplayName(".getEffectiveRelease() returns the effective release")
+ @ValueSource(strings = {"10", "11", "12", "foobar"})
+ @ParameterizedTest(name = "for effective release = {0}")
+ void getEffectiveReleaseReturnsTheEffectiveRelease(String effectiveRelease) {
+ // Given
+ try (var fileManager = new JctFileManagerImpl(effectiveRelease)) {
+ // Then
+ assertThat(fileManager.getEffectiveRelease()).isEqualTo(effectiveRelease);
+ }
+ }
+
+ @DisplayName(".getFileForInput(...) tests")
+ @Nested
+ class GetFileForInputTest {
+
+ @DisplayName(".getFileForInput(...) returns null if the group does not exist")
+ @Test
+ void getFileForInputReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var location = someLocation();
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getFileForInput(location, packageName, relativeName);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".getFileForInput(...) calls .getFileForInput(...) on the container group")
+ @Test
+ void getFileForInputCallsGetFileForInputOnTheContainerGroup() {
+ // Given
+ var location = someLocation();
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+ PackageContainerGroup containerGroup = mock();
+ PathFileObject fileObject = mock();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.getFileForInput(any(), any()))
+ .thenReturn(fileObject);
+
+ // When
+ var result = fileManager.getFileForInput(location, packageName, relativeName);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).getFileForInput(packageName, relativeName);
+ verifyNoMoreInteractions(containerGroup);
+
+ assertThat(result).isSameAs(fileObject);
+ }
+ }
+
+ @DisplayName(".getFileForOutput(...) tests")
+ @Nested
+ class GetFileForOutputTest {
+
+ @DisplayName(".getFileForOutput(...) throws an exception if the location isn't output-oriented")
+ @Test
+ void throwsExceptionIfLocationIsNotOutputOriented() {
+ // Given
+ var name = someText();
+ Location location = mock(someText());
+ when(location.isOutputLocation()).thenReturn(false);
+ when(location.getName()).thenReturn(name);
+
+ // Then
+ assertThatThrownBy(() -> fileManager
+ .getFileForOutput(
+ location,
+ somePackageName(),
+ someRelativePath().toString(),
+ someJavaFileObject()
+ ))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be an output location", name);
+ }
+
+ @DisplayName(".getFileForOutput(ModuleLocation, ...) returns null if the group does not exist")
+ @Test
+ void moduleLocationReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var parentLocation = StandardLocation.CLASS_OUTPUT;
+ var moduleLocation = new ModuleLocation(parentLocation, someModuleName());
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+ var sibling = someJavaFileObject();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getFileForOutput(moduleLocation, packageName, relativeName, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(parentLocation);
+ verifyNoMoreInteractions(repository);
+
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(
+ ".getFileForOutput(ModuleLocation, ...) creates the new location and "
+ + "returns the file object")
+ @Test
+ void moduleLocationCreatesTheNewLocationAndReturnsTheFileObject() {
+ // Given
+ var parentLocation = StandardLocation.SOURCE_OUTPUT;
+ var moduleName = someModuleName();
+ var moduleLocation = new ModuleLocation(parentLocation, moduleName);
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+ var sibling = someJavaFileObject();
+ OutputContainerGroup outputContainerGroup = mock();
+ PackageContainerGroup moduleGroup = mock();
+ PathFileObject fileForOutput = mock();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(outputContainerGroup);
+ when(outputContainerGroup.getOrCreateModule(any()))
+ .thenReturn(moduleGroup);
+ when(moduleGroup.getFileForOutput(any(), any()))
+ .thenReturn(fileForOutput);
+
+ // When
+ var result = fileManager.getFileForOutput(moduleLocation, packageName, relativeName, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(parentLocation);
+ verifyNoMoreInteractions(repository);
+
+ verify(outputContainerGroup).getOrCreateModule(moduleName);
+ verifyNoMoreInteractions(outputContainerGroup);
+
+ verify(moduleGroup).getFileForOutput(packageName, relativeName);
+ verifyNoMoreInteractions(moduleGroup);
+
+ assertThat(result).isSameAs(fileForOutput);
+ }
+
+ @DisplayName(".getFileForOutput(Location, ...) returns null if the group does not exist")
+ @Test
+ void locationReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var location = StandardLocation.SOURCE_OUTPUT;
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+ var sibling = someJavaFileObject();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getFileForOutput(location, packageName, relativeName, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".getFileForOutput(Location, ...) returns the file object")
+ @Test
+ void locationReturnsTheFileObject() {
+ // Given
+ var location = StandardLocation.NATIVE_HEADER_OUTPUT;
+ var packageName = somePackageName();
+ var relativeName = someRelativePath().toString();
+ var sibling = someJavaFileObject();
+ OutputContainerGroup outputContainerGroup = mock();
+ PathFileObject fileForOutput = mock();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(outputContainerGroup);
+ when(outputContainerGroup.getFileForOutput(any(), any()))
+ .thenReturn(fileForOutput);
+
+ // When
+ var result = fileManager.getFileForOutput(location, packageName, relativeName, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(outputContainerGroup).getFileForOutput(packageName, relativeName);
+ verifyNoMoreInteractions(outputContainerGroup);
+
+ assertThat(result).isSameAs(fileForOutput);
+ }
+ }
+
+ @DisplayName(".getJavaFileForInput(...) tests")
+ @Nested
+ class GetJavaFileForInputTest {
+
+ @DisplayName(".getJavaFileForInput(...) returns null if the group does not exist")
+ @Test
+ void getJavaFileForInputReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var location = someLocation();
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getJavaFileForInput(location, className, kind);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".getJavaFileForInput(...) calls .getJavaFileForInput(...) on the container group")
+ @Test
+ void getJavaFileForInputCallsGetJavaFileForInputOnTheContainerGroup() {
+ // Given
+ var location = someLocation();
+ PackageContainerGroup containerGroup = mock();
+ PathFileObject fileObject = mock();
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.getJavaFileForInput(any(), any()))
+ .thenReturn(fileObject);
+
+ // When
+ var result = fileManager.getJavaFileForInput(location, className, kind);
+
+ // Then
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).getJavaFileForInput(className, kind);
+ verifyNoMoreInteractions(containerGroup);
+
+ assertThat(result).isSameAs(fileObject);
+ }
+ }
+
+
+ @DisplayName(".getJavaFileForOutput(...) tests")
+ @Nested
+ class GetJavaFileForOutputTest {
+
+ @DisplayName(
+ ".getJavaFileForOutput(...) throws an exception if the location isn't output-oriented"
+ )
+ @Test
+ void throwsExceptionIfLocationIsNotOutputOriented() {
+ // Given
+ var name = someText();
+ Location location = mock(someText());
+ var kind = oneOf(Kind.class);
+ when(location.isOutputLocation()).thenReturn(false);
+ when(location.getName()).thenReturn(name);
+
+ // Then
+ assertThatThrownBy(() -> fileManager
+ .getJavaFileForOutput(
+ location,
+ somePackageName(),
+ kind,
+ someJavaFileObject()
+ ))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be an output location", name);
+ }
+
+ @DisplayName(
+ ".getJavaFileForOutput(ModuleLocation, ...) returns null if the group does not exist"
+ )
+ @Test
+ void moduleLocationReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var parentLocation = StandardLocation.CLASS_OUTPUT;
+ var moduleLocation = new ModuleLocation(parentLocation, someModuleName());
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+ var sibling = someJavaFileObject();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getJavaFileForOutput(moduleLocation, className, kind, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(parentLocation);
+ verifyNoMoreInteractions(repository);
+
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(
+ ".getJavaFileForOutput(ModuleLocation, ...) creates the new location and "
+ + "returns the file object")
+ @Test
+ void moduleLocationCreatesTheNewLocationAndReturnsTheFileObject() {
+ // Given
+ var parentLocation = StandardLocation.SOURCE_OUTPUT;
+ var moduleName = someModuleName();
+ var moduleLocation = new ModuleLocation(parentLocation, moduleName);
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+ var sibling = someJavaFileObject();
+ OutputContainerGroup outputContainerGroup = mock();
+ PackageContainerGroup moduleGroup = mock();
+ PathFileObject javaFileForOutput = mock();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(outputContainerGroup);
+ when(outputContainerGroup.getOrCreateModule(any()))
+ .thenReturn(moduleGroup);
+ when(moduleGroup.getJavaFileForOutput(any(), any()))
+ .thenReturn(javaFileForOutput);
+
+ // When
+ var result = fileManager.getJavaFileForOutput(moduleLocation, className, kind, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(parentLocation);
+ verifyNoMoreInteractions(repository);
+
+ verify(outputContainerGroup).getOrCreateModule(moduleName);
+ verifyNoMoreInteractions(outputContainerGroup);
+
+ verify(moduleGroup).getJavaFileForOutput(className, kind);
+ verifyNoMoreInteractions(moduleGroup);
+
+ assertThat(result).isSameAs(javaFileForOutput);
+ }
+
+ @DisplayName(".getJavaFileForOutput(Location, ...) returns null if the group does not exist")
+ @Test
+ void locationReturnsNullIfGroupDoesNotExist() {
+ // Given
+ var location = StandardLocation.SOURCE_OUTPUT;
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+ var sibling = someJavaFileObject();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.getJavaFileForOutput(location, className, kind, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".getJavaFileForOutput(Location, ...) returns the file object")
+ @Test
+ void locationReturnsTheFileObject() {
+ // Given
+ var location = StandardLocation.NATIVE_HEADER_OUTPUT;
+ var className = someClassName();
+ var kind = oneOf(Kind.class);
+ var sibling = someJavaFileObject();
+ OutputContainerGroup outputContainerGroup = mock();
+ PathFileObject javaFileForOutput = mock();
+
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(outputContainerGroup);
+ when(outputContainerGroup.getJavaFileForOutput(any(), any()))
+ .thenReturn(javaFileForOutput);
+
+ // When
+ var result = fileManager.getJavaFileForOutput(location, className, kind, sibling);
+
+ // Then
+ verify(repository).getOutputContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(outputContainerGroup).getJavaFileForOutput(className, kind);
+ verifyNoMoreInteractions(outputContainerGroup);
+
+ assertThat(result).isSameAs(javaFileForOutput);
+ }
+ }
+
+ @DisplayName(".getLocationForModule(...) tests")
+ @Nested
+ class GetLocationForModuleTest {
+
+ @DisplayName(
+ ".getLocationForModule(Location, String) throws an exception for package locations"
+ )
+ @Test
+ void getLocationForModuleStringThrowsExceptionForPackageLocations() {
+ // Given
+ var location = StandardLocation.CLASS_PATH;
+ var moduleName = someModuleName();
+
+ // Then
+ assertThatThrownBy(() -> fileManager.getLocationForModule(location, moduleName))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be output or module-oriented", location.getName());
+ }
+
+ @DisplayName(".getLocationForModule(Location, String) returns the module location")
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"CLASS_OUTPUT", "SOURCE_OUTPUT", "MODULE_SOURCE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void getLocationForModuleStringReturnsModuleLocation(Location location) {
+ // Given
+ var moduleName = someModuleName();
+
+ // When
+ var moduleLocation = fileManager.getLocationForModule(location, moduleName);
+
+ // Then
+ assertThat(moduleLocation.getModuleName()).isEqualTo(moduleName);
+ assertThat(moduleLocation.getParent()).isEqualTo(location);
+ }
+
+ @DisplayName(
+ ".getLocationForModule(Location, FileObject) throws an exception for package locations"
+ )
+ @Test
+ void getLocationForModuleFileObjectThrowsExceptionForPackageLocations() {
+ // Given
+ var location = StandardLocation.CLASS_PATH;
+ var fileObject = someJavaFileObject();
+
+ // Then
+ assertThatThrownBy(() -> fileManager.getLocationForModule(location, fileObject))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be output or module-oriented", location.getName());
+ }
+
+ @DisplayName(".getLocationForModule(Location, FileObject) throws an exception")
+ @Test
+ void getLocationForModuleFileObjectThrowsException() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ var fileObject = someJavaFileObject();
+
+ // Then
+ assertThatThrownBy(() -> fileManager.getLocationForModule(location, fileObject))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("File object %s is not compatible with this file manager", fileObject);
+ }
+
+ @DisplayName(
+ ".getLocationForModule(Location, PathFileObject) returns null for non-module file objects"
+ )
+ @Test
+ void getLocationForModulePathFileObjectReturnsNullForNonModuleFileObjects() {
+ // Given
+ var location = StandardLocation.MODULE_PATH;
+ PathFileObject fileObject = mock();
+ when(fileObject.getLocation()).thenReturn(StandardLocation.SOURCE_PATH);
+
+ // When
+ var moduleLocation = fileManager.getLocationForModule(location, fileObject);
+
+ // Then
+ assertThat(moduleLocation).isNull();
+ }
+
+ @DisplayName(
+ ".getLocationForModule(Location, PathFileObject) returns the module "
+ + "location for file objects"
+ )
+ @Test
+ void getLocationForModulePathFileObjectReturnsTheModuleLocationForFileObjects() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ var expectedModuleLocation = new ModuleLocation(location, someModuleName());
+ PathFileObject fileObject = mock();
+ when(fileObject.getLocation()).thenReturn(expectedModuleLocation);
+
+ // When
+ var returnedModuleLocation = fileManager.getLocationForModule(location, fileObject);
+
+ // Then
+ assertThat(returnedModuleLocation).isSameAs(expectedModuleLocation);
+ }
+ }
+
+ @DisplayName(".getModuleContainerGroup(...) tests")
+ @Nested
+ class GetModuleContainerGroupTest {
+
+ @DisplayName(
+ ".getModuleContainerGroup(...) throws an exception for non module-oriented container groups"
+ )
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"SOURCE_PATH", "CLASS_OUTPUT"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void getModuleContainerGroupThrowsExceptionForNonModuleOrientedContainerGroups(
+ Location location
+ ) {
+ // Then
+ assertThatThrownBy(() -> fileManager.getModuleContainerGroup(location))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be module-oriented", location.getName());
+
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".getModuleContainerGroup(...) delegates to the repository")
+ @Test
+ void getModuleContainerGroupDelegatesToTheRepository() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ ModuleContainerGroup containerGroup = mock();
+ when(repository.getModuleContainerGroup(any()))
+ .thenReturn(containerGroup);
+
+ // When
+ var result = fileManager.getModuleContainerGroup(location);
+
+ // Then
+ verify(repository).getModuleContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(containerGroup);
+ }
+ }
+
+ @DisplayName(".getModuleContainerGroups() delegates to the repository")
+ @Test
+ void getModuleContainerGroupsDelegatesToTheRepository() {
+ // Given
+ Collection moduleContainerGroups = mock();
+ when(repository.getModuleContainerGroups())
+ .thenReturn(moduleContainerGroups);
+
+ // When
+ var result = fileManager.getModuleContainerGroups();
+
+ // Then
+ verify(repository).getModuleContainerGroups();
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(moduleContainerGroups);
+ }
+
+ @DisplayName(".getOutputContainerGroup(...) tests")
+ @Nested
+ class GetOutputContainerGroupTest {
+
+ @DisplayName(
+ ".getOutputContainerGroup(...) throws an exception for non output-oriented container groups"
+ )
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"SOURCE_PATH", "MODULE_SOURCE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void getOutputContainerGroupThrowsExceptionForNonOutputOrientedContainerGroups(
+ Location location
+ ) {
+ // Then
+ assertThatThrownBy(() -> fileManager.getOutputContainerGroup(location))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be an output location", location.getName());
+
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".getOutputContainerGroup(...) delegates to the repository")
+ @Test
+ void getOutputContainerGroupDelegatesToTheRepository() {
+ // Given
+ var location = StandardLocation.CLASS_OUTPUT;
+ OutputContainerGroup containerGroup = mock();
+ when(repository.getOutputContainerGroup(any()))
+ .thenReturn(containerGroup);
+
+ // When
+ var result = fileManager.getOutputContainerGroup(location);
+
+ // Then
+ verify(repository).getOutputContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(containerGroup);
+ }
+ }
+
+
+ @DisplayName(".getOutputContainerGroups() delegates to the repository")
+ @Test
+ void getOutputContainerGroupsDelegatesToTheRepository() {
+ // Given
+ Collection outputContainerGroups = mock();
+ when(repository.getOutputContainerGroups())
+ .thenReturn(outputContainerGroups);
+
+ // When
+ var result = fileManager.getOutputContainerGroups();
+
+ // Then
+ verify(repository).getOutputContainerGroups();
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(outputContainerGroups);
+ }
+
+ @DisplayName(".getPackageContainerGroup(...) tests")
+ @Nested
+ class GetPackageContainerGroupTest {
+
+ @DisplayName(
+ ".getPackageContainerGroup(...) throws an exception for non package container groups"
+ )
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"CLASS_OUTPUT", "MODULE_SOURCE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void getPackageContainerGroupThrowsExceptionForNonPackageContainerGroups(Location location) {
+ // Then
+ assertThatThrownBy(() -> fileManager.getPackageContainerGroup(location))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be an input package location", location.getName());
+
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".getPackageContainerGroup(...) delegates to the repository")
+ @Test
+ void getPackageContainerGroupDelegatesToTheRepository() {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ PackageContainerGroup containerGroup = mock();
+ when(repository.getPackageContainerGroup(any()))
+ .thenReturn(containerGroup);
+
+ // When
+ var result = fileManager.getPackageContainerGroup(location);
+
+ // Then
+ verify(repository).getPackageContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(containerGroup);
+ }
+ }
+
+ @DisplayName(".getPackageContainerGroups() delegates to the repository")
+ @Test
+ void getPackageContainerGroupsDelegatesToTheRepository() {
+ // Given
+ Collection packageContainerGroups = mock();
+ when(repository.getPackageContainerGroups())
+ .thenReturn(packageContainerGroups);
+
+ // When
+ var result = fileManager.getPackageContainerGroups();
+
+ // Then
+ verify(repository).getPackageContainerGroups();
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isSameAs(packageContainerGroups);
+ }
+
+ @DisplayName(".getServiceLoader(...) tests")
+ @Nested
+ class GetServiceLoaderTest {
+
+ @DisplayName(".getServiceLoader(...) throws an exception if the location does not exist")
+ @Test
+ void getServiceLoaderThrowsExceptionIfLocationDoesNotExist() {
+ // Given
+ class Some {}
+
+ var location = someLocation();
+ when(repository.getContainerGroup(any()))
+ .thenReturn(null);
+
+ // Then
+ assertThatThrownBy(() -> fileManager.getServiceLoader(location, Some.class))
+ .isInstanceOf(NoSuchElementException.class)
+ .hasMessage("No container group for location %s exists in this file manager",
+ location.getName());
+
+ verify(repository).getContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".getServiceLoader(...) delegates to the group")
+ @Test
+ void getServiceLoaderDelegatesToTheGroup() {
+ // Given
+ class Some {}
+ var location = someLocation();
+ ContainerGroup containerGroup = mock();
+ ServiceLoader serviceLoader = mock();
+ when(repository.getContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.getServiceLoader(any()))
+ .thenAnswer(ctx -> serviceLoader);
+
+ // When
+ var result = fileManager.getServiceLoader(location, Some.class);
+
+ // Then
+ verify(repository).getContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).getServiceLoader(Some.class);
+ verifyNoMoreInteractions(containerGroup);
+
+ assertThat(result).isSameAs(serviceLoader);
+ }
+ }
+
+ @DisplayName(".handleOption(...) always returns false")
+ @RepeatedTest(10)
+ void handleOptionAlwaysReturnsFalse() {
+ // Given
+ var originalFlagIterator = someFlags().iterator();
+ var flagIterator = spy(originalFlagIterator);
+ var flag = originalFlagIterator.next();
+
+ // When
+ var result = fileManager.handleOption(flag, flagIterator);
+
+ // Then
+ assertThat(result).isFalse();
+ verifyNoInteractions(flagIterator);
+ }
+
+ @DisplayName(".hasLocation(...) delegates to the repository")
+ @ValueSource(booleans = {true, false})
+ @ParameterizedTest(name = "for repository.hasLocation(...) = {0}")
+ void hasLocationDelegatesToTheRepository(boolean hasLocation) {
+ // Given
+ var location = someLocation();
+ when(repository.hasLocation(any())).thenReturn(hasLocation);
+
+ // When
+ var result = fileManager.hasLocation(location);
+
+ // Then
+ verify(repository).hasLocation(location);
+ verifyNoMoreInteractions(repository);
+ assertThat(result).isEqualTo(hasLocation);
+ }
+
+ @DisplayName(".inferBinaryName(...) tests")
+ @Nested
+ class InferBinaryNameTest {
+
+ @DisplayName(".inferBinaryName(...) throws an exception for module-oriented locations")
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"MODULE_SOURCE_PATH", "MODULE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void inferBinaryNameThrowsExceptionForModuleOrientedLocations(Location location) {
+ // Then
+ assertThatThrownBy(() -> fileManager.inferBinaryName(location, someJavaFileObject()))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be package-oriented", location.getName());
+
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".inferBinaryName(...) returns null for non-PathFileObject objects")
+ @Test
+ void inferBinaryNameReturnsNullForNonPathFileObjects() {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ JavaFileObject fileObject = mock();
+
+ // When
+ var result = fileManager.inferBinaryName(location, fileObject);
+
+ // Then
+ assertThat(result).isNull();
+ verifyNoInteractions(repository);
+ }
+
+ @DisplayName(".inferBinaryName(...) returns null if the location does not exist")
+ @Test
+ void inferBinaryNameReturnsNullIfTheLocationDoesNotExist() {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ PathFileObject fileObject = mock();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.inferBinaryName(location, fileObject);
+
+ // Then
+ assertThat(result).isNull();
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".inferBinaryName(...) infers the binary name from the group")
+ @Test
+ void inferBinaryNameInfersTheBinaryNameFromTheGroup() {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ PathFileObject fileObject = mock();
+ PackageContainerGroup containerGroup = mock();
+ var binaryName = someBinaryName();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.inferBinaryName(any()))
+ .thenReturn(binaryName);
+
+ // When
+ var result = fileManager.inferBinaryName(location, fileObject);
+
+ // Then
+ assertThat(result).isEqualTo(binaryName);
+
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).inferBinaryName(fileObject);
+ verifyNoMoreInteractions(containerGroup);
+ }
+ }
+
+ @DisplayName(".inferModuleName(...) tests")
+ @Nested
+ class InferModuleNameTest {
+
+ @DisplayName(
+ ".inferModuleName(...) throws an exception if the location is not package oriented"
+ )
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"MODULE_SOURCE_PATH", "MODULE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void inferModuleNameThrowsAnExceptionIfLocationIsNotPackageOriented(Location location) {
+ // Then
+ assertThatThrownBy(() -> fileManager.inferModuleName(location))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be package-oriented", location.getName());
+ }
+
+ @DisplayName(".inferModuleName(...) returns null for non-module locations")
+ @Test
+ void inferModuleNameReturnsNullForNonModuleLocations() {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+
+ // When
+ var result = fileManager.inferModuleName(location);
+
+ // Then
+ assertThat(result).isNull();
+ }
+
+ @DisplayName(".inferModuleName(...) returns the module name for module locations")
+ @Test
+ void inferModuleNameReturnsModuleNameForModuleLocations() {
+ // Given
+ var parentLocation = StandardLocation.MODULE_SOURCE_PATH;
+ var moduleName = someModuleName();
+ var moduleLocation = new ModuleLocation(parentLocation, moduleName);
+
+ // When
+ var result = fileManager.inferModuleName(moduleLocation);
+
+ // Then
+ assertThat(result).isEqualTo(moduleName);
+ }
+ }
+
+ @DisplayName(".isSameFile(...) tests")
+ @Nested
+ class IsSameFileTest {
+
+ @DisplayName(".isSameFile(...) returns false if the first argument is null")
+ @Test
+ void isSameFileReturnsFalseIfFirstArgumentIsNull() {
+ // Then
+ assertThat(fileManager.isSameFile(null, mock())).isFalse();
+ }
+
+ @DisplayName(".isSameFile(...) returns false if the second argument is null")
+ @Test
+ void isSameFileReturnsFalseIfSecondArgumentIsNull() {
+ // Then
+ assertThat(fileManager.isSameFile(mock(), null)).isFalse();
+ }
+
+ @DisplayName(".isSameFile(...) returns false if both arguments are null")
+ @Test
+ void isSameFileReturnsFalseIfBothArgumentsAreNull() {
+ // Then
+ assertThat(fileManager.isSameFile(null, null)).isFalse();
+ }
+
+ @DisplayName(".isSameFile(...) returns false if both files have different URIs")
+ @Test
+ void isSameFileReturnsFalseIfBothFilesHaveDifferentUris() {
+ // Given
+ FileObject first = mock();
+ FileObject second = mock();
+
+ URI firstUri = someAbsolutePath().toUri();
+ URI secondUri = someAbsolutePath().toUri();
+
+ when(first.toUri()).thenReturn(firstUri);
+ when(second.toUri()).thenReturn(secondUri);
+
+ // Then
+ assertThat(fileManager.isSameFile(first, second)).isFalse();
+ }
+
+ @DisplayName(".isSameFile(...) returns true if both files have the same URI")
+ @Test
+ void isSameFileReturnsTrueIfBothFilesHaveTheSameUri() {
+ // Given
+ FileObject first = mock();
+ FileObject second = mock();
+
+ URI uri = someAbsolutePath().toUri();
+
+ when(first.toUri()).thenReturn(uri);
+ when(second.toUri()).thenReturn(uri);
+
+ // Then
+ assertThat(fileManager.isSameFile(first, second)).isTrue();
+ }
+ }
+
+ @DisplayName(".isSupportedOption(...) always returns -1")
+ @RepeatedTest(10)
+ void isSupportedOptionAlwaysReturnsFalse() {
+ // Given
+ var flag = someFlags().iterator().next();
+
+ // Then
+ assertThat(fileManager.isSupportedOption(flag)).isEqualTo(-1);
+ }
+
+ @DisplayName(".list(...) tests")
+ @Nested
+ class ListTests {
+
+ @DisplayName(".list(...) throws an exception if the location is not package oriented")
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"MODULE_SOURCE_PATH", "MODULE_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void listThrowsAnExceptionIfLocationIsNotPackageOriented(Location location) {
+ // Given
+ var packageName = somePackageName();
+ var kinds = Stream.generate(() -> oneOf(Kind.class))
+ .limit(someInt(1, 4))
+ .collect(Collectors.toSet());
+ var recurse = someBoolean();
+
+ // Then
+ assertThatThrownBy(() -> fileManager.list(location, packageName, kinds, recurse))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be package-oriented", location.getName());
+ }
+
+ @DisplayName(".list(...) returns an empty set if the location does not exist")
+ @Test
+ void listReturnsEmptySetIfLocationDoesNotExist() throws IOException {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ var packageName = somePackageName();
+ var kinds = Stream.generate(() -> oneOf(Kind.class))
+ .limit(someInt(1, 4))
+ .collect(Collectors.toSet());
+ var recurse = someBoolean();
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(null);
+
+ // When
+ var result = fileManager.list(location, packageName, kinds, recurse);
+
+ // Then
+ assertThat(result)
+ .isInstanceOf(Set.class)
+ .isEmpty();
+
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+ }
+
+ @DisplayName(".list(...) returns the file listing for the location")
+ @Test
+ void listReturnsFileListingForTheLocation() throws IOException {
+ // Given
+ var location = StandardLocation.SOURCE_PATH;
+ var packageName = somePackageName();
+ var kinds = Stream.generate(() -> oneOf(Kind.class))
+ .limit(someInt(1, 4))
+ .collect(Collectors.toSet());
+
+ var recurse = someBoolean();
+ PackageContainerGroup containerGroup = mock();
+ var files = Set.of(someJavaFileObject(), someJavaFileObject(), someJavaFileObject());
+
+ when(repository.getPackageOrientedContainerGroup(any()))
+ .thenReturn(containerGroup);
+ when(containerGroup.listFileObjects(any(), any(), anyBoolean()))
+ .thenReturn(files);
+
+ // When
+ var result = fileManager.list(location, packageName, kinds, recurse);
+
+ // Then
+ assertThat(result)
+ .isInstanceOf(Set.class)
+ .containsExactlyInAnyOrderElementsOf(files);
+
+ verify(repository).getPackageOrientedContainerGroup(location);
+ verifyNoMoreInteractions(repository);
+
+ verify(containerGroup).listFileObjects(packageName, kinds, recurse);
+ verifyNoMoreInteractions(containerGroup);
+ }
+ }
+
+ @DisplayName(".listLocationsForModules(...) tests")
+ @Nested
+ class ListLocationsForModulesTest {
+
+ @DisplayName(
+ ".listLocationsForModules(...) throws an exception if the location is package oriented"
+ )
+ @EnumSource(
+ value = StandardLocation.class,
+ names = {"SOURCE_PATH", "CLASS_PATH"}
+ )
+ @ParameterizedTest(name = "for location StandardLocation.{0}")
+ void listLocationsForModulesThrowsExceptionIfLocationIsPackageOriented(Location location) {
+ // Then
+ assertThatThrownBy(() -> fileManager.listLocationsForModules(location))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Location %s must be output or module-oriented", location.getName());
+ }
+
+ @DisplayName(".listLocationsForModules(...) returns the results in a single-element list")
+ @Test
+ void listLocationsForModulesReturnsTheResultsInSingleElementList() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ var expectedLocations = Stream.generate(() -> new ModuleLocation(location, someModuleName()))
+ .limit(someInt(1, 10))
+ .map(Location.class::cast)
+ .collect(Collectors.toSet());
+
+ when(repository.listLocationsForModules(location))
+ .thenReturn(expectedLocations);
+
+ // When
+ var result = fileManager.listLocationsForModules(location);
+
+ // Then
+ assertThat(result)
+ .singleElement(collection(Location.class))
+ .containsExactlyInAnyOrderElementsOf(expectedLocations);
+ }
+ }
+
+ @DisplayName(".toString() returns the expected value")
+ @Test
+ void toStringReturnsExpectedValue() {
+ // Then
+ assertThat(fileManager.toString())
+ .isEqualTo("JctFileManagerImpl{repository=%s}", repository);
}
}