Skip to content

Commit

Permalink
feat(assert): added fileNotExists (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
itzg authored Sep 10, 2022
1 parent 0779252 commit 705b1fc
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@Command(name = "assert", description = "Provides assertion operators for verifying container setup",
subcommands = {
FileExists.class,
FileNotExists.class,
JsonPathEquals.class,
PropertyEquals.class,
}
Expand Down
75 changes: 75 additions & 0 deletions src/main/java/me/itzg/helpers/assertcmd/EvalExistence.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package me.itzg.helpers.assertcmd;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;

class EvalExistence {

private static final Pattern globSymbols = Pattern.compile("[*?]|\\{.+?}");
// matches everything up to the last path separator either / or \
private static final Pattern pathSeparators = Pattern.compile(".*[/\\\\]");

static boolean exists(String pathSpec) throws IOException {
return !matchingPaths(pathSpec).paths.isEmpty();
}

@AllArgsConstructor
static class MatchingPaths {

final boolean globbing;
final List<Path> paths;
}

static MatchingPaths matchingPaths(String pathSpec) throws IOException {
final Matcher globMatcher = globSymbols.matcher(pathSpec);
// find the first globbing symbol
final boolean hasGlob = globMatcher.find();
if (!hasGlob) {
// no globbing, just a specific path
final Path path = Paths.get(pathSpec);
return new MatchingPaths(false,
Files.exists(path) ? Collections.singletonList(path) : Collections.emptyList()
);
}

// find last path separator in the text before the glob
final Matcher sepMatcher = pathSeparators.matcher(pathSpec.substring(0, globMatcher.start()));
final Path walkStart;
// ...by looking from the start of the string
if (sepMatcher.lookingAt()) {
// ...and grabbing the end of the matched text
walkStart = Paths.get(pathSpec.substring(0, sepMatcher.end()));
}
else {
// no separator, so process relative paths
walkStart = Paths.get("");
}

final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
"glob:" +
// escape any Windows backslashes
pathSpec.replace("\\", "\\\\")
);
try (Stream<Path> pathStream = Files.walk(walkStart)) {
return new MatchingPaths(true,
pathStream
.filter(Files::isRegularFile)
.filter(pathMatcher::matches)
.collect(Collectors.toList())
);
}

}

}
46 changes: 1 addition & 45 deletions src/main/java/me/itzg/helpers/assertcmd/FileExists.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
package me.itzg.helpers.assertcmd;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import picocli.CommandLine.Command;
import picocli.CommandLine.ExitCode;
import picocli.CommandLine.Parameters;

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

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

if (paths != null) {
for (String path : paths) {
if (!exists(path)) {
if (!EvalExistence.exists(path)) {
System.err.printf("%s does not exist%n", path);
missing = true;
}
Expand All @@ -40,37 +28,5 @@ public Integer call() throws Exception {
return missing ? ExitCode.SOFTWARE : ExitCode.OK;
}

private boolean exists(String pathSpec) throws IOException {
final Matcher globMatcher = globSymbols.matcher(pathSpec);
// find the first globbing symbol
if (!globMatcher.find()) {
// no globbing, just a specific path
return Files.exists(Paths.get(pathSpec));
}

// find last path separator in the text before the glob
final Matcher sepMatcher = pathSeparators.matcher(pathSpec.substring(0, globMatcher.start()));
final Path walkStart;
// ...by looking from the start of the string
if (sepMatcher.lookingAt()) {
// ...and grabbing the end of the matched text
walkStart = Paths.get(pathSpec.substring(0, sepMatcher.end()));
}
else {
// no separator, so process relative paths
walkStart = Paths.get("");
}

final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(
"glob:" +
// escape any Windows backslashes
pathSpec.replace("\\", "\\\\")
);
try (Stream<Path> pathStream = Files.walk(walkStart)) {
return pathStream
.filter(Files::isRegularFile)
.anyMatch(pathMatcher::matches);
}
}

}
38 changes: 38 additions & 0 deletions src/main/java/me/itzg/helpers/assertcmd/FileNotExists.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package me.itzg.helpers.assertcmd;

import java.util.List;
import java.util.concurrent.Callable;
import me.itzg.helpers.assertcmd.EvalExistence.MatchingPaths;
import picocli.CommandLine.Command;
import picocli.CommandLine.ExitCode;
import picocli.CommandLine.Parameters;

@Command(name = "fileNotExists")
class FileNotExists implements Callable<Integer> {

@Parameters
List<String> paths;

@Override
public Integer call() throws Exception {
boolean failed = false;

if (paths != null) {
for (String path : paths) {
final MatchingPaths matchingPaths = EvalExistence.matchingPaths(path);
if (!matchingPaths.paths.isEmpty()) {
if (matchingPaths.globbing) {
System.err.printf("The files %s exist looking at %s%n", matchingPaths.paths, path);
}
else {
System.err.printf("%s exists%n", path);
}
failed = true;
}
}
}

return failed ? ExitCode.SOFTWARE : ExitCode.OK;
}

}
14 changes: 14 additions & 0 deletions src/test/java/me/itzg/helpers/MoreAssertions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.itzg.helpers;

import java.util.Arrays;
import org.assertj.core.api.ListAssert;

public class MoreAssertions {

public static ListAssert<String> assertThatLines(String content) {
final String[] lines = content.split("\n|\r\n|\r");
return new ListAssert<>(Arrays.stream(lines, 0,
lines[lines.length - 1].isEmpty() ? lines.length - 1 : lines.length
));
}
}
87 changes: 87 additions & 0 deletions src/test/java/me/itzg/helpers/assertcmd/FileNotExistsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package me.itzg.helpers.assertcmd;

import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemErr;
import static me.itzg.helpers.MoreAssertions.assertThatLines;
import static org.assertj.core.api.Assertions.assertThat;

import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import picocli.CommandLine;
import picocli.CommandLine.ExitCode;

class FileNotExistsTest {

@Test
void failsWhenAnyExist(@TempDir Path tempDir) throws Exception {
final Path file1 = Files.createFile(tempDir.resolve("file1"));
final Path file2 = tempDir.resolve("file2");

final String errOut = tapSystemErr(() -> {
int exitCode = new CommandLine(new FileNotExists())
.execute(
file1.toString(),
file2.toString()
);

assertThat(exitCode).isEqualTo(ExitCode.SOFTWARE);
});

assertThatLines(errOut)
.contains(file1 +" exists");
}

@Test
void passesWhenAllMissing(@TempDir Path tempDir) throws Exception {
final Path pathA = tempDir.resolve("fileA");
final Path fileB = tempDir.resolve("fileB");

final String errOut = tapSystemErr(() -> {
int exitCode = new CommandLine(new FileNotExists())
.execute(
pathA.toString(),
fileB.toString()
);

assertThat(exitCode).isEqualTo(ExitCode.OK);
});

assertThat(errOut).isBlank();
}

@Test
void failsWhenGlobFindsAnyFiles(@TempDir Path tempDir) throws Exception {
final Path file1 = Files.createFile(tempDir.resolve("file1"));

final String errOut = tapSystemErr(() -> {
int exitCode = new CommandLine(new FileNotExists())
.execute(
String.format("%s/file*", tempDir)
);

assertThat(exitCode).isEqualTo(ExitCode.SOFTWARE);
});

assertThatLines(errOut)
.hasSize(1)
.element(0)
.asString()
.contains(file1.toString());
}

@Test
void passesWhenGlobFindsNothing(@TempDir Path tempDir) throws Exception {
final String errOut = tapSystemErr(() -> {
int exitCode = new CommandLine(new FileNotExists())
.execute(
// working directory is top of project
tempDir+"/*.md"
);

assertThat(exitCode).isEqualTo(0);
});

assertThat(errOut).isBlank();
}
}
Loading

0 comments on commit 705b1fc

Please sign in to comment.