io.vertx.core.file.FileSystemTest.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.core.file.FileSystemTest.java

Source

/*
 * Copyright (c) 2014 Red Hat, Inc. and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 */

package io.vertx.core.file;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.file.impl.AsyncFileImpl;
import io.vertx.core.impl.Utils;
import io.vertx.core.json.JsonObject;
import io.vertx.core.streams.Pump;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
import io.vertx.test.core.TestUtils;
import io.vertx.test.core.VertxTestBase;
import org.junit.Assume;
import org.junit.AssumptionViolatedException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import static io.vertx.test.core.TestUtils.*;
import static org.hamcrest.CoreMatchers.instanceOf;

/**
 * @author <a href="http://tfox.org">Tim Fox</a>
 */
public class FileSystemTest extends VertxTestBase {

    private static final String DEFAULT_DIR_PERMS = "rwxr-xr-x";
    private static final String DEFAULT_FILE_PERMS = "rw-r--r--";

    private String pathSep;
    private String testDir;

    @Rule
    public TemporaryFolder testFolder = new TemporaryFolder();

    public void setUp() throws Exception {
        super.setUp();
        java.nio.file.FileSystem fs = FileSystems.getDefault();
        pathSep = fs.getSeparator();
        File ftestDir = testFolder.newFolder();
        testDir = ftestDir.toString();
    }

    @Test
    public void testIllegalArguments() throws Exception {
        assertNullPointerException(() -> vertx.fileSystem().copy(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().copy("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().copyBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().copyBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().copyRecursive(null, "ignored", true, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().copyRecursive("ignored", null, true, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().copyRecursiveBlocking(null, "ignored", true));
        assertNullPointerException(() -> vertx.fileSystem().copyRecursiveBlocking("ignored", null, true));
        assertNullPointerException(() -> vertx.fileSystem().move(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().move("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().moveBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().moveBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().truncate(null, 0, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().truncateBlocking(null, 0));
        assertNullPointerException(() -> vertx.fileSystem().chmod(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().chmod("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().chmodBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().chmodBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().chmodRecursive(null, "ignored", "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().chmodRecursive("ignored", null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().chmodRecursiveBlocking(null, "ignored", "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().chmodRecursiveBlocking("ignored", null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().chown(null, "ignored", "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().chownBlocking(null, "ignored", "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().props(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().propsBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().lprops(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().lpropsBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().link(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().link("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().linkBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().linkBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().symlink(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().symlink("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().symlinkBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().symlinkBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().unlink(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().unlinkBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().readSymlink(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().readSymlinkBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().delete(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().deleteBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().deleteRecursive(null, true, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().deleteRecursiveBlocking(null, true));
        assertNullPointerException(() -> vertx.fileSystem().mkdir(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().mkdirBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().mkdir(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().mkdirBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().mkdirs(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().mkdirsBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().mkdirs(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().mkdirsBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().readDir(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().readDirBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().readDir(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().readDirBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().readFile(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().readFileBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().writeFile(null, Buffer.buffer(), h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().writeFile("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().writeFileBlocking(null, Buffer.buffer()));
        assertNullPointerException(() -> vertx.fileSystem().writeFileBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().open(null, new OpenOptions(), h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().open("ignored", null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().openBlocking(null, new OpenOptions()));
        assertNullPointerException(() -> vertx.fileSystem().openBlocking("ignored", null));
        assertNullPointerException(() -> vertx.fileSystem().createFile(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().createFileBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().createFile(null, "ignored", h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().createFileBlocking(null, "ignored"));
        assertNullPointerException(() -> vertx.fileSystem().exists(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().existsBlocking(null));
        assertNullPointerException(() -> vertx.fileSystem().fsProps(null, h -> {
        }));
        assertNullPointerException(() -> vertx.fileSystem().fsPropsBlocking(null));

        String fileName = "some-file.dat";
        AsyncFile asyncFile = vertx.fileSystem().openBlocking(testDir + pathSep + fileName, new OpenOptions());

        assertNullPointerException(() -> asyncFile.write(null));
        assertIllegalArgumentException(() -> asyncFile.setWriteQueueMaxSize(1));
        assertIllegalArgumentException(() -> asyncFile.setWriteQueueMaxSize(0));
        assertIllegalArgumentException(() -> asyncFile.setWriteQueueMaxSize(-1));
        assertNullPointerException(() -> asyncFile.write(null, 0, h -> {
        }));
        assertNullPointerException(() -> asyncFile.write(Buffer.buffer(), 0, null));
        assertIllegalArgumentException(() -> asyncFile.write(Buffer.buffer(), -1, h -> {
        }));

        assertNullPointerException(() -> asyncFile.read(null, 0, 0, 0, h -> {
        }));
        assertNullPointerException(() -> asyncFile.read(Buffer.buffer(), 0, 0, 0, null));

        assertIllegalArgumentException(() -> asyncFile.read(Buffer.buffer(), -1, 0, 0, h -> {
        }));
        assertIllegalArgumentException(() -> asyncFile.read(Buffer.buffer(), 0, -1, 0, h -> {
        }));
        assertIllegalArgumentException(() -> asyncFile.read(Buffer.buffer(), 0, 0, -1, h -> {
        }));
    }

    @Test
    public void testSimpleCopy() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        testCopy(source, target, false, true, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testSimpleCopyFileAlreadyExists() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        createFileWithJunk(target, 100);
        testCopy(source, target, false, false, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testCopyIntoDir() throws Exception {
        String source = "foo.txt";
        String dir = "some-dir";
        String target = dir + pathSep + "bar.txt";
        mkDir(dir);
        createFileWithJunk(source, 100);
        testCopy(source, target, false, true, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testCopyEmptyDir() throws Exception {
        String source = "some-dir";
        String target = "some-other-dir";
        mkDir(source);
        testCopy(source, target, false, true, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testCopyNonEmptyDir() throws Exception {
        String source = "some-dir";
        String target = "some-other-dir";
        String file1 = pathSep + "somefile.bar";
        mkDir(source);
        createFileWithJunk(source + file1, 100);
        testCopy(source, target, false, true, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
            assertFalse(fileExists(target + file1));
        });
        await();
    }

    @Test
    public void testFailCopyDirAlreadyExists() throws Exception {
        String source = "some-dir";
        String target = "some-other-dir";
        mkDir(source);
        mkDir(target);
        testCopy(source, target, false, false, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testRecursiveCopy() throws Exception {
        String dir = "some-dir";
        String file1 = pathSep + "file1.dat";
        String file2 = pathSep + "index.html";
        String dir2 = "next-dir";
        String file3 = pathSep + "blah.java";
        mkDir(dir);
        createFileWithJunk(dir + file1, 100);
        createFileWithJunk(dir + file2, 100);
        mkDir(dir + pathSep + dir2);
        createFileWithJunk(dir + pathSep + dir2 + file3, 100);
        String target = "some-other-dir";
        testCopy(dir, target, true, true, v -> {
            assertTrue(fileExists(dir));
            assertTrue(fileExists(target));
            assertTrue(fileExists(target + file1));
            assertTrue(fileExists(target + file2));
            assertTrue(fileExists(target + pathSep + dir2 + file3));
        });
        await();
    }

    private void testCopy(String source, String target, boolean recursive, boolean shouldPass,
            Handler<Void> afterOK) {
        if (recursive) {
            vertx.fileSystem().copyRecursive(testDir + pathSep + source, testDir + pathSep + target, true,
                    createHandler(shouldPass, afterOK));
        } else {
            vertx.fileSystem().copy(testDir + pathSep + source, testDir + pathSep + target,
                    createHandler(shouldPass, afterOK));
        }
    }

    @Test
    public void testSimpleMove() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        testMove(source, target, true, v -> {
            assertFalse(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testSimpleMoveFileAlreadyExists() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        createFileWithJunk(target, 100);
        testMove(source, target, false, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testMoveEmptyDir() throws Exception {
        String source = "some-dir";
        String target = "some-other-dir";
        mkDir(source);
        testMove(source, target, true, v -> {
            assertFalse(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testMoveEmptyDirTargetExists() throws Exception {
        String source = "some-dir";
        String target = "some-other-dir";
        mkDir(source);
        mkDir(target);
        testMove(source, target, false, v -> {
            assertTrue(fileExists(source));
            assertTrue(fileExists(target));
        });
        await();
    }

    @Test
    public void testMoveNonEmptyDir() throws Exception {
        String dir = "some-dir";
        String file1 = pathSep + "file1.dat";
        String file2 = pathSep + "index.html";
        String dir2 = "next-dir";
        String file3 = pathSep + "blah.java";
        mkDir(dir);
        createFileWithJunk(dir + file1, 100);
        createFileWithJunk(dir + file2, 100);
        mkDir(dir + pathSep + dir2);
        createFileWithJunk(dir + pathSep + dir2 + file3, 100);
        String target = "some-other-dir";
        testMove(dir, target, true, v -> {
            assertFalse(fileExists(dir));
            assertTrue(fileExists(target));
            assertTrue(fileExists(target + file1));
            assertTrue(fileExists(target + file2));
            assertTrue(fileExists(target + pathSep + dir2 + file3));
        });
        await();
    }

    private void testMove(String source, String target, boolean shouldPass, Handler<Void> afterOK)
            throws Exception {
        vertx.fileSystem().move(testDir + pathSep + source, testDir + pathSep + target,
                createHandler(shouldPass, afterOK));
    }

    @Test
    public void testTruncate() throws Exception {
        String file1 = "some-file.dat";
        long initialLen = 1000;
        long truncatedLen = 534;
        createFileWithJunk(file1, initialLen);
        assertEquals(initialLen, fileLength(file1));
        testTruncate(file1, truncatedLen, true, v -> {
            assertEquals(truncatedLen, fileLength(file1));
        });
        await();
    }

    @Test
    public void testTruncateExtendsFile() throws Exception {
        String file1 = "some-file.dat";
        long initialLen = 500;
        long truncatedLen = 1000;
        createFileWithJunk(file1, initialLen);
        assertEquals(initialLen, fileLength(file1));
        testTruncate(file1, truncatedLen, true, v -> {
            assertEquals(truncatedLen, fileLength(file1));
        });
        await();
    }

    @Test
    public void testTruncateFileDoesNotExist() throws Exception {
        String file1 = "some-file.dat";
        long truncatedLen = 534;
        testTruncate(file1, truncatedLen, false, null);
        await();
    }

    private void testTruncate(String file, long truncatedLen, boolean shouldPass, Handler<Void> afterOK)
            throws Exception {
        vertx.fileSystem().truncate(testDir + pathSep + file, truncatedLen, createHandler(shouldPass, afterOK));
    }

    @Test
    public void testChmodNonRecursive1() throws Exception {
        testChmodNonRecursive("rw-------");
    }

    @Test
    public void testChmodNonRecursive2() throws Exception {
        testChmodNonRecursive("rwx------");
    }

    @Test
    public void testChmodNonRecursive3() throws Exception {
        testChmodNonRecursive("rw-rw-rw-");
    }

    @Test
    public void testChmodNonRecursive4() throws Exception {
        testChmodNonRecursive("rw-r--r--");
    }

    @Test
    public void testChmodNonRecursive5() throws Exception {
        testChmodNonRecursive("rw--w--w-");
    }

    @Test
    public void testChmodNonRecursive6() throws Exception {
        testChmodNonRecursive("rw-rw-rw-");
    }

    private void testChmodNonRecursive(String perms) throws Exception {
        String file1 = "some-file.dat";
        createFileWithJunk(file1, 100);
        testChmod(file1, perms, null, true, v -> {
            assertPerms(perms, file1);
            deleteFile(file1);
        });
        await();
    }

    private void assertPerms(String perms, String file1) {
        if (!Utils.isWindows()) {
            assertEquals(perms, getPerms(file1));
        }
    }

    @Test
    public void testChmodRecursive1() throws Exception {
        testChmodRecursive("rw-------", "rwx------");
    }

    @Test
    public void testChmodRecursive2() throws Exception {
        testChmodRecursive("rwx------", "rwx------");
    }

    @Test
    public void testChmodRecursive3() throws Exception {
        testChmodRecursive("rw-rw-rw-", "rwxrw-rw-");
    }

    @Test
    public void testChmodRecursive4() throws Exception {
        testChmodRecursive("rw-r--r--", "rwxr--r--");
    }

    @Test
    public void testChmodRecursive5() throws Exception {
        testChmodRecursive("rw--w--w-", "rwx-w--w-");
    }

    @Test
    public void testChmodRecursive6() throws Exception {
        testChmodRecursive("rw-rw-rw-", "rwxrw-rw-");
    }

    private void testChmodRecursive(String perms, String dirPerms) throws Exception {
        String dir = "some-dir";
        String file1 = pathSep + "file1.dat";
        String file2 = pathSep + "index.html";
        String dir2 = "next-dir";
        String file3 = pathSep + "blah.java";
        mkDir(dir);
        createFileWithJunk(dir + file1, 100);
        createFileWithJunk(dir + file2, 100);
        mkDir(dir + pathSep + dir2);
        createFileWithJunk(dir + pathSep + dir2 + file3, 100);
        testChmod(dir, perms, dirPerms, true, v -> {
            assertPerms(dirPerms, dir);
            assertPerms(perms, dir + file1);
            assertPerms(perms, dir + file2);
            assertPerms(dirPerms, dir + pathSep + dir2);
            assertPerms(perms, dir + pathSep + dir2 + file3);
            deleteDir(dir);
        });
        await();
    }

    @Test
    public void testChownToRootFails() throws Exception {
        testChownFails("root");
    }

    @Test
    public void testChownToNotExistingUserFails() throws Exception {
        testChownFails("jfhfhjejweg");
    }

    private void testChownFails(String user) throws Exception {
        String file1 = "some-file.dat";
        createFileWithJunk(file1, 100);
        vertx.fileSystem().chown(testDir + pathSep + file1, user, null, ar -> {
            deleteFile(file1);
            assertTrue(ar.failed());
            testComplete();
        });
        await();
    }

    @Test
    public void testChownToOwnUser() throws Exception {
        String file1 = "some-file.dat";
        createFileWithJunk(file1, 100);
        String fullPath = testDir + pathSep + file1;
        Path path = Paths.get(fullPath);
        UserPrincipal owner = Files.getOwner(path);
        String user = owner.getName();
        vertx.fileSystem().chown(fullPath, user, null, ar -> {
            deleteFile(file1);
            assertTrue(ar.succeeded());
            testComplete();
        });
        await();
    }

    @Test
    public void testChownToOwnGroup() throws Exception {
        // Not supported in WindowsFileSystemProvider
        Assume.assumeFalse(Utils.isWindows());
        String file1 = "some-file.dat";
        createFileWithJunk(file1, 100);
        String fullPath = testDir + pathSep + file1;
        Path path = Paths.get(fullPath);
        GroupPrincipal group = Files.readAttributes(path, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS)
                .group();
        vertx.fileSystem().chown(fullPath, null, group.getName(), ar -> {
            deleteFile(file1);
            assertTrue(ar.succeeded());
            testComplete();
        });
        await();
    }

    private void testChmod(String file, String perms, String dirPerms, boolean shouldPass, Handler<Void> afterOK)
            throws Exception {
        if (Files.isDirectory(Paths.get(testDir + pathSep + file))) {
            assertPerms(DEFAULT_DIR_PERMS, file);
        } else {
            assertPerms(DEFAULT_FILE_PERMS, file);
        }
        if (dirPerms != null) {
            vertx.fileSystem().chmodRecursive(testDir + pathSep + file, perms, dirPerms,
                    createHandler(shouldPass, afterOK));
        } else {
            vertx.fileSystem().chmod(testDir + pathSep + file, perms, createHandler(shouldPass, afterOK));
        }
    }

    @Test
    public void testProps() throws Exception {
        String fileName = "some-file.txt";
        long fileSize = 1234;

        // The times are quite inaccurate so we give 1 second leeway
        long start = 1000 * (System.currentTimeMillis() / 1000 - 1);
        createFileWithJunk(fileName, fileSize);

        testProps(fileName, false, true, st -> {
            assertNotNull(st);
            assertEquals(fileSize, st.size());
            assertTrue(st.creationTime() >= start);
            assertTrue(st.lastAccessTime() >= start);
            assertTrue(st.lastModifiedTime() >= start);
            assertFalse(st.isDirectory());
            assertTrue(st.isRegularFile());
            assertFalse(st.isSymbolicLink());
        });
        await();
    }

    @Test
    public void testPropsFileDoesNotExist() throws Exception {
        String fileName = "some-file.txt";
        testProps(fileName, false, false, null);
        await();
    }

    @Test
    public void testPropsFollowLink() throws Exception {
        // Symlinks require a modified security policy in Windows. -- See http://stackoverflow.com/questions/23217460/how-to-create-soft-symbolic-link-using-java-nio-files
        Assume.assumeFalse(Utils.isWindows());
        String fileName = "some-file.txt";
        long fileSize = 1234;

        // The times are quite inaccurate so we give 1 second leeway
        long start = 1000 * (System.currentTimeMillis() / 1000 - 1);
        createFileWithJunk(fileName, fileSize);
        long end = 1000 * (System.currentTimeMillis() / 1000 + 1);

        String linkName = "some-link.txt";
        Files.createSymbolicLink(Paths.get(testDir + pathSep + linkName), Paths.get(fileName));

        testProps(linkName, false, true, st -> {
            assertNotNull(st);
            assertEquals(fileSize, st.size());
            assertTrue(st.creationTime() >= start);
            assertTrue(st.creationTime() <= end);
            assertTrue(st.lastAccessTime() >= start);
            assertTrue(st.lastAccessTime() <= end);
            assertTrue(st.lastModifiedTime() >= start);
            assertTrue(st.lastModifiedTime() <= end);
            assertFalse(st.isDirectory());
            assertFalse(st.isOther());
            assertTrue(st.isRegularFile());
            assertFalse(st.isSymbolicLink());
        });
        await();
    }

    @Test
    public void testPropsDontFollowLink() throws Exception {
        // Symlinks require a modified security policy in Windows. -- See http://stackoverflow.com/questions/23217460/how-to-create-soft-symbolic-link-using-java-nio-files
        Assume.assumeFalse(Utils.isWindows());
        String fileName = "some-file.txt";
        long fileSize = 1234;
        createFileWithJunk(fileName, fileSize);
        String linkName = "some-link.txt";
        Files.createSymbolicLink(Paths.get(testDir + pathSep + linkName), Paths.get(fileName));
        testProps(linkName, true, true, st -> {
            assertNotNull(st != null);
            assertTrue(st.isSymbolicLink());
        });
        await();
    }

    private void testProps(String fileName, boolean link, boolean shouldPass, Handler<FileProps> afterOK)
            throws Exception {
        Handler<AsyncResult<FileProps>> handler = ar -> {
            if (ar.failed()) {
                if (shouldPass) {
                    fail(ar.cause().getMessage());
                } else {
                    assertTrue(ar.cause() instanceof io.vertx.core.file.FileSystemException);
                    if (afterOK != null) {
                        afterOK.handle(ar.result());
                    }
                    testComplete();
                }
            } else {
                if (shouldPass) {
                    if (afterOK != null) {
                        afterOK.handle(ar.result());
                    }
                    testComplete();
                } else {
                    fail("stat should fail");
                }
            }
        };
        if (link) {
            vertx.fileSystem().lprops(testDir + pathSep + fileName, handler);
        } else {
            vertx.fileSystem().props(testDir + pathSep + fileName, handler);
        }
    }

    @Test
    public void testLink() throws Exception {
        String fileName = "some-file.txt";
        long fileSize = 1234;
        createFileWithJunk(fileName, fileSize);
        String linkName = "some-link.txt";
        testLink(linkName, fileName, false, true, v -> {
            assertEquals(fileSize, fileLength(linkName));
            assertFalse(Files.isSymbolicLink(Paths.get(testDir + pathSep + linkName)));
        });
        await();
    }

    @Test
    public void testSymLink() throws Exception {
        // Symlinks require a modified security policy in Windows. -- See http://stackoverflow.com/questions/23217460/how-to-create-soft-symbolic-link-using-java-nio-files
        Assume.assumeFalse(Utils.isWindows());
        String fileName = "some-file.txt";
        long fileSize = 1234;
        createFileWithJunk(fileName, fileSize);
        String symlinkName = "some-sym-link.txt";
        testLink(symlinkName, fileName, true, true, v -> {
            assertEquals(fileSize, fileLength(symlinkName));
            assertTrue(Files.isSymbolicLink(Paths.get(testDir + pathSep + symlinkName)));
            // Now try reading it
            String read = vertx.fileSystem().readSymlinkBlocking(testDir + pathSep + symlinkName);
            assertEquals(fileName, read);
        });
        await();
    }

    private void testLink(String from, String to, boolean symbolic, boolean shouldPass, Handler<Void> afterOK)
            throws Exception {
        if (symbolic) {
            // Symlink is relative
            vertx.fileSystem().symlink(testDir + pathSep + from, to, createHandler(shouldPass, afterOK));
        } else {
            vertx.fileSystem().link(testDir + pathSep + from, testDir + pathSep + to,
                    createHandler(shouldPass, afterOK));
        }
    }

    @Test
    public void testUnlink() throws Exception {
        String fileName = "some-file.txt";
        long fileSize = 1234;
        createFileWithJunk(fileName, fileSize);
        String linkName = "some-link.txt";
        Files.createLink(Paths.get(testDir + pathSep + linkName), Paths.get(testDir + pathSep + fileName));
        assertEquals(fileSize, fileLength(linkName));
        vertx.fileSystem().unlink(testDir + pathSep + linkName,
                createHandler(true, v -> assertFalse(fileExists(linkName))));
        await();
    }

    @Test
    public void testReadSymLink() throws Exception {
        // Symlinks require a modified security policy in Windows. -- See http://stackoverflow.com/questions/23217460/how-to-create-soft-symbolic-link-using-java-nio-files
        Assume.assumeFalse(Utils.isWindows());
        String fileName = "some-file.txt";
        long fileSize = 1234;
        createFileWithJunk(fileName, fileSize);
        String linkName = "some-link.txt";
        Files.createSymbolicLink(Paths.get(testDir + pathSep + linkName), Paths.get(fileName));
        vertx.fileSystem().readSymlink(testDir + pathSep + linkName, ar -> {
            if (ar.failed()) {
                fail(ar.cause().getMessage());
            } else {
                assertEquals(fileName, ar.result());
                testComplete();
            }
        });
        await();
    }

    @Test
    public void testSimpleDelete() throws Exception {
        String fileName = "some-file.txt";
        createFileWithJunk(fileName, 100);
        assertTrue(fileExists(fileName));
        testDelete(fileName, false, true, v -> {
            assertFalse(fileExists(fileName));
        });
        await();
    }

    @Test
    public void testDeleteEmptyDir() throws Exception {
        String dirName = "some-dir";
        mkDir(dirName);
        assertTrue(fileExists(dirName));
        testDelete(dirName, false, true, v -> {
            assertFalse(fileExists(dirName));
        });
        await();
    }

    @Test
    public void testDeleteNonExistent() throws Exception {
        String dirName = "some-dir";
        assertFalse(fileExists(dirName));
        testDelete(dirName, false, false, null);
        await();
    }

    @Test
    public void testDeleteNonEmptyFails() throws Exception {
        String dirName = "some-dir";
        mkDir(dirName);
        String file1 = "some-file.txt";
        createFileWithJunk(dirName + pathSep + file1, 100);
        testDelete(dirName, false, false, null);
        await();
    }

    @Test
    public void testDeleteRecursive() throws Exception {
        String dir = "some-dir";
        String file1 = pathSep + "file1.dat";
        String file2 = pathSep + "index.html";
        String dir2 = "next-dir";
        String file3 = pathSep + "blah.java";
        mkDir(dir);
        createFileWithJunk(dir + file1, 100);
        createFileWithJunk(dir + file2, 100);
        mkDir(dir + pathSep + dir2);
        createFileWithJunk(dir + pathSep + dir2 + file3, 100);
        testDelete(dir, true, true, v -> {
            assertFalse(fileExists(dir));
        });
        await();
    }

    private void testDelete(String fileName, boolean recursive, boolean shouldPass, Handler<Void> afterOK)
            throws Exception {
        if (recursive) {
            vertx.fileSystem().deleteRecursive(testDir + pathSep + fileName, recursive,
                    createHandler(shouldPass, afterOK));
        } else {
            vertx.fileSystem().delete(testDir + pathSep + fileName, createHandler(shouldPass, afterOK));
        }
    }

    @Test
    public void testMkdirSimple() throws Exception {
        String dirName = "some-dir";
        testMkdir(dirName, null, false, true, v -> {
            assertTrue(fileExists(dirName));
            assertTrue(Files.isDirectory(Paths.get(testDir + pathSep + dirName)));
        });
        await();
    }

    @Test
    public void testMkdirWithParentsFails() throws Exception {
        String dirName = "top-dir" + pathSep + "some-dir";
        testMkdir(dirName, null, false, false, null);
        await();
    }

    @Test
    public void testMkdirWithPerms() throws Exception {
        String dirName = "some-dir";
        String perms = "rwx--x--x";
        testMkdir(dirName, perms, false, true, v -> {
            assertTrue(fileExists(dirName));
            assertTrue(Files.isDirectory(Paths.get(testDir + pathSep + dirName)));
            assertPerms(perms, dirName);
        });
        await();
    }

    @Test
    public void testMkdirCreateParents() throws Exception {
        String dirName = "top-dir" + pathSep + "/some-dir";
        testMkdir(dirName, null, true, true, v -> {
            assertTrue(fileExists(dirName));
            assertTrue(Files.isDirectory(Paths.get(testDir + pathSep + dirName)));
        });
        await();
    }

    @Test
    public void testMkdirCreateParentsWithPerms() throws Exception {
        String dirName = "top-dir" + pathSep + "/some-dir";
        String perms = "rwx--x--x";
        testMkdir(dirName, perms, true, true, v -> {
            assertTrue(fileExists(dirName));
            assertTrue(Files.isDirectory(Paths.get(testDir + pathSep + dirName)));
            assertPerms(perms, dirName);
        });
        await();
    }

    private void testMkdir(String dirName, String perms, boolean createParents, boolean shouldPass,
            Handler<Void> afterOK) throws Exception {
        Handler<AsyncResult<Void>> handler = createHandler(shouldPass, afterOK);
        if (createParents) {
            if (perms != null) {
                vertx.fileSystem().mkdirs(testDir + pathSep + dirName, perms, handler);
            } else {
                vertx.fileSystem().mkdirs(testDir + pathSep + dirName, handler);
            }
        } else {
            if (perms != null) {
                vertx.fileSystem().mkdir(testDir + pathSep + dirName, perms, handler);
            } else {
                vertx.fileSystem().mkdir(testDir + pathSep + dirName, handler);
            }
        }
    }

    @Test
    public void testReadDirSimple() throws Exception {
        String dirName = "some-dir";
        mkDir(dirName);
        int numFiles = 10;
        for (int i = 0; i < numFiles; i++) {
            createFileWithJunk(dirName + pathSep + "file-" + i + ".dat", 100);
        }
        testReadDir(dirName, null, true, fileNames -> {
            assertEquals(numFiles, fileNames.size());
            Set<String> fset = new HashSet<String>();
            for (String fileName : fileNames) {
                fset.add(fileName);
            }
            File dir = new File(testDir + pathSep + dirName);
            String root;
            try {
                root = dir.getCanonicalPath();
            } catch (IOException e) {
                fail(e.getMessage());
                return;
            }
            for (int i = 0; i < numFiles; i++) {
                assertTrue(fset.contains(root + pathSep + "file-" + i + ".dat"));
            }
        });
        await();
    }

    @Test
    public void testReadDirWithFilter() throws Exception {
        String dirName = "some-dir";
        mkDir(dirName);
        int numFiles = 10;
        for (int i = 0; i < numFiles; i++) {
            createFileWithJunk(dirName + pathSep + "foo-" + i + ".txt", 100);
        }
        for (int i = 0; i < numFiles; i++) {
            createFileWithJunk(dirName + pathSep + "bar-" + i + ".txt", 100);
        }
        testReadDir(dirName, "foo.+", true, fileNames -> {
            assertEquals(numFiles, fileNames.size());
            Set<String> fset = new HashSet<>();
            for (String fileName : fileNames) {
                fset.add(fileName);
            }
            File dir = new File(testDir + pathSep + dirName);
            String root;
            try {
                root = dir.getCanonicalPath();
            } catch (IOException e) {
                fail(e.getMessage());
                return;
            }
            for (int i = 0; i < numFiles; i++) {
                assertTrue(fset.contains(root + pathSep + "foo-" + i + ".txt"));
            }
        });
        await();
    }

    private void testReadDir(String dirName, String filter, boolean shouldPass, Handler<List<String>> afterOK)
            throws Exception {
        Handler<AsyncResult<List<String>>> handler = ar -> {
            if (ar.failed()) {
                if (shouldPass) {
                    fail(ar.cause().getMessage());
                } else {
                    assertTrue(ar.cause() instanceof FileSystemException);
                    if (afterOK != null) {
                        afterOK.handle(null);
                    }
                    testComplete();
                }
            } else {
                if (shouldPass) {
                    if (afterOK != null) {
                        afterOK.handle(ar.result());
                    }
                    testComplete();
                } else {
                    fail("read should fail");
                }
            }
        };
        if (filter == null) {
            vertx.fileSystem().readDir(testDir + pathSep + dirName, handler);
        } else {
            vertx.fileSystem().readDir(testDir + pathSep + dirName, filter, handler);
        }
    }

    @Test
    public void testReadFile() throws Exception {
        byte[] content = TestUtils.randomByteArray(1000);
        String fileName = "some-file.dat";
        createFile(fileName, content);

        vertx.fileSystem().readFile(testDir + pathSep + fileName, ar -> {
            if (ar.failed()) {
                fail(ar.cause().getMessage());
            } else {
                assertEquals(Buffer.buffer(content), ar.result());
                testComplete();
            }
        });
        await();
    }

    @Test
    public void testWriteFile() throws Exception {
        byte[] content = TestUtils.randomByteArray(1000);
        Buffer buff = Buffer.buffer(content);
        String fileName = "some-file.dat";
        vertx.fileSystem().writeFile(testDir + pathSep + fileName, buff, ar -> {
            if (ar.failed()) {
                fail(ar.cause().getMessage());
            } else {
                assertTrue(fileExists(fileName));
                assertEquals(content.length, fileLength(fileName));
                byte[] readBytes;
                try {
                    readBytes = Files.readAllBytes(Paths.get(testDir + pathSep + fileName));
                } catch (IOException e) {
                    fail(e.getMessage());
                    return;
                }
                assertEquals(buff, Buffer.buffer(readBytes));
                testComplete();
            }
        });
        await();
    }

    @Test
    public void testWriteAsync() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        Buffer buff = Buffer.buffer(content);
        AtomicInteger count = new AtomicInteger();
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), arr -> {
            if (arr.succeeded()) {
                for (int i = 0; i < chunks; i++) {
                    Buffer chunk = buff.getBuffer(i * chunkSize, (i + 1) * chunkSize);
                    assertEquals(chunkSize, chunk.length());
                    arr.result().write(chunk, i * chunkSize, ar -> {
                        if (ar.succeeded()) {
                            if (count.incrementAndGet() == chunks) {
                                arr.result().close(ar2 -> {
                                    if (ar2.failed()) {
                                        fail(ar2.cause().getMessage());
                                    } else {
                                        assertTrue(fileExists(fileName));
                                        byte[] readBytes;
                                        try {
                                            readBytes = Files.readAllBytes(Paths.get(testDir + pathSep + fileName));
                                        } catch (IOException e) {
                                            fail(e.getMessage());
                                            return;
                                        }
                                        Buffer read = Buffer.buffer(readBytes);
                                        assertEquals(buff, read);
                                        testComplete();
                                    }
                                });
                            }
                        } else {
                            fail(ar.cause().getMessage());
                        }
                    });
                }
            } else {
                fail(arr.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testWriteEmptyAsync() throws Exception {
        String fileName = "some-file.dat";
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), onSuccess(file -> {
            file.write(Buffer.buffer(), 0, onSuccess(v -> {
                testComplete();
            }));
        }));
        await();
    }

    @Test
    public void testReadAsync() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        Buffer expected = Buffer.buffer(content);
        createFile(fileName, content);
        AtomicInteger reads = new AtomicInteger();
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), arr -> {
            if (arr.succeeded()) {
                Buffer buff = Buffer.buffer(chunks * chunkSize);
                for (int i = 0; i < chunks; i++) {
                    arr.result().read(buff, i * chunkSize, i * chunkSize, chunkSize, arb -> {
                        if (arb.succeeded()) {
                            if (reads.incrementAndGet() == chunks) {
                                arr.result().close(ar -> {
                                    if (ar.failed()) {
                                        fail(ar.cause().getMessage());
                                    } else {
                                        assertEquals(expected, buff);
                                        assertEquals(buff, arb.result());
                                        testComplete();
                                    }
                                });
                            }
                        } else {
                            fail(arb.cause().getMessage());
                        }
                    });
                }
            } else {
                fail(arr.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testWriteStream() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        Buffer buff = Buffer.buffer(content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                WriteStream<Buffer> ws = ar.result();
                ws.exceptionHandler(t -> fail(t.getMessage()));
                for (int i = 0; i < chunks; i++) {
                    Buffer chunk = buff.getBuffer(i * chunkSize, (i + 1) * chunkSize);
                    assertEquals(chunkSize, chunk.length());
                    ws.write(chunk);
                }
                ar.result().close(ar2 -> {
                    if (ar2.failed()) {
                        fail(ar2.cause().getMessage());
                    } else {
                        assertTrue(fileExists(fileName));
                        byte[] readBytes;
                        try {
                            readBytes = Files.readAllBytes(Paths.get(testDir + pathSep + fileName));
                        } catch (IOException e) {
                            fail(e.getMessage());
                            return;
                        }
                        assertEquals(buff, Buffer.buffer(readBytes));
                        testComplete();
                    }
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testWriteStreamAppend() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] existing = TestUtils.randomByteArray(1000);
        createFile(fileName, existing);
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        Buffer buff = Buffer.buffer(content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions().setAppend(true), ar -> {
            if (ar.succeeded()) {
                AsyncFile ws = ar.result();
                ws.exceptionHandler(t -> fail(t.getMessage()));
                for (int i = 0; i < chunks; i++) {
                    Buffer chunk = buff.getBuffer(i * chunkSize, (i + 1) * chunkSize);
                    assertEquals(chunkSize, chunk.length());
                    ws.write(chunk);
                }
                ar.result().close(ar2 -> {
                    if (ar2.failed()) {
                        fail(ar2.cause().getMessage());
                    } else {
                        assertTrue(fileExists(fileName));
                        byte[] readBytes;
                        try {
                            readBytes = Files.readAllBytes(Paths.get(testDir + pathSep + fileName));
                        } catch (IOException e) {
                            fail(e.getMessage());
                            return;
                        }
                        assertEquals(Buffer.buffer(existing).appendBuffer(buff), Buffer.buffer(readBytes));
                        testComplete();
                    }
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testWriteStreamWithCompositeBuffer() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content1 = TestUtils.randomByteArray(chunkSize * (chunks / 2));
        byte[] content2 = TestUtils.randomByteArray(chunkSize * (chunks / 2));
        ByteBuf byteBuf = Unpooled.wrappedBuffer(content1, content2);
        Buffer buff = Buffer.buffer(byteBuf);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                WriteStream<Buffer> ws = ar.result();
                ws.exceptionHandler(t -> fail(t.getMessage()));
                ws.write(buff);
                ar.result().close(ar2 -> {
                    if (ar2.failed()) {
                        fail(ar2.cause().getMessage());
                    } else {
                        assertTrue(fileExists(fileName));
                        byte[] readBytes;
                        try {
                            readBytes = Files.readAllBytes(Paths.get(testDir + pathSep + fileName));
                        } catch (IOException e) {
                            fail(e.getMessage());
                            return;
                        }
                        assertEquals(buff, Buffer.buffer(readBytes));
                        byteBuf.release();
                        testComplete();
                    }
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    enum ReadStrategy {

        NONE {
            @Override
            void init(ReadStream<Buffer> stream) {
            }

            @Override
            Future<Void> handle(ReadStream<Buffer> stream) {
                return Future.succeededFuture();
            }
        },

        FLOWING {
            @Override
            void init(ReadStream<Buffer> stream) {
                flowing.set(true);
            }

            @Override
            Future<Void> handle(ReadStream<Buffer> stream) {
                Future<Void> fut = Future.future();
                assert flowing.getAndSet(false);
                stream.pause();
                Vertx.currentContext().owner().setTimer(1, id -> {
                    assert !flowing.getAndSet(true);
                    stream.resume();
                    fut.complete();
                });
                return fut;
            }
        },

        FETCH {
            @Override
            void init(ReadStream<Buffer> stream) {
                fetching.set(true);
                stream.pause();
                stream.fetch(1);
            }

            @Override
            Future<Void> handle(ReadStream<Buffer> stream) {
                Future<Void> fut = Future.future();
                assert fetching.getAndSet(false);
                Vertx.currentContext().owner().setTimer(1, id -> {
                    assert !fetching.getAndSet(true);
                    stream.fetch(1);
                    fut.complete();
                });
                return fut;
            }
        };

        final static AtomicBoolean flowing = new AtomicBoolean();
        final static AtomicBoolean fetching = new AtomicBoolean();

        abstract void init(ReadStream<Buffer> stream);

        abstract Future<Void> handle(ReadStream<Buffer> stream);

    }

    @Test
    public void testReadStream() throws Exception {
        testReadStream(ReadStrategy.NONE);
    }

    @Test
    public void testReadStreamFlowing() throws Exception {
        testReadStream(ReadStrategy.FLOWING);
    }

    @Test
    public void testReadStreamFetch() throws Exception {
        testReadStream(ReadStrategy.FETCH);
    }

    private void testReadStream(ReadStrategy strategy) throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        createFile(fileName, content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AtomicInteger inProgress = new AtomicInteger();
                AtomicBoolean ended = new AtomicBoolean();
                Buffer buff = Buffer.buffer();
                Runnable checkEnd = () -> {
                    if (ended.get() && inProgress.get() == 0) {
                        ar.result().close(ar2 -> {
                            if (ar2.failed()) {
                                fail(ar2.cause().getMessage());
                            } else {
                                assertEquals(Buffer.buffer(content), buff);
                                testComplete();
                            }
                        });
                    }
                };
                ReadStream<Buffer> rs = ar.result();
                strategy.init(rs);
                rs.handler(chunk -> {
                    buff.appendBuffer(chunk);
                    inProgress.incrementAndGet();
                    Future<Void> fut = strategy.handle(rs);
                    fut.setHandler(v -> {
                        inProgress.decrementAndGet();
                        checkEnd.run();
                    });
                });
                rs.exceptionHandler(t -> fail(t.getMessage()));
                rs.endHandler(v -> {
                    ended.set(true);
                    checkEnd.run();
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testReadStreamWithBufferSize() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 16384;
        int chunks = 1;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        createFile(fileName, content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AsyncFile rs = ar.result();
                rs.setReadBufferSize(chunkSize);
                Buffer buff = Buffer.buffer();
                int[] callCount = { 0 };
                rs.handler((buff1) -> {
                    buff.appendBuffer(buff1);
                    callCount[0]++;
                });
                rs.exceptionHandler(t -> fail(t.getMessage()));
                rs.endHandler(v -> {
                    ar.result().close(ar2 -> {
                        if (ar2.failed()) {
                            fail(ar2.cause().getMessage());
                        } else {
                            assertEquals(1, callCount[0]);
                            assertEquals(Buffer.buffer(content), buff);
                            testComplete();
                        }
                    });
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testReadStreamSetReadPos() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        createFile(fileName, content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AsyncFile rs = ar.result();
                rs.setReadPos(chunkSize * chunks / 2);
                Buffer buff = Buffer.buffer();
                rs.handler(buff::appendBuffer);
                rs.exceptionHandler(t -> fail(t.getMessage()));
                rs.endHandler(v -> {
                    ar.result().close(ar2 -> {
                        if (ar2.failed()) {
                            fail(ar2.cause().getMessage());
                        } else {
                            assertEquals(chunkSize * chunks / 2, buff.length());
                            byte[] lastHalf = new byte[chunkSize * chunks / 2];
                            System.arraycopy(content, chunkSize * chunks / 2, lastHalf, 0, chunkSize * chunks / 2);
                            assertEquals(Buffer.buffer(lastHalf), buff);
                            testComplete();
                        }
                    });
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testReadStreamSetReadLength() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        int readLength = chunkSize * chunks / 3;
        createFile(fileName, content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AsyncFile rs = ar.result();
                rs.setReadLength(readLength);
                Buffer buff = Buffer.buffer();
                rs.handler(buff::appendBuffer);
                rs.exceptionHandler(t -> fail(t.getMessage()));
                rs.endHandler(v -> {
                    ar.result().close(ar2 -> {
                        if (ar2.failed()) {
                            fail(ar2.cause().getMessage());
                        } else {
                            assertEquals(readLength, buff.length());
                            byte[] firstThird = new byte[readLength];
                            System.arraycopy(content, 0, firstThird, 0, readLength);
                            assertEquals(Buffer.buffer(firstThird), buff);
                            testComplete();
                        }
                    });
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testReadStreamSetReadPosReadLengthBufferSize() throws Exception {
        String fileName = "some-file.dat";
        int chunkSize = 1000;
        int chunks = 10;
        byte[] content = TestUtils.randomByteArray(chunkSize * chunks);
        int readLength = chunkSize * chunks / 3;
        int readPos = chunkSize * chunks / 3;
        int readBufferSize = 1000;
        int numOfReads = readLength / readBufferSize + (readLength % readBufferSize > 0 ? 1 : 0);
        createFile(fileName, content);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AsyncFile rs = ar.result();
                rs.setReadPos(readPos);
                rs.setReadLength(readLength);
                rs.setReadBufferSize(readBufferSize);
                final Buffer buff = Buffer.buffer();
                final int[] appendCount = new int[] { 0 };
                rs.handler((rsBuff) -> {
                    buff.appendBuffer(rsBuff);
                    appendCount[0]++;
                });
                rs.exceptionHandler(t -> fail(t.getMessage()));
                rs.endHandler(v -> {
                    ar.result().close(ar2 -> {
                        if (ar2.failed()) {
                            fail(ar2.cause().getMessage());
                        } else {
                            assertEquals(buff.length(), readLength);
                            assertEquals(numOfReads, appendCount[0]);
                            byte[] middleThird = new byte[readLength];
                            System.arraycopy(content, readPos, middleThird, 0, readLength);
                            assertEquals(Buffer.buffer(middleThird), buff);
                            testComplete();
                        }
                    });
                });
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    @SuppressWarnings("unchecked")
    public void testPumpFileStreams() throws Exception {
        String fileName1 = "some-file.dat";
        String fileName2 = "some-other-file.dat";

        //Non integer multiple of buffer size
        int fileSize = (int) (AsyncFileImpl.DEFAULT_READ_BUFFER_SIZE * 1000.3);
        byte[] content = TestUtils.randomByteArray(fileSize);
        createFile(fileName1, content);

        vertx.fileSystem().open(testDir + pathSep + fileName1, new OpenOptions(), arr -> {
            if (arr.succeeded()) {
                ReadStream rs = arr.result();
                //Open file for writing
                vertx.fileSystem().open(testDir + pathSep + fileName2, new OpenOptions(), ar -> {
                    if (ar.succeeded()) {
                        WriteStream ws = ar.result();
                        Pump p = Pump.pump(rs, ws);
                        p.start();
                        rs.endHandler(v -> {
                            arr.result().close(car -> {
                                if (car.failed()) {
                                    fail(ar.cause().getMessage());
                                } else {
                                    ar.result().close(ar2 -> {
                                        if (ar2.failed()) {
                                            fail(ar2.cause().getMessage());
                                        } else {
                                            assertTrue(fileExists(fileName2));
                                            byte[] readBytes;
                                            try {
                                                readBytes = Files
                                                        .readAllBytes(Paths.get(testDir + pathSep + fileName2));
                                            } catch (IOException e) {
                                                fail(e.getMessage());
                                                return;
                                            }
                                            assertEquals(Buffer.buffer(content), Buffer.buffer(readBytes));
                                            testComplete();
                                        }
                                    });
                                }
                            });
                        });
                    } else {
                        fail(ar.cause().getMessage());
                    }
                });
            } else {
                fail(arr.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testCreateFileNoPerms() throws Exception {
        testCreateFile(null, true);
    }

    @Test
    public void testCreateFileWithPerms() throws Exception {
        testCreateFile("rwx------", true);
    }

    @Test
    public void testCreateFileAlreadyExists() throws Exception {
        createFileWithJunk("some-file.dat", 100);
        testCreateFile(null, false);
    }

    private void testCreateFile(String perms, boolean shouldPass) throws Exception {
        String fileName = "some-file.dat";
        Handler<AsyncResult<Void>> handler = ar -> {
            if (ar.failed()) {
                if (shouldPass) {
                    fail(ar.cause().getMessage());
                } else {
                    assertTrue(ar.cause() instanceof FileSystemException);
                    testComplete();
                }
            } else {
                if (shouldPass) {
                    assertTrue(fileExists(fileName));
                    assertEquals(0, fileLength(fileName));
                    if (perms != null) {
                        assertPerms(perms, fileName);
                    }
                    testComplete();
                } else {
                    fail("test should fail");
                }
            }
        };
        if (perms != null) {
            vertx.fileSystem().createFile(testDir + pathSep + fileName, perms, handler);
        } else {
            vertx.fileSystem().createFile(testDir + pathSep + fileName, handler);
        }
        await();
    }

    @Test
    public void testExists() throws Exception {
        testExists(true);
    }

    @Test
    public void testNotExists() throws Exception {
        testExists(false);
    }

    private void testExists(boolean exists) throws Exception {
        String fileName = "some-file.dat";
        if (exists) {
            createFileWithJunk(fileName, 100);
        }
        vertx.fileSystem().exists(testDir + pathSep + fileName, ar -> {
            if (ar.succeeded()) {
                if (exists) {
                    assertTrue(ar.result());
                } else {
                    assertFalse(ar.result());
                }
                testComplete();
            } else {
                fail(ar.cause().getMessage());
            }
        });
        await();
    }

    @Test
    public void testFSProps() throws Exception {
        String fileName = "some-file.txt";
        createFileWithJunk(fileName, 1234);
        testFSProps(fileName, props -> {
            assertTrue(props.totalSpace() > 0);
            assertTrue(props.unallocatedSpace() > 0);
            assertTrue(props.usableSpace() > 0);
        });
        await();
    }

    private void testFSProps(String fileName, Handler<FileSystemProps> afterOK) throws Exception {
        vertx.fileSystem().fsProps(testDir + pathSep + fileName, ar -> {
            if (ar.failed()) {
                fail(ar.cause().getMessage());
            } else {
                afterOK.handle(ar.result());
                testComplete();
            }
        });
    }

    @Test
    public void testOpenOptions() {
        OpenOptions opts = new OpenOptions();
        assertNull(opts.getPerms());
        String perms = "rwxrwxrwx";
        assertEquals(opts, opts.setPerms(perms));
        assertEquals(perms, opts.getPerms());
        assertTrue(opts.isCreate());
        assertEquals(opts, opts.setCreate(false));
        assertFalse(opts.isCreate());
        assertFalse(opts.isCreateNew());
        assertEquals(opts, opts.setCreateNew(true));
        assertTrue(opts.isCreateNew());
        assertTrue(opts.isRead());
        assertEquals(opts, opts.setRead(false));
        assertFalse(opts.isRead());
        assertTrue(opts.isWrite());
        assertEquals(opts, opts.setWrite(false));
        assertFalse(opts.isWrite());
        assertFalse(opts.isDsync());
        assertEquals(opts, opts.setDsync(true));
        assertTrue(opts.isDsync());
        assertFalse(opts.isSync());
        assertEquals(opts, opts.setSync(true));
        assertTrue(opts.isSync());
        assertFalse(opts.isDeleteOnClose());
        assertEquals(opts, opts.setDeleteOnClose(true));
        assertTrue(opts.isDeleteOnClose());
        assertFalse(opts.isTruncateExisting());
        assertEquals(opts, opts.setTruncateExisting(true));
        assertTrue(opts.isTruncateExisting());
        assertFalse(opts.isSparse());
        assertEquals(opts, opts.setSparse(true));
        assertTrue(opts.isSparse());
    }

    @Test
    public void testDefaultOptionOptions() {
        OpenOptions def = new OpenOptions();
        OpenOptions json = new OpenOptions(new JsonObject());
        assertEquals(def.getPerms(), json.getPerms());
        assertEquals(def.isRead(), json.isRead());
        assertEquals(def.isWrite(), json.isWrite());
        assertEquals(def.isCreate(), json.isCreate());
        assertEquals(def.isCreateNew(), json.isCreateNew());
        assertEquals(def.isDeleteOnClose(), json.isDeleteOnClose());
        assertEquals(def.isTruncateExisting(), json.isTruncateExisting());
        assertEquals(def.isSparse(), json.isSparse());
        assertEquals(def.isSync(), json.isSync());
        assertEquals(def.isDsync(), json.isDsync());
    }

    @Test
    public void testAsyncFileCloseHandlerIsAsync() throws Exception {
        String fileName = "some-file.dat";
        createFileWithJunk(fileName, 100);
        AsyncFile file = vertx.fileSystem().openBlocking(testDir + pathSep + fileName, new OpenOptions());
        ThreadLocal stack = new ThreadLocal();
        stack.set(true);
        file.close(ar -> {
            assertNull(stack.get());
            assertTrue(Vertx.currentContext().isEventLoopContext());
            testComplete();
        });
        await();
    }

    @Test
    public void testDrainNotCalledAfterClose() throws Exception {
        String fileName = "some-file.dat";
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), onSuccess(file -> {
            Buffer buf = TestUtils.randomBuffer(1024 * 1024);
            file.write(buf);
            AtomicBoolean drainAfterClose = new AtomicBoolean();
            file.drainHandler(v -> {
                drainAfterClose.set(true);
            });
            file.close(ar -> {
                assertFalse(drainAfterClose.get());
                testComplete();
            });
        }));
        await();
    }

    @Test
    public void testResumeFileInEndHandler() throws Exception {
        Buffer expected = TestUtils.randomBuffer(10000);
        String fileName = "some-file.dat";
        createFile(fileName, expected.getBytes());
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), onSuccess(file -> {
            Buffer buffer = Buffer.buffer();
            file.endHandler(v -> {
                assertEquals(buffer.length(), expected.length());
                file.pause();
                file.resume();
                complete();
            });
            file.handler(buffer::appendBuffer);
        }));
        await();
    }

    @Test
    public void testPausedEnd() throws Exception {
        String fileName = "some-file.dat";
        createFile(fileName, new byte[0]);
        AtomicBoolean paused = new AtomicBoolean(false);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), onSuccess(file -> {
            Buffer buffer = Buffer.buffer();
            paused.set(true);
            file.pause();
            vertx.setTimer(100, id -> {
                paused.set(false);
                file.resume();
            });
            file.endHandler(v -> {
                assertFalse(paused.get());
                testComplete();
            });
            file.handler(buffer::appendBuffer);
        }));
        await();
    }

    private Handler<AsyncResult<Void>> createHandler(boolean shouldPass, Handler<Void> afterOK) {
        return ar -> {
            if (ar.failed()) {
                if (shouldPass) {
                    fail(ar.cause().getMessage());
                } else {
                    assertTrue(ar.cause() instanceof FileSystemException);
                    if (afterOK != null) {
                        afterOK.handle(null);
                    }
                    testComplete();
                }
            } else {
                if (shouldPass) {
                    if (afterOK != null) {
                        afterOK.handle(null);
                    }
                    testComplete();
                } else {
                    fail("operation should fail");
                }
            }
        };
    }

    // Helper methods

    private boolean fileExists(String fileName) {
        File file = new File(testDir, fileName);
        return file.exists();
    }

    private void createFileWithJunk(String fileName, long length) throws Exception {
        createFile(fileName, TestUtils.randomByteArray((int) length));
    }

    private void createFile(String fileName, byte[] bytes) throws Exception {
        File file = new File(testDir, fileName);
        Path path = Paths.get(file.getCanonicalPath());
        Files.write(path, bytes);

        setPerms(path, DEFAULT_FILE_PERMS);
    }

    private void deleteDir(File dir) {
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                deleteDir(files[i]);
            } else {
                files[i].delete();
            }
        }
        dir.delete();
    }

    private void deleteDir(String dir) {
        deleteDir(new File(testDir + pathSep + dir));
    }

    private void mkDir(String dirName) throws Exception {
        File dir = new File(testDir + pathSep + dirName);
        dir.mkdir();

        setPerms(Paths.get(dir.getCanonicalPath()), DEFAULT_DIR_PERMS);
    }

    private long fileLength(String fileName) {
        File file = new File(testDir, fileName);
        return file.length();
    }

    private void setPerms(Path path, String perms) {
        if (Utils.isWindows() == false) {
            try {
                Files.setPosixFilePermissions(path, PosixFilePermissions.fromString(perms));
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    }

    private String getPerms(String fileName) {
        try {
            Set<PosixFilePermission> perms = Files.getPosixFilePermissions(Paths.get(testDir + pathSep + fileName));
            return PosixFilePermissions.toString(perms);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    private void deleteFile(String fileName) {
        File file = new File(testDir + pathSep + fileName);
        file.delete();
    }

    // @Repeat(times=1000)
    @Test
    public void testAsyncFileConcurrency() throws Exception {
        String fileName = "some-file.dat";

        AtomicReference<AsyncFile> arFile = new AtomicReference<>();
        CountDownLatch latch = new CountDownLatch(1);
        vertx.fileSystem().open(testDir + pathSep + fileName, new OpenOptions(), ar -> {
            if (ar.succeeded()) {
                AsyncFile af = ar.result();
                arFile.set(af);
            } else {
                fail(ar.cause().getMessage());
            }
            latch.countDown();
        });
        awaitLatch(latch);

        AsyncFile af = arFile.get();

        Buffer buff = Buffer.buffer(randomByteArray(4096));
        for (int i = 0; i < 100000; i++) {
            af.write(buff);
        }

        af.close(onSuccess(v -> {
            testComplete();
        }));

        await();
    }

    @Test
    public void testAtomicMove() throws Exception {
        String source = "foo.txt";
        String middle = "baz.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);

        try {
            Files.move(new File(testDir, source).toPath(), new File(testDir, middle).toPath(),
                    StandardCopyOption.ATOMIC_MOVE);
        } catch (AtomicMoveNotSupportedException e) {
            throw new AssumptionViolatedException("Atomic move unsupported");
        }

        FileSystem fs = vertx.fileSystem();
        String from = testDir + pathSep + middle;
        String to = testDir + pathSep + target;
        CopyOptions options = new CopyOptions().setAtomicMove(true);

        fs.move(from, to, options, onSuccess(v -> {
            assertFalse(fileExists(middle));
            assertTrue(fileExists(target));
            complete();
        }));
        await();
    }

    @Test
    public void testCopyReplaceExisting() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        createFileWithJunk(target, 100);

        FileSystem fs = vertx.fileSystem();
        String from = testDir + pathSep + source;
        String to = testDir + pathSep + target;
        CopyOptions options = new CopyOptions().setReplaceExisting(true);

        fs.copy(from, to, options, onSuccess(v -> {
            fs.readFile(from, onSuccess(expected -> {
                fs.readFile(to, onSuccess(actual -> {
                    assertEquals(expected, actual);
                    complete();
                }));
            }));
        }));
        await();
    }

    @Test
    public void testCopyNoReplaceExisting() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);
        createFileWithJunk(target, 100);

        FileSystem fs = vertx.fileSystem();
        String from = testDir + pathSep + source;
        String to = testDir + pathSep + target;
        CopyOptions options = new CopyOptions();

        fs.copy(from, to, options, onFailure(t -> {
            assertThat(t, instanceOf(FileSystemException.class));
            assertThat(t.getCause(), instanceOf(FileAlreadyExistsException.class));
            complete();
        }));
        await();
    }

    @Test
    public void testCopyFileAttributes() throws Exception {
        String source = "foo.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);

        try {
            Files.setPosixFilePermissions(new File(testDir, source).toPath(),
                    EnumSet.of(PosixFilePermission.OWNER_READ));
        } catch (UnsupportedOperationException e) {
            throw new AssumptionViolatedException("POSIX file perms unsupported");
        }

        FileSystem fs = vertx.fileSystem();
        String from = testDir + pathSep + source;
        String to = testDir + pathSep + target;
        CopyOptions options = new CopyOptions().setCopyAttributes(false);

        fs.copy(from, to, options, onSuccess(v -> {
            fs.props(from, onSuccess(expected -> {
                fs.props(from, onSuccess(actual -> {
                    assertEquals(expected.creationTime(), actual.creationTime());
                    assertEquals(expected.lastModifiedTime(), actual.lastModifiedTime());
                    vertx.<Set<PosixFilePermission>>executeBlocking(fut -> {
                        try {
                            fut.complete(Files.getPosixFilePermissions(new File(testDir, target).toPath(),
                                    LinkOption.NOFOLLOW_LINKS));
                        } catch (IOException e) {
                            fut.fail(e);
                        }
                    }, onSuccess(perms -> {
                        assertEquals(EnumSet.of(PosixFilePermission.OWNER_READ), perms);
                        complete();
                    }));
                }));
            }));
        }));
        await();
    }

    @Test
    public void testCopyNoFollowLinks() throws Exception {
        // Symlinks require a modified security policy in Windows. -- See http://stackoverflow.com/questions/23217460/how-to-create-soft-symbolic-link-using-java-nio-files
        Assume.assumeFalse(Utils.isWindows());

        String source = "foo.txt";
        String link = "link.txt";
        String target = "bar.txt";
        createFileWithJunk(source, 100);

        try {
            Files.createSymbolicLink(new File(testDir, link).toPath(), new File(testDir, source).toPath());
        } catch (UnsupportedOperationException e) {
            throw new AssumptionViolatedException("Links unsupported");
        }

        FileSystem fs = vertx.fileSystem();
        String from = testDir + pathSep + link;
        String to = testDir + pathSep + target;
        CopyOptions options = new CopyOptions().setNofollowLinks(true);

        fs.copy(from, to, options, onSuccess(v -> {
            fs.lprops(to, onSuccess(props -> {
                assertTrue(props.isSymbolicLink());
                fs.readFile(from, onSuccess(expected -> {
                    fs.readFile(to, onSuccess(actual -> {
                        assertEquals(expected, actual);
                        complete();
                    }));
                }));
            }));
        }));
        await();
    }

    @Test
    public void testCreateTempDirectory() {
        FileSystem fs = vertx.fileSystem();
        fs.createTempDirectory("project", onSuccess(tempDirectory -> {
            assertNotNull(tempDirectory);
            assertTrue(Files.exists(Paths.get(tempDirectory)));
            complete();
        }));
        await();
    }

    @Test
    public void testCreateTempDirectoryBlocking() {
        FileSystem fs = vertx.fileSystem();
        String tempDirectory = fs.createTempDirectoryBlocking("project");
        assertTrue(Files.exists(Paths.get(tempDirectory)));
    }

    @Test
    public void testCreateTempDirectoryWithPerms() {
        Assume.assumeFalse(Utils.isWindows());
        FileSystem fs = vertx.fileSystem();
        fs.createTempDirectory("project", DEFAULT_DIR_PERMS, onSuccess(tempDirectory -> {
            try {
                String perms = PosixFilePermissions
                        .toString(Files.getPosixFilePermissions(Paths.get(tempDirectory)));
                assertEquals(perms, DEFAULT_DIR_PERMS);
            } catch (IOException e) {
                fail(e);
            }
            complete();
        }));
        await();
    }

    @Test
    public void testCreateTempDirectoryWithPermsBlocking() throws Exception {
        Assume.assumeFalse(Utils.isWindows());

        FileSystem fs = vertx.fileSystem();
        String tempDirectory = fs.createTempDirectoryBlocking("project", DEFAULT_DIR_PERMS);
        String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(Paths.get(tempDirectory)));
        assertEquals(perms, DEFAULT_DIR_PERMS);
    }

    @Test
    public void testCreateTempDirectoryWithDirectory() {
        FileSystem fs = vertx.fileSystem();
        fs.createTempDirectory(testDir, "project", null, onSuccess(tempDirectory -> {
            Path path = Paths.get(tempDirectory);
            assertTrue(Files.exists(path));
            assertTrue(path.startsWith(testDir));
            complete();
        }));
        await();
    }

    @Test
    public void testCreateTempDirectoryWithDirectoryBlocking() {
        FileSystem fs = vertx.fileSystem();
        String tempDirectory = fs.createTempDirectoryBlocking(testDir, "project", null);
        Path path = Paths.get(tempDirectory);
        assertTrue(Files.exists(Paths.get(tempDirectory)));
        assertTrue(path.startsWith(testDir));
    }

    @Test
    public void testCreateTempFile() {
        FileSystem fs = vertx.fileSystem();
        fs.createTempFile("project", ".tmp", onSuccess(tempFile -> {
            assertNotNull(tempFile);
            assertTrue(Files.exists(Paths.get(tempFile)));
            complete();
        }));
        await();
    }

    @Test
    public void testCreateTempFileBlocking() {
        FileSystem fs = vertx.fileSystem();
        String tempFile = fs.createTempFileBlocking("project", ".tmp");
        assertNotNull(tempFile);
        assertTrue(Files.exists(Paths.get(tempFile)));
    }

    @Test
    public void testCreateTempFileWithDirectory() {
        FileSystem fs = vertx.fileSystem();
        fs.createTempFile(testDir, "project", ".tmp", null, onSuccess(tempFile -> {
            assertNotNull(tempFile);
            Path path = Paths.get(tempFile);
            assertTrue(Files.exists(path));
            assertTrue(path.startsWith(testDir));
            complete();
        }));
        await();
    }

    @Test
    public void testCreateTempFileWithDirectoryBlocking() {
        FileSystem fs = vertx.fileSystem();
        String tempFile = fs.createTempFileBlocking(testDir, "project", ".tmp", null);
        assertNotNull(tempFile);
        Path path = Paths.get(tempFile);
        assertTrue(Files.exists(path));
        assertTrue(path.startsWith(testDir));
    }

    @Test
    public void testCreateTempFileWithPerms() {
        Assume.assumeFalse(Utils.isWindows());

        FileSystem fs = vertx.fileSystem();
        fs.createTempFile("project", ".tmp", DEFAULT_FILE_PERMS, onSuccess(tempFile -> {
            Path path = Paths.get(tempFile);
            assertTrue(Files.exists(path));
            try {
                String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(path));
                assertEquals(perms, DEFAULT_FILE_PERMS);
            } catch (IOException e) {
                fail(e);
            }
        }));
    }

    @Test
    public void testCreateTempFileWithPermsBlocking() throws Exception {
        Assume.assumeFalse(Utils.isWindows());

        FileSystem fs = vertx.fileSystem();
        String tempFile = fs.createTempFileBlocking("project", ".tmp", DEFAULT_FILE_PERMS);
        Path path = Paths.get(tempFile);
        assertTrue(Files.exists(path));
        String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(path));
        assertEquals(perms, DEFAULT_FILE_PERMS);
    }
}