From 705b1fc8ec446fcf5eaada89fb5c54c4d4a1cecd Mon Sep 17 00:00:00 2001 From: Geoff Bourne Date: Sat, 10 Sep 2022 11:51:27 -0500 Subject: [PATCH] feat(assert): added fileNotExists (#60) --- .../itzg/helpers/assertcmd/AssertCommand.java | 1 + .../itzg/helpers/assertcmd/EvalExistence.java | 75 ++++++++++++++++ .../me/itzg/helpers/assertcmd/FileExists.java | 46 +--------- .../itzg/helpers/assertcmd/FileNotExists.java | 38 ++++++++ .../java/me/itzg/helpers/MoreAssertions.java | 14 +++ .../helpers/assertcmd/FileNotExistsTest.java | 87 +++++++++++++++++++ .../me/itzg/helpers/find/FindCommandTest.java | 41 ++++----- 7 files changed, 233 insertions(+), 69 deletions(-) create mode 100644 src/main/java/me/itzg/helpers/assertcmd/EvalExistence.java create mode 100644 src/main/java/me/itzg/helpers/assertcmd/FileNotExists.java create mode 100644 src/test/java/me/itzg/helpers/MoreAssertions.java create mode 100644 src/test/java/me/itzg/helpers/assertcmd/FileNotExistsTest.java diff --git a/src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java b/src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java index 2a66530e..23614a69 100644 --- a/src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java +++ b/src/main/java/me/itzg/helpers/assertcmd/AssertCommand.java @@ -5,6 +5,7 @@ @Command(name = "assert", description = "Provides assertion operators for verifying container setup", subcommands = { FileExists.class, + FileNotExists.class, JsonPathEquals.class, PropertyEquals.class, } diff --git a/src/main/java/me/itzg/helpers/assertcmd/EvalExistence.java b/src/main/java/me/itzg/helpers/assertcmd/EvalExistence.java new file mode 100644 index 00000000..390fd7b7 --- /dev/null +++ b/src/main/java/me/itzg/helpers/assertcmd/EvalExistence.java @@ -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 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 pathStream = Files.walk(walkStart)) { + return new MatchingPaths(true, + pathStream + .filter(Files::isRegularFile) + .filter(pathMatcher::matches) + .collect(Collectors.toList()) + ); + } + + } + +} diff --git a/src/main/java/me/itzg/helpers/assertcmd/FileExists.java b/src/main/java/me/itzg/helpers/assertcmd/FileExists.java index 0417e48a..ce9559d0 100644 --- a/src/main/java/me/itzg/helpers/assertcmd/FileExists.java +++ b/src/main/java/me/itzg/helpers/assertcmd/FileExists.java @@ -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 { - 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 paths; @@ -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; } @@ -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 pathStream = Files.walk(walkStart)) { - return pathStream - .filter(Files::isRegularFile) - .anyMatch(pathMatcher::matches); - } - } } diff --git a/src/main/java/me/itzg/helpers/assertcmd/FileNotExists.java b/src/main/java/me/itzg/helpers/assertcmd/FileNotExists.java new file mode 100644 index 00000000..f7d01f2e --- /dev/null +++ b/src/main/java/me/itzg/helpers/assertcmd/FileNotExists.java @@ -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 { + + @Parameters + List 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; + } + +} diff --git a/src/test/java/me/itzg/helpers/MoreAssertions.java b/src/test/java/me/itzg/helpers/MoreAssertions.java new file mode 100644 index 00000000..7fc84024 --- /dev/null +++ b/src/test/java/me/itzg/helpers/MoreAssertions.java @@ -0,0 +1,14 @@ +package me.itzg.helpers; + +import java.util.Arrays; +import org.assertj.core.api.ListAssert; + +public class MoreAssertions { + + public static ListAssert 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 + )); + } +} diff --git a/src/test/java/me/itzg/helpers/assertcmd/FileNotExistsTest.java b/src/test/java/me/itzg/helpers/assertcmd/FileNotExistsTest.java new file mode 100644 index 00000000..1911712e --- /dev/null +++ b/src/test/java/me/itzg/helpers/assertcmd/FileNotExistsTest.java @@ -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(); + } +} diff --git a/src/test/java/me/itzg/helpers/find/FindCommandTest.java b/src/test/java/me/itzg/helpers/find/FindCommandTest.java index d31993b9..c12743a7 100644 --- a/src/test/java/me/itzg/helpers/find/FindCommandTest.java +++ b/src/test/java/me/itzg/helpers/find/FindCommandTest.java @@ -7,8 +7,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import org.assertj.core.api.ListAssert; +import me.itzg.helpers.MoreAssertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -65,7 +64,7 @@ void regularSuffixGlob() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( one.toString(), two.toString() ); @@ -87,7 +86,7 @@ void findsDirectoryType() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( thisDir.toString() ); } @@ -110,7 +109,7 @@ void findsFilesAndDirectories() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( a1.toString(), a2.toString() ); @@ -133,7 +132,7 @@ void findsDirectoriesWithinDirectories(@TempDir Path tempDir) throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( this1.toString(), this2.toString(), this3.toString() @@ -162,7 +161,7 @@ void findsShallowestFile() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( expected.toString() ); } @@ -184,7 +183,7 @@ void findsShallowestDir() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( expected.toString() ); } @@ -212,7 +211,7 @@ void appliesMaxDepth() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout) + MoreAssertions.assertThatLines(stdout) .containsExactlyInAnyOrder( expected1.toString(), expected2.toString() @@ -238,7 +237,7 @@ void dirAtStartingPoint() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout) + MoreAssertions.assertThatLines(stdout) .containsExactlyInAnyOrder( a.toString() ); @@ -259,7 +258,7 @@ void defaultDepthZero() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout) + MoreAssertions.assertThatLines(stdout) .containsExactlyInAnyOrder( tempDir.toString(), a.toString() @@ -283,7 +282,7 @@ void fileInDir() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout) + MoreAssertions.assertThatLines(stdout) .containsExactlyInAnyOrder( b.toString() ); @@ -340,7 +339,7 @@ void handlesMultipleNames() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( one.toString(), two.toString(), three.toString() @@ -364,7 +363,7 @@ void stopsOnFirstMatch() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsAnyOf( + MoreAssertions.assertThatLines(stdout).containsAnyOf( a.toString(), b.toString(), c.toString() @@ -388,7 +387,7 @@ void handlesMultipleStartingPoints() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( one.toString(), two.toString() ); @@ -411,7 +410,7 @@ void excludesByFiles() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( a.toString(), c.toString() ); @@ -437,7 +436,7 @@ void excludesByDirectory() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( two.toString(), three.toString() ); @@ -457,7 +456,7 @@ void acceptsShortFindTypes() throws Exception { assertThat(exitCode).isEqualTo(ExitCode.OK); }); - assertThatLines(stdout).containsExactlyInAnyOrder( + MoreAssertions.assertThatLines(stdout).containsExactlyInAnyOrder( expected.toString() ); } @@ -682,10 +681,4 @@ void directories() throws IOException { } - static ListAssert 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 - )); - } } \ No newline at end of file