Skip to content

Commit 9b11606

Browse files
committed
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.
1 parent 963ac17 commit 9b11606

File tree

6 files changed

+200
-3
lines changed

6 files changed

+200
-3
lines changed

spring-modulith-junit/src/main/java/org/springframework/modulith/junit/diff/FileModificationDetector.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public interface FileModificationDetector {
5252
* @return will never be {@literal null}.
5353
*/
5454
public static FileModificationDetector getDetector(PropertyResolver propertyResolver) {
55+
return WorkingDirectoryChangesDetector.of(getTargetDetector(propertyResolver));
56+
}
57+
58+
static FileModificationDetector getTargetDetector(PropertyResolver propertyResolver) {
5559

5660
Assert.notNull(propertyResolver, "PropertyResolver must not be null!");
5761

spring-modulith-junit/src/main/java/org/springframework/modulith/junit/diff/ModifiedFile.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Arrays;
1919
import java.util.stream.Stream;
2020

21+
import org.springframework.util.Assert;
2122
import org.springframework.util.StringUtils;
2223

2324
/**
@@ -39,4 +40,20 @@ public boolean isJavaSource() {
3940
public static Stream<ModifiedFile> of(String... paths) {
4041
return Arrays.stream(paths).map(ModifiedFile::new);
4142
}
43+
44+
/**
45+
* Returns the current {@link ModifiedFile} as relative to the given reference path. I.e., a {@code foo/bar.txt} with
46+
* a reference of {@code foo} would result in {@code bar.txt}.
47+
*
48+
* @param reference must not be {@literal null}.
49+
* @return will never be {@literal null}.
50+
*/
51+
ModifiedFile asRelativeTo(String reference) {
52+
53+
Assert.notNull(reference, "Path must not be null!");
54+
Assert.isTrue(reference.startsWith(reference),
55+
() -> "Modified file at %s is not located in %s!".formatted(reference, reference));
56+
57+
return reference.isEmpty() ? this : new ModifiedFile(this.path.substring(reference.length() + 1));
58+
}
4259
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.junit.diff;
17+
18+
import java.io.File;
19+
import java.util.stream.Stream;
20+
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* A {@link FileModificationDetector} that filters the {@link ModifiedFile} instances returned by a delegate
25+
* {@link FileModificationDetector} to only contain those nested in the current, repository-relative working directory.
26+
*
27+
* @author Oliver Drotbohm
28+
* @since 1.3
29+
*/
30+
class WorkingDirectoryChangesDetector implements FileModificationDetector {
31+
32+
private final FileModificationDetector delegate;
33+
private final String workingDirectory;
34+
35+
/**
36+
* Creates a new {@link WorkingDirectoryChangesDetector} for the given {@link FileModificationDetector} delegate and
37+
* directory;
38+
*
39+
* @param delegate must not be {@literal null}.
40+
* @param workingDirectory must not be {@literal null}.
41+
*/
42+
WorkingDirectoryChangesDetector(FileModificationDetector delegate, String workingDirectory) {
43+
44+
Assert.notNull(delegate, "FileModificationDetector must not be null!");
45+
Assert.notNull(workingDirectory, "Working folder must not be null!");
46+
47+
this.delegate = delegate;
48+
this.workingDirectory = workingDirectory;
49+
}
50+
51+
/**
52+
* Creates a new {@link WorkingDirectoryChangesDetector} for the current Git repository-relative working directory.
53+
*
54+
* @param delegate must not be {@literal null}.
55+
* @return will never be {@literal null}.
56+
*/
57+
public static WorkingDirectoryChangesDetector of(FileModificationDetector delegate) {
58+
59+
var pathToRepo = JGitUtil.withRepository(it -> it.getDirectory().getParent());
60+
61+
// someFolder/.
62+
var currentWorkingDirectory = new File(".").getAbsolutePath();
63+
64+
// Strip repository base and /.
65+
var repositoryRelative = currentWorkingDirectory.substring(pathToRepo.length() + 1,
66+
currentWorkingDirectory.length() - 2);
67+
68+
return new WorkingDirectoryChangesDetector(delegate, repositoryRelative);
69+
}
70+
71+
/*
72+
* (non-Javadoc)
73+
* @see org.springframework.modulith.junit.diff.FileModificationDetector#getModifiedFiles()
74+
*/
75+
@Override
76+
public Stream<ModifiedFile> getModifiedFiles() {
77+
78+
return delegate.getModifiedFiles()
79+
.filter(it -> it.path().startsWith(workingDirectory))
80+
.map(it -> it.asRelativeTo(workingDirectory));
81+
}
82+
}

spring-modulith-junit/src/test/java/org/springframework/modulith/junit/diff/FileModificationDetectorUnitTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ void registersCustomDetectorByType() {
5656
@Test // GH-31
5757
void selectingDefaultExplicitlyUsesDefault() {
5858

59-
var explicitDetector = FileModificationDetector.getDetector(setupEnvironment("default", null));
59+
var explicitDetector = FileModificationDetector.getTargetDetector(setupEnvironment("default", null));
6060

61-
assertThat(FileModificationDetector.getDetector(setupEnvironment(null, null)))
61+
assertThat(FileModificationDetector.getTargetDetector(setupEnvironment(null, null)))
6262
.isEqualTo(explicitDetector);
6363
}
6464

@@ -74,7 +74,7 @@ private static void assertDetector(Class<?> expected, String detector, String re
7474

7575
var environment = setupEnvironment(detector, referenceCommit);
7676

77-
assertThat(FileModificationDetector.getDetector(environment)).isInstanceOf(expected);
77+
assertThat(FileModificationDetector.getTargetDetector(environment)).isInstanceOf(expected);
7878
}
7979

8080
private static Environment setupEnvironment(String detector, String referenceCommit) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.junit.diff;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
/**
23+
* Unit tests for {@link ModifiedFile}.
24+
*
25+
* @author Oliver Drotbohm
26+
* @since 1.3
27+
*/
28+
class ModifiedFileUnitTest {
29+
30+
@Test // GH-861
31+
void returnsRelativeFile() {
32+
33+
assertThat(new ModifiedFile("foo/bar.txt").asRelativeTo("foo"))
34+
.isEqualTo(new ModifiedFile("bar.txt"));
35+
}
36+
37+
@Test // GH-861
38+
void returnsFileAsIsForEmptyReference() {
39+
40+
assertThat(new ModifiedFile("foo/bar.txt").asRelativeTo(""))
41+
.isEqualTo(new ModifiedFile("foo/bar.txt"));
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.junit.diff;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import java.util.stream.Stream;
22+
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.ExtendWith;
25+
import org.mockito.Mock;
26+
import org.mockito.junit.jupiter.MockitoExtension;
27+
28+
/**
29+
* Unit tests for {@link WorkingDirectoryChangesDetector}.
30+
*
31+
* @author Oliver Drotbohm
32+
* @since 1.3
33+
*/
34+
@ExtendWith(MockitoExtension.class)
35+
class WorkingDirectoryChangesDetectorUnitTests {
36+
37+
@Mock FileModificationDetector delegate;
38+
39+
@Test // GH-861
40+
void filtersFilesContainedInReferenceFolder() {
41+
42+
when(delegate.getModifiedFiles())
43+
.thenReturn(Stream.of("rootPom.xml", "nested/nestedPom.xml").map(ModifiedFile::new));
44+
45+
var detector = new WorkingDirectoryChangesDetector(delegate, "nested");
46+
47+
assertThat(detector.getModifiedFiles())
48+
.extracting(ModifiedFile::path)
49+
.containsExactly("nestedPom.xml");
50+
}
51+
}

0 commit comments

Comments
 (0)