diff options
Diffstat (limited to 'jimfs/src/test/java/com/google/common/jimfs/PathSubject.java')
-rw-r--r-- | jimfs/src/test/java/com/google/common/jimfs/PathSubject.java | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/jimfs/src/test/java/com/google/common/jimfs/PathSubject.java b/jimfs/src/test/java/com/google/common/jimfs/PathSubject.java new file mode 100644 index 0000000..f6927a5 --- /dev/null +++ b/jimfs/src/test/java/com/google/common/jimfs/PathSubject.java @@ -0,0 +1,406 @@ +/* + * Copyright 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.common.jimfs; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.truth.Fact.fact; +import static com.google.common.truth.Fact.simpleFact; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Arrays.asList; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.BaseEncoding; +import com.google.common.truth.FailureMetadata; +import com.google.common.truth.Subject; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; + +/** + * Subject for doing assertions on file system paths. + * + * @author Colin Decker + */ +public final class PathSubject extends Subject { + + /** Returns the subject factory for doing assertions on paths. */ + public static Subject.Factory<PathSubject, Path> paths() { + return new PathSubjectFactory(); + } + + private static final LinkOption[] FOLLOW_LINKS = new LinkOption[0]; + private static final LinkOption[] NOFOLLOW_LINKS = {LinkOption.NOFOLLOW_LINKS}; + + private final Path actual; + protected LinkOption[] linkOptions = FOLLOW_LINKS; + private Charset charset = UTF_8; + + private PathSubject(FailureMetadata failureMetadata, Path subject) { + super(failureMetadata, subject); + this.actual = subject; + } + + private Path toPath(String path) { + return actual.getFileSystem().getPath(path); + } + + /** Returns this, for readability of chained assertions. */ + public PathSubject and() { + return this; + } + + /** Do not follow links when looking up the path. */ + public PathSubject noFollowLinks() { + this.linkOptions = NOFOLLOW_LINKS; + return this; + } + + /** + * Set the given charset to be used when reading the file at this path as text. Default charset if + * not set is UTF-8. + */ + public PathSubject withCharset(Charset charset) { + this.charset = checkNotNull(charset); + return this; + } + + /** Asserts that the path is absolute (it has a root component). */ + public PathSubject isAbsolute() { + if (!actual.isAbsolute()) { + failWithActual(simpleFact("expected to be absolute")); + } + return this; + } + + /** Asserts that the path is relative (it has no root component). */ + public PathSubject isRelative() { + if (actual.isAbsolute()) { + failWithActual(simpleFact("expected to be relative")); + } + return this; + } + + /** Asserts that the path has the given root component. */ + public PathSubject hasRootComponent(@NullableDecl String root) { + Path rootComponent = actual.getRoot(); + if (root == null && rootComponent != null) { + failWithActual("expected to have root component", root); + } else if (root != null && !root.equals(rootComponent.toString())) { + failWithActual("expected to have root component", root); + } + return this; + } + + /** Asserts that the path has no name components. */ + public PathSubject hasNoNameComponents() { + check("getNameCount()").that(actual.getNameCount()).isEqualTo(0); + return this; + } + + /** Asserts that the path has the given name components. */ + public PathSubject hasNameComponents(String... names) { + ImmutableList.Builder<String> builder = ImmutableList.builder(); + for (Path name : actual) { + builder.add(name.toString()); + } + + if (!builder.build().equals(ImmutableList.copyOf(names))) { + failWithActual("expected components", asList(names)); + } + return this; + } + + /** Asserts that the path matches the given syntax and pattern. */ + public PathSubject matches(String syntaxAndPattern) { + PathMatcher matcher = actual.getFileSystem().getPathMatcher(syntaxAndPattern); + if (!matcher.matches(actual)) { + failWithActual("expected to match ", syntaxAndPattern); + } + return this; + } + + /** Asserts that the path does not match the given syntax and pattern. */ + public PathSubject doesNotMatch(String syntaxAndPattern) { + PathMatcher matcher = actual.getFileSystem().getPathMatcher(syntaxAndPattern); + if (matcher.matches(actual)) { + failWithActual("expected not to match", syntaxAndPattern); + } + return this; + } + + /** Asserts that the path exists. */ + public PathSubject exists() { + if (!Files.exists(actual, linkOptions)) { + failWithActual(simpleFact("expected to exist")); + } + if (Files.notExists(actual, linkOptions)) { + failWithActual(simpleFact("expected to exist")); + } + return this; + } + + /** Asserts that the path does not exist. */ + public PathSubject doesNotExist() { + if (!Files.notExists(actual, linkOptions)) { + failWithActual(simpleFact("expected not to exist")); + } + if (Files.exists(actual, linkOptions)) { + failWithActual(simpleFact("expected not to exist")); + } + return this; + } + + /** Asserts that the path is a directory. */ + public PathSubject isDirectory() { + exists(); // check for directoryness should imply check for existence + + if (!Files.isDirectory(actual, linkOptions)) { + failWithActual(simpleFact("expected to be directory")); + } + return this; + } + + /** Asserts that the path is a regular file. */ + public PathSubject isRegularFile() { + exists(); // check for regular fileness should imply check for existence + + if (!Files.isRegularFile(actual, linkOptions)) { + failWithActual(simpleFact("expected to be regular file")); + } + return this; + } + + /** Asserts that the path is a symbolic link. */ + public PathSubject isSymbolicLink() { + exists(); // check for symbolic linkness should imply check for existence + + if (!Files.isSymbolicLink(actual)) { + failWithActual(simpleFact("expected to be symbolic link")); + } + return this; + } + + /** Asserts that the path, which is a symbolic link, has the given path as a target. */ + public PathSubject withTarget(String targetPath) throws IOException { + Path actualTarget = Files.readSymbolicLink(actual); + if (!actualTarget.equals(toPath(targetPath))) { + failWithoutActual( + fact("expected link target", targetPath), + fact("but target was", actualTarget), + fact("for path", actual)); + } + return this; + } + + /** + * Asserts that the file the path points to exists and has the given number of links to it. Fails + * on a file system that does not support the "unix" view. + */ + public PathSubject hasLinkCount(int count) throws IOException { + exists(); + + int linkCount = (int) Files.getAttribute(actual, "unix:nlink", linkOptions); + if (linkCount != count) { + failWithActual("expected to have link count", count); + } + return this; + } + + /** Asserts that the path resolves to the same file as the given path. */ + public PathSubject isSameFileAs(String path) throws IOException { + return isSameFileAs(toPath(path)); + } + + /** Asserts that the path resolves to the same file as the given path. */ + public PathSubject isSameFileAs(Path path) throws IOException { + if (!Files.isSameFile(actual, path)) { + failWithActual("expected to be same file as", path); + } + return this; + } + + /** Asserts that the path does not resolve to the same file as the given path. */ + public PathSubject isNotSameFileAs(String path) throws IOException { + if (Files.isSameFile(actual, toPath(path))) { + failWithActual("expected not to be same file as", path); + } + return this; + } + + /** Asserts that the directory has no children. */ + public PathSubject hasNoChildren() throws IOException { + isDirectory(); + + try (DirectoryStream<Path> stream = Files.newDirectoryStream(actual)) { + if (stream.iterator().hasNext()) { + failWithActual(simpleFact("expected to have no children")); + } + } + return this; + } + + /** Asserts that the directory has children with the given names, in the given order. */ + public PathSubject hasChildren(String... children) throws IOException { + isDirectory(); + + List<Path> expectedNames = new ArrayList<>(); + for (String child : children) { + expectedNames.add(actual.getFileSystem().getPath(child)); + } + + try (DirectoryStream<Path> stream = Files.newDirectoryStream(actual)) { + List<Path> actualNames = new ArrayList<>(); + for (Path path : stream) { + actualNames.add(path.getFileName()); + } + + if (!actualNames.equals(expectedNames)) { + failWithoutActual( + fact("expected to have children", expectedNames), + fact("but had children", actualNames), + fact("for path", actual)); + } + } + return this; + } + + /** Asserts that the file has the given size. */ + public PathSubject hasSize(long size) throws IOException { + if (Files.size(actual) != size) { + failWithActual("expected to have size", size); + } + return this; + } + + /** Asserts that the file is a regular file containing no bytes. */ + public PathSubject containsNoBytes() throws IOException { + return containsBytes(new byte[0]); + } + + /** + * Asserts that the file is a regular file containing exactly the byte values of the given ints. + */ + public PathSubject containsBytes(int... bytes) throws IOException { + byte[] realBytes = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + realBytes[i] = (byte) bytes[i]; + } + return containsBytes(realBytes); + } + + /** Asserts that the file is a regular file containing exactly the given bytes. */ + public PathSubject containsBytes(byte[] bytes) throws IOException { + isRegularFile(); + hasSize(bytes.length); + + byte[] actual = Files.readAllBytes(this.actual); + if (!Arrays.equals(bytes, actual)) { + System.out.println(BaseEncoding.base16().encode(actual)); + System.out.println(BaseEncoding.base16().encode(bytes)); + failWithActual("expected to contain bytes", BaseEncoding.base16().encode(bytes)); + } + return this; + } + + /** + * Asserts that the file is a regular file containing the same bytes as the regular file at the + * given path. + */ + public PathSubject containsSameBytesAs(String path) throws IOException { + isRegularFile(); + + byte[] expectedBytes = Files.readAllBytes(toPath(path)); + if (!Arrays.equals(expectedBytes, Files.readAllBytes(actual))) { + failWithActual("expected to contain same bytes as", path); + } + return this; + } + + /** + * Asserts that the file is a regular file containing the given lines of text. By default, the + * bytes are decoded as UTF-8; for a different charset, use {@link #withCharset(Charset)}. + */ + public PathSubject containsLines(String... lines) throws IOException { + return containsLines(Arrays.asList(lines)); + } + + /** + * Asserts that the file is a regular file containing the given lines of text. By default, the + * bytes are decoded as UTF-8; for a different charset, use {@link #withCharset(Charset)}. + */ + public PathSubject containsLines(Iterable<String> lines) throws IOException { + isRegularFile(); + + List<String> expected = ImmutableList.copyOf(lines); + List<String> actual = Files.readAllLines(this.actual, charset); + check("lines()").that(actual).isEqualTo(expected); + return this; + } + + /** Returns an object for making assertions about the given attribute. */ + public Attribute attribute(final String attribute) { + return new Attribute() { + @Override + public Attribute is(Object value) throws IOException { + Object actualValue = Files.getAttribute(actual, attribute, linkOptions); + check("attribute(%s)", attribute).that(actualValue).isEqualTo(value); + return this; + } + + @Override + public Attribute isNot(Object value) throws IOException { + Object actualValue = Files.getAttribute(actual, attribute, linkOptions); + check("attribute(%s)", attribute).that(actualValue).isNotEqualTo(value); + return this; + } + + @Override + public PathSubject and() { + return PathSubject.this; + } + }; + } + + private static class PathSubjectFactory implements Subject.Factory<PathSubject, Path> { + + @Override + public PathSubject createSubject(FailureMetadata failureMetadata, Path that) { + return new PathSubject(failureMetadata, that); + } + } + + /** Interface for assertions about a file attribute. */ + public interface Attribute { + + /** Asserts that the value of this attribute is equal to the given value. */ + Attribute is(Object value) throws IOException; + + /** Asserts that the value of this attribute is not equal to the given value. */ + Attribute isNot(Object value) throws IOException; + + /** Returns the path subject for further chaining. */ + PathSubject and(); + } +} |