GH-861 - JUnit change detection now properly detects changes in current working directory.

Added a decorating FileModificationDetector implementation that filters changes to only return those that affect the current working directory, so that the detection works properly if executed from a nested folder in multi-module projects.
This commit is contained in:
Oliver Drotbohm
2024-10-08 11:10:21 +02:00
parent 963ac173e9
commit 9b11606425
6 changed files with 200 additions and 3 deletions

View File

@@ -52,6 +52,10 @@ public interface FileModificationDetector {
* @return will never be {@literal null}.
*/
public static FileModificationDetector getDetector(PropertyResolver propertyResolver) {
return WorkingDirectoryChangesDetector.of(getTargetDetector(propertyResolver));
}
static FileModificationDetector getTargetDetector(PropertyResolver propertyResolver) {
Assert.notNull(propertyResolver, "PropertyResolver must not be null!");

View File

@@ -18,6 +18,7 @@ package org.springframework.modulith.junit.diff;
import java.util.Arrays;
import java.util.stream.Stream;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -39,4 +40,20 @@ public record ModifiedFile(String path) {
public static Stream<ModifiedFile> of(String... paths) {
return Arrays.stream(paths).map(ModifiedFile::new);
}
/**
* Returns the current {@link ModifiedFile} as relative to the given reference path. I.e., a {@code foo/bar.txt} with
* a reference of {@code foo} would result in {@code bar.txt}.
*
* @param reference must not be {@literal null}.
* @return will never be {@literal null}.
*/
ModifiedFile asRelativeTo(String reference) {
Assert.notNull(reference, "Path must not be null!");
Assert.isTrue(reference.startsWith(reference),
() -> "Modified file at %s is not located in %s!".formatted(reference, reference));
return reference.isEmpty() ? this : new ModifiedFile(this.path.substring(reference.length() + 1));
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2024 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
*
* https://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 org.springframework.modulith.junit.diff;
import java.io.File;
import java.util.stream.Stream;
import org.springframework.util.Assert;
/**
* A {@link FileModificationDetector} that filters the {@link ModifiedFile} instances returned by a delegate
* {@link FileModificationDetector} to only contain those nested in the current, repository-relative working directory.
*
* @author Oliver Drotbohm
* @since 1.3
*/
class WorkingDirectoryChangesDetector implements FileModificationDetector {
private final FileModificationDetector delegate;
private final String workingDirectory;
/**
* Creates a new {@link WorkingDirectoryChangesDetector} for the given {@link FileModificationDetector} delegate and
* directory;
*
* @param delegate must not be {@literal null}.
* @param workingDirectory must not be {@literal null}.
*/
WorkingDirectoryChangesDetector(FileModificationDetector delegate, String workingDirectory) {
Assert.notNull(delegate, "FileModificationDetector must not be null!");
Assert.notNull(workingDirectory, "Working folder must not be null!");
this.delegate = delegate;
this.workingDirectory = workingDirectory;
}
/**
* Creates a new {@link WorkingDirectoryChangesDetector} for the current Git repository-relative working directory.
*
* @param delegate must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static WorkingDirectoryChangesDetector of(FileModificationDetector delegate) {
var pathToRepo = JGitUtil.withRepository(it -> it.getDirectory().getParent());
// someFolder/.
var currentWorkingDirectory = new File(".").getAbsolutePath();
// Strip repository base and /.
var repositoryRelative = currentWorkingDirectory.substring(pathToRepo.length() + 1,
currentWorkingDirectory.length() - 2);
return new WorkingDirectoryChangesDetector(delegate, repositoryRelative);
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.junit.diff.FileModificationDetector#getModifiedFiles()
*/
@Override
public Stream<ModifiedFile> getModifiedFiles() {
return delegate.getModifiedFiles()
.filter(it -> it.path().startsWith(workingDirectory))
.map(it -> it.asRelativeTo(workingDirectory));
}
}

View File

@@ -56,9 +56,9 @@ class FileModificationDetectorUnitTests {
@Test // GH-31
void selectingDefaultExplicitlyUsesDefault() {
var explicitDetector = FileModificationDetector.getDetector(setupEnvironment("default", null));
var explicitDetector = FileModificationDetector.getTargetDetector(setupEnvironment("default", null));
assertThat(FileModificationDetector.getDetector(setupEnvironment(null, null)))
assertThat(FileModificationDetector.getTargetDetector(setupEnvironment(null, null)))
.isEqualTo(explicitDetector);
}
@@ -74,7 +74,7 @@ class FileModificationDetectorUnitTests {
var environment = setupEnvironment(detector, referenceCommit);
assertThat(FileModificationDetector.getDetector(environment)).isInstanceOf(expected);
assertThat(FileModificationDetector.getTargetDetector(environment)).isInstanceOf(expected);
}
private static Environment setupEnvironment(String detector, String referenceCommit) {

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2024 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
*
* https://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 org.springframework.modulith.junit.diff;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link ModifiedFile}.
*
* @author Oliver Drotbohm
* @since 1.3
*/
class ModifiedFileUnitTest {
@Test // GH-861
void returnsRelativeFile() {
assertThat(new ModifiedFile("foo/bar.txt").asRelativeTo("foo"))
.isEqualTo(new ModifiedFile("bar.txt"));
}
@Test // GH-861
void returnsFileAsIsForEmptyReference() {
assertThat(new ModifiedFile("foo/bar.txt").asRelativeTo(""))
.isEqualTo(new ModifiedFile("foo/bar.txt"));
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2024 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
*
* https://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 org.springframework.modulith.junit.diff;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* Unit tests for {@link WorkingDirectoryChangesDetector}.
*
* @author Oliver Drotbohm
* @since 1.3
*/
@ExtendWith(MockitoExtension.class)
class WorkingDirectoryChangesDetectorUnitTests {
@Mock FileModificationDetector delegate;
@Test // GH-861
void filtersFilesContainedInReferenceFolder() {
when(delegate.getModifiedFiles())
.thenReturn(Stream.of("rootPom.xml", "nested/nestedPom.xml").map(ModifiedFile::new));
var detector = new WorkingDirectoryChangesDetector(delegate, "nested");
assertThat(detector.getModifiedFiles())
.extracting(ModifiedFile::path)
.containsExactly("nestedPom.xml");
}
}