Skip to content

Commit 62cec01

Browse files
committed
Created PathMatchers class and unit tests
Has matchers for java.nio.file.Path class. Replacement for FileMatchers.
1 parent 7d8d755 commit 62cec01

File tree

2 files changed

+462
-0
lines changed

2 files changed

+462
-0
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
package org.hamcrest.io;
2+
3+
import org.hamcrest.FeatureMatcher;
4+
import org.hamcrest.Matcher;
5+
6+
import java.io.IOException;
7+
import java.io.UncheckedIOException;
8+
import java.nio.file.FileSystem;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
11+
import java.util.function.Predicate;
12+
13+
import static org.hamcrest.TypeSafeDiagnosingMatcher.matcher;
14+
import static org.hamcrest.core.IsEqual.equalTo;
15+
16+
/**
17+
* Matchers for properties of files.
18+
*/
19+
public final class PathMatchers {
20+
21+
private PathMatchers() {
22+
}
23+
24+
/**
25+
* A matcher that checks if a directory exists.
26+
* @return the file matcher
27+
*/
28+
public static Matcher<Path> anExistingDirectory() {
29+
return matcher(Files::isDirectory, "an existing directory", "is not a directory", Path.class);
30+
}
31+
32+
/**
33+
* A matcher that checks if a file or directory exists.
34+
* @return the file matcher
35+
*/
36+
public static Matcher<Path> anExistingFileOrDirectory() {
37+
return matcher(Files::exists, "an existing file or directory", "does not exist", Path.class);
38+
}
39+
40+
/**
41+
* A matcher that checks if a file exists.
42+
* @return the file matcher
43+
*/
44+
public static Matcher<Path> anExistingFile() {
45+
return matcher(Files::isRegularFile, "an existing file", "is not a file", Path.class);
46+
}
47+
48+
/**
49+
* A matcher that checks if a file or directory is readable.
50+
* @return the file matcher
51+
*/
52+
public static Matcher<Path> isReadable() {
53+
return matcher(Files::isReadable, "a readable file or directory", "cannot be read", Path.class);
54+
}
55+
56+
/**
57+
* A matcher that checks if a file/directory is writable.
58+
* @return the file matcher
59+
*/
60+
public static Matcher<Path> isWritable() {
61+
return matcher(Files::isWritable, "a writable file or directory", "cannot be written to", Path.class);
62+
}
63+
64+
/**
65+
* A matcher that checks if a file/directory is executable.
66+
* @return the file matcher
67+
*/
68+
public static Matcher<Path> isExecutable() {
69+
return matcher(Files::isExecutable, "an executable file or directory", "is not executable", Path.class);
70+
}
71+
72+
/**
73+
* A matcher that checks if a file/directory is executable.
74+
* @return the file matcher
75+
*/
76+
public static Matcher<Path> isSameFile(Path target) {
77+
return matcher(toUncheckedEx(p->Files.isSameFile(target, p)), "the same file or directory", "is not the same file or directory", Path.class);
78+
}
79+
80+
/**
81+
* A matcher that checks if a file/directory is a symbolic link.
82+
* @return the file matcher
83+
*/
84+
public static Matcher<Path> isSymbolicLink() {
85+
return matcher(Files::isSymbolicLink, "a file or directory is a symbolic link", "is not a symbolic link", Path.class);
86+
}
87+
88+
/**
89+
* A matcher that checks if a file/directory is executable.
90+
* @return the file matcher
91+
*/
92+
public static Matcher<Path> isHidden() {
93+
return matcher(toUncheckedEx(p->PathMatchers.isHidden(p)), "a hidden file or directory", "is not hidden", Path.class);
94+
}
95+
96+
/**
97+
* A matcher that checks if a file has a specific size.
98+
* @param size the expected size
99+
* @return the file matcher
100+
*/
101+
public static Matcher<Path> hasSizeEqualTo(long size) {
102+
return hasSize(equalTo(size));
103+
}
104+
105+
/**
106+
* A matcher that checks if a file size matches an expected size.
107+
* @param expected matcher for the expected size
108+
* @return the file matcher
109+
*/
110+
public static Matcher<Path> hasSize(final Matcher<Long> expected) {
111+
return FeatureMatcher.matcher(expected, p->toUncheckedEx(()->Files.size(p)), "A file with size", "size", Path.class);
112+
// return new FeatureMatcher<Path, Long>(expected, "A file with size", "size") {
113+
// @Override protected Long featureValueOf(Path actual) { return toUncheckedEx(()->Files.size(actual)); }
114+
// };
115+
}
116+
117+
/**
118+
* A matcher that checks if a file name matches an expected name.
119+
* @param expected the expected name
120+
* @return the file matcher
121+
*/
122+
public static Matcher<Path> hasFileName(final Matcher<Path> expected) {
123+
return new FeatureMatcher<Path, Path>(expected, "A file with name", "name") {
124+
@Override protected Path featureValueOf(Path actual) { return actual.getFileName(); }
125+
};
126+
}
127+
128+
/**
129+
* A matcher that checks if a file name matches an expected name.
130+
* @param expected the expected name
131+
* @return the file matcher
132+
*/
133+
public static Matcher<Path> hasFileNameString(final Matcher<String> expected) {
134+
return new FeatureMatcher<Path, String>(expected, "A file with name", "name") {
135+
@Override protected String featureValueOf(Path actual) { return actual.getFileName().toString(); }
136+
};
137+
}
138+
139+
/**
140+
* A matcher that checks if a file real path matches an expected path.
141+
* @param expected the expected path
142+
* @return the file matcher
143+
*/
144+
public static Matcher<Path> hasRealPath(final Matcher<Path> expected) {
145+
return new FeatureMatcher<Path, Path>(expected, "A file with real path", "path") {
146+
@Override protected Path featureValueOf(Path actual) { return toUncheckedEx(()->actual.toRealPath()); }
147+
};
148+
}
149+
150+
/**
151+
* A matcher that checks if a file real path matches an expected path.
152+
* @param expected the expected path
153+
* @return the file matcher
154+
*/
155+
public static Matcher<Path> hasRealPathString(final Matcher<String> expected) {
156+
return new FeatureMatcher<Path, String>(expected, "A file with real path", "path") {
157+
@Override protected String featureValueOf(Path actual) { return toUncheckedEx(()->actual.toRealPath().toString()); }
158+
};
159+
}
160+
161+
/**
162+
* A matcher that checks if a file canonical path matches an expected path.
163+
* @deprecated Use {@link #hasRealPath(Matcher)} instead. Provided for backward compatibility with FileMatchers.
164+
*
165+
* @param expected the expected path
166+
* @return the file matcher
167+
*/
168+
public static Matcher<Path> hasCanonicalPathString(final Matcher<String> expected) {
169+
return hasRealPathString(expected); //
170+
}
171+
172+
/**
173+
* A matcher that checks if a file absolute path matches an expected path.
174+
* @param expected the expected path
175+
* @return the file matcher
176+
*/
177+
public static Matcher<Path> hasAbsolutePath(final Matcher<Path> expected) {
178+
return new FeatureMatcher<Path, Path>(expected, "A file with absolute path", "path") {
179+
@Override protected Path featureValueOf(Path actual) { return actual.toAbsolutePath(); }
180+
};
181+
}
182+
/**
183+
* A matcher that checks if a file absolute path matches an expected path.
184+
* @param expected the expected path
185+
* @return the file matcher
186+
*/
187+
public static Matcher<Path> hasAbsolutePathString(final Matcher<String> expected) {
188+
return new FeatureMatcher<Path, String>(expected, "A file with absolute path", "path") {
189+
@Override protected String featureValueOf(Path actual) { return actual.toAbsolutePath().toString(); }
190+
};
191+
}
192+
193+
/**
194+
* A matcher that checks if a file's FileSystem matches an expected FileSystem.
195+
* @param expected
196+
* @return
197+
*/
198+
public static Matcher<Path> hasFileSystem(final Matcher<FileSystem> expected) {
199+
return new FeatureMatcher<Path, FileSystem>(expected, "A file with file system", "file system") {
200+
@Override protected java.nio.file.FileSystem featureValueOf(Path actual) { return actual.getFileSystem(); }
201+
};
202+
}
203+
204+
205+
// Possible additions:
206+
// - hasParent(Matcher<Path>)
207+
// - hasRoot(Matcher<Path>)
208+
// - hasAttributes(Matcher<String>>...)
209+
// - hasLastModifiedTime(Matcher<Instant>)
210+
// - hasOwner(Matcher<UserPrincipal>)
211+
// - hasPosixPermissions(Matcher<Set<PosixFilePermission>>)
212+
213+
// - hasCreationTime(Matcher<Instant>)
214+
// - hasGroup(Matcher<GroupPrincipal>)
215+
// - hasFileKey(Matcher<FileKey>)
216+
// - hasFileAttribute(String, Matcher<Object>)
217+
// - hasProvider(Matcher<FileSystemProvider>)
218+
219+
// - hasContent(Matcher<String>)
220+
// - containsStrings(String...)
221+
222+
// Workaround for JDK 8 not supporting Files.isHidden(Path) for directories (JDK-8215467). Fixed in Java 13.
223+
private static boolean isHidden(Path path) throws IOException {
224+
if (path.getFileSystem().provider().getClass().getName().contains("WindowsFileSystemProvider")) {
225+
// WindowsFileSystemProvider does not support isHidden(Path) for directories
226+
return Files.readAttributes(path, "dos:hidden", java.nio.file.LinkOption.NOFOLLOW_LINKS)
227+
.get("hidden").equals(Boolean.TRUE);
228+
} else {
229+
return Files.isHidden(path);
230+
}
231+
}
232+
233+
@FunctionalInterface
234+
private interface Predicate_WithExceptions<T, E extends Exception> {
235+
boolean test(T t) throws E;
236+
}
237+
238+
private static <T, E extends IOException> Predicate<T> toUncheckedEx(Predicate_WithExceptions<T, E> predicate) {
239+
return value -> {
240+
try {
241+
return predicate.test(value);
242+
} catch (IOException e) {
243+
throw new UncheckedIOException(e);
244+
}
245+
};
246+
}
247+
248+
@FunctionalInterface
249+
private interface Supplier_WithExceptions<T, E extends IOException> {
250+
T get() throws E;
251+
}
252+
253+
private static <T> T toUncheckedEx(Supplier_WithExceptions<T, ?> supplier) {
254+
try {
255+
return supplier.get();
256+
} catch (IOException e) {
257+
throw new UncheckedIOException(e);
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)