package com.github.marschall.memoryfilesystem; import static com.github.marschall.memoryfilesystem.FileContentsMatcher.hasContents; import static com.github.marschall.memoryfilesystem.FileExistsMatcher.exists; import static com.github.marschall.memoryfilesystem.IsSameFileMatcher.isSameFile; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.GroupPrincipal; import java.nio.file.attribute.UserPrincipal; import java.text.Normalizer; import java.text.Normalizer.Form; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class UnixFileSystemComptiblityTest { @Rule public final PosixFileSystemRule rule = new PosixFileSystemRule(); private FileSystem fileSystem; private final boolean useDefault; public UnixFileSystemComptiblityTest(boolean useDefault) { this.useDefault = useDefault; } FileSystem getFileSystem() { if (this.fileSystem == null) { if (this.useDefault) { this.fileSystem = FileSystems.getDefault(); } else { this.fileSystem = this.rule.getFileSystem(); } } return this.fileSystem; } @Parameters(name = "navite: {0}") public static List<Object[]> fileSystems() throws IOException { FileSystem defaultFileSystem = FileSystems.getDefault(); boolean isPosix = defaultFileSystem.supportedFileAttributeViews().contains("posix"); String osName = (String) System.getProperties().get("os.name"); boolean isMac = osName.startsWith("Mac"); if (isPosix && !isMac) { return Arrays.asList(new Object[]{true}, new Object[]{false}); } else { return Collections.singletonList(new Object[]{false}); } } @Test public void forbiddenCharacters() throws IOException { try { char c = 0; this.getFileSystem().getPath(c + ".txt"); fail("0x00 should be forbidden"); } catch (InvalidPathException e) { // should reach here } } @Test public void notForbiddenCharacters() throws IOException { for (int i = 1; i < 128; ++i) { char c = (char) i; if (c != '/') { Path path = this.getFileSystem().getPath(c + ".txt"); assertNotNull(path); try { Files.createFile(path); } finally { Files.delete(path); } } } } @Test public void isHidden() throws IOException { Path hidden = this.getFileSystem().getPath(".hidden"); Files.createFile(hidden); try { assertTrue(Files.isHidden(hidden)); } finally { Files.delete(hidden); } } @Test public void isNotHidden() throws IOException { Path hidden = this.getFileSystem().getPath("not_hidden"); Files.createFile(hidden); try { assertFalse(Files.isHidden(hidden)); } finally { Files.delete(hidden); } } @Test public void rootAttributes() throws IOException { Path root = this.getFileSystem().getRootDirectories().iterator().next(); BasicFileAttributes attributes = Files.readAttributes(root, BasicFileAttributes.class); assertTrue(attributes.isDirectory()); assertFalse(attributes.isRegularFile()); } @Test(expected = UnsupportedOperationException.class) public void initialLastModifiedTime() throws ParseException, IOException { this.assertUnsupportedCreateOption("lastAccessTime"); } @Test(expected = UnsupportedOperationException.class) public void initialCreationTime() throws ParseException, IOException { this.assertUnsupportedCreateOption("creationTime"); } @Test(expected = UnsupportedOperationException.class) public void initiallastModifiedTime() throws ParseException, IOException { this.assertUnsupportedCreateOption("lastModifiedTime"); } private void assertUnsupportedCreateOption(String attributeName) throws IOException, ParseException { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); FileTime time = FileTime.fromMillis(format.parse("2012-11-07T20:30:22").getTime()); FileAttribute<?> lastModifiedAttribute = new StubFileAttribute<>(attributeName, time); Path path = this.getFileSystem().getPath("time"); Files.createFile(path, lastModifiedAttribute); fail("'" + attributeName + "' not supported as initial attribute"); } @Test public void supportsOwner() { assertThat(this.getFileSystem().supportedFileAttributeViews(), hasItem("owner")); } @Test public void notExistingView() throws IOException { Path path = this.getFileSystem().getPath("/foo/bar/does/not/exist"); BasicFileAttributeView attributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class); assertNotNull(attributeView); } @Test public void outputStreamDontTruncate() throws IOException { Path path = Paths.get("output"); try { Files.createFile(path); try (OutputStream output = Files.newOutputStream(path, StandardOpenOption.WRITE)) { output.write("11111".getBytes("US-ASCII")); output.flush(); } try (OutputStream output = Files.newOutputStream(path, StandardOpenOption.WRITE)) { output.write("22".getBytes("US-ASCII")); } assertThat(path, hasContents("22111")); } finally { Files.deleteIfExists(path); } } @Test public void outputStreamAppend() throws IOException { Path path = Paths.get("output"); try { Files.createFile(path); try (OutputStream output = Files.newOutputStream(path, StandardOpenOption.WRITE)) { output.write("11111".getBytes("US-ASCII")); output.flush(); } try (OutputStream output = Files.newOutputStream(path, StandardOpenOption.APPEND)) { output.write("22".getBytes("US-ASCII")); } assertThat(path, hasContents("1111122")); } finally { Files.deleteIfExists(path); } } @Test public void outputStreamTruncateByDefault() throws IOException { Path path = Paths.get("output"); try { Files.createFile(path); try (OutputStream output = Files.newOutputStream(path)) { output.write("11111".getBytes("US-ASCII")); output.flush(); } try (OutputStream output = Files.newOutputStream(path)) { output.write("22".getBytes("US-ASCII")); } assertThat(path, hasContents("22")); } finally { Files.deleteIfExists(path); } } @Test public void readOwner() throws IOException { Path path = this.getFileSystem().getPath("/"); Map<String, Object> attributes = Files.readAttributes(path, "owner:owner"); //TODO fix hamcrest //assertThat(attributes, hasSize(1)); assertEquals(1, attributes.size()); assertEquals(Collections.singleton("owner"), attributes.keySet()); //TODO fix hamcrest //assertThat(attributes.values().iterator().next(), isA(Long.class)); Object value = attributes.values().iterator().next(); assertNotNull(value); assertTrue(value instanceof UserPrincipal); assertFalse(value instanceof GroupPrincipal); } @Test public void readPosixSize() throws IOException { Path path = this.getFileSystem().getPath("/"); Map<String, Object> attributes = Files.readAttributes(path, "posix:size"); //TODO fix hamcrest //assertThat(attributes, hasSize(1)); assertEquals(1, attributes.size()); assertEquals(Collections.singleton("size"), attributes.keySet()); //TODO fix hamcrest //assertThat(attributes.values().iterator().next(), isA(Long.class)); assertTrue(attributes.values().iterator().next() instanceof Long); } @Test public void readPosixAttributeNames() throws IOException { Path path = this.getFileSystem().getPath("/"); Map<String, Object> attributes = Files.readAttributes(path, "posix:*"); Set<String> expectedAttributeNames = new HashSet<>(Arrays.asList( "lastModifiedTime", "fileKey", "isDirectory", "lastAccessTime", "isOther", "isSymbolicLink", "owner", "permissions", "isRegularFile", "creationTime", "group", "size")); assertEquals(expectedAttributeNames, attributes.keySet()); } @Test public void readOwnerAttributeNames() throws IOException { Path path = this.getFileSystem().getPath("/"); Map<String, Object> attributes = Files.readAttributes(path, "owner:*"); Set<String> expectedAttributeNames = Collections.singleton("owner"); assertEquals(expectedAttributeNames, attributes.keySet()); } @Test public void startsWith() { FileSystem fileSystem = this.getFileSystem(); assertTrue(fileSystem.getPath("a").startsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("a").startsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("a").startsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("a").startsWith(fileSystem.getPath(""))); assertTrue(fileSystem.getPath("/a").startsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("/a").startsWith(fileSystem.getPath("a"))); assertTrue(fileSystem.getPath("/a").startsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("/a").startsWith(fileSystem.getPath(""))); assertTrue(fileSystem.getPath("/").startsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("/").startsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("/").startsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("/").startsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("/").startsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("/").startsWith(fileSystem.getPath("/a/b"))); assertFalse(fileSystem.getPath("").startsWith(fileSystem.getPath("/"))); assertTrue(fileSystem.getPath("").startsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("").startsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("").startsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("").startsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("").startsWith(fileSystem.getPath("/a/b"))); } @Test public void endsWith() { FileSystem fileSystem = this.getFileSystem(); assertTrue(fileSystem.getPath("a").endsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("a").endsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("a").endsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("a").endsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("a").endsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("a/b").endsWith(fileSystem.getPath("a"))); assertTrue(fileSystem.getPath("a/b").endsWith(fileSystem.getPath("b"))); assertTrue(fileSystem.getPath("a/b").endsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("a/b").endsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("a/b").endsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("a/b").endsWith(fileSystem.getPath(""))); assertTrue(fileSystem.getPath("/a").endsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("/a").endsWith(fileSystem.getPath("/a/b"))); assertTrue(fileSystem.getPath("/a").endsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("/a").endsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("/a").endsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("/a"))); assertTrue(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("/a/b"))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("/b"))); assertTrue(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("b"))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("/a/b/c"))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("/a/b").endsWith(fileSystem.getPath(""))); assertTrue(fileSystem.getPath("/").endsWith(fileSystem.getPath("/"))); assertFalse(fileSystem.getPath("/").endsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("/").endsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("/").endsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("/").endsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("/").endsWith(fileSystem.getPath("/a/b"))); assertFalse(fileSystem.getPath("").endsWith(fileSystem.getPath("/"))); assertTrue(fileSystem.getPath("").endsWith(fileSystem.getPath(""))); assertFalse(fileSystem.getPath("").endsWith(fileSystem.getPath("a"))); assertFalse(fileSystem.getPath("").endsWith(fileSystem.getPath("/a"))); assertFalse(fileSystem.getPath("").endsWith(fileSystem.getPath("a/b"))); assertFalse(fileSystem.getPath("").endsWith(fileSystem.getPath("/a/b"))); } @Test public void resolve() { FileSystem fileSystem = this.getFileSystem(); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("/a").resolve(fileSystem.getPath("/"))); } @Test(expected = IllegalArgumentException.class) public void slash() { FileSystem fileSystem = this.getFileSystem(); Path path = fileSystem.getPath("/"); path.subpath(0, 1); } @Test public void test() { FileSystem fileSystem = this.getFileSystem(); Iterable<Path> rootDirectories = fileSystem.getRootDirectories(); Path root = rootDirectories.iterator().next(); assertTrue(root.endsWith(root)); try { root.endsWith((String) null); fail("path#endsWith(null) should not work"); } catch (NullPointerException e) { // should reach here } try { root.startsWith((String) null); fail("path#startsWith(null) should not work"); } catch (NullPointerException e) { // should reach here } assertTrue(root.startsWith(root)); assertFalse(root.startsWith("")); assertTrue(root.startsWith("/")); assertTrue(root.endsWith("/")); assertFalse(root.endsWith("")); } @Test public void aboluteGetParent() { FileSystem fileSystem = this.getFileSystem(); Path usrBin = fileSystem.getPath("/usr/bin"); Path usr = fileSystem.getPath("/usr"); assertEquals(usr, usrBin.getParent()); Path root = fileSystem.getRootDirectories().iterator().next(); assertEquals(root, usr.getParent()); assertEquals(fileSystem.getPath("usr/bin/a"), fileSystem.getPath("usr/bin").resolve(fileSystem.getPath("a"))); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("/../../..").normalize()); assertEquals(fileSystem.getPath("/a/b"), fileSystem.getPath("/../a/b").normalize()); assertEquals(fileSystem.getPath("../../.."), fileSystem.getPath("../../..").normalize()); assertEquals(fileSystem.getPath("../../.."), fileSystem.getPath(".././../..").normalize()); assertEquals(fileSystem.getPath("../../a/b/c"), fileSystem.getPath("../../a/b/c").normalize()); assertEquals(fileSystem.getPath("a"), fileSystem.getPath("a/b/..").normalize()); assertEquals(fileSystem.getPath(""), fileSystem.getPath("a/..").normalize()); assertEquals(fileSystem.getPath(".."), fileSystem.getPath("..").normalize()); // System.out.println(fileSystem.getPath("a").subpath(0, 0)); // throws excepption assertEquals(fileSystem.getPath("a"), fileSystem.getPath("/a/b").getName(0)); assertEquals(fileSystem.getPath("b"), fileSystem.getPath("/a/b").getName(1)); assertEquals(fileSystem.getPath("a"), fileSystem.getPath("a/b").getName(0)); assertEquals(fileSystem.getPath("b"), fileSystem.getPath("a/b").getName(1)); } @Test public void pathOrdering() { FileSystem fileSystem = this.getFileSystem(); Path root = fileSystem.getPath("/"); Path empty = fileSystem.getPath(""); Path a = fileSystem.getPath("a"); Path slashA = fileSystem.getPath("/a"); Path slashAA = fileSystem.getPath("/a/a"); assertTrue(root.compareTo(a) < 0); assertTrue(root.compareTo(slashA) < 0); assertTrue(a.compareTo(slashA) > 0); assertThat(a, greaterThan(slashA)); assertThat(slashA, lessThan(a)); assertTrue(slashA.compareTo(slashAA) < 0); assertThat(a, greaterThan(empty)); } @Test public void relativeGetParent() { FileSystem fileSystem = this.getFileSystem(); Path usrBin = fileSystem.getPath("usr/bin"); Path usr = fileSystem.getPath("usr"); assertEquals(usr, usrBin.getParent()); assertNull(usr.getParent()); } @Test public void resolveSibling() { FileSystem fileSystem = this.getFileSystem(); assertEquals(fileSystem.getPath("a"), fileSystem.getPath("/").resolveSibling(fileSystem.getPath("a"))); assertEquals(fileSystem.getPath("b"), fileSystem.getPath("a").resolveSibling(fileSystem.getPath("b"))); assertEquals(fileSystem.getPath(""), fileSystem.getPath("/").resolveSibling(fileSystem.getPath(""))); assertEquals(fileSystem.getPath("/c/d"), fileSystem.getPath("/a/b").resolveSibling(fileSystem.getPath("/c/d"))); assertEquals(fileSystem.getPath("/c/d"), fileSystem.getPath("a/b").resolveSibling(fileSystem.getPath("/c/d"))); assertEquals(fileSystem.getPath(""), fileSystem.getPath("a").resolveSibling(fileSystem.getPath(""))); assertEquals(fileSystem.getPath("/a"), fileSystem.getPath("/a/b").resolveSibling(fileSystem.getPath(""))); assertEquals(fileSystem.getPath("a"), fileSystem.getPath("a/b").resolveSibling(fileSystem.getPath(""))); assertEquals(fileSystem.getPath("a/b"), fileSystem.getPath("").resolveSibling(fileSystem.getPath("a/b"))); assertEquals(fileSystem.getPath("/a/b"), fileSystem.getPath("").resolveSibling(fileSystem.getPath("/a/b"))); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("a/b").resolveSibling(fileSystem.getPath("/"))); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("/a/b").resolveSibling(fileSystem.getPath("/"))); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("").resolveSibling(fileSystem.getPath("/"))); assertEquals(fileSystem.getPath("/"), fileSystem.getPath("/a").resolveSibling(fileSystem.getPath(""))); assertEquals(fileSystem.getPath(""), fileSystem.getPath("a").resolveSibling(fileSystem.getPath(""))); } @Test public void unixNoNormalization() throws IOException { /* * Verifies that Linux does no Unicode normalization and that we can have * both a NFC and NFD file. */ // https://github.com/marschall/memoryfilesystem/pull/51 assumeTrue(Charset.defaultCharset().equals(UTF_8)); FileSystem fileSystem = this.getFileSystem(); String aUmlaut = "\u00C4"; Path nfcPath = fileSystem.getPath(aUmlaut); String normalized = Normalizer.normalize(aUmlaut, Form.NFD); Path nfdPath = fileSystem.getPath(normalized); Path nfcFile = null; Path nfdFile = null; try { nfcFile = Files.createFile(nfcPath); assertEquals(1, nfcFile.getFileName().toString().length()); assertEquals(1, nfcFile.toAbsolutePath().getFileName().toString().length()); assertEquals(1, nfcFile.toRealPath().getFileName().toString().length()); assertThat(nfcPath, exists()); assertThat(nfdPath, not(exists())); nfdFile = Files.createFile(nfdPath); assertEquals(2, nfdFile.getFileName().toString().length()); assertEquals(2, nfdFile.toAbsolutePath().getFileName().toString().length()); assertEquals(2, nfdFile.toRealPath().getFileName().toString().length()); assertThat(nfcPath, not(equalTo(nfdPath))); assertThat(nfcPath, not(isSameFile(nfdPath))); assertThat(nfdPath, not(isSameFile(nfcPath))); } finally { if (nfcFile != null) { Files.delete(nfcFile); } if (nfdFile != null) { Files.delete(nfdFile); } } } @Test public void unixPaths() throws IOException { // https://github.com/marschall/memoryfilesystem/pull/51 assumeTrue(Charset.defaultCharset().equals(UTF_8)); FileSystem fileSystem = this.getFileSystem(); String aUmlaut = "\u00C4"; String normalized = Normalizer.normalize(aUmlaut, Form.NFD); assertEquals(1, aUmlaut.length()); assertEquals(2, normalized.length()); Path aPath = fileSystem.getPath("/" + aUmlaut); Path nPath = fileSystem.getPath("/" + normalized); assertEquals(1, aPath.getName(0).toString().length()); // verify a NFC path is not equal to a NFD path assertThat(aPath, not(equalTo(nPath))); } }