Skip to content

Commit 5c464e8

Browse files
committed
feat: setup testing env for cli
1 parent 6680e1b commit 5c464e8

File tree

10 files changed

+454
-21
lines changed

10 files changed

+454
-21
lines changed

cli/build.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
plugins {
22
id 'org.graalvm.buildtools.native'
3-
id 'application'
43
id 'com.gradleup.shadow'
54
}
65
apply from: rootProject.file('gradle/changelog.gradle')
@@ -37,6 +36,11 @@ dependencies {
3736
annotationProcessor "info.picocli:picocli-codegen:${VER_PICOCLI}"
3837
}
3938

39+
apply from: rootProject.file('gradle/special-tests.gradle')
40+
tasks.withType(Test).configureEach {
41+
testLogging.showStandardStreams = true
42+
}
43+
4044
compileJava {
4145
// options for picocli codegen
4246
// https://github.com/remkop/picocli/tree/main/picocli-codegen#222-other-options

cli/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.diffplug.spotless.cli;
1717

18+
import java.io.File;
1819
import java.nio.charset.Charset;
1920
import java.nio.file.Path;
2021
import java.util.List;
@@ -26,6 +27,7 @@
2627
import com.diffplug.spotless.LineEnding;
2728
import com.diffplug.spotless.LintState;
2829
import com.diffplug.spotless.ThrowingEx;
30+
import com.diffplug.spotless.cli.core.FileResolver;
2931
import com.diffplug.spotless.cli.core.SpotlessActionContext;
3032
import com.diffplug.spotless.cli.core.TargetFileTypeInferer;
3133
import com.diffplug.spotless.cli.core.TargetResolver;
@@ -52,6 +54,9 @@ public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessAct
5254
@CommandLine.Option(names = {"--mode", "-m"}, defaultValue = "APPLY", description = "The mode to run spotless in." + OptionConstants.VALID_VALUES_SUFFIX + OptionConstants.DEFAULT_VALUE_SUFFIX, scope = CommandLine.ScopeType.INHERIT)
5355
SpotlessMode spotlessMode;
5456

57+
@CommandLine.Option(names = {"--basedir"}, hidden = true, description = "The base directory to run spotless in. Intended for testing purposes only.")
58+
Path baseDir;
59+
5560
@CommandLine.Option(names = {"--target", "-t"}, required = true, arity = "1..*", description = "The target files to format.", scope = CommandLine.ScopeType.INHERIT)
5661
public List<String> targets;
5762

@@ -63,7 +68,7 @@ public class SpotlessCLI implements SpotlessAction, SpotlessCommand, SpotlessAct
6368

6469
@Override
6570
public Integer executeSpotlessAction(@Nonnull List<FormatterStep> formatterSteps) {
66-
TargetResolver targetResolver = new TargetResolver(targets);
71+
TargetResolver targetResolver = targetResolver();
6772

6873
try (Formatter formatter = Formatter.builder()
6974
.lineEndingsPolicy(lineEnding.createPolicy())
@@ -89,20 +94,22 @@ private ResultType handleResult(Formatter formatter, Result result) {
8994
System.err.println("File did not converge: " + result.target.toFile().getName()); // TODO: where to print the output to?
9095
return ResultType.DID_NOT_CONVERGE;
9196
}
92-
this.spotlessMode.handleResult(formatter, result);
93-
return ResultType.DIRTY;
94-
97+
return this.spotlessMode.handleResult(formatter, result);
9598
}
9699

97100
private TargetResolver targetResolver() {
98-
return new TargetResolver(targets);
101+
return new TargetResolver(baseDir == null ? Path.of(File.separator) : baseDir, targets);
102+
}
103+
104+
private Path baseDir() {
105+
return baseDir == null ? Path.of(File.separator) : baseDir;
99106
}
100107

101108
@Override
102109
public SpotlessActionContext spotlessActionContext() {
103110
TargetResolver targetResolver = targetResolver();
104111
TargetFileTypeInferer targetFileTypeInferer = new TargetFileTypeInferer(targetResolver);
105-
return new SpotlessActionContext(targetFileTypeInferer.inferTargetFileType());
112+
return new SpotlessActionContext(targetFileTypeInferer.inferTargetFileType(), new FileResolver(baseDir()));
106113
}
107114

108115
public static void main(String... args) {
@@ -113,22 +120,31 @@ public static void main(String... args) {
113120
args = new String[]{"--mode=CHECK", "--target", "src/poc/java/**/*.java", "--encoding=UTF-8", "license-header", "--header", "abc", "license-header", "--header-file", "TestHeader.txt"};
114121
// args = new String[]{"--version"};
115122
}
116-
int exitCode = new CommandLine(new SpotlessCLI())
117-
.setExecutionStrategy(new SpotlessExecutionStrategy())
118-
.setCaseInsensitiveEnumValuesAllowed(true)
123+
int exitCode = createCommandLine(createInstance())
119124
.execute(args);
120125
System.exit(exitCode);
121126
}
122127

128+
static SpotlessCLI createInstance() {
129+
return new SpotlessCLI();
130+
}
131+
132+
static CommandLine createCommandLine(SpotlessCLI spotlessCLI) {
133+
return new CommandLine(spotlessCLI)
134+
.setExecutionStrategy(new SpotlessExecutionStrategy())
135+
.setCaseInsensitiveEnumValuesAllowed(true);
136+
}
137+
123138
private enum SpotlessMode {
124139
CHECK {
125140
@Override
126-
void handleResult(Formatter formatter, Result result) {
141+
ResultType handleResult(Formatter formatter, Result result) {
127142
if (result.lintState.isHasLints()) {
128143
result.lintState.asStringOneLine(result.target.toFile(), formatter);
129144
} else {
130145
System.out.println(String.format("%s is violating formatting rules.", result.target));
131146
}
147+
return ResultType.DIRTY;
132148
}
133149

134150
@Override
@@ -147,8 +163,14 @@ Integer translateResultTypeToExitCode(ResultType resultType) {
147163
},
148164
APPLY {
149165
@Override
150-
void handleResult(Formatter formatter, Result result) {
166+
ResultType handleResult(Formatter formatter, Result result) {
167+
if (result.lintState.isHasLints()) {
168+
// something went wrong, we should not apply the changes
169+
System.err.println("File has lints: " + result.target.toFile().getName());
170+
return ResultType.DIRTY;
171+
}
151172
ThrowingEx.run(() -> result.lintState.getDirtyState().writeCanonicalTo(result.target.toFile()));
173+
return ResultType.CLEAN;
152174
}
153175

154176
@Override
@@ -166,7 +188,7 @@ Integer translateResultTypeToExitCode(ResultType resultType) {
166188
}
167189
};
168190

169-
abstract void handleResult(Formatter formatter, Result result);
191+
abstract ResultType handleResult(Formatter formatter, Result result);
170192

171193
abstract Integer translateResultTypeToExitCode(ResultType resultType);
172194

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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 com.diffplug.spotless.cli.core;
17+
18+
import java.io.File;
19+
import java.nio.file.Path;
20+
import java.util.Objects;
21+
22+
import javax.annotation.Nonnull;
23+
24+
public class FileResolver {
25+
private final Path baseDir;
26+
27+
public FileResolver(@Nonnull Path baseDir) {
28+
this.baseDir = Objects.requireNonNull(baseDir);
29+
}
30+
31+
Path baseDir() {
32+
return baseDir;
33+
}
34+
35+
public File resolveFile(File file) {
36+
return resolvePath(file.toPath()).toFile();
37+
}
38+
39+
public Path resolvePath(Path path) {
40+
if (path.isAbsolute()) {
41+
return path;
42+
}
43+
return baseDir.resolve(path);
44+
}
45+
}

cli/src/main/java/com/diffplug/spotless/cli/core/SpotlessActionContext.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,32 @@
1515
*/
1616
package com.diffplug.spotless.cli.core;
1717

18+
import java.io.File;
19+
import java.nio.file.Path;
1820
import java.util.Objects;
1921

2022
import javax.annotation.Nonnull;
2123

2224
public class SpotlessActionContext {
2325

2426
private final TargetFileTypeInferer.TargetFileType targetFileType;
27+
private final FileResolver fileResolver;
2528

26-
public SpotlessActionContext(@Nonnull TargetFileTypeInferer.TargetFileType targetFileType) {
29+
public SpotlessActionContext(@Nonnull TargetFileTypeInferer.TargetFileType targetFileType, @Nonnull FileResolver fileResolver) {
2730
this.targetFileType = Objects.requireNonNull(targetFileType);
31+
this.fileResolver = fileResolver;
2832
}
2933

3034
@Nonnull
3135
public TargetFileTypeInferer.TargetFileType targetFileType() {
3236
return targetFileType;
3337
}
38+
39+
public File resolveFile(File file) {
40+
return fileResolver.resolveFile(file);
41+
}
42+
43+
public Path resolvePath(Path path) {
44+
return fileResolver.resolvePath(path);
45+
}
3446
}

cli/src/main/java/com/diffplug/spotless/cli/core/TargetResolver.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@
2929
import java.util.ArrayList;
3030
import java.util.EnumSet;
3131
import java.util.List;
32+
import java.util.Objects;
3233
import java.util.stream.Collectors;
3334
import java.util.stream.Stream;
3435

36+
import javax.annotation.Nonnull;
37+
3538
import com.diffplug.spotless.ThrowingEx;
3639

3740
public class TargetResolver {
3841

3942
private final List<String> targets;
4043

41-
public TargetResolver(List<String> targets) {
42-
this.targets = targets;
44+
private final FileResolver fileResolver;
45+
46+
public TargetResolver(@Nonnull Path baseDir, @Nonnull List<String> targets) {
47+
this.fileResolver = new FileResolver(baseDir);
48+
this.targets = Objects.requireNonNull(targets);
4349
}
4450

4551
public Stream<Path> resolveTargets() {
@@ -54,7 +60,15 @@ private Stream<Path> resolveTarget(String target) {
5460
if (isGlob) {
5561
return resolveGlob(target);
5662
}
57-
return resolveDir(Path.of(target));
63+
Path targetPath = fileResolver.resolvePath(Path.of(target));
64+
if (Files.isReadable(targetPath)) {
65+
return Stream.of(targetPath);
66+
}
67+
if (Files.isDirectory(targetPath)) {
68+
return resolveDir(targetPath);
69+
}
70+
// TODO log warn?
71+
return Stream.empty();
5872
}
5973

6074
private Stream<Path> resolveDir(Path startDir) {
@@ -81,7 +95,7 @@ private Stream<Path> resolveGlob(String glob) {
8195
.takeWhile(not(TargetResolver::isGlobPathPart))
8296
.collect(Collectors.toList());
8397

84-
startDir = Path.of(glob.startsWith(File.separator) ? File.separator : "", startDirParts.toArray(String[]::new));
98+
startDir = Path.of(glob.startsWith(File.separator) ? File.separator : fileResolver.baseDir().toString(), startDirParts.toArray(String[]::new));
8599
globPart = Stream.of(parts)
86100
.skip(startDirParts.size())
87101
.collect(Collectors.joining(File.separator));

cli/src/main/java/com/diffplug/spotless/cli/steps/generic/LicenseHeader.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ static class LicenseHeaderSourceOption {
5252
@Nonnull
5353
@Override
5454
public List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
55-
FormatterStep licenseHeaderStep = LicenseHeaderStep.headerDelimiter(headerSource(), delimiter(context.targetFileType()))
55+
FormatterStep licenseHeaderStep = LicenseHeaderStep.headerDelimiter(headerSource(context), delimiter(context.targetFileType()))
5656
// TODO add more config options
5757
.build();
5858
return List.of(licenseHeaderStep);
5959
}
6060

61-
private ThrowingEx.Supplier<String> headerSource() {
61+
private ThrowingEx.Supplier<String> headerSource(SpotlessActionContext context) {
6262
if (licenseHeaderSourceOption.header != null) {
6363
return () -> licenseHeaderSourceOption.header;
6464
} else {
65-
return () -> ThrowingEx.get(() -> Files.readString(licenseHeaderSourceOption.headerFile.toPath()));
65+
return () -> ThrowingEx.get(() -> Files.readString(context.resolveFile(licenseHeaderSourceOption.headerFile).toPath()));
6666
}
6767
}
6868

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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 com.diffplug.spotless.cli;
17+
18+
import java.io.IOException;
19+
20+
import org.junit.jupiter.api.BeforeEach;
21+
22+
import com.diffplug.spotless.ResourceHarness;
23+
24+
public abstract class CLIIntegrationHarness extends ResourceHarness {
25+
26+
/**
27+
* Each test gets its own temp folder, and we create a gradle
28+
* build there and run it.
29+
* <p>
30+
* Because those test folders don't have a .gitattributes file,
31+
* git (on windows) will default to \r\n. So now if you read a
32+
* test file from the spotless test resources, and compare it
33+
* to a build result, the line endings won't match.
34+
* <p>
35+
* By sticking this .gitattributes file into the test directory,
36+
* we ensure that the default Spotless line endings policy of
37+
* GIT_ATTRIBUTES will use \n, so that tests match the test
38+
* resources on win and linux.
39+
*/
40+
@BeforeEach
41+
void gitAttributes() throws IOException {
42+
setFile(".gitattributes").toContent("* text eol=lf");
43+
}
44+
45+
protected SpotlessCLIRunner cliRunner() {
46+
return SpotlessCLIRunner.create()
47+
.withWorkingDir(rootFolder());
48+
}
49+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2024 DiffPlug
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+
* http://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 com.diffplug.spotless.cli;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
public class SpotlessCLIHelpAndVersionTest extends CLIIntegrationHarness {
23+
24+
@Test
25+
void testHelp() {
26+
SpotlessCLIRunner.Result result = cliRunner().withOption("--help").run();
27+
assertThat(result.stdOut()).contains("Usage: spotless");
28+
}
29+
30+
@Test
31+
void testVersion() {
32+
SpotlessCLIRunner.Result result = cliRunner().withOption("--version").run();
33+
assertThat(result.stdOut()).contains("Spotless CLI version");
34+
}
35+
36+
}

0 commit comments

Comments
 (0)