From babc91415ed3666581deb866b19c1809a495e189 Mon Sep 17 00:00:00 2001
From: Ashley Scopes <73482956+ascopes@users.noreply.github.com>
Date: Sun, 5 Feb 2023 14:04:39 +0000
Subject: [PATCH] Start writing ContainerGroupRepositoryImpl tests
---
.../impl/ContainerGroupRepositoryImpl.java | 5 +-
.../ascopes/jct/tests/helpers/Fixtures.java | 18 +-
.../ContainerGroupRepositoryImplTest.java | 259 ++++++++++++++++++
.../unit/containers/impl/package-info.java | 19 ++
.../tests/unit/containers/package-info.java | 19 ++
5 files changed, 315 insertions(+), 5 deletions(-)
create mode 100644 java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/ContainerGroupRepositoryImplTest.java
create mode 100644 java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/package-info.java
create mode 100644 java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/package-info.java
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 d5d7532c2..0c0d2c74d 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
@@ -354,9 +354,8 @@ private void addModuleRoot(Location location, PathRoot pathRoot) {
if (modules.isEmpty()) {
LOGGER.warn(
- "There were no valid modules present in {}, so nothing has been registered. "
- + "If you are sure that there are files, this is probably a bug.",
- modules
+ "There were no valid modules present in {}, so nothing has been registered.",
+ pathRoot.getPath()
);
} else {
var moduleGroup = getOrCreateModuleContainerGroup(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 7fd12763d..ebc458d31 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
@@ -57,7 +57,6 @@
*
* @author Ashley Scopes
*/
-@SuppressWarnings("NullableProblems")
public final class Fixtures {
private static final Random RANDOM = new Random();
@@ -369,7 +368,22 @@ public static Location someLocation() {
* @return some mock path root object.
*/
public static PathRoot somePathRoot() {
- return mock(PathRoot.class, "PathRoot-" + someText());
+ var name = "PathRoot-" + someText();
+ var path = someRelativePath().resolve(name);
+ PathRoot pathRoot = mock(withSettings()
+ .strictness(Strictness.LENIENT)
+ .name(path.toString()));
+ when(pathRoot.getPath()).thenReturn(path);
+ when(pathRoot.getUri()).thenReturn(path.toUri());
+
+ try {
+ when(pathRoot.getUrl()).thenReturn(path.toUri().toURL());
+ } catch (Exception ex) {
+ throw new IllegalStateException("unreachable", ex);
+ }
+
+ when(pathRoot.getParent()).thenReturn(null);
+ return pathRoot;
}
/**
diff --git a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/ContainerGroupRepositoryImplTest.java b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/ContainerGroupRepositoryImplTest.java
new file mode 100644
index 000000000..abd30808a
--- /dev/null
+++ b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/ContainerGroupRepositoryImplTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 - 2023, the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.github.ascopes.jct.tests.unit.containers.impl;
+
+import static io.github.ascopes.jct.tests.helpers.Fixtures.somePathRoot;
+import static io.github.ascopes.jct.tests.helpers.Fixtures.someRelease;
+import static java.util.stream.Collectors.toMap;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.InstanceOfAssertFactories.list;
+import static org.mockito.ArgumentMatchers.any;
+
+import io.github.ascopes.jct.containers.Container;
+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.utils.ModuleDiscoverer;
+import io.github.ascopes.jct.workspaces.PathRoot;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import javax.tools.StandardLocation;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * {@link ContainerGroupRepositoryImpl} tests.
+ *
+ *
This is partially an integration test in nature, but this is only done since there is
+ * little benefit in mocking all moving components. Components that perform things like module
+ * discovery will still be mocked internally. The idea behind this class is to ensure that the
+ * implementation performs the expected behaviour as a pure black box (i.e. an opaque implementation
+ * that may be subject to structural change over time).
+ *
+ * @author Ashley Scopes
+ */
+@DisplayName("ContainerGroupRepositoryImpl tests")
+@ExtendWith(MockitoExtension.class)
+@SuppressWarnings({"AssertBetweenInconvertibleTypes", "resource"})
+class ContainerGroupRepositoryImplTest {
+
+ String release;
+ ContainerGroupRepositoryImpl repository;
+
+ @Mock(answer = Answers.RETURNS_SMART_NULLS)
+ MockedStatic moduleDiscoverer;
+
+ @BeforeEach
+ void setUp() {
+ release = someRelease();
+ repository = new ContainerGroupRepositoryImpl(release);
+ }
+
+ @AfterEach
+ void tearDown() {
+ repository.close();
+ }
+
+ @DisplayName("Initialising the repository sets the release")
+ @Test
+ void initialisingTheRepositorySetsTheRelease() {
+ // Given
+ var release = someRelease();
+ try (var repository = new ContainerGroupRepositoryImpl(release)) {
+ // Then
+ assertThat(repository.getRelease()).isEqualTo(release);
+ }
+ }
+
+ @DisplayName(".addPath(...) tests")
+ @Nested
+ class AddPathTest {
+
+ @DisplayName("adding a module location in a non-existent parent registers the module correctly")
+ @Test
+ void addingModuleLocationInNonExistentParentRegistersModuleCorrectly() {
+ // Given
+ var moduleLocation = new ModuleLocation(StandardLocation.MODULE_SOURCE_PATH, "foo.bar");
+ PathRoot pathRoot = somePathRoot();
+
+ // When
+ repository.addPath(moduleLocation, pathRoot);
+
+ // Then
+ var groups = repository.getModuleContainerGroups();
+ assertThat(groups)
+ .as("module oriented groups")
+ .hasSize(1);
+
+ var group = groups.iterator().next();
+ assertThat(group.getLocation())
+ .as("module oriented group location")
+ .isEqualTo(StandardLocation.MODULE_SOURCE_PATH);
+
+ var modules = group.getModules();
+ assertThat(modules)
+ .as("map of modules in module oriented group")
+ .hasSize(1)
+ .containsKey(moduleLocation);
+
+ var module = modules.get(moduleLocation);
+ assertThat(module.getLocation())
+ .as("module group location")
+ .isEqualTo(moduleLocation);
+
+ var packages = module.getPackages();
+ assertThat(packages)
+ .as("collection of module group packages")
+ .hasSize(1);
+
+ var modulePackage = packages.iterator().next();
+ assertThat(modulePackage.getPathRoot())
+ .as("module group package path root")
+ .isEqualTo(pathRoot);
+ }
+
+ @DisplayName("adding a package to a non-existent output group registers it as a package")
+ @Test
+ void addingPackageToNonExistentOutputGroupRegistersItAsPackage() {
+ // Given
+ var location = StandardLocation.CLASS_OUTPUT;
+ PathRoot pathRoot = somePathRoot();
+
+ // When
+ repository.addPath(location, pathRoot);
+
+ // Then
+ var groups = repository.getOutputContainerGroups();
+ assertThat(groups)
+ .as("output container groups")
+ .hasSize(1);
+
+ var group = groups.iterator().next();
+ assertThat(group.getLocation())
+ .as("output container group location")
+ .isEqualTo(location);
+
+ assertThat(group.getModules())
+ .as("output container group modules")
+ .isEmpty();
+
+ var packages = group.getPackages();
+ assertThat(packages)
+ .as("output container group packages")
+ .hasSize(1);
+
+ var outputPackage = packages.iterator().next();
+ assertThat(outputPackage.getPathRoot())
+ .as("output package path root")
+ .isEqualTo(pathRoot);
+ }
+
+ @DisplayName(
+ "adding an empty directory to a module-oriented group does not register any modules"
+ )
+ @Test
+ void addingEmptyDirectoryToModuleOrientedGroupDoesNotRegisterModules() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ PathRoot pathRoot = somePathRoot();
+
+ moduleDiscoverer.when(() -> ModuleDiscoverer.findModulesIn(any()))
+ .thenReturn(Map.of());
+
+ // When
+ repository.addPath(location, pathRoot);
+
+ // Then
+ var groups = repository.getModuleContainerGroups();
+ assertThat(groups)
+ .as("module container groups")
+ .isEmpty();
+
+ moduleDiscoverer.verify(() -> ModuleDiscoverer.findModulesIn(pathRoot.getPath()));
+ }
+
+ @DisplayName(
+ "adding a directory of modules to a module-oriented group registers them as modules"
+ )
+ @Test
+ void addingDirectoryOfModulesToModuleOrientedGroupRegistersThemAsModules() {
+ // Given
+ var location = StandardLocation.MODULE_SOURCE_PATH;
+ PathRoot pathRoot = somePathRoot();
+
+ var discoveredModules = Stream.of("foo.bar", "baz.bork")
+ .collect(toMap(
+ Function.identity(),
+ pathRoot.getPath()::resolve
+ ));
+
+ moduleDiscoverer.when(() -> ModuleDiscoverer.findModulesIn(any()))
+ .thenReturn(discoveredModules);
+
+ // When
+ repository.addPath(location, pathRoot);
+
+ // Then
+ var groups = repository.getModuleContainerGroups();
+ assertThat(groups)
+ .as("module container groups")
+ .hasSize(1);
+
+ var group = groups.iterator().next();
+ assertThat(group.getLocation())
+ .as("module container group location")
+ .isEqualTo(location);
+
+ var modules = group.getModules();
+ assertThat(modules)
+ .as("module container group modules")
+ .hasSize(2);
+
+ assertThat(modules.get(new ModuleLocation(StandardLocation.MODULE_SOURCE_PATH, "foo.bar")))
+ .as("module container group module ")
+ .extracting(PackageContainerGroup::getPackages, list(Container.class))
+ .singleElement()
+ .extracting(Container::getPathRoot)
+ .extracting(PathRoot::getPath)
+ .isEqualTo(pathRoot.getPath().resolve("foo.bar"));
+
+ assertThat(modules.get(new ModuleLocation(StandardLocation.MODULE_SOURCE_PATH, "baz.bork")))
+ .as("module container group module ")
+ .extracting(PackageContainerGroup::getPackages, list(Container.class))
+ .singleElement()
+ .extracting(Container::getPathRoot)
+ .extracting(PathRoot::getPath)
+ .isEqualTo(pathRoot.getPath().resolve("baz.bork"));
+
+ moduleDiscoverer.verify(() -> ModuleDiscoverer.findModulesIn(pathRoot.getPath()));
+ }
+
+ @DisplayName("adding a package root registers the package root")
+ @Test
+ void addingPackageRootRegistersPackageRoot() {
+
+ }
+ }
+}
diff --git a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/package-info.java b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/package-info.java
new file mode 100644
index 000000000..9351d3aac
--- /dev/null
+++ b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/impl/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 - 2023, the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@NullMarked
+package io.github.ascopes.jct.tests.unit.containers.impl;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/package-info.java b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/package-info.java
new file mode 100644
index 000000000..12e74719e
--- /dev/null
+++ b/java-compiler-testing/src/test/java/io/github/ascopes/jct/tests/unit/containers/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 - 2023, the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@NullMarked
+package io.github.ascopes.jct.tests.unit.containers;
+
+import org.jspecify.annotations.NullMarked;