http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java new file mode 100644 index 0000000..e29b732 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/SftpVersionsTest.java @@ -0,0 +1,510 @@ +/* + * 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.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclEntryFlag; +import java.nio.file.attribute.AclEntryPermission; +import java.nio.file.attribute.AclEntryType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.SftpClient.Attributes; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry; +import org.apache.sshd.client.subsystem.sftp.SftpClient.OpenMode; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.SftpHelper; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.subsystem.sftp.AbstractSftpEventListenerAdapter; +import org.apache.sshd.server.subsystem.sftp.DefaultGroupPrincipal; +import org.apache.sshd.server.subsystem.sftp.SftpEventListener; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystem; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemEnvironment; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +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; + +/** + * @author <a href="mailto:d...@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) +public class SftpVersionsTest extends AbstractSftpClientTestSupport { + private static final List<Integer> VERSIONS = + Collections.unmodifiableList( + IntStream.rangeClosed(SftpSubsystemEnvironment.LOWER_SFTP_IMPL, SftpSubsystemEnvironment.HIGHER_SFTP_IMPL) + .boxed() + .collect(Collectors.toList())); + + private final int testVersion; + + public SftpVersionsTest(int version) throws IOException { + testVersion = version; + } + + @Parameters(name = "version={0}") + public static Collection<Object[]> parameters() { + return parameterize(VERSIONS); + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + public final int getTestedVersion() { + return testVersion; + } + + @Test // See SSHD-749 + public void testSftpOpenFlags() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path lclParent = assertHierarchyTargetFolderExists(lclSftp); + Path lclFile = lclParent.resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt"); + Files.deleteIfExists(lclFile); + + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + try (OutputStream out = sftp.write(remotePath, OpenMode.Create, OpenMode.Write)) { + out.write(getCurrentTestName().getBytes(StandardCharsets.UTF_8)); + } + assertTrue("File should exist on disk: " + lclFile, Files.exists(lclFile)); + sftp.remove(remotePath); + } + } + } + + @Test + public void testSftpVersionSelector() throws Exception { + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + assertEquals("Mismatched negotiated version", getTestedVersion(), sftp.getVersion()); + } + } + } + + @Test // see SSHD-572 + public void testSftpFileTimesUpdate() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt"); + Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8)); + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclFile); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + Attributes attrs = sftp.lstat(remotePath); + long expectedSeconds = TimeUnit.SECONDS.convert(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1L), TimeUnit.MILLISECONDS); + attrs.getFlags().clear(); + attrs.modifyTime(expectedSeconds); + sftp.setStat(remotePath, attrs); + + attrs = sftp.lstat(remotePath); + long actualSeconds = attrs.getModifyTime().to(TimeUnit.SECONDS); + // The NTFS file system delays updates to the last access time for a file by up to 1 hour after the last access + if (expectedSeconds != actualSeconds) { + System.err.append("Mismatched last modified time for ").append(lclFile.toString()) + .append(" - expected=").append(String.valueOf(expectedSeconds)) + .append('[').append(new Date(TimeUnit.SECONDS.toMillis(expectedSeconds)).toString()).append(']') + .append(", actual=").append(String.valueOf(actualSeconds)) + .append('[').append(new Date(TimeUnit.SECONDS.toMillis(actualSeconds)).toString()).append(']') + .println(); + } + } + } + } + + @Test // see SSHD-573 + public void testSftpFileTypeAndPermissionsUpdate() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path subFolder = Files.createDirectories(lclSftp.resolve("sub-folder")); + String subFolderName = subFolder.getFileName().toString(); + Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt"); + String lclFileName = lclFile.getFileName().toString(); + Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8)); + + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + for (DirEntry entry : sftp.readDir(remotePath)) { + String fileName = entry.getFilename(); + if (".".equals(fileName) || "..".equals(fileName)) { + continue; + } + + Attributes attrs = validateSftpFileTypeAndPermissions(fileName, getTestedVersion(), entry.getAttributes()); + if (subFolderName.equals(fileName)) { + assertEquals("Mismatched sub-folder type", SftpConstants.SSH_FILEXFER_TYPE_DIRECTORY, attrs.getType()); + assertTrue("Sub-folder not marked as directory", attrs.isDirectory()); + } else if (lclFileName.equals(fileName)) { + assertEquals("Mismatched sub-file type", SftpConstants.SSH_FILEXFER_TYPE_REGULAR, attrs.getType()); + assertTrue("Sub-folder not marked as directory", attrs.isRegularFile()); + } + } + } + } + } + + @Test // see SSHD-574 + public void testSftpACLEncodeDecode() throws Exception { + AclEntryType[] types = AclEntryType.values(); + final List<AclEntry> aclExpected = new ArrayList<>(types.length); + for (AclEntryType t : types) { + aclExpected.add(AclEntry.newBuilder() + .setType(t) + .setFlags(EnumSet.allOf(AclEntryFlag.class)) + .setPermissions(EnumSet.allOf(AclEntryPermission.class)) + .setPrincipal(new DefaultGroupPrincipal(getCurrentTestName() + "@" + getClass().getPackage().getName())) + .build()); + } + + final AtomicInteger numInvocations = new AtomicInteger(0); + SftpSubsystemFactory factory = new SftpSubsystemFactory() { + @Override + public Command create() { + SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), + getUnsupportedAttributePolicy(), getFileSystemAccessor(), getErrorStatusDataHandler()) { + @Override + protected NavigableMap<String, Object> resolveFileAttributes(Path file, int flags, LinkOption... options) throws IOException { + NavigableMap<String, Object> attrs = super.resolveFileAttributes(file, flags, options); + if (GenericUtils.isEmpty(attrs)) { + attrs = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + + @SuppressWarnings("unchecked") + List<AclEntry> aclActual = (List<AclEntry>) attrs.put("acl", aclExpected); + if (aclActual != null) { + log.info("resolveFileAttributes(" + file + ") replaced ACL: " + aclActual); + } + return attrs; + } + + @Override + protected void setFileAccessControl(Path file, List<AclEntry> aclActual, LinkOption... options) throws IOException { + if (aclActual != null) { + assertListEquals("Mismatched ACL set for file=" + file, aclExpected, aclActual); + numInvocations.incrementAndGet(); + } + } + }; + Collection<? extends SftpEventListener> listeners = getRegisteredListeners(); + if (GenericUtils.size(listeners) > 0) { + for (SftpEventListener l : listeners) { + subsystem.addSftpEventListener(l); + } + } + + return subsystem; + } + }; + + factory.addSftpEventListener(new AbstractSftpEventListenerAdapter() { + @Override + public void modifyingAttributes(ServerSession session, Path path, Map<String, ?> attrs) { + @SuppressWarnings("unchecked") + List<AclEntry> aclActual = GenericUtils.isEmpty(attrs) ? null : (List<AclEntry>) attrs.get("acl"); + if (getTestedVersion() > SftpConstants.SFTP_V3) { + assertListEquals("Mismatched modifying ACL for file=" + path, aclExpected, aclActual); + } else { + assertNull("Unexpected modifying ACL for file=" + path, aclActual); + } + } + + @Override + public void modifiedAttributes(ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown) { + @SuppressWarnings("unchecked") + List<AclEntry> aclActual = GenericUtils.isEmpty(attrs) ? null : (List<AclEntry>) attrs.get("acl"); + if (getTestedVersion() > SftpConstants.SFTP_V3) { + assertListEquals("Mismatched modified ACL for file=" + path, aclExpected, aclActual); + } else { + assertNull("Unexpected modified ACL for file=" + path, aclActual); + } + } + }); + + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Files.createDirectories(lclSftp.resolve("sub-folder")); + Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt"); + Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8)); + + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp); + int numInvoked = 0; + + List<NamedFactory<Command>> factories = sshd.getSubsystemFactories(); + sshd.setSubsystemFactories(Collections.singletonList(factory)); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + for (DirEntry entry : sftp.readDir(remotePath)) { + String fileName = entry.getFilename(); + if (".".equals(fileName) || "..".equals(fileName)) { + continue; + } + + Attributes attrs = validateSftpFileTypeAndPermissions(fileName, getTestedVersion(), entry.getAttributes()); + List<AclEntry> aclActual = attrs.getAcl(); + if (getTestedVersion() == SftpConstants.SFTP_V3) { + assertNull("Unexpected ACL for entry=" + fileName, aclActual); + } else { + assertListEquals("Mismatched ACL for entry=" + fileName, aclExpected, aclActual); + } + + attrs.getFlags().clear(); + attrs.setAcl(aclExpected); + sftp.setStat(remotePath + "/" + fileName, attrs); + if (getTestedVersion() > SftpConstants.SFTP_V3) { + numInvoked++; + } + } + } + } finally { + sshd.setSubsystemFactories(factories); + } + + assertEquals("Mismatched invocations count", numInvoked, numInvocations.get()); + } + + @Test // see SSHD-575 + public void testSftpExtensionsEncodeDecode() throws Exception { + final Class<?> anchor = getClass(); + final Map<String, String> expExtensions = GenericUtils.<String, String>mapBuilder() + .put("class", anchor.getSimpleName()) + .put("package", anchor.getPackage().getName()) + .put("method", getCurrentTestName()) + .build(); + + final AtomicInteger numInvocations = new AtomicInteger(0); + SftpSubsystemFactory factory = new SftpSubsystemFactory() { + @Override + public Command create() { + SftpSubsystem subsystem = new SftpSubsystem(getExecutorService(), isShutdownOnExit(), + getUnsupportedAttributePolicy(), getFileSystemAccessor(), getErrorStatusDataHandler()) { + @Override + protected NavigableMap<String, Object> resolveFileAttributes(Path file, int flags, LinkOption... options) throws IOException { + NavigableMap<String, Object> attrs = super.resolveFileAttributes(file, flags, options); + if (GenericUtils.isEmpty(attrs)) { + attrs = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + + @SuppressWarnings("unchecked") + Map<String, String> actExtensions = (Map<String, String>) attrs.put("extended", expExtensions); + if (actExtensions != null) { + log.info("resolveFileAttributes(" + file + ") replaced extensions: " + actExtensions); + } + return attrs; + } + + @Override + protected void setFileExtensions(Path file, Map<String, byte[]> extensions, LinkOption... options) throws IOException { + assertExtensionsMapEquals("setFileExtensions(" + file + ")", expExtensions, extensions); + numInvocations.incrementAndGet(); + + int currentVersion = getTestedVersion(); + try { + super.setFileExtensions(file, extensions, options); + assertFalse("Expected exception not generated for version=" + currentVersion, currentVersion >= SftpConstants.SFTP_V6); + } catch (UnsupportedOperationException e) { + assertTrue("Unexpected exception for version=" + currentVersion, currentVersion >= SftpConstants.SFTP_V6); + } + } + }; + Collection<? extends SftpEventListener> listeners = getRegisteredListeners(); + if (GenericUtils.size(listeners) > 0) { + for (SftpEventListener l : listeners) { + subsystem.addSftpEventListener(l); + } + } + + return subsystem; + } + }; + + factory.addSftpEventListener(new AbstractSftpEventListenerAdapter() { + @Override + public void modifyingAttributes(ServerSession session, Path path, Map<String, ?> attrs) { + @SuppressWarnings("unchecked") + Map<String, byte[]> actExtensions = GenericUtils.isEmpty(attrs) ? null : (Map<String, byte[]>) attrs.get("extended"); + assertExtensionsMapEquals("modifying(" + path + ")", expExtensions, actExtensions); + } + + @Override + public void modifiedAttributes(ServerSession session, Path path, Map<String, ?> attrs, Throwable thrown) { + @SuppressWarnings("unchecked") + Map<String, byte[]> actExtensions = GenericUtils.isEmpty(attrs) ? null : (Map<String, byte[]>) attrs.get("extended"); + assertExtensionsMapEquals("modified(" + path + ")", expExtensions, actExtensions); + } + }); + + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Files.createDirectories(lclSftp.resolve("sub-folder")); + Path lclFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + "-" + getTestedVersion() + ".txt"); + Files.write(lclFile, getClass().getName().getBytes(StandardCharsets.UTF_8)); + + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, lclSftp); + int numInvoked = 0; + + List<NamedFactory<Command>> factories = sshd.getSubsystemFactories(); + sshd.setSubsystemFactories(Collections.singletonList(factory)); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + for (DirEntry entry : sftp.readDir(remotePath)) { + String fileName = entry.getFilename(); + if (".".equals(fileName) || "..".equals(fileName)) { + continue; + } + + Attributes attrs = validateSftpFileTypeAndPermissions(fileName, getTestedVersion(), entry.getAttributes()); + Map<String, byte[]> actExtensions = attrs.getExtensions(); + assertExtensionsMapEquals("dirEntry=" + fileName, expExtensions, actExtensions); + attrs.getFlags().clear(); + attrs.setStringExtensions(expExtensions); + sftp.setStat(remotePath + "/" + fileName, attrs); + numInvoked++; + } + } + } finally { + sshd.setSubsystemFactories(factories); + } + + assertEquals("Mismatched invocations count", numInvoked, numInvocations.get()); + } + + @Test // see SSHD-623 + public void testEndOfListIndicator() throws Exception { + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session, getTestedVersion())) { + AtomicReference<Boolean> eolIndicator = new AtomicReference<>(); + int version = sftp.getVersion(); + Path targetPath = detectTargetFolder(); + Path parentPath = targetPath.getParent(); + String remotePath = Utils.resolveRelativeRemotePath(parentPath, targetPath); + + try (CloseableHandle handle = sftp.openDir(remotePath)) { + List<DirEntry> entries = sftp.readDir(handle, eolIndicator); + for (int index = 1; entries != null; entries = sftp.readDir(handle, eolIndicator), index++) { + Boolean value = eolIndicator.get(); + if (version < SftpConstants.SFTP_V6) { + assertNull("Unexpected indicator value at iteration #" + index, value); + } else { + assertNotNull("No indicator returned at iteration #" + index, value); + if (value) { + break; + } + } + eolIndicator.set(null); // make sure starting fresh + } + + Boolean value = eolIndicator.get(); + if (version < SftpConstants.SFTP_V6) { + assertNull("Unexpected end-of-list indication received at end of entries", value); + assertNull("Unexpected no last entries indication", entries); + } else { + assertNotNull("No end-of-list indication received at end of entries", value); + assertNotNull("No last received entries", entries); + assertTrue("Bad end-of-list value", value); + } + } + } + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getTestedVersion() + "]"; + } + + public static void assertExtensionsMapEquals(String message, Map<String, String> expected, Map<String, byte[]> actual) { + assertMapEquals(message, expected, SftpHelper.toStringExtensions(actual)); + } + + private static Attributes validateSftpFileTypeAndPermissions(String fileName, int version, Attributes attrs) { + int actualPerms = attrs.getPermissions(); + if (version == SftpConstants.SFTP_V3) { + int expected = SftpHelper.permissionsToFileType(actualPerms); + assertEquals(fileName + ": Mismatched file type", expected, attrs.getType()); + } else { + int expected = SftpHelper.fileTypeToPermission(attrs.getType()); + assertTrue(fileName + ": Missing permision=0x" + Integer.toHexString(expected) + " in 0x" + Integer.toHexString(actualPerms), + (actualPerms & expected) == expected); + } + + return attrs; + } +}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensionsTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensionsTest.java new file mode 100644 index 0000000..e05105d --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/BuiltinSftpClientExtensionsTest.java @@ -0,0 +1,84 @@ +/* + * 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.extensions; + +import org.apache.sshd.client.subsystem.sftp.RawSftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.util.test.BaseTestSupport; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.mockito.Mockito; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class BuiltinSftpClientExtensionsTest extends BaseTestSupport { + public BuiltinSftpClientExtensionsTest() { + super(); + } + + @Test + public void testFromName() { + for (String name : new String[]{null, "", getCurrentTestName()}) { + assertNull("Unexpected result for name='" + name + "'", BuiltinSftpClientExtensions.fromName(name)); + } + + for (BuiltinSftpClientExtensions expected : BuiltinSftpClientExtensions.VALUES) { + String name = expected.getName(); + for (int index = 0; index < name.length(); index++) { + BuiltinSftpClientExtensions actual = BuiltinSftpClientExtensions.fromName(name); + assertSame(name, expected, actual); + name = shuffleCase(name); + } + } + } + + @Test + public void testFromType() { + for (Class<?> clazz : new Class<?>[]{null, getClass(), SftpClientExtension.class}) { + assertNull("Unexpected value for class=" + clazz, BuiltinSftpClientExtensions.fromType(clazz)); + } + + for (BuiltinSftpClientExtensions expected : BuiltinSftpClientExtensions.VALUES) { + Class<?> type = expected.getType(); + BuiltinSftpClientExtensions actual = BuiltinSftpClientExtensions.fromType(type); + assertSame(type.getSimpleName(), expected, actual); + } + } + + @Test + public void testFromInstance() { + for (Object instance : new Object[]{null, this}) { + assertNull("Unexpected value for " + instance, BuiltinSftpClientExtensions.fromInstance(instance)); + } + + SftpClient mockClient = Mockito.mock(SftpClient.class); + RawSftpClient mockRaw = Mockito.mock(RawSftpClient.class); + + for (BuiltinSftpClientExtensions expected : BuiltinSftpClientExtensions.VALUES) { + SftpClientExtension e = expected.create(mockClient, mockRaw); + BuiltinSftpClientExtensions actual = BuiltinSftpClientExtensions.fromInstance(e); + assertSame(expected.getName(), expected, actual); + assertEquals("Mismatched extension name", expected.getName(), actual.getName()); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java new file mode 100644 index 0000000..e3537ea --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractCheckFileExtensionTest.java @@ -0,0 +1,228 @@ +/* + * 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.extensions.helpers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.extensions.CheckFileHandleExtension; +import org.apache.sshd.client.subsystem.sftp.extensions.CheckFileNameExtension; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.digest.BuiltinDigests; +import org.apache.sshd.common.digest.Digest; +import org.apache.sshd.common.digest.DigestFactory; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.SftpException; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.NumberUtils; +import org.apache.sshd.common.util.buffer.BufferUtils; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +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; + +/** + * @author <a href="mailto:d...@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) +public class AbstractCheckFileExtensionTest extends AbstractSftpClientTestSupport { + private static final Collection<Integer> DATA_SIZES = + Collections.unmodifiableList( + Arrays.asList( + (int) Byte.MAX_VALUE, + SftpConstants.MIN_CHKFILE_BLOCKSIZE, + IoUtils.DEFAULT_COPY_SIZE, + Byte.SIZE * IoUtils.DEFAULT_COPY_SIZE + )); + private static final Collection<Integer> BLOCK_SIZES = + Collections.unmodifiableList( + Arrays.asList( + 0, + SftpConstants.MIN_CHKFILE_BLOCKSIZE, + 1024, + IoUtils.DEFAULT_COPY_SIZE + )); + private static final Collection<Object[]> PARAMETERS; + + static { + Collection<Object[]> list = new ArrayList<>(); + for (DigestFactory factory : BuiltinDigests.VALUES) { + if (!factory.isSupported()) { + System.out.println("Skip unsupported digest=" + factory.getAlgorithm()); + continue; + } + + String algorithm = factory.getName(); + for (Number dataSize : DATA_SIZES) { + for (Number blockSize : BLOCK_SIZES) { + list.add(new Object[]{algorithm, dataSize, blockSize}); + } + } + } + PARAMETERS = list; + } + + + private final String algorithm; + private final int dataSize; + private final int blockSize; + + public AbstractCheckFileExtensionTest(String algorithm, int dataSize, int blockSize) throws IOException { + this.algorithm = algorithm; + this.dataSize = dataSize; + this.blockSize = blockSize; + } + + @Parameters(name = "{0} - dataSize={1}, blockSize={2}") + public static Collection<Object[]> parameters() { + return PARAMETERS; + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testCheckFileExtension() throws Exception { + testCheckFileExtension(algorithm, dataSize, blockSize); + } + + private void testCheckFileExtension(String expectedAlgorithm, int inputDataSize, int hashBlockSize) throws Exception { + NamedFactory<? extends Digest> factory = BuiltinDigests.fromFactoryName(expectedAlgorithm); + Digest digest = null; + if (blockSize == 0) { + digest = factory.create(); + digest.init(); + } + + byte[] seed = (getClass().getName() + "#" + getCurrentTestName() + + "-" + expectedAlgorithm + + "-" + inputDataSize + "/" + hashBlockSize + + IoUtils.EOL) + .getBytes(StandardCharsets.UTF_8); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(inputDataSize + seed.length)) { + while (baos.size() < inputDataSize) { + baos.write(seed); + + if (digest != null) { + digest.update(seed); + } + } + + testCheckFileExtension(factory, baos.toByteArray(), hashBlockSize, (digest == null) ? null : digest.digest()); + } + } + + @SuppressWarnings("checkstyle:nestedtrydepth") + private void testCheckFileExtension(NamedFactory<? extends Digest> factory, byte[] data, int hashBlockSize, byte[] expectedHash) throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(factory.getName() + "-data-" + data.length + "-" + hashBlockSize + ".txt"); + Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS); + + List<String> algorithms = new ArrayList<>(BuiltinDigests.VALUES.size()); + // put the selected algorithm 1st and then the rest + algorithms.add(factory.getName()); + for (NamedFactory<? extends Digest> f : BuiltinDigests.VALUES) { + if (f == factory) { + continue; + } + + algorithms.add(f.getName()); + } + + Path parentPath = targetPath.getParent(); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent()); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + CheckFileNameExtension file = assertExtensionCreated(sftp, CheckFileNameExtension.class); + try { + Map.Entry<String, ?> result = file.checkFileName(srcFolder, algorithms, 0L, 0L, hashBlockSize); + fail("Unexpected success to hash folder=" + srcFolder + ": " + result.getKey()); + } catch (IOException e) { // expected - not allowed to hash a folder + assertTrue("Not an SftpException", e instanceof SftpException); + } + + CheckFileHandleExtension hndl = assertExtensionCreated(sftp, CheckFileHandleExtension.class); + try (CloseableHandle dirHandle = sftp.openDir(srcFolder)) { + try { + Map.Entry<String, ?> result = hndl.checkFileHandle(dirHandle, algorithms, 0L, 0L, hashBlockSize); + fail("Unexpected handle success on folder=" + srcFolder + ": " + result.getKey()); + } catch (IOException e) { // expected - not allowed to hash a folder + assertTrue("Not an SftpException", e instanceof SftpException); + } + } + + validateHashResult(file, file.checkFileName(srcPath, algorithms, 0L, 0L, hashBlockSize), algorithms.get(0), expectedHash); + try (CloseableHandle fileHandle = sftp.open(srcPath, SftpClient.OpenMode.Read)) { + validateHashResult(hndl, hndl.checkFileHandle(fileHandle, algorithms, 0L, 0L, hashBlockSize), algorithms.get(0), expectedHash); + } + } + } + } + + private void validateHashResult(NamedResource hasher, Map.Entry<String, ? extends Collection<byte[]>> result, String expectedAlgorithm, byte[] expectedHash) { + String name = hasher.getName(); + assertNotNull("No result for hash=" + name, result); + assertEquals("Mismatched hash algorithms for " + name, expectedAlgorithm, result.getKey()); + + if (NumberUtils.length(expectedHash) > 0) { + Collection<byte[]> values = result.getValue(); + assertEquals("Mismatched hash values count for " + name, 1, GenericUtils.size(values)); + + byte[] actualHash = values.iterator().next(); + if (!Arrays.equals(expectedHash, actualHash)) { + fail("Mismatched hashes for " + name + + ": expected=" + BufferUtils.toHex(':', expectedHash) + + ", actual=" + BufferUtils.toHex(':', expectedHash)); + } + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java new file mode 100644 index 0000000..ea2783a --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractMD5HashExtensionTest.java @@ -0,0 +1,177 @@ +/* + * 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.extensions.helpers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.extensions.MD5FileExtension; +import org.apache.sshd.client.subsystem.sftp.extensions.MD5HandleExtension; +import org.apache.sshd.common.digest.BuiltinDigests; +import org.apache.sshd.common.digest.Digest; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.SftpException; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.buffer.BufferUtils; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +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; + +/** + * @author <a href="mailto:d...@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) +public class AbstractMD5HashExtensionTest extends AbstractSftpClientTestSupport { + private static final List<Integer> DATA_SIZES = + Collections.unmodifiableList( + Arrays.asList( + (int) Byte.MAX_VALUE, + SftpConstants.MD5_QUICK_HASH_SIZE, + IoUtils.DEFAULT_COPY_SIZE, + Byte.SIZE * IoUtils.DEFAULT_COPY_SIZE + )); + + private final int size; + + public AbstractMD5HashExtensionTest(int size) throws IOException { + this.size = size; + } + + @Parameters(name = "dataSize={0}") + public static Collection<Object[]> parameters() { + return parameterize(DATA_SIZES); + } + + @BeforeClass + public static void checkMD5Supported() { + Assume.assumeTrue("MD5 not supported", BuiltinDigests.md5.isSupported()); + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testMD5HashExtension() throws Exception { + testMD5HashExtension(size); + } + + private void testMD5HashExtension(int dataSize) throws Exception { + byte[] seed = (getClass().getName() + "#" + getCurrentTestName() + "-" + dataSize + IoUtils.EOL).getBytes(StandardCharsets.UTF_8); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize + seed.length)) { + while (baos.size() < dataSize) { + baos.write(seed); + } + + testMD5HashExtension(baos.toByteArray()); + } + } + + @SuppressWarnings("checkstyle:nestedtrydepth") + private void testMD5HashExtension(byte[] data) throws Exception { + Digest digest = BuiltinDigests.md5.create(); + digest.init(); + digest.update(data); + + byte[] expectedHash = digest.digest(); + byte[] quickHash = expectedHash; + if (data.length > SftpConstants.MD5_QUICK_HASH_SIZE) { + byte[] quickData = new byte[SftpConstants.MD5_QUICK_HASH_SIZE]; + System.arraycopy(data, 0, quickData, 0, quickData.length); + digest = BuiltinDigests.md5.create(); + digest.init(); + digest.update(quickData); + quickHash = digest.digest(); + } + + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("data-" + data.length + ".txt"); + Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS); + + Path parentPath = targetPath.getParent(); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + String srcFolder = Utils.resolveRelativeRemotePath(parentPath, srcFile.getParent()); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + MD5FileExtension file = assertExtensionCreated(sftp, MD5FileExtension.class); + try { + byte[] actual = file.getHash(srcFolder, 0L, 0L, quickHash); + fail("Unexpected file success on folder=" + srcFolder + ": " + BufferUtils.toHex(':', actual)); + } catch (IOException e) { // expected - not allowed to hash a folder + assertTrue("Not an SftpException for file hash on " + srcFolder, e instanceof SftpException); + } + + MD5HandleExtension hndl = assertExtensionCreated(sftp, MD5HandleExtension.class); + try (CloseableHandle dirHandle = sftp.openDir(srcFolder)) { + try { + byte[] actual = hndl.getHash(dirHandle, 0L, 0L, quickHash); + fail("Unexpected handle success on folder=" + srcFolder + ": " + BufferUtils.toHex(':', actual)); + } catch (IOException e) { // expected - not allowed to hash a folder + assertTrue("Not an SftpException for handle hash on " + srcFolder, e instanceof SftpException); + } + } + + try (CloseableHandle fileHandle = sftp.open(srcPath, SftpClient.OpenMode.Read)) { + for (byte[] qh : new byte[][]{GenericUtils.EMPTY_BYTE_ARRAY, quickHash}) { + for (boolean useFile : new boolean[]{true, false}) { + byte[] actualHash = useFile ? file.getHash(srcPath, 0L, 0L, qh) : hndl.getHash(fileHandle, 0L, 0L, qh); + String type = useFile ? file.getClass().getSimpleName() : hndl.getClass().getSimpleName(); + if (!Arrays.equals(expectedHash, actualHash)) { + fail("Mismatched hash for quick=" + BufferUtils.toHex(':', qh) + + " using " + type + " on " + srcFile + + ": expected=" + BufferUtils.toHex(':', expectedHash) + + ", actual=" + BufferUtils.toHex(':', actualHash)); + } + } + } + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java new file mode 100644 index 0000000..01d3334 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyDataExtensionImplTest.java @@ -0,0 +1,192 @@ +/* + * 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.extensions.helpers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.extensions.CopyDataExtension; +import org.apache.sshd.common.Factory; +import org.apache.sshd.common.random.Random; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +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; + +/** + * @author <a href="mailto:d...@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) +public class CopyDataExtensionImplTest extends AbstractSftpClientTestSupport { + private static final List<Object[]> PARAMETERS = + Collections.unmodifiableList( + Arrays.asList( + new Object[]{ + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE), + Integer.valueOf(0), + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE), + Long.valueOf(0L) + }, + new Object[]{ + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE), + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE / 2), + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE / 4), + Long.valueOf(0L) + }, + new Object[]{ + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE), + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE / 2), + Integer.valueOf(IoUtils.DEFAULT_COPY_SIZE / 4), + Long.valueOf(IoUtils.DEFAULT_COPY_SIZE / 2) + }, + new Object[]{ + Integer.valueOf(Byte.MAX_VALUE), + Integer.valueOf(Byte.MAX_VALUE / 2), + Integer.valueOf(Byte.MAX_VALUE), // attempt to read more than available + Long.valueOf(0L) + } + )); + + private int size; + private int srcOffset; + private int length; + private long dstOffset; + + public CopyDataExtensionImplTest(int size, int srcOffset, int length, long dstOffset) throws IOException { + this.size = size; + this.srcOffset = srcOffset; + this.length = length; + this.dstOffset = dstOffset; + } + + @Parameters(name = "size={0}, readOffset={1}, readLength={2}, writeOffset={3}") + public static Collection<Object[]> parameters() { + return PARAMETERS; + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testCopyDataExtension() throws Exception { + testCopyDataExtension(size, srcOffset, length, dstOffset); + } + + private void testCopyDataExtension(int dataSize, int readOffset, int readLength, long writeOffset) throws Exception { + byte[] seed = (getClass().getName() + "#" + getCurrentTestName() + + "-" + dataSize + + "-" + readOffset + "/" + readLength + "/" + writeOffset + + IoUtils.EOL) + .getBytes(StandardCharsets.UTF_8); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(dataSize + seed.length)) { + while (baos.size() < dataSize) { + baos.write(seed); + } + + testCopyDataExtension(baos.toByteArray(), readOffset, readLength, writeOffset); + } + } + + private void testCopyDataExtension(byte[] data, int readOffset, int readLength, long writeOffset) throws Exception { + Path targetPath = detectTargetFolder(); + Path parentPath = targetPath.getParent(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + LinkOption[] options = IoUtils.getLinkOptions(true); + String baseName = readOffset + "-" + readLength + "-" + writeOffset; + Path srcFile = assertHierarchyTargetFolderExists(lclSftp, options).resolve(baseName + "-src.txt"); + Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + + Path dstFile = srcFile.getParent().resolve(baseName + "-dst.txt"); + if (Files.exists(dstFile, options)) { + Files.delete(dstFile); + } + String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile); + if (writeOffset > 0L) { + Factory<? extends Random> factory = client.getRandomFactory(); + Random randomizer = factory.create(); + long totalLength = writeOffset + readLength; + byte[] workBuf = new byte[(int) Math.min(totalLength, IoUtils.DEFAULT_COPY_SIZE)]; + try (OutputStream output = Files.newOutputStream(dstFile, IoUtils.EMPTY_OPEN_OPTIONS)) { + while (totalLength > 0L) { + randomizer.fill(workBuf); + output.write(workBuf); + totalLength -= workBuf.length; + } + } + } + + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + CopyDataExtension ext = assertExtensionCreated(sftp, CopyDataExtension.class); + try (CloseableHandle readHandle = sftp.open(srcPath, SftpClient.OpenMode.Read); + CloseableHandle writeHandle = sftp.open(dstPath, SftpClient.OpenMode.Write, SftpClient.OpenMode.Create)) { + ext.copyData(readHandle, readOffset, readLength, writeHandle, writeOffset); + } + } + } + + int available = data.length; + int required = readOffset + readLength; + if (required > available) { + required = available; + } + byte[] expected = new byte[required - readOffset]; + System.arraycopy(data, readOffset, expected, 0, expected.length); + + byte[] actual = new byte[expected.length]; + try (FileChannel channel = FileChannel.open(dstFile, IoUtils.EMPTY_OPEN_OPTIONS)) { + int readLen = channel.read(ByteBuffer.wrap(actual), writeOffset); + assertEquals("Mismatched read data size", expected.length, readLen); + } + assertArrayEquals("Mismatched copy data", expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java new file mode 100644 index 0000000..b21da13 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/CopyFileExtensionImplTest.java @@ -0,0 +1,96 @@ +/* + * 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.extensions.helpers; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.extensions.CopyFileExtension; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.SftpException; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class CopyFileExtensionImplTest extends AbstractSftpClientTestSupport { + public CopyFileExtensionImplTest() throws IOException { + super(); + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testCopyFileExtension() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName()); + Utils.deleteRecursive(lclSftp); + + byte[] data = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8); + Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve("src.txt"); + Files.write(srcFile, data, IoUtils.EMPTY_OPEN_OPTIONS); + + Path parentPath = targetPath.getParent(); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + Path dstFile = lclSftp.resolve("dst.txt"); + String dstPath = Utils.resolveRelativeRemotePath(parentPath, dstFile); + + LinkOption[] options = IoUtils.getLinkOptions(true); + assertFalse("Destination file unexpectedly exists", Files.exists(dstFile, options)); + + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + CopyFileExtension ext = assertExtensionCreated(sftp, CopyFileExtension.class); + ext.copyFile(srcPath, dstPath, false); + assertTrue("Source file not preserved", Files.exists(srcFile, options)); + assertTrue("Destination file not created", Files.exists(dstFile, options)); + + byte[] actual = Files.readAllBytes(dstFile); + assertArrayEquals("Mismatched copied data", data, actual); + + try { + ext.copyFile(srcPath, dstPath, false); + fail("Unexpected success to overwrite existing destination: " + dstFile); + } catch (IOException e) { + assertTrue("Not an SftpException", e instanceof SftpException); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java new file mode 100644 index 0000000..0c33113 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/SpaceAvailableExtensionImplTest.java @@ -0,0 +1,101 @@ +/* + * 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.extensions.helpers; + +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.extensions.SpaceAvailableExtension; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.extensions.SpaceAvailableExtensionInfo; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystem; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SpaceAvailableExtensionImplTest extends AbstractSftpClientTestSupport { + public SpaceAvailableExtensionImplTest() throws IOException { + super(); + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testFileStoreReport() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName(), getCurrentTestName()); + Path parentPath = targetPath.getParent(); + FileStore store = Files.getFileStore(lclSftp.getRoot()); + final String queryPath = Utils.resolveRelativeRemotePath(parentPath, lclSftp); + final SpaceAvailableExtensionInfo expected = new SpaceAvailableExtensionInfo(store); + + List<NamedFactory<Command>> factories = sshd.getSubsystemFactories(); + sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory() { + @Override + public Command create() { + return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), + getUnsupportedAttributePolicy(), getFileSystemAccessor(), getErrorStatusDataHandler()) { + @Override + protected SpaceAvailableExtensionInfo doSpaceAvailable(int id, String path) throws IOException { + if (!queryPath.equals(path)) { + throw new StreamCorruptedException("Mismatched query paths: expected=" + queryPath + ", actual=" + path); + } + + return expected; + } + }; + } + })); + + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + SpaceAvailableExtension ext = assertExtensionCreated(sftp, SpaceAvailableExtension.class); + SpaceAvailableExtensionInfo actual = ext.available(queryPath); + assertEquals("Mismatched information", expected, actual); + } + } finally { + sshd.setSubsystemFactories(factories); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java new file mode 100644 index 0000000..ac8ed34 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/helpers/OpenSSHExtensionsTest.java @@ -0,0 +1,207 @@ +/* + * 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.extensions.openssh.helpers; + +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.subsystem.sftp.AbstractSftpClientTestSupport; +import org.apache.sshd.client.subsystem.sftp.SftpClient; +import org.apache.sshd.client.subsystem.sftp.SftpClient.CloseableHandle; +import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHFsyncExtension; +import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHStatExtensionInfo; +import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHStatHandleExtension; +import org.apache.sshd.client.subsystem.sftp.extensions.openssh.OpenSSHStatPathExtension; +import org.apache.sshd.common.subsystem.sftp.SftpConstants; +import org.apache.sshd.common.subsystem.sftp.extensions.openssh.AbstractOpenSSHExtensionParser.OpenSSHExtension; +import org.apache.sshd.common.subsystem.sftp.extensions.openssh.FstatVfsExtensionParser; +import org.apache.sshd.common.subsystem.sftp.extensions.openssh.StatVfsExtensionParser; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.buffer.Buffer; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystem; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.apache.sshd.util.test.Utils; +import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class OpenSSHExtensionsTest extends AbstractSftpClientTestSupport { + public OpenSSHExtensionsTest() throws IOException { + super(); + } + + @Before + public void setUp() throws Exception { + setupServer(); + } + + @Test + public void testFsync() throws IOException { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt"); + byte[] expected = (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8); + + Path parentPath = targetPath.getParent(); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + session.addPasswordIdentity(getCurrentTestName()); + session.auth().verify(5L, TimeUnit.SECONDS); + + try (SftpClient sftp = createSftpClient(session)) { + OpenSSHFsyncExtension fsync = assertExtensionCreated(sftp, OpenSSHFsyncExtension.class); + try (CloseableHandle fileHandle = sftp.open(srcPath, SftpClient.OpenMode.Write, SftpClient.OpenMode.Create)) { + sftp.write(fileHandle, 0L, expected); + fsync.fsync(fileHandle); + + byte[] actual = Files.readAllBytes(srcFile); + assertArrayEquals("Mismatched written data", expected, actual); + } + } + } + } + + @Test + public void testStat() throws Exception { + Path targetPath = detectTargetFolder(); + Path lclSftp = Utils.resolve(targetPath, SftpConstants.SFTP_SUBSYSTEM_NAME, getClass().getSimpleName()); + Path srcFile = assertHierarchyTargetFolderExists(lclSftp).resolve(getCurrentTestName() + ".txt"); + Files.write(srcFile, (getClass().getName() + "#" + getCurrentTestName()).getBytes(StandardCharsets.UTF_8), IoUtils.EMPTY_OPEN_OPTIONS); + Path parentPath = targetPath.getParent(); + String srcPath = Utils.resolveRelativeRemotePath(parentPath, srcFile); + + final AtomicReference<String> extensionHolder = new AtomicReference<>(null); + final OpenSSHStatExtensionInfo expected = new OpenSSHStatExtensionInfo(); + expected.f_bavail = Short.MAX_VALUE; + expected.f_bfree = Integer.MAX_VALUE; + expected.f_blocks = Short.MAX_VALUE; + expected.f_bsize = IoUtils.DEFAULT_COPY_SIZE; + expected.f_favail = Long.MAX_VALUE; + expected.f_ffree = Byte.MAX_VALUE; + expected.f_files = 3777347L; + expected.f_flag = OpenSSHStatExtensionInfo.SSH_FXE_STATVFS_ST_RDONLY; + expected.f_frsize = 7365L; + expected.f_fsid = 1L; + expected.f_namemax = 256; + + sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory() { + @Override + public Command create() { + return new SftpSubsystem(getExecutorService(), isShutdownOnExit(), + getUnsupportedAttributePolicy(), getFileSystemAccessor(), getErrorStatusDataHandler()) { + @Override + protected List<OpenSSHExtension> resolveOpenSSHExtensions(ServerSession session) { + List<OpenSSHExtension> original = super.resolveOpenSSHExtensions(session); + int numOriginal = GenericUtils.size(original); + List<OpenSSHExtension> result = new ArrayList<>(numOriginal + 2); + if (numOriginal > 0) { + result.addAll(original); + } + + for (String name : new String[]{StatVfsExtensionParser.NAME, FstatVfsExtensionParser.NAME}) { + result.add(new OpenSSHExtension(name, "2")); + } + + return result; + } + + @Override + protected void executeExtendedCommand(Buffer buffer, int id, String extension) throws IOException { + if (StatVfsExtensionParser.NAME.equals(extension) + || FstatVfsExtensionParser.NAME.equals(extension)) { + String prev = extensionHolder.getAndSet(extension); + if (prev != null) { + throw new StreamCorruptedException("executeExtendedCommand(" + extension + ") previous not null: " + prev); + } + + buffer.clear(); + buffer.putByte((byte) SftpConstants.SSH_FXP_EXTENDED_REPLY); + buffer.putInt(id); + OpenSSHStatExtensionInfo.encode(buffer, expected); + send(buffer); + } else { + super.executeExtendedCommand(buffer, id, extension); + } + } + }; + } + })); + + 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 (SftpClient sftp = createSftpClient(session)) { + OpenSSHStatPathExtension pathStat = assertExtensionCreated(sftp, OpenSSHStatPathExtension.class); + OpenSSHStatExtensionInfo actual = pathStat.stat(srcPath); + String invokedExtension = extensionHolder.getAndSet(null); + assertEquals("Mismatched invoked extension", pathStat.getName(), invokedExtension); + assertOpenSSHStatExtensionInfoEquals(invokedExtension, expected, actual); + + try (CloseableHandle handle = sftp.open(srcPath)) { + OpenSSHStatHandleExtension handleStat = assertExtensionCreated(sftp, OpenSSHStatHandleExtension.class); + actual = handleStat.stat(handle); + invokedExtension = extensionHolder.getAndSet(null); + assertEquals("Mismatched invoked extension", handleStat.getName(), invokedExtension); + assertOpenSSHStatExtensionInfoEquals(invokedExtension, expected, actual); + } + } + } + } + } + + private static void assertOpenSSHStatExtensionInfoEquals(String extension, OpenSSHStatExtensionInfo expected, OpenSSHStatExtensionInfo actual) throws Exception { + Field[] fields = expected.getClass().getFields(); + for (Field f : fields) { + String name = f.getName(); + int mod = f.getModifiers(); + if (Modifier.isStatic(mod)) { + continue; + } + + Object expValue = f.get(expected); + Object actValue = f.get(actual); + assertEquals(extension + "[" + name + "]", expValue, actValue); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java new file mode 100644 index 0000000..d059d36 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpConstantsTest.java @@ -0,0 +1,75 @@ +/* + * 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.common.subsystem.sftp; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.BaseTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class SftpConstantsTest extends BaseTestSupport { + public SftpConstantsTest() { + super(); + } + + @Test + public void testRenameModesNotMarkedAsOpcodes() { + for (int cmd : new int[]{ + SftpConstants.SSH_FXP_RENAME_OVERWRITE, + SftpConstants.SSH_FXP_RENAME_ATOMIC, + SftpConstants.SSH_FXP_RENAME_NATIVE + }) { + String name = SftpConstants.getCommandMessageName(cmd); + assertFalse("Mismatched name for " + cmd + ": " + name, name.startsWith("SSH_FXP_RENAME_")); + } + } + + @Test + public void testRealPathModesNotMarkedAsOpcodes() { + for (int cmd = SftpConstants.SSH_FXP_REALPATH_NO_CHECK; cmd <= SftpConstants.SSH_FXP_REALPATH_STAT_IF; cmd++) { + String name = SftpConstants.getCommandMessageName(cmd); + assertFalse("Mismatched name for " + cmd + ": " + name, name.startsWith("SSH_FXP_REALPATH_")); + } + } + + @Test + public void testSubstatusNameResolution() { + for (int status = SftpConstants.SSH_FX_OK; status <= SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) { + String name = SftpConstants.getStatusName(status); + assertTrue("Failed to convert status=" + status + ": " + name, name.startsWith("SSH_FX_")); + } + } + + @Test + public void testSubstatusMessageResolution() { + for (int status = SftpConstants.SSH_FX_OK; status <= SftpConstants.SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK; status++) { + String message = SftpHelper.resolveStatusMessage(status); + assertTrue("Missing message for status=" + status, GenericUtils.isNotEmpty(message)); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/251db9b9/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java ---------------------------------------------------------------------- diff --git a/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java new file mode 100644 index 0000000..704aa05 --- /dev/null +++ b/sshd-sftp/src/test/java/org/apache/sshd/common/subsystem/sftp/SftpUniversalOwnerAndGroupTest.java @@ -0,0 +1,70 @@ +/* + * 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.common.subsystem.sftp; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.BaseTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class SftpUniversalOwnerAndGroupTest extends BaseTestSupport { + public SftpUniversalOwnerAndGroupTest() { + super(); + } + + @Test + public void testNameFormat() { + for (SftpUniversalOwnerAndGroup value : SftpUniversalOwnerAndGroup.VALUES) { + String name = value.getName(); + assertFalse(value.name() + ": empty name", GenericUtils.isEmpty(name)); + assertTrue(value.name() + ": bad suffix", name.charAt(name.length() - 1) == '@'); + + for (int index = 0; index < name.length() - 1; index++) { + char ch = name.charAt(index); + if ((ch < 'A') || (ch > 'Z')) { + fail("Non-uppercase character in " + name); + } + } + } + } + + @Test + public void testFromName() { + for (String name : new String[]{null, "", getCurrentTestName()}) { + assertNull("Unexpected value for '" + name + "'", SftpUniversalOwnerAndGroup.fromName(name)); + } + + for (SftpUniversalOwnerAndGroup expected : SftpUniversalOwnerAndGroup.VALUES) { + String name = expected.getName(); + for (int index = 0; index < name.length(); index++) { + assertSame(name, expected, SftpUniversalOwnerAndGroup.fromName(name)); + name = shuffleCase(name); + } + } + } +}