com.facebook.buck.util.ProjectFilesystemTest.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.util.ProjectFilesystemTest.java

Source

/*
 * Copyright 2012-present Facebook, 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.facebook.buck.util;

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import com.facebook.buck.testutil.WatchEvents;
import com.facebook.buck.testutil.integration.ZipInspector;
import com.facebook.buck.util.ProjectFilesystem.CopySourceMode;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.Set;

/** Unit test for {@link ProjectFilesystem}. */
public class ProjectFilesystemTest {

    @Rule
    public TemporaryFolder tmp = new TemporaryFolder();

    private ProjectFilesystem filesystem;

    @Before
    public void setUp() {
        filesystem = new ProjectFilesystem(tmp.getRoot());
    }

    @Test
    public void testIsFile() throws IOException {
        tmp.newFolder("foo");
        tmp.newFile("foo/bar.txt");

        assertTrue(filesystem.isFile(Paths.get("foo/bar.txt")));
        assertFalse(filesystem.isFile(Paths.get("i_do_not_exist")));
        assertFalse("foo/ is a directory, but not an ordinary file", filesystem.isFile(Paths.get("foo")));
    }

    @Test
    public void testIsDirectory() throws IOException {
        File dir = tmp.newFolder("src");
        File file = tmp.newFile("BUCK");
        assertTrue(filesystem.isDirectory(Paths.get(dir.getName())));
        assertFalse(filesystem.isDirectory(Paths.get(file.getName())));
    }

    @Test
    public void testMkdirsCanCreateNestedFolders() throws IOException {
        filesystem.mkdirs(new File("foo/bar/baz").toPath());
        assertTrue(new File(tmp.getRoot(), "foo/bar/baz").isDirectory());
    }

    @Test
    public void testCreateParentDirs() throws IOException {
        Path pathRelativeToProjectRoot = Paths.get("foo/bar/baz.txt");
        filesystem.createParentDirs(pathRelativeToProjectRoot);
        assertTrue(new File(tmp.getRoot(), "foo").isDirectory());
        assertTrue(new File(tmp.getRoot(), "foo/bar").isDirectory());
        assertFalse("createParentDirs() should create directories, but not the leaf/file part of the path.",
                new File(tmp.getRoot(), "foo/bar/baz.txt").exists());
    }

    @Test(expected = NullPointerException.class)
    public void testReadFirstLineRejectsNullString() {
        filesystem.readFirstLine(/* pathRelativeToProjectRoot */ (String) null);
    }

    @Test(expected = NullPointerException.class)
    public void testReadFirstLineRejectsNullPath() {
        filesystem.readFirstLine(/* pathRelativeToProjectRoot */ (Path) null);
    }

    @Test
    public void testReadFirstLineToleratesNonExistentFile() {
        assertEquals(Optional.absent(), filesystem.readFirstLine("foo.txt"));
    }

    @Test
    public void testReadFirstLineWithEmptyFile() throws IOException {
        File emptyFile = tmp.newFile("foo.txt");
        Files.write(new byte[0], emptyFile);
        assertTrue(emptyFile.isFile());
        assertEquals(Optional.absent(), filesystem.readFirstLine("foo.txt"));
    }

    @Test
    public void testReadFirstLineFromMultiLineFile() throws IOException {
        File multiLineFile = tmp.newFile("foo.txt");
        Files.write("foo\nbar\nbaz\n", multiLineFile, Charsets.UTF_8);
        assertEquals(Optional.of("foo"), filesystem.readFirstLine("foo.txt"));
    }

    @Test
    public void getReaderIfFileExists() throws IOException {
        File file = tmp.newFile("foo.txt");
        Files.write("fooooo\nbar\nbaz\n", file, Charsets.UTF_8);
        assertEquals("fooooo\nbar\nbaz\n",
                CharStreams.toString(filesystem.getReaderIfFileExists(Paths.get("foo.txt")).get()));
    }

    @Test
    public void getReaderIfFileExistsNoFile() throws IOException {
        assertEquals(Optional.absent(), filesystem.getReaderIfFileExists(Paths.get("foo.txt")));
    }

    @Test
    public void testGetFileSize() throws IOException {
        File wordsFile = tmp.newFile("words.txt");
        String content = "Here\nare\nsome\nwords.\n";
        Files.write(content, wordsFile, Charsets.UTF_8);

        assertEquals(content.length(), filesystem.getFileSize(Paths.get("words.txt")));
    }

    @Test(expected = IOException.class)
    public void testGetFileSizeThrowsForNonExistentFile() throws IOException {
        filesystem.getFileSize(Paths.get("words.txt"));
    }

    @Test
    public void testWriteLinesToPath() throws IOException {
        Iterable<String> lines = ImmutableList.of("foo", "bar", "baz");
        filesystem.writeLinesToPath(lines, Paths.get("lines.txt"));

        String contents = Files.toString(new File(tmp.getRoot(), "lines.txt"), Charsets.UTF_8);
        assertEquals("foo\nbar\nbaz\n", contents);
    }

    @Test
    public void testWriteBytesToPath() throws IOException {
        String content = "Hello, World!";
        byte[] bytes = content.getBytes();
        filesystem.writeBytesToPath(bytes, Paths.get("hello.txt"));
        assertEquals(content, Files.toString(new File(tmp.getRoot(), "hello.txt"), Charsets.UTF_8));
    }

    @Test
    public void testCopyToPath() throws IOException {
        InputStream inputStream = new ByteArrayInputStream("Hello, world!".getBytes());
        filesystem.copyToPath(inputStream, Paths.get("bytes.txt"));

        assertEquals("The bytes on disk should match those from the InputStream.", "Hello, world!",
                Files.toString(new File(tmp.getRoot(), "bytes.txt"), Charsets.UTF_8));
    }

    @Test
    public void testCopyToPathWithOptions() throws IOException {
        InputStream inputStream = new ByteArrayInputStream("hello!".getBytes());
        filesystem.copyToPath(inputStream, Paths.get("replace_me.txt"));

        inputStream = new ByteArrayInputStream("hello again!".getBytes());
        filesystem.copyToPath(inputStream, Paths.get("replace_me.txt"), StandardCopyOption.REPLACE_EXISTING);

        assertEquals("The bytes on disk should match those from the second InputStream.", "hello again!",
                Files.toString(new File(tmp.getRoot(), "replace_me.txt"), Charsets.UTF_8));
    }

    @Test
    public void testCopyFolder() throws IOException {
        // Build up a directory of dummy files.
        tmp.newFolder("src");
        tmp.newFolder("src/com");
        tmp.newFolder("src/com/example");
        tmp.newFolder("src/com/example/foo");
        tmp.newFile("src/com/example/foo/Foo.java");
        tmp.newFile("src/com/example/foo/package.html");
        tmp.newFolder("src/com/example/bar");
        tmp.newFile("src/com/example/bar/Bar.java");
        tmp.newFile("src/com/example/bar/package.html");

        // Copy the contents of src/ to dest/.
        tmp.newFolder("dest");
        filesystem.copyFolder(Paths.get("src"), Paths.get("dest"));

        assertTrue(new File(tmp.getRoot(), "dest/com/example/foo/Foo.java").exists());
        assertTrue(new File(tmp.getRoot(), "dest/com/example/foo/package.html").exists());
        assertTrue(new File(tmp.getRoot(), "dest/com/example/bar/Bar.java").exists());
        assertTrue(new File(tmp.getRoot(), "dest/com/example/bar/package.html").exists());
    }

    @Test
    public void testCopyFolderAndContents() throws IOException {
        // Build up a directory of dummy files.
        tmp.newFolder("src");
        tmp.newFolder("src/com");
        tmp.newFolder("src/com/example");
        tmp.newFolder("src/com/example/foo");
        tmp.newFile("src/com/example/foo/Foo.java");
        tmp.newFile("src/com/example/foo/package.html");
        tmp.newFolder("src/com/example/bar");
        tmp.newFile("src/com/example/bar/Bar.java");
        tmp.newFile("src/com/example/bar/package.html");

        // Copy the contents of src/ to dest/ (including src itself).
        tmp.newFolder("dest");
        filesystem.copy(Paths.get("src"), Paths.get("dest"), CopySourceMode.DIRECTORY_AND_CONTENTS);

        assertTrue(new File(tmp.getRoot(), "dest/src/com/example/foo/Foo.java").exists());
        assertTrue(new File(tmp.getRoot(), "dest/src/com/example/foo/package.html").exists());
        assertTrue(new File(tmp.getRoot(), "dest/src/com/example/bar/Bar.java").exists());
        assertTrue(new File(tmp.getRoot(), "dest/src/com/example/bar/package.html").exists());
    }

    @Test
    public void testCopyFile() throws IOException {
        tmp.newFolder("foo");
        File file = tmp.newFile("foo/bar.txt");
        String content = "Hello, World!";
        Files.write(content, file, Charsets.UTF_8);

        filesystem.copyFile(Paths.get("foo/bar.txt"), Paths.get("foo/baz.txt"));
        assertEquals(content, Files.toString(new File(tmp.getRoot(), "foo/baz.txt"), Charsets.UTF_8));
    }

    @Test
    public void testDeleteFileAtPath() throws IOException {
        Path path = Paths.get("foo.txt");
        File file = tmp.newFile(path.toString());
        assertTrue(file.exists());
        filesystem.deleteFileAtPath(path);
        assertFalse(file.exists());
    }

    @Test
    public void testCreateContextStringForModifyEvent() throws IOException {
        Path file = tmp.newFile("foo.txt").toPath();
        WatchEvent<Path> modifyEvent = WatchEvents.createPathEvent(file, StandardWatchEventKinds.ENTRY_MODIFY);
        assertEquals(file.toAbsolutePath().toString(), filesystem.createContextString(modifyEvent));
    }

    @Test
    public void testCreateContextStringForOverflowEvent() {
        WatchEvent<Object> overflowEvent = new WatchEvent<Object>() {
            @Override
            public Kind<Object> kind() {
                return StandardWatchEventKinds.OVERFLOW;
            }

            @Override
            public int count() {
                return 0;
            }

            @Override
            public Object context() {
                return new Object() {
                    @Override
                    public String toString() {
                        return "I am the context string.";
                    }
                };
            }
        };
        assertEquals("I am the context string.", filesystem.createContextString(overflowEvent));
    }

    @Test
    public void testWalkFileTreeWhenProjectRootIsNotWorkingDir() throws IOException {
        tmp.newFolder("dir");
        tmp.newFile("dir/file.txt");
        tmp.newFolder("dir/dir2");
        tmp.newFile("dir/dir2/file2.txt");

        final ImmutableList.Builder<String> fileNames = ImmutableList.builder();

        filesystem.walkRelativeFileTree(Paths.get("dir"), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                fileNames.add(file.getFileName().toString());
                return FileVisitResult.CONTINUE;
            }
        });

        assertThat(fileNames.build(), containsInAnyOrder("file.txt", "file2.txt"));
    }

    @Test
    public void testWalkFileTreeWhenProjectRootIsWorkingDir() throws IOException {
        ProjectFilesystem projectFilesystem = new ProjectFilesystem(Paths.get("."));
        final ImmutableList.Builder<String> fileNames = ImmutableList.builder();

        Path pathRelativeToProjectRoot = Paths.get("test/com/facebook/buck/util/testdata");
        projectFilesystem.walkRelativeFileTree(pathRelativeToProjectRoot, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                fileNames.add(file.getFileName().toString());
                return FileVisitResult.CONTINUE;
            }
        });

        assertThat(fileNames.build(), containsInAnyOrder("file", "a_file", "b_file", "b_c_file", "b_d_file"));
    }

    @Test
    public void testWalkFileTreeFollowsSymlinks() throws IOException {
        tmp.newFolder("dir");
        tmp.newFile("dir/file.txt");
        java.nio.file.Files.createSymbolicLink(tmp.getRoot().toPath().resolve("linkdir"),
                tmp.getRoot().toPath().resolve("dir"));

        final ImmutableList.Builder<Path> filePaths = ImmutableList.builder();

        filesystem.walkRelativeFileTree(Paths.get(""), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                filePaths.add(file);
                return FileVisitResult.CONTINUE;
            }
        });

        assertThat(filePaths.build(), containsInAnyOrder(Paths.get("dir/file.txt"), Paths.get("linkdir/file.txt")));
    }

    @Test
    public void whenContextNullThenCreateContextStringReturnsValidString() {
        ProjectFilesystem projectFilesystem = new ProjectFilesystem(Paths.get("."));
        assertThat("Context string should contain null.",
                projectFilesystem.createContextString(WatchEvents.createOverflowEvent()),
                Matchers.containsString("null"));
    }

    @Test
    public void testGetFilesUnderPath() throws IOException {
        tmp.newFile("file1");
        tmp.newFolder("dir1");
        tmp.newFile("dir1/file2");
        tmp.newFolder("dir1/dir2");
        tmp.newFile("dir1/dir2/file3");

        assertThat(
                filesystem.getFilesUnderPath(Paths.get("dir1"), Predicates.<Path>alwaysTrue(),
                        EnumSet.noneOf(FileVisitOption.class)),
                containsInAnyOrder(Paths.get("dir1/file2"), Paths.get("dir1/dir2/file3")));

        assertThat(filesystem.getFilesUnderPath(Paths.get("dir1"), Predicates.equalTo(Paths.get("dir1/dir2/file3")),
                EnumSet.noneOf(FileVisitOption.class)), containsInAnyOrder(Paths.get("dir1/dir2/file3")));

        assertThat(filesystem.getFilesUnderPath(Paths.get("dir1")),
                containsInAnyOrder(Paths.get("dir1/file2"), Paths.get("dir1/dir2/file3")));

        assertThat(filesystem.getFilesUnderPath(Paths.get("dir1"), Predicates.equalTo(Paths.get("dir1/file2"))),
                containsInAnyOrder(Paths.get("dir1/file2")));
    }

    @Test
    public void testCreateZipPreservesExecutablePermissions() throws IOException {

        // Create a empty executable file.
        File exe = tmp.newFile("test.exe");
        exe.setExecutable(true);

        // Archive it into a zipfile using `ProjectFileSystem.createZip`.
        File zipFile = new File(tmp.getRoot().toPath().toString() + "/test.zip");
        filesystem.createZip(ImmutableList.of(exe.toPath()), zipFile);

        // Now unpack the archive (using apache's common-compress, as it preserves
        // executable permissions) and verify that the archive entry has executable
        // permissions.
        try (ZipFile zip = new ZipFile(zipFile)) {
            Enumeration<ZipArchiveEntry> entries = zip.getEntries();
            assertTrue(entries.hasMoreElements());
            ZipArchiveEntry entry = entries.nextElement();
            Set<PosixFilePermission> permissions = MorePosixFilePermissions
                    .fromMode(entry.getExternalAttributes() >> 16);
            assertTrue(permissions.contains(PosixFilePermission.OWNER_EXECUTE));
            assertFalse(entries.hasMoreElements());
        }

    }

    @Test
    public void testCreateReadOnlyFileSetsPermissions() throws IOException {
        Path path = Paths.get("hello.txt");
        ImmutableSet<PosixFilePermission> permissions = ImmutableSet.<PosixFilePermission>of(
                PosixFilePermission.OWNER_READ, PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ);

        filesystem.writeContentsToPath("hello world", path, PosixFilePermissions.asFileAttribute(permissions));
        // The umask may restrict the actual permissions on the filesystem:
        // https://fburl.com/26569549
        // So the best we can do is to check that the actual permissions are a
        // strict subset of the expected permissions.
        PosixFileAttributes attrs = filesystem.readAttributes(path, PosixFileAttributes.class);
        assertTrue(permissions.containsAll(attrs.permissions()));
    }

    @Test
    public void testCreateZip() throws IOException {
        tmp.newFolder("foo");
        tmp.newFile("foo/bar.txt");
        tmp.newFile("foo/baz.txt");

        File output = tmp.newFile("out.zip");

        filesystem.createZip(ImmutableList.of(Paths.get("foo/bar.txt"), Paths.get("foo/baz.txt")), output);

        ZipInspector zipInspector = new ZipInspector(output);
        assertEquals(ImmutableSet.of("foo/bar.txt", "foo/baz.txt"), zipInspector.getZipFileEntries());
    }

    @Test
    public void testCreateZipWithAdditionalFiles() throws IOException {
        tmp.newFolder("foo");
        tmp.newFile("foo/bar.txt");
        tmp.newFile("foo/baz.txt");

        File output = tmp.newFile("out.zip");

        filesystem.createZip(ImmutableList.of(Paths.get("foo/bar.txt"), Paths.get("foo/baz.txt")), output,
                ImmutableMap.of(Paths.get("log/info.txt"), "hello"));

        ZipInspector zipInspector = new ZipInspector(output);
        assertEquals(ImmutableSet.of("foo/bar.txt", "foo/baz.txt", "log/info.txt"),
                zipInspector.getZipFileEntries());
    }
}