Skip to content

Commit 705b1fc

Browse files
authored
feat(assert): added fileNotExists (#60)
1 parent 0779252 commit 705b1fc

File tree

7 files changed

+233
-69
lines changed

7 files changed

+233
-69
lines changed

src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@Command(name = "assert", description = "Provides assertion operators for verifying container setup",
66
subcommands = {
77
FileExists.class,
8+
FileNotExists.class,
89
JsonPathEquals.class,
910
PropertyEquals.class,
1011
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package me.itzg.helpers.assertcmd;
2+
3+
import java.io.IOException;
4+
import java.nio.file.FileSystems;
5+
import java.nio.file.Files;
6+
import java.nio.file.Path;
7+
import java.nio.file.PathMatcher;
8+
import java.nio.file.Paths;
9+
import java.util.Collections;
10+
import java.util.List;
11+
import java.util.regex.Matcher;
12+
import java.util.regex.Pattern;
13+
import java.util.stream.Collectors;
14+
import java.util.stream.Stream;
15+
import lombok.AllArgsConstructor;
16+
17+
class EvalExistence {
18+
19+
private static final Pattern globSymbols = Pattern.compile("[*?]|\\{.+?}");
20+
// matches everything up to the last path separator either / or \
21+
private static final Pattern pathSeparators = Pattern.compile(".*[/\\\\]");
22+
23+
static boolean exists(String pathSpec) throws IOException {
24+
return !matchingPaths(pathSpec).paths.isEmpty();
25+
}
26+
27+
@AllArgsConstructor
28+
static class MatchingPaths {
29+
30+
final boolean globbing;
31+
final List<Path> paths;
32+
}
33+
34+
static MatchingPaths matchingPaths(String pathSpec) throws IOException {
35+
final Matcher globMatcher = globSymbols.matcher(pathSpec);
36+
// find the first globbing symbol
37+
final boolean hasGlob = globMatcher.find();
38+
if (!hasGlob) {
39+
// no globbing, just a specific path
40+
final Path path = Paths.get(pathSpec);
41+
return new MatchingPaths(false,
42+
Files.exists(path) ? Collections.singletonList(path) : Collections.emptyList()
43+
);
44+
}
45+
46+
// find last path separator in the text before the glob
47+
final Matcher sepMatcher = pathSeparators.matcher(pathSpec.substring(0, globMatcher.start()));
48+
final Path walkStart;
49+
// ...by looking from the start of the string
50+
if (sepMatcher.lookingAt()) {
51+
// ...and grabbing the end of the matched text
52+
walkStart = Paths.get(pathSpec.substring(0, sepMatcher.end()));
53+
}
54+
else {
55+
// no separator, so process relative paths
56+
walkStart = Paths.get("");
57+
}
58+
59+
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
60+
"glob:" +
61+
// escape any Windows backslashes
62+
pathSpec.replace("\\", "\\\\")
63+
);
64+
try (Stream<Path> pathStream = Files.walk(walkStart)) {
65+
return new MatchingPaths(true,
66+
pathStream
67+
.filter(Files::isRegularFile)
68+
.filter(pathMatcher::matches)
69+
.collect(Collectors.toList())
70+
);
71+
}
72+
73+
}
74+
75+
}
Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,13 @@
11
package me.itzg.helpers.assertcmd;
22

3-
import java.io.IOException;
4-
import java.nio.file.FileSystems;
5-
import java.nio.file.Files;
6-
import java.nio.file.Path;
7-
import java.nio.file.PathMatcher;
8-
import java.nio.file.Paths;
93
import java.util.List;
104
import java.util.concurrent.Callable;
11-
import java.util.regex.Matcher;
12-
import java.util.regex.Pattern;
13-
import java.util.stream.Stream;
145
import picocli.CommandLine.Command;
156
import picocli.CommandLine.ExitCode;
167
import picocli.CommandLine.Parameters;
178

189
@Command(name = "fileExists")
1910
class FileExists implements Callable<Integer> {
20-
private static final Pattern globSymbols = Pattern.compile("[*?]|\\{.+?}");
21-
// matches everything up to the last path separator either / or \
22-
private static final Pattern pathSeparators = Pattern.compile(".*[/\\\\]");
2311

2412
@Parameters
2513
List<String> paths;
@@ -30,7 +18,7 @@ public Integer call() throws Exception {
3018

3119
if (paths != null) {
3220
for (String path : paths) {
33-
if (!exists(path)) {
21+
if (!EvalExistence.exists(path)) {
3422
System.err.printf("%s does not exist%n", path);
3523
missing = true;
3624
}
@@ -40,37 +28,5 @@ public Integer call() throws Exception {
4028
return missing ? ExitCode.SOFTWARE : ExitCode.OK;
4129
}
4230

43-
private boolean exists(String pathSpec) throws IOException {
44-
final Matcher globMatcher = globSymbols.matcher(pathSpec);
45-
// find the first globbing symbol
46-
if (!globMatcher.find()) {
47-
// no globbing, just a specific path
48-
return Files.exists(Paths.get(pathSpec));
49-
}
50-
51-
// find last path separator in the text before the glob
52-
final Matcher sepMatcher = pathSeparators.matcher(pathSpec.substring(0, globMatcher.start()));
53-
final Path walkStart;
54-
// ...by looking from the start of the string
55-
if (sepMatcher.lookingAt()) {
56-
// ...and grabbing the end of the matched text
57-
walkStart = Paths.get(pathSpec.substring(0, sepMatcher.end()));
58-
}
59-
else {
60-
// no separator, so process relative paths
61-
walkStart = Paths.get("");
62-
}
63-
64-
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
65-
"glob:" +
66-
// escape any Windows backslashes
67-
pathSpec.replace("\\", "\\\\")
68-
);
69-
try (Stream<Path> pathStream = Files.walk(walkStart)) {
70-
return pathStream
71-
.filter(Files::isRegularFile)
72-
.anyMatch(pathMatcher::matches);
73-
}
74-
}
7531

7632
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package me.itzg.helpers.assertcmd;
2+
3+
import java.util.List;
4+
import java.util.concurrent.Callable;
5+
import me.itzg.helpers.assertcmd.EvalExistence.MatchingPaths;
6+
import picocli.CommandLine.Command;
7+
import picocli.CommandLine.ExitCode;
8+
import picocli.CommandLine.Parameters;
9+
10+
@Command(name = "fileNotExists")
11+
class FileNotExists implements Callable<Integer> {
12+
13+
@Parameters
14+
List<String> paths;
15+
16+
@Override
17+
public Integer call() throws Exception {
18+
boolean failed = false;
19+
20+
if (paths != null) {
21+
for (String path : paths) {
22+
final MatchingPaths matchingPaths = EvalExistence.matchingPaths(path);
23+
if (!matchingPaths.paths.isEmpty()) {
24+
if (matchingPaths.globbing) {
25+
System.err.printf("The files %s exist looking at %s%n", matchingPaths.paths, path);
26+
}
27+
else {
28+
System.err.printf("%s exists%n", path);
29+
}
30+
failed = true;
31+
}
32+
}
33+
}
34+
35+
return failed ? ExitCode.SOFTWARE : ExitCode.OK;
36+
}
37+
38+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package me.itzg.helpers;
2+
3+
import java.util.Arrays;
4+
import org.assertj.core.api.ListAssert;
5+
6+
public class MoreAssertions {
7+
8+
public static ListAssert<String> assertThatLines(String content) {
9+
final String[] lines = content.split("\n|\r\n|\r");
10+
return new ListAssert<>(Arrays.stream(lines, 0,
11+
lines[lines.length - 1].isEmpty() ? lines.length - 1 : lines.length
12+
));
13+
}
14+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package me.itzg.helpers.assertcmd;
2+
3+
import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemErr;
4+
import static me.itzg.helpers.MoreAssertions.assertThatLines;
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import org.junit.jupiter.api.Test;
10+
import org.junit.jupiter.api.io.TempDir;
11+
import picocli.CommandLine;
12+
import picocli.CommandLine.ExitCode;
13+
14+
class FileNotExistsTest {
15+
16+
@Test
17+
void failsWhenAnyExist(@TempDir Path tempDir) throws Exception {
18+
final Path file1 = Files.createFile(tempDir.resolve("file1"));
19+
final Path file2 = tempDir.resolve("file2");
20+
21+
final String errOut = tapSystemErr(() -> {
22+
int exitCode = new CommandLine(new FileNotExists())
23+
.execute(
24+
file1.toString(),
25+
file2.toString()
26+
);
27+
28+
assertThat(exitCode).isEqualTo(ExitCode.SOFTWARE);
29+
});
30+
31+
assertThatLines(errOut)
32+
.contains(file1 +" exists");
33+
}
34+
35+
@Test
36+
void passesWhenAllMissing(@TempDir Path tempDir) throws Exception {
37+
final Path pathA = tempDir.resolve("fileA");
38+
final Path fileB = tempDir.resolve("fileB");
39+
40+
final String errOut = tapSystemErr(() -> {
41+
int exitCode = new CommandLine(new FileNotExists())
42+
.execute(
43+
pathA.toString(),
44+
fileB.toString()
45+
);
46+
47+
assertThat(exitCode).isEqualTo(ExitCode.OK);
48+
});
49+
50+
assertThat(errOut).isBlank();
51+
}
52+
53+
@Test
54+
void failsWhenGlobFindsAnyFiles(@TempDir Path tempDir) throws Exception {
55+
final Path file1 = Files.createFile(tempDir.resolve("file1"));
56+
57+
final String errOut = tapSystemErr(() -> {
58+
int exitCode = new CommandLine(new FileNotExists())
59+
.execute(
60+
String.format("%s/file*", tempDir)
61+
);
62+
63+
assertThat(exitCode).isEqualTo(ExitCode.SOFTWARE);
64+
});
65+
66+
assertThatLines(errOut)
67+
.hasSize(1)
68+
.element(0)
69+
.asString()
70+
.contains(file1.toString());
71+
}
72+
73+
@Test
74+
void passesWhenGlobFindsNothing(@TempDir Path tempDir) throws Exception {
75+
final String errOut = tapSystemErr(() -> {
76+
int exitCode = new CommandLine(new FileNotExists())
77+
.execute(
78+
// working directory is top of project
79+
tempDir+"/*.md"
80+
);
81+
82+
assertThat(exitCode).isEqualTo(0);
83+
});
84+
85+
assertThat(errOut).isBlank();
86+
}
87+
}

0 commit comments

Comments
 (0)