You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/11/11 16:50:51 UTC

[3/8] mina-sshd git commit: [SSHD-861] Added SftpFileSystemClientSessionInitializer hook in SftpFileSystemProvider

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
deleted file mode 100644
index 9ecb690..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemTest.java
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.client.subsystem.sftp;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.nio.channels.OverlappingFileLockException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileAlreadyExistsException;
-import java.nio.file.FileStore;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.StandardOpenOption;
-import java.nio.file.attribute.AclEntry;
-import java.nio.file.attribute.AclFileAttributeView;
-import java.nio.file.attribute.FileAttributeView;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.GroupPrincipal;
-import java.nio.file.attribute.PosixFilePermissions;
-import java.nio.file.attribute.UserPrincipalLookupService;
-import java.nio.file.attribute.UserPrincipalNotFoundException;
-import java.nio.file.spi.FileSystemProvider;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.file.FileSystemFactory;
-import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.subsystem.sftp.SftpConstants;
-import org.apache.sshd.common.util.GenericUtils;
-import org.apache.sshd.common.util.OsUtils;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.server.SshServer;
-import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
-import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.apache.sshd.util.test.CommonTestSupportUtils;
-import org.apache.sshd.util.test.CoreTestSupportUtils;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@SuppressWarnings("checkstyle:MethodCount")
-public class SftpFileSystemTest extends BaseTestSupport {
-    private static SshServer sshd;
-    private static int port;
-
-    private final FileSystemFactory fileSystemFactory;
-
-    public SftpFileSystemTest() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path parentPath = targetPath.getParent();
-        fileSystemFactory = new VirtualFileSystemFactory(parentPath);
-    }
-
-    @BeforeClass
-    public static void setupServerInstance() throws Exception {
-        sshd = CoreTestSupportUtils.setupTestServer(SftpFileSystemTest.class);
-        sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
-        sshd.start();
-        port = sshd.getPort();
-    }
-
-    @AfterClass
-    public static void tearDownServerInstance() throws Exception {
-        if (sshd != null) {
-            try {
-                sshd.stop(true);
-            } finally {
-                sshd = null;
-            }
-        }
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        sshd.setFileSystemFactory(fileSystemFactory);
-    }
-
-    @Test
-    public void testFileSystem() throws Exception {
-        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
-                GenericUtils.<String, Object>mapBuilder()
-                        .put(SftpFileSystemProvider.READ_BUFFER_PROP_NAME, IoUtils.DEFAULT_COPY_SIZE)
-                        .put(SftpFileSystemProvider.WRITE_BUFFER_PROP_NAME, IoUtils.DEFAULT_COPY_SIZE)
-                        .build())) {
-            assertTrue("Not an SftpFileSystem", fs instanceof SftpFileSystem);
-            testFileSystem(fs, ((SftpFileSystem) fs).getVersion());
-        }
-    }
-
-    @Test   // see SSHD-578
-    public void testFileSystemURIParameters() throws Exception {
-        Map<String, Object> params = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        params.put("test-class-name", getClass().getSimpleName());
-        params.put("test-pkg-name", getClass().getPackage().getName());
-        params.put("test-name", getCurrentTestName());
-
-        int expectedVersion = (SftpSubsystemEnvironment.LOWER_SFTP_IMPL + SftpSubsystemEnvironment.HIGHER_SFTP_IMPL) / 2;
-        params.put(SftpFileSystemProvider.VERSION_PARAM, expectedVersion);
-        try (SftpFileSystem fs = (SftpFileSystem) FileSystems.newFileSystem(createDefaultFileSystemURI(params), Collections.<String, Object>emptyMap())) {
-            try (SftpClient sftpClient = fs.getClient()) {
-                assertEquals("Mismatched negotiated version", expectedVersion, sftpClient.getVersion());
-
-                Session session = sftpClient.getClientSession();
-                params.forEach((key, expected) -> {
-                    if (SftpFileSystemProvider.VERSION_PARAM.equalsIgnoreCase(key)) {
-                        return;
-                    }
-
-                    Object actual = session.getObject(key);
-                    assertEquals("Mismatched value for param '" + key + "'", expected, actual);
-                });
-            }
-        }
-    }
-
-    @Test
-    public void testAttributes() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
-            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        CommonTestSupportUtils.deleteRecursive(lclSftp);
-
-        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
-                GenericUtils.<String, Object>mapBuilder()
-                    .put(SftpFileSystemProvider.READ_BUFFER_PROP_NAME, SftpClient.MIN_READ_BUFFER_SIZE)
-                    .put(SftpFileSystemProvider.WRITE_BUFFER_PROP_NAME, SftpClient.MIN_WRITE_BUFFER_SIZE)
-                    .build())) {
-
-            Path parentPath = targetPath.getParent();
-            Path clientFolder = lclSftp.resolve("client");
-            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
-            Path file = fs.getPath(remFilePath);
-            assertHierarchyTargetFolderExists(file.getParent());
-            Files.write(file, (getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
-
-            Map<String, Object> attrs = Files.readAttributes(file, "posix:*");
-            assertNotNull("No attributes read for " + file, attrs);
-
-            Files.setAttribute(file, "basic:size", 2L);
-            Files.setAttribute(file, "posix:permissions", PosixFilePermissions.fromString("rwxr-----"));
-            Files.setAttribute(file, "basic:lastModifiedTime", FileTime.fromMillis(100000L));
-
-            FileSystem fileSystem = file.getFileSystem();
-            try {
-                UserPrincipalLookupService userLookupService = fileSystem.getUserPrincipalLookupService();
-                GroupPrincipal group = userLookupService.lookupPrincipalByGroupName("everyone");
-                Files.setAttribute(file, "posix:group", group);
-            } catch (UserPrincipalNotFoundException e) {
-                // Also, according to the Javadoc:
-                //      "Where an implementation does not support any notion of
-                //       group then this method always throws UserPrincipalNotFoundException."
-                // Therefore we are lenient with this exception for Windows
-                if (OsUtils.isWin32()) {
-                    System.err.println(e.getClass().getSimpleName() + ": " + e.getMessage());
-                } else {
-                    throw e;
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testRootFileSystem() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path rootNative = targetPath.resolve("root").toAbsolutePath();
-        CommonTestSupportUtils.deleteRecursive(rootNative);
-        assertHierarchyTargetFolderExists(rootNative);
-
-        try (FileSystem fs = FileSystems.newFileSystem(URI.create("root:" + rootNative.toUri().toString() + "!/"), null)) {
-            Path dir = assertHierarchyTargetFolderExists(fs.getPath("test/foo"));
-            outputDebugMessage("Created %s", dir);
-        }
-    }
-
-    @Test   // see SSHD-697
-    public void testFileChannel() throws IOException {
-        Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
-            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
-        Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
-        Files.deleteIfExists(lclFile);
-        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
-        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
-            Path parentPath = targetPath.getParent();
-            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
-            Path file = fs.getPath(remFilePath);
-
-            FileSystemProvider provider = fs.provider();
-            try (FileChannel fc = provider.newFileChannel(file, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE))) {
-                int writeLen = fc.write(ByteBuffer.wrap(expected));
-                assertEquals("Mismatched written length", expected.length, writeLen);
-
-                FileChannel fcPos = fc.position(0L);
-                assertSame("Mismatched positioned file channel", fc, fcPos);
-
-                byte[] actual = new byte[expected.length];
-                int readLen = fc.read(ByteBuffer.wrap(actual));
-                assertEquals("Mismatched read len", writeLen, readLen);
-                assertArrayEquals("Mismatched read data", expected, actual);
-            }
-        }
-
-        byte[] actual = Files.readAllBytes(lclFile);
-        assertArrayEquals("Mismatched persisted data", expected, actual);
-    }
-
-    @Test
-    public void testFileStore() throws IOException {
-        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
-            Iterable<FileStore> iter = fs.getFileStores();
-            assertTrue("Not a list", iter instanceof List<?>);
-
-            List<FileStore> list = (List<FileStore>) iter;
-            assertEquals("Mismatched stores count", 1, list.size());
-
-            FileStore store = list.get(0);
-            assertEquals("Mismatched type", SftpConstants.SFTP_SUBSYSTEM_NAME, store.type());
-            assertFalse("Read-only ?", store.isReadOnly());
-
-            for (String name : fs.supportedFileAttributeViews()) {
-                assertTrue("Unsupported view name: " + name, store.supportsFileAttributeView(name));
-            }
-
-            for (Class<? extends FileAttributeView> type : SftpFileSystemProvider.UNIVERSAL_SUPPORTED_VIEWS) {
-                assertTrue("Unsupported view type: " + type.getSimpleName(), store.supportsFileAttributeView(type));
-            }
-        }
-    }
-
-    @Test
-    public void testMultipleFileStoresOnSameProvider() throws IOException {
-        try (SshClient client = setupTestClient()) {
-            client.start();
-
-            SftpFileSystemProvider provider = new SftpFileSystemProvider(client);
-            Collection<SftpFileSystem> fsList = new LinkedList<>();
-            try {
-                Collection<String> idSet = new HashSet<>();
-                Map<String, Object> empty = Collections.emptyMap();
-                for (int index = 0; index < 4; index++) {
-                    String credentials = getCurrentTestName() + "-user-" + index;
-                    SftpFileSystem expected = provider.newFileSystem(createFileSystemURI(credentials, empty), empty);
-                    fsList.add(expected);
-
-                    String id = expected.getId();
-                    assertTrue("Non unique file system id: " + id, idSet.add(id));
-
-                    SftpFileSystem actual = provider.getFileSystem(id);
-                    assertSame("Mismatched cached instances for " + id, expected, actual);
-                    outputDebugMessage("Created file system id: %s", id);
-                }
-
-                for (SftpFileSystem fs : fsList) {
-                    String id = fs.getId();
-                    fs.close();
-                    assertNull("File system not removed from cache: " + id, provider.getFileSystem(id));
-                }
-            } finally {
-                IOException err = null;
-                for (FileSystem fs : fsList) {
-                    try {
-                        fs.close();
-                    } catch (IOException e) {
-                        err = GenericUtils.accumulateException(err, e);
-                    }
-                }
-
-                client.stop();
-
-                if (err != null) {
-                    throw err;
-                }
-            }
-        }
-    }
-
-    @Test
-    public void testSftpVersionSelector() throws Exception {
-        final AtomicInteger selected = new AtomicInteger(-1);
-        SftpVersionSelector selector = (session, current, available) -> {
-            int value = GenericUtils.stream(available)
-                    .mapToInt(Integer::intValue)
-                    .filter(v -> v != current)
-                    .max()
-                    .orElseGet(() -> current);
-            selected.set(value);
-            return value;
-        };
-
-        try (SshClient client = setupTestClient()) {
-            client.start();
-
-            try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) {
-                session.addPasswordIdentity(getCurrentTestName());
-                session.auth().verify(5L, TimeUnit.SECONDS);
-
-                try (FileSystem fs = createSftpFileSystem(session, selector)) {
-                    assertTrue("Not an SftpFileSystem", fs instanceof SftpFileSystem);
-                    Collection<String> views = fs.supportedFileAttributeViews();
-                    assertTrue("Universal views (" + SftpFileSystem.UNIVERSAL_SUPPORTED_VIEWS + ") not supported: " + views,
-                               views.containsAll(SftpFileSystem.UNIVERSAL_SUPPORTED_VIEWS));
-                    int expectedVersion = selected.get();
-                    assertEquals("Mismatched negotiated version", expectedVersion, ((SftpFileSystem) fs).getVersion());
-                    testFileSystem(fs, expectedVersion);
-                }
-            } finally {
-                client.stop();
-            }
-        }
-    }
-
-    private FileSystem createSftpFileSystem(ClientSession session, SftpVersionSelector selector) throws IOException {
-        return SftpClientFactory.instance().createSftpFileSystem(session, selector);
-    }
-
-    private void testFileSystem(FileSystem fs, int version) throws Exception {
-        Iterable<Path> rootDirs = fs.getRootDirectories();
-        for (Path root : rootDirs) {
-            String rootName = root.toString();
-            try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
-                for (Path child : ds) {
-                    String name = child.getFileName().toString();
-                    assertNotEquals("Unexpected dot name", ".", name);
-                    assertNotEquals("Unexpected dotdot name", "..", name);
-                    outputDebugMessage("[%s] %s", rootName, child);
-                }
-            } catch (IOException | RuntimeException e) {
-                // TODO on Windows one might get share problems for *.sys files
-                // e.g. "C:\hiberfil.sys: The process cannot access the file because it is being used by another process"
-                // for now, Windows is less of a target so we are lenient with it
-                if (OsUtils.isWin32()) {
-                    System.err.println(e.getClass().getSimpleName() + " while accessing children of root=" + root + ": " + e.getMessage());
-                } else {
-                    throw e;
-                }
-            }
-        }
-
-        Path targetPath = detectTargetFolder();
-        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
-            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
-        CommonTestSupportUtils.deleteRecursive(lclSftp);
-
-        Path current = fs.getPath(".").toRealPath().normalize();
-        outputDebugMessage("CWD: %s", current);
-
-        Path parentPath = targetPath.getParent();
-        Path clientFolder = lclSftp.resolve("client");
-        String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
-        Path file1 = fs.getPath(remFile1Path);
-        assertHierarchyTargetFolderExists(file1.getParent());
-
-        String expected = "Hello world: " + getCurrentTestName();
-        outputDebugMessage("Write initial data to %s", file1);
-        Files.write(file1, expected.getBytes(StandardCharsets.UTF_8));
-        String buf = new String(Files.readAllBytes(file1), StandardCharsets.UTF_8);
-        assertEquals("Mismatched read test data", expected, buf);
-
-        if (version >= SftpConstants.SFTP_V4) {
-            outputDebugMessage("getFileAttributeView(%s)", file1);
-            AclFileAttributeView aclView = Files.getFileAttributeView(file1, AclFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
-            assertNotNull("No ACL view for " + file1, aclView);
-
-            Map<String, ?> attrs = Files.readAttributes(file1, "acl:*", LinkOption.NOFOLLOW_LINKS);
-            outputDebugMessage("readAttributes(%s) %s", file1, attrs);
-            assertEquals("Mismatched owner for " + file1, aclView.getOwner(), attrs.get("owner"));
-
-            @SuppressWarnings("unchecked")
-            List<AclEntry> acl = (List<AclEntry>) attrs.get("acl");
-            outputDebugMessage("acls(%s) %s", file1, acl);
-            assertListEquals("Mismatched ACLs for " + file1, aclView.getAcl(), acl);
-        }
-
-        String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
-        Path file2 = fs.getPath(remFile2Path);
-        String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
-        Path file3 = fs.getPath(remFile3Path);
-        try {
-            outputDebugMessage("Move with failure expected %s => %s", file2, file3);
-            Files.move(file2, file3, LinkOption.NOFOLLOW_LINKS);
-            fail("Unexpected success in moving " + file2 + " => " + file3);
-        } catch (NoSuchFileException e) {
-            // expected
-        }
-
-        Files.write(file2, "h".getBytes(StandardCharsets.UTF_8));
-        try {
-            outputDebugMessage("Move with failure expected %s => %s", file1, file2);
-            Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS);
-            fail("Unexpected success in moving " + file1 + " => " + file2);
-        } catch (FileAlreadyExistsException e) {
-            // expected
-        }
-
-        outputDebugMessage("Move with success expected %s => %s", file1, file2);
-        Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
-        outputDebugMessage("Move with success expected %s => %s", file2, file1);
-        Files.move(file2, file1, LinkOption.NOFOLLOW_LINKS);
-
-        Map<String, Object> attrs = Files.readAttributes(file1, "*");
-        outputDebugMessage("%s attributes: %s", file1, attrs);
-
-        // TODO there are many issues with symbolic links on Windows
-        if (OsUtils.isUNIX()) {
-            Path link = fs.getPath(remFile2Path);
-            Path linkParent = link.getParent();
-            Path relPath = linkParent.relativize(file1);
-            outputDebugMessage("Create symlink %s => %s", link, relPath);
-            Files.createSymbolicLink(link, relPath);
-            assertTrue("Not a symbolic link: " + link, Files.isSymbolicLink(link));
-
-            Path symLink = Files.readSymbolicLink(link);
-            assertEquals("mismatched symbolic link name", relPath.toString(), symLink.toString());
-
-            outputDebugMessage("Delete symlink %s", link);
-            Files.delete(link);
-        }
-
-        attrs = Files.readAttributes(file1, "*", LinkOption.NOFOLLOW_LINKS);
-        outputDebugMessage("%s no-follow attributes: %s", file1, attrs);
-        assertEquals("Mismatched symlink data", expected, new String(Files.readAllBytes(file1), StandardCharsets.UTF_8));
-
-        try (FileChannel channel = FileChannel.open(file1)) {
-            try (FileLock lock = channel.lock()) {
-                outputDebugMessage("Lock %s: %s", file1, lock);
-
-                try (FileChannel channel2 = FileChannel.open(file1)) {
-                    try (FileLock lock2 = channel2.lock()) {
-                        fail("Unexpected success in re-locking " + file1 + ": " + lock2);
-                    } catch (OverlappingFileLockException e) {
-                        // expected
-                    }
-                }
-            }
-        }
-
-        Files.delete(file1);
-    }
-
-    private URI createDefaultFileSystemURI() {
-        return createDefaultFileSystemURI(Collections.emptyMap());
-    }
-
-    private URI createDefaultFileSystemURI(Map<String, ?> params) {
-        return createFileSystemURI(getCurrentTestName(), params);
-    }
-
-    private URI createFileSystemURI(String username, Map<String, ?> params) {
-        return createFileSystemURI(username, port, params);
-    }
-
-    private static URI createFileSystemURI(String username, int port, Map<String, ?> params) {
-        return SftpFileSystemProvider.createFileSystemURI(TEST_LOCALHOST, port, username, username, params);
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemURITest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemURITest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemURITest.java
deleted file mode 100644
index 1e89e47..0000000
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpFileSystemURITest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.sshd.client.subsystem.sftp;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.auth.BasicCredentialsProvider;
-import org.apache.sshd.common.subsystem.sftp.SftpConstants;
-import org.apache.sshd.common.util.net.SshdSocketAddress;
-import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
-import org.apache.sshd.util.test.JUnitTestSupport;
-import org.apache.sshd.util.test.NoIoTestCase;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.MethodSorters;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-
-/**
- * TODO Add javadoc
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
-@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
-@Category({ NoIoTestCase.class })
-public class SftpFileSystemURITest extends JUnitTestSupport {
-    private final String host;
-    private final int port;
-    private final String username;
-    private final String password;
-    private final Map<String, ?> params;
-
-    public SftpFileSystemURITest(String host, int port, String username, String password, Map<String, ?> params) {
-        this.host = host;
-        this.port = port;
-        this.username = username;
-        this.password = password;
-        this.params = params;
-    }
-
-    @Parameters(name = "host={0}, port={1}, user={2}, password={3}, params={4}")
-    public static List<Object[]> parameters() {
-        return new ArrayList<Object[]>() {
-            // Not serializing it
-            private static final long serialVersionUID = 1L;
-
-            {
-                add(new Object[] {SshdSocketAddress.LOCALHOST_NAME, 0, "user", "password", null});
-                add(new Object[] {"37.77.34.7", 2222, "user", "password", Collections.singletonMap("non-default-port", true)});
-                add(new Object[] {SshdSocketAddress.LOCALHOST_NAME, SshConstants.DEFAULT_PORT, "J@ck", "d@Ripper", new HashMap<String, Object>() {
-                        // not serializing it
-                        private static final long serialVersionUID = 1L;
-
-                        {
-                            put("param1", "1st");
-                            put("param2", 2);
-                            put("param3", false);
-                        }
-                    }
-                });
-                add(new Object[] {"19.65.7.3", 0, "J%ck", "d%Ripper", null});
-            }
-        };
-    }
-
-    @Test
-    public void testFullURIEncoding() {
-        URI uri = SftpFileSystemProvider.createFileSystemURI(host, port, username, password, params);
-        assertEquals("Mismatched scheme", SftpConstants.SFTP_SUBSYSTEM_NAME, uri.getScheme());
-        assertEquals("Mismatched host", host, uri.getHost());
-        assertEquals("Mismatched port", port, uri.getPort());
-
-        BasicCredentialsProvider credentials = SftpFileSystemProvider.parseCredentials(uri);
-        assertNotNull("No credentials provided", credentials);
-        assertEquals("Mismatched user", username, credentials.getUsername());
-        assertEquals("Mismatched password", password, credentials.getPassword());
-
-        Map<String, ?> uriParams = SftpFileSystemProvider.parseURIParameters(uri);
-        assertMapEquals(getCurrentTestName(), params, uriParams, (v1, v2) -> Objects.equals(v1.toString(), v2.toString()));
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName()
-            + "[host=" + host
-            + ", port=" + port
-            + ", username=" + username
-            + ", password=" + password
-            + ", params=" + params
-            + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemTest.java
new file mode 100644
index 0000000..5b78325
--- /dev/null
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemTest.java
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.client.subsystem.sftp.fs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.AclEntry;
+import java.nio.file.attribute.AclFileAttributeView;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.GroupPrincipal;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.attribute.UserPrincipalNotFoundException;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.client.subsystem.sftp.SftpClient;
+import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
+import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
+import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.OsUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment;
+import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CommonTestSupportUtils;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@SuppressWarnings("checkstyle:MethodCount")
+public class SftpFileSystemTest extends BaseTestSupport {
+    private static SshServer sshd;
+    private static int port;
+
+    private final FileSystemFactory fileSystemFactory;
+
+    public SftpFileSystemTest() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path parentPath = targetPath.getParent();
+        fileSystemFactory = new VirtualFileSystemFactory(parentPath);
+    }
+
+    @BeforeClass
+    public static void setupServerInstance() throws Exception {
+        sshd = CoreTestSupportUtils.setupTestServer(SftpFileSystemTest.class);
+        sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @AfterClass
+    public static void tearDownServerInstance() throws Exception {
+        if (sshd != null) {
+            try {
+                sshd.stop(true);
+            } finally {
+                sshd = null;
+            }
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        sshd.setFileSystemFactory(fileSystemFactory);
+    }
+
+    @Test
+    public void testFileSystem() throws Exception {
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
+                GenericUtils.<String, Object>mapBuilder()
+                        .put(SftpFileSystemProvider.READ_BUFFER_PROP_NAME, IoUtils.DEFAULT_COPY_SIZE)
+                        .put(SftpFileSystemProvider.WRITE_BUFFER_PROP_NAME, IoUtils.DEFAULT_COPY_SIZE)
+                        .build())) {
+            assertTrue("Not an SftpFileSystem", fs instanceof SftpFileSystem);
+            testFileSystem(fs, ((SftpFileSystem) fs).getVersion());
+        }
+    }
+
+    @Test   // see SSHD-578
+    public void testFileSystemURIParameters() throws Exception {
+        Map<String, Object> params = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+        params.put("test-class-name", getClass().getSimpleName());
+        params.put("test-pkg-name", getClass().getPackage().getName());
+        params.put("test-name", getCurrentTestName());
+
+        int expectedVersion = (SftpSubsystemEnvironment.LOWER_SFTP_IMPL + SftpSubsystemEnvironment.HIGHER_SFTP_IMPL) / 2;
+        params.put(SftpFileSystemProvider.VERSION_PARAM, expectedVersion);
+        try (SftpFileSystem fs = (SftpFileSystem) FileSystems.newFileSystem(createDefaultFileSystemURI(params), Collections.<String, Object>emptyMap())) {
+            try (SftpClient sftpClient = fs.getClient()) {
+                assertEquals("Mismatched negotiated version", expectedVersion, sftpClient.getVersion());
+
+                Session session = sftpClient.getClientSession();
+                params.forEach((key, expected) -> {
+                    if (SftpFileSystemProvider.VERSION_PARAM.equalsIgnoreCase(key)) {
+                        return;
+                    }
+
+                    Object actual = session.getObject(key);
+                    assertEquals("Mismatched value for param '" + key + "'", expected, actual);
+                });
+            }
+        }
+    }
+
+    @Test
+    public void testAttributes() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
+
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(),
+                GenericUtils.<String, Object>mapBuilder()
+                    .put(SftpFileSystemProvider.READ_BUFFER_PROP_NAME, SftpClient.MIN_READ_BUFFER_SIZE)
+                    .put(SftpFileSystemProvider.WRITE_BUFFER_PROP_NAME, SftpClient.MIN_WRITE_BUFFER_SIZE)
+                    .build())) {
+
+            Path parentPath = targetPath.getParent();
+            Path clientFolder = lclSftp.resolve("client");
+            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file.txt"));
+            Path file = fs.getPath(remFilePath);
+            assertHierarchyTargetFolderExists(file.getParent());
+            Files.write(file, (getCurrentTestName() + "\n").getBytes(StandardCharsets.UTF_8));
+
+            Map<String, Object> attrs = Files.readAttributes(file, "posix:*");
+            assertNotNull("No attributes read for " + file, attrs);
+
+            Files.setAttribute(file, "basic:size", 2L);
+            Files.setAttribute(file, "posix:permissions", PosixFilePermissions.fromString("rwxr-----"));
+            Files.setAttribute(file, "basic:lastModifiedTime", FileTime.fromMillis(100000L));
+
+            FileSystem fileSystem = file.getFileSystem();
+            try {
+                UserPrincipalLookupService userLookupService = fileSystem.getUserPrincipalLookupService();
+                GroupPrincipal group = userLookupService.lookupPrincipalByGroupName("everyone");
+                Files.setAttribute(file, "posix:group", group);
+            } catch (UserPrincipalNotFoundException e) {
+                // Also, according to the Javadoc:
+                //      "Where an implementation does not support any notion of
+                //       group then this method always throws UserPrincipalNotFoundException."
+                // Therefore we are lenient with this exception for Windows
+                if (OsUtils.isWin32()) {
+                    System.err.println(e.getClass().getSimpleName() + ": " + e.getMessage());
+                } else {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testRootFileSystem() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path rootNative = targetPath.resolve("root").toAbsolutePath();
+        CommonTestSupportUtils.deleteRecursive(rootNative);
+        assertHierarchyTargetFolderExists(rootNative);
+
+        try (FileSystem fs = FileSystems.newFileSystem(URI.create("root:" + rootNative.toUri().toString() + "!/"), null)) {
+            Path dir = assertHierarchyTargetFolderExists(fs.getPath("test/foo"));
+            outputDebugMessage("Created %s", dir);
+        }
+    }
+
+    @Test   // see SSHD-697
+    public void testFileChannel() throws IOException {
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName());
+        Path lclFile = lclSftp.resolve(getCurrentTestName() + ".txt");
+        Files.deleteIfExists(lclFile);
+        byte[] expected = (getClass().getName() + "#" + getCurrentTestName() + "(" + new Date() + ")").getBytes(StandardCharsets.UTF_8);
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
+            Path parentPath = targetPath.getParent();
+            String remFilePath = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, lclFile);
+            Path file = fs.getPath(remFilePath);
+
+            FileSystemProvider provider = fs.provider();
+            try (FileChannel fc = provider.newFileChannel(file, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.READ, StandardOpenOption.WRITE))) {
+                int writeLen = fc.write(ByteBuffer.wrap(expected));
+                assertEquals("Mismatched written length", expected.length, writeLen);
+
+                FileChannel fcPos = fc.position(0L);
+                assertSame("Mismatched positioned file channel", fc, fcPos);
+
+                byte[] actual = new byte[expected.length];
+                int readLen = fc.read(ByteBuffer.wrap(actual));
+                assertEquals("Mismatched read len", writeLen, readLen);
+                assertArrayEquals("Mismatched read data", expected, actual);
+            }
+        }
+
+        byte[] actual = Files.readAllBytes(lclFile);
+        assertArrayEquals("Mismatched persisted data", expected, actual);
+    }
+
+    @Test
+    public void testFileStore() throws IOException {
+        try (FileSystem fs = FileSystems.newFileSystem(createDefaultFileSystemURI(), Collections.emptyMap())) {
+            Iterable<FileStore> iter = fs.getFileStores();
+            assertTrue("Not a list", iter instanceof List<?>);
+
+            List<FileStore> list = (List<FileStore>) iter;
+            assertEquals("Mismatched stores count", 1, list.size());
+
+            FileStore store = list.get(0);
+            assertEquals("Mismatched type", SftpConstants.SFTP_SUBSYSTEM_NAME, store.type());
+            assertFalse("Read-only ?", store.isReadOnly());
+
+            for (String name : fs.supportedFileAttributeViews()) {
+                assertTrue("Unsupported view name: " + name, store.supportsFileAttributeView(name));
+            }
+
+            for (Class<? extends FileAttributeView> type : SftpFileSystemProvider.UNIVERSAL_SUPPORTED_VIEWS) {
+                assertTrue("Unsupported view type: " + type.getSimpleName(), store.supportsFileAttributeView(type));
+            }
+        }
+    }
+
+    @Test
+    public void testMultipleFileStoresOnSameProvider() throws IOException {
+        try (SshClient client = setupTestClient()) {
+            client.start();
+
+            SftpFileSystemProvider provider = new SftpFileSystemProvider(client);
+            Collection<SftpFileSystem> fsList = new LinkedList<>();
+            try {
+                Collection<String> idSet = new HashSet<>();
+                Map<String, Object> empty = Collections.emptyMap();
+                for (int index = 0; index < 4; index++) {
+                    String credentials = getCurrentTestName() + "-user-" + index;
+                    SftpFileSystem expected = provider.newFileSystem(createFileSystemURI(credentials, empty), empty);
+                    fsList.add(expected);
+
+                    String id = expected.getId();
+                    assertTrue("Non unique file system id: " + id, idSet.add(id));
+
+                    SftpFileSystem actual = provider.getFileSystem(id);
+                    assertSame("Mismatched cached instances for " + id, expected, actual);
+                    outputDebugMessage("Created file system id: %s", id);
+                }
+
+                for (SftpFileSystem fs : fsList) {
+                    String id = fs.getId();
+                    fs.close();
+                    assertNull("File system not removed from cache: " + id, provider.getFileSystem(id));
+                }
+            } finally {
+                IOException err = null;
+                for (FileSystem fs : fsList) {
+                    try {
+                        fs.close();
+                    } catch (IOException e) {
+                        err = GenericUtils.accumulateException(err, e);
+                    }
+                }
+
+                client.stop();
+
+                if (err != null) {
+                    throw err;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testSftpVersionSelector() throws Exception {
+        AtomicInteger selected = new AtomicInteger(-1);
+        SftpVersionSelector selector = (session, current, available) -> {
+            int value = GenericUtils.stream(available)
+                    .mapToInt(Integer::intValue)
+                    .filter(v -> v != current)
+                    .max()
+                    .orElseGet(() -> current);
+            selected.set(value);
+            return value;
+        };
+
+        try (SshClient client = setupTestClient()) {
+            client.start();
+
+            try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
+                    .verify(7L, TimeUnit.SECONDS)
+                    .getSession()) {
+                session.addPasswordIdentity(getCurrentTestName());
+                session.auth().verify(5L, TimeUnit.SECONDS);
+
+                try (FileSystem fs = createSftpFileSystem(session, selector)) {
+                    assertTrue("Not an SftpFileSystem", fs instanceof SftpFileSystem);
+                    Collection<String> views = fs.supportedFileAttributeViews();
+                    assertTrue("Universal views (" + SftpFileSystem.UNIVERSAL_SUPPORTED_VIEWS + ") not supported: " + views,
+                               views.containsAll(SftpFileSystem.UNIVERSAL_SUPPORTED_VIEWS));
+                    int expectedVersion = selected.get();
+                    assertEquals("Mismatched negotiated version", expectedVersion, ((SftpFileSystem) fs).getVersion());
+                    testFileSystem(fs, expectedVersion);
+                }
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    @Test
+    public void testFileSystemProviderServiceEntry() throws IOException {
+        Path configFile = CommonTestSupportUtils.resolve(detectSourcesFolder(),
+            MAIN_SUBFOLDER, "filtered-resources", "META-INF", "services", FileSystemProvider.class.getName());
+        assertTrue("Missing " + configFile, Files.exists(configFile));
+
+        boolean found = false;
+        try (InputStream stream = Files.newInputStream(configFile);
+             Reader r = new InputStreamReader(stream, StandardCharsets.UTF_8);
+             BufferedReader b = new BufferedReader(r)) {
+
+            for (String line = b.readLine(); line != null; line = b.readLine()) {
+                line = line.trim();
+                if (GenericUtils.isEmpty(line) || (line.charAt(0) == '#')) {
+                    continue;
+                }
+
+                assertFalse("Multiple configurations: " + line, found);
+                assertEquals("Mismatched configuration", SftpFileSystemProvider.class.getName(), line);
+                found = true;
+            }
+        }
+
+        assertTrue("No configuration found", found);
+    }
+
+    private FileSystem createSftpFileSystem(ClientSession session, SftpVersionSelector selector) throws IOException {
+        return SftpClientFactory.instance().createSftpFileSystem(session, selector);
+    }
+
+    private void testFileSystem(FileSystem fs, int version) throws Exception {
+        Iterable<Path> rootDirs = fs.getRootDirectories();
+        for (Path root : rootDirs) {
+            String rootName = root.toString();
+            try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
+                for (Path child : ds) {
+                    String name = child.getFileName().toString();
+                    assertNotEquals("Unexpected dot name", ".", name);
+                    assertNotEquals("Unexpected dotdot name", "..", name);
+                    outputDebugMessage("[%s] %s", rootName, child);
+                }
+            } catch (IOException | RuntimeException e) {
+                // TODO on Windows one might get share problems for *.sys files
+                // e.g. "C:\hiberfil.sys: The process cannot access the file because it is being used by another process"
+                // for now, Windows is less of a target so we are lenient with it
+                if (OsUtils.isWin32()) {
+                    System.err.println(e.getClass().getSimpleName() + " while accessing children of root=" + root + ": " + e.getMessage());
+                } else {
+                    throw e;
+                }
+            }
+        }
+
+        Path targetPath = detectTargetFolder();
+        Path lclSftp = CommonTestSupportUtils.resolve(targetPath,
+            SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName());
+        CommonTestSupportUtils.deleteRecursive(lclSftp);
+
+        Path current = fs.getPath(".").toRealPath().normalize();
+        outputDebugMessage("CWD: %s", current);
+
+        Path parentPath = targetPath.getParent();
+        Path clientFolder = lclSftp.resolve("client");
+        String remFile1Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-1.txt"));
+        Path file1 = fs.getPath(remFile1Path);
+        assertHierarchyTargetFolderExists(file1.getParent());
+
+        String expected = "Hello world: " + getCurrentTestName();
+        outputDebugMessage("Write initial data to %s", file1);
+        Files.write(file1, expected.getBytes(StandardCharsets.UTF_8));
+        String buf = new String(Files.readAllBytes(file1), StandardCharsets.UTF_8);
+        assertEquals("Mismatched read test data", expected, buf);
+
+        if (version >= SftpConstants.SFTP_V4) {
+            outputDebugMessage("getFileAttributeView(%s)", file1);
+            AclFileAttributeView aclView = Files.getFileAttributeView(file1, AclFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
+            assertNotNull("No ACL view for " + file1, aclView);
+
+            Map<String, ?> attrs = Files.readAttributes(file1, "acl:*", LinkOption.NOFOLLOW_LINKS);
+            outputDebugMessage("readAttributes(%s) %s", file1, attrs);
+            assertEquals("Mismatched owner for " + file1, aclView.getOwner(), attrs.get("owner"));
+
+            @SuppressWarnings("unchecked")
+            List<AclEntry> acl = (List<AclEntry>) attrs.get("acl");
+            outputDebugMessage("acls(%s) %s", file1, acl);
+            assertListEquals("Mismatched ACLs for " + file1, aclView.getAcl(), acl);
+        }
+
+        String remFile2Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-2.txt"));
+        Path file2 = fs.getPath(remFile2Path);
+        String remFile3Path = CommonTestSupportUtils.resolveRelativeRemotePath(parentPath, clientFolder.resolve("file-3.txt"));
+        Path file3 = fs.getPath(remFile3Path);
+        try {
+            outputDebugMessage("Move with failure expected %s => %s", file2, file3);
+            Files.move(file2, file3, LinkOption.NOFOLLOW_LINKS);
+            fail("Unexpected success in moving " + file2 + " => " + file3);
+        } catch (NoSuchFileException e) {
+            // expected
+        }
+
+        Files.write(file2, "h".getBytes(StandardCharsets.UTF_8));
+        try {
+            outputDebugMessage("Move with failure expected %s => %s", file1, file2);
+            Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS);
+            fail("Unexpected success in moving " + file1 + " => " + file2);
+        } catch (FileAlreadyExistsException e) {
+            // expected
+        }
+
+        outputDebugMessage("Move with success expected %s => %s", file1, file2);
+        Files.move(file1, file2, LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
+        outputDebugMessage("Move with success expected %s => %s", file2, file1);
+        Files.move(file2, file1, LinkOption.NOFOLLOW_LINKS);
+
+        Map<String, Object> attrs = Files.readAttributes(file1, "*");
+        outputDebugMessage("%s attributes: %s", file1, attrs);
+
+        // TODO there are many issues with symbolic links on Windows
+        if (OsUtils.isUNIX()) {
+            Path link = fs.getPath(remFile2Path);
+            Path linkParent = link.getParent();
+            Path relPath = linkParent.relativize(file1);
+            outputDebugMessage("Create symlink %s => %s", link, relPath);
+            Files.createSymbolicLink(link, relPath);
+            assertTrue("Not a symbolic link: " + link, Files.isSymbolicLink(link));
+
+            Path symLink = Files.readSymbolicLink(link);
+            assertEquals("mismatched symbolic link name", relPath.toString(), symLink.toString());
+
+            outputDebugMessage("Delete symlink %s", link);
+            Files.delete(link);
+        }
+
+        attrs = Files.readAttributes(file1, "*", LinkOption.NOFOLLOW_LINKS);
+        outputDebugMessage("%s no-follow attributes: %s", file1, attrs);
+        assertEquals("Mismatched symlink data", expected, new String(Files.readAllBytes(file1), StandardCharsets.UTF_8));
+
+        try (FileChannel channel = FileChannel.open(file1)) {
+            try (FileLock lock = channel.lock()) {
+                outputDebugMessage("Lock %s: %s", file1, lock);
+
+                try (FileChannel channel2 = FileChannel.open(file1)) {
+                    try (FileLock lock2 = channel2.lock()) {
+                        fail("Unexpected success in re-locking " + file1 + ": " + lock2);
+                    } catch (OverlappingFileLockException e) {
+                        // expected
+                    }
+                }
+            }
+        }
+
+        Files.delete(file1);
+    }
+
+    private URI createDefaultFileSystemURI() {
+        return createDefaultFileSystemURI(Collections.emptyMap());
+    }
+
+    private URI createDefaultFileSystemURI(Map<String, ?> params) {
+        return createFileSystemURI(getCurrentTestName(), params);
+    }
+
+    private URI createFileSystemURI(String username, Map<String, ?> params) {
+        return createFileSystemURI(username, port, params);
+    }
+
+    private static URI createFileSystemURI(String username, int port, Map<String, ?> params) {
+        return SftpFileSystemProvider.createFileSystemURI(TEST_LOCALHOST, port, username, username, params);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e3b8acd7/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemURITest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemURITest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemURITest.java
new file mode 100644
index 0000000..a8bfaf8
--- /dev/null
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/fs/SftpFileSystemURITest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.client.subsystem.sftp.fs;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.auth.BasicCredentialsProvider;
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Parameterized.UseParametersRunnerFactory;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+@Category({ NoIoTestCase.class })
+public class SftpFileSystemURITest extends JUnitTestSupport {
+    private final String host;
+    private final int port;
+    private final String username;
+    private final String password;
+    private final Map<String, ?> params;
+
+    public SftpFileSystemURITest(String host, int port, String username, String password, Map<String, ?> params) {
+        this.host = host;
+        this.port = port;
+        this.username = username;
+        this.password = password;
+        this.params = params;
+    }
+
+    @Parameters(name = "host={0}, port={1}, user={2}, password={3}, params={4}")
+    public static List<Object[]> parameters() {
+        return new ArrayList<Object[]>() {
+            // Not serializing it
+            private static final long serialVersionUID = 1L;
+
+            {
+                add(new Object[] {SshdSocketAddress.LOCALHOST_NAME, 0, "user", "password", null});
+                add(new Object[] {"37.77.34.7", 2222, "user", "password", Collections.singletonMap("non-default-port", true)});
+                add(new Object[] {SshdSocketAddress.LOCALHOST_NAME, SshConstants.DEFAULT_PORT, "J@ck", "d@Ripper", new HashMap<String, Object>() {
+                        // not serializing it
+                        private static final long serialVersionUID = 1L;
+
+                        {
+                            put("param1", "1st");
+                            put("param2", 2);
+                            put("param3", false);
+                        }
+                    }
+                });
+                add(new Object[] {"19.65.7.3", 0, "J%ck", "d%Ripper", null});
+            }
+        };
+    }
+
+    @Test
+    public void testFullURIEncoding() {
+        URI uri = SftpFileSystemProvider.createFileSystemURI(host, port, username, password, params);
+        assertEquals("Mismatched scheme", SftpConstants.SFTP_SUBSYSTEM_NAME, uri.getScheme());
+        assertEquals("Mismatched host", host, uri.getHost());
+        assertEquals("Mismatched port", port, uri.getPort());
+
+        BasicCredentialsProvider credentials = SftpFileSystemProvider.parseCredentials(uri);
+        assertNotNull("No credentials provided", credentials);
+        assertEquals("Mismatched user", username, credentials.getUsername());
+        assertEquals("Mismatched password", password, credentials.getPassword());
+
+        Map<String, ?> uriParams = SftpFileSystemProvider.parseURIParameters(uri);
+        assertMapEquals(getCurrentTestName(), params, uriParams, (v1, v2) -> Objects.equals(v1.toString(), v2.toString()));
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName()
+            + "[host=" + host
+            + ", port=" + port
+            + ", username=" + username
+            + ", password=" + password
+            + ", params=" + params
+            + "]";
+    }
+}