mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-11 13:39:09 +00:00
Introduce extensible remote path quoting mechanism
Introduce extensible remote path quoting mechanism. Fixes issues #256 and #108.
This commit is contained in:
@@ -308,6 +308,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -578,6 +581,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -314,6 +314,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -572,6 +575,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
@@ -949,7 +964,7 @@
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties />
|
||||
</FlavorProperties>
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -323,6 +323,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -581,6 +584,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
@@ -955,7 +970,7 @@
|
||||
<FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
|
||||
<SilverlightProjectProperties />
|
||||
</FlavorProperties>
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -273,6 +273,12 @@
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\CountdownEventTest.cs">
|
||||
<Link>Classes\Common\CountdownEventTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\CountdownEventTest_Dispose_NotSet.cs">
|
||||
<Link>Classes\Common\CountdownEventTest_Dispose_NotSet.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\CountdownEventTest_Dispose_Set.cs">
|
||||
<Link>Classes\Common\CountdownEventTest_Dispose_Set.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\DerDataTest.cs">
|
||||
<Link>Classes\Common\DerDataTest.cs</Link>
|
||||
</Compile>
|
||||
@@ -327,6 +333,9 @@
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\PortForwardEventArgsTest.cs">
|
||||
<Link>Classes\Common\PortForwardEventArgsTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\PosixPathTest_GetFileName.cs">
|
||||
<Link>Classes\Common\PosixPathTest_GetFileName.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\Common\ProxyExceptionTest.cs">
|
||||
<Link>Classes\Common\ProxyExceptionTest.cs</Link>
|
||||
</Compile>
|
||||
@@ -765,9 +774,18 @@
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\PrivateKeyFileTest.cs">
|
||||
<Link>Classes\PrivateKeyFileTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\RemotePathDoubleQuoteTransformationTest.cs">
|
||||
<Link>Classes\RemotePathDoubleQuoteTransformationTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\RemotePathShellQuoteTransformationTest.cs">
|
||||
<Link>Classes\RemotePathShellQuoteTransformationTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\ScpClientTest.cs">
|
||||
<Link>Classes\ScpClientTest.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\ScpClientTestBase.cs">
|
||||
<Link>Classes\ScpClientTestBase.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet.Tests\Classes\ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs">
|
||||
<Link>Classes\ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs</Link>
|
||||
</Compile>
|
||||
@@ -1584,7 +1602,7 @@
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
+61
-37
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
@@ -21,9 +20,59 @@ namespace Renci.SshNet.Tests.Classes.Channels
|
||||
private uint _remotePacketSize;
|
||||
private ChannelStub _channel;
|
||||
private List<ChannelEventArgs> _channelClosedRegister;
|
||||
private List<ChannelEventArgs> _channelEndOfDataRegister;
|
||||
private IList<ExceptionEventArgs> _channelExceptionRegister;
|
||||
private ManualResetEvent _channelClosedReceived;
|
||||
|
||||
private void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_localChannelNumber = (uint) random.Next(0, int.MaxValue);
|
||||
_localWindowSize = (uint) random.Next(0, int.MaxValue);
|
||||
_localPacketSize = (uint) random.Next(0, int.MaxValue);
|
||||
_remoteChannelNumber = (uint) random.Next(0, int.MaxValue);
|
||||
_remoteWindowSize = (uint) random.Next(0, int.MaxValue);
|
||||
_remotePacketSize = (uint) random.Next(0, int.MaxValue);
|
||||
_channelClosedRegister = new List<ChannelEventArgs>();
|
||||
_channelEndOfDataRegister = new List<ChannelEventArgs>();
|
||||
_channelExceptionRegister = new List<ExceptionEventArgs>();
|
||||
_channelClosedReceived = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
private void CreateMocks()
|
||||
{
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
}
|
||||
|
||||
private void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
|
||||
.Callback<WaitHandle>(w =>
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
// raise ChannelCloseReceived event to set waithandle for receiving
|
||||
// SSH_MSG_CHANNEL_CLOSE message from server which is waited on after
|
||||
// sending the SSH_MSG_CHANNEL_CLOSE message to the server
|
||||
//
|
||||
// we're mocking the wait on the ChannelCloseMessage, but we still want
|
||||
// to get the channel in the state that it would have after actually receiving
|
||||
// the ChannelCloseMessage
|
||||
_sessionMock.Raise(s => s.ChannelCloseReceived += null, new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
|
||||
// signal that the ChannelCloseMessage was received; we use this to verify whether we've actually
|
||||
// waited on the EventWaitHandle to be set
|
||||
_channelClosedReceived.Set();
|
||||
}).Start();
|
||||
w.WaitOne();
|
||||
});
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -43,45 +92,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
|
||||
|
||||
private void Arrange()
|
||||
{
|
||||
var random = new Random();
|
||||
_localChannelNumber = (uint)random.Next(0, int.MaxValue);
|
||||
_localWindowSize = (uint)random.Next(0, int.MaxValue);
|
||||
_localPacketSize = (uint)random.Next(0, int.MaxValue);
|
||||
_remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
|
||||
_remoteWindowSize = (uint)random.Next(0, int.MaxValue);
|
||||
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
|
||||
_channelClosedRegister = new List<ChannelEventArgs>();
|
||||
_channelExceptionRegister = new List<ExceptionEventArgs>();
|
||||
_channelClosedReceived = new ManualResetEvent(false);
|
||||
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
|
||||
var sequence = new MockSequence();
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
|
||||
.Callback<WaitHandle>(w =>
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
// signal that the ChannelCloseMessage was received; we use this to verify whether we've actually
|
||||
// waited on the EventWaitHandle to be set
|
||||
_channelClosedReceived.Set();
|
||||
// raise ChannelCloseReceived event to set waithandle for receiving
|
||||
// SSH_MSG_CHANNEL_CLOSE message from server which is waited on after
|
||||
// sending the SSH_MSG_CHANNEL_CLOSE message to the server
|
||||
//
|
||||
// we're mocking the wait on the ChannelCloseMessage, but we still want
|
||||
// to get the channel in the state that it would have after actually receiving
|
||||
// the ChannelCloseMessage
|
||||
_sessionMock.Raise(s => s.ChannelCloseReceived += null, new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
|
||||
}).Start();
|
||||
w.WaitOne();
|
||||
});
|
||||
SetupData();
|
||||
CreateMocks();
|
||||
SetupMocks();
|
||||
|
||||
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
|
||||
_channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
|
||||
_channel.EndOfData += (sender, args) => _channelEndOfDataRegister.Add(args);
|
||||
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
|
||||
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
|
||||
_channel.SetIsOpen(true);
|
||||
@@ -137,6 +154,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
|
||||
Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EndOfDataEventShouldHaveFiredOnce()
|
||||
{
|
||||
Assert.AreEqual(1, _channelEndOfDataRegister.Count);
|
||||
Assert.AreEqual(_localChannelNumber, _channelEndOfDataRegister[0].ChannelNumber);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExceptionShouldNeverHaveFired()
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
var countdownEvent = CreateCountdownEvent(initialCount);
|
||||
Assert.AreEqual(initialCount, countdownEvent.CurrentCount);
|
||||
Assert.IsFalse(countdownEvent.IsSet);
|
||||
Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0));
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
var countdownEvent = CreateCountdownEvent(0);
|
||||
Assert.AreEqual(initialCount, countdownEvent.CurrentCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
@@ -49,6 +51,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
Assert.IsFalse(countdownEvent.Signal());
|
||||
Assert.AreEqual(--initialCount, countdownEvent.CurrentCount);
|
||||
Assert.IsFalse(countdownEvent.IsSet);
|
||||
Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0));
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
@@ -59,6 +62,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
Assert.IsTrue(countdownEvent.Signal());
|
||||
Assert.AreEqual(0, countdownEvent.CurrentCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
@@ -82,10 +86,6 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
}
|
||||
}
|
||||
|
||||
public void CurrentCountShouldReturnZeroAfterAttemptToDecrementCountBelowZero()
|
||||
{
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Wait_TimeoutInfinite_ShouldBlockUntilCountdownEventIsSet()
|
||||
{
|
||||
@@ -118,6 +118,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
Assert.IsTrue(actual);
|
||||
Assert.AreEqual(expectedSignalCount, signalCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= sleep);
|
||||
Assert.IsTrue(elapsedTime <= sleep.Add(TimeSpan.FromMilliseconds(100)));
|
||||
|
||||
@@ -156,6 +157,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
Assert.IsTrue(actual);
|
||||
Assert.AreEqual(expectedSignalCount, signalCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= sleep);
|
||||
Assert.IsTrue(elapsedTime <= timeout);
|
||||
|
||||
@@ -163,7 +165,7 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Wait_ShouldReturnFalseTimeoutExpiresBeforeCountdownEventIsSet()
|
||||
public void Wait_ShouldReturnFalseWhenTimeoutExpiresBeforeCountdownEventIsSet()
|
||||
{
|
||||
var sleep = TimeSpan.FromMilliseconds(100);
|
||||
var timeout = TimeSpan.FromMilliseconds(30);
|
||||
@@ -193,6 +195,135 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
|
||||
Assert.IsFalse(actual);
|
||||
Assert.IsFalse(countdownEvent.IsSet);
|
||||
Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= timeout);
|
||||
|
||||
countdownEvent.Wait(Session.InfiniteTimeSpan);
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_ShouldAlwaysReturnSameInstance()
|
||||
{
|
||||
var countdownEvent = CreateCountdownEvent(1);
|
||||
|
||||
var waitHandleA = countdownEvent.WaitHandle;
|
||||
Assert.IsNotNull(waitHandleA);
|
||||
|
||||
var waitHandleB = countdownEvent.WaitHandle;
|
||||
Assert.AreSame(waitHandleA, waitHandleB);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_WaitOne_TimeoutInfinite_ShouldBlockUntilCountdownEventIsSet()
|
||||
{
|
||||
var sleep = TimeSpan.FromMilliseconds(100);
|
||||
var timeout = Session.InfiniteTimeSpan;
|
||||
|
||||
var countdownEvent = CreateCountdownEvent(1);
|
||||
var signalCount = 0;
|
||||
var expectedSignalCount = _random.Next(5, 20);
|
||||
|
||||
for (var i = 0; i < (expectedSignalCount - 1); i++)
|
||||
countdownEvent.AddCount();
|
||||
|
||||
var threads = new Thread[expectedSignalCount];
|
||||
for (var i = 0; i < expectedSignalCount; i++)
|
||||
{
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(sleep);
|
||||
Interlocked.Increment(ref signalCount);
|
||||
countdownEvent.Signal();
|
||||
});
|
||||
threads[i].Start();
|
||||
}
|
||||
|
||||
var start = DateTime.Now;
|
||||
var actual = countdownEvent.WaitHandle.WaitOne(timeout);
|
||||
var elapsedTime = DateTime.Now - start;
|
||||
|
||||
Assert.IsTrue(actual);
|
||||
Assert.AreEqual(expectedSignalCount, signalCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= sleep);
|
||||
Assert.IsTrue(elapsedTime <= sleep.Add(TimeSpan.FromMilliseconds(100)));
|
||||
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_WaitOne_ShouldReturnTrueWhenCountdownEventIsSetBeforeTimeoutExpires()
|
||||
{
|
||||
var sleep = TimeSpan.FromMilliseconds(100);
|
||||
var timeout = sleep.Add(TimeSpan.FromSeconds(2));
|
||||
|
||||
var countdownEvent = CreateCountdownEvent(1);
|
||||
var signalCount = 0;
|
||||
var expectedSignalCount = _random.Next(5, 20);
|
||||
|
||||
for (var i = 0; i < (expectedSignalCount - 1); i++)
|
||||
countdownEvent.AddCount();
|
||||
|
||||
var threads = new Thread[expectedSignalCount];
|
||||
for (var i = 0; i < expectedSignalCount; i++)
|
||||
{
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(sleep);
|
||||
Interlocked.Increment(ref signalCount);
|
||||
countdownEvent.Signal();
|
||||
});
|
||||
threads[i].Start();
|
||||
}
|
||||
|
||||
var start = DateTime.Now;
|
||||
var actual = countdownEvent.Wait(timeout);
|
||||
var elapsedTime = DateTime.Now - start;
|
||||
|
||||
Assert.IsTrue(actual);
|
||||
Assert.AreEqual(expectedSignalCount, signalCount);
|
||||
Assert.IsTrue(countdownEvent.IsSet);
|
||||
Assert.IsTrue(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= sleep);
|
||||
Assert.IsTrue(elapsedTime <= timeout);
|
||||
|
||||
countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_WaitOne_ShouldReturnFalseWhenTimeoutExpiresBeforeCountdownEventIsSet()
|
||||
{
|
||||
var sleep = TimeSpan.FromMilliseconds(100);
|
||||
var timeout = TimeSpan.FromMilliseconds(30);
|
||||
|
||||
var countdownEvent = CreateCountdownEvent(1);
|
||||
var signalCount = 0;
|
||||
var expectedSignalCount = _random.Next(5, 20);
|
||||
|
||||
for (var i = 0; i < (expectedSignalCount - 1); i++)
|
||||
countdownEvent.AddCount();
|
||||
|
||||
var threads = new Thread[expectedSignalCount];
|
||||
for (var i = 0; i < expectedSignalCount; i++)
|
||||
{
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
Thread.Sleep(sleep);
|
||||
countdownEvent.Signal();
|
||||
Interlocked.Increment(ref signalCount);
|
||||
});
|
||||
threads[i].Start();
|
||||
}
|
||||
|
||||
var start = DateTime.Now;
|
||||
var actual = countdownEvent.WaitHandle.WaitOne(timeout);
|
||||
var elapsedTime = DateTime.Now - start;
|
||||
|
||||
Assert.IsFalse(actual);
|
||||
Assert.IsFalse(countdownEvent.IsSet);
|
||||
Assert.IsFalse(countdownEvent.WaitHandle.WaitOne(0));
|
||||
Assert.IsTrue(elapsedTime >= timeout);
|
||||
|
||||
countdownEvent.Wait(Session.InfiniteTimeSpan);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
#if !FEATURE_THREAD_COUNTDOWNEVENT
|
||||
using CountdownEvent = Renci.SshNet.Common.CountdownEvent;
|
||||
#else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes.Common
|
||||
{
|
||||
[TestClass]
|
||||
public class CountdownEventTest_Dispose_NotSet
|
||||
{
|
||||
private int _signalsRequired;
|
||||
private CountdownEvent _countdownEvent;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
private void Arrange()
|
||||
{
|
||||
_signalsRequired = new Random().Next(1, 20);
|
||||
_countdownEvent = new CountdownEvent(_signalsRequired);
|
||||
}
|
||||
|
||||
private void Act()
|
||||
{
|
||||
_countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddCount_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
_countdownEvent.AddCount();
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CurrentCount_ShouldReturnRemainingSignalsRequiredToSetEvent()
|
||||
{
|
||||
var actual = _countdownEvent.CurrentCount;
|
||||
|
||||
Assert.AreEqual(_signalsRequired, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dispose_ShouldNotThrow()
|
||||
{
|
||||
_countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsSet_ShouldReturnFalse()
|
||||
{
|
||||
var actual = _countdownEvent.IsSet;
|
||||
|
||||
Assert.IsFalse(actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Signal_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var set = _countdownEvent.Signal();
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + set);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Wait_TimeSpan_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var set = _countdownEvent.Wait(TimeSpan.FromSeconds(5));
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + set);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var waitHandle = _countdownEvent.WaitHandle;
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + waitHandle);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
#if !FEATURE_THREAD_COUNTDOWNEVENT
|
||||
using CountdownEvent = Renci.SshNet.Common.CountdownEvent;
|
||||
#else
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes.Common
|
||||
{
|
||||
[TestClass]
|
||||
public class CountdownEventTest_Dispose_Set
|
||||
{
|
||||
private CountdownEvent _countdownEvent;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
private void Arrange()
|
||||
{
|
||||
_countdownEvent = new CountdownEvent(0);
|
||||
}
|
||||
|
||||
private void Act()
|
||||
{
|
||||
_countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void AddCount_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
_countdownEvent.AddCount();
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CurrentCount_ShouldReturnZero()
|
||||
{
|
||||
var actual = _countdownEvent.CurrentCount;
|
||||
|
||||
Assert.AreEqual(0, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Dispose_ShouldNotThrow()
|
||||
{
|
||||
_countdownEvent.Dispose();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IsSet_ShouldReturnTrue()
|
||||
{
|
||||
var actual = _countdownEvent.IsSet;
|
||||
|
||||
Assert.IsTrue(actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Signal_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var set = _countdownEvent.Signal();
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + set);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Wait_TimeSpan_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var set = _countdownEvent.Wait(TimeSpan.FromSeconds(5));
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + set);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WaitHandle_ShouldThrowObjectDisposedException()
|
||||
{
|
||||
try
|
||||
{
|
||||
var waitHandle = _countdownEvent.WaitHandle;
|
||||
Assert.Fail("Should have thrown ObjectDisposedException, but returned: " + waitHandle);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,5 +116,14 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
|
||||
Assert.AreEqual("'one\n\ntwo'", actual);
|
||||
}
|
||||
|
||||
public void SequenceOfSingleQuoteAndExclamationMark()
|
||||
{
|
||||
var value = "/var/would be 'kewl'!/not?";
|
||||
|
||||
var actual = value.ShellQuote();
|
||||
|
||||
Assert.AreEqual("'/var/would be '\"'\"'kewl'\"'\"\\!'/not?'", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes.Common
|
||||
{
|
||||
[TestClass]
|
||||
public class PosixPathTest_GetFileName
|
||||
{
|
||||
[TestMethod]
|
||||
public void Path_Null()
|
||||
{
|
||||
const string path = null;
|
||||
|
||||
try
|
||||
{
|
||||
PosixPath.GetFileName(path);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (NullReferenceException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_Empty()
|
||||
{
|
||||
var path = string.Empty;
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreSame(path, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_TrailingForwardSlash()
|
||||
{
|
||||
var path = "/abc/";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual(string.Empty, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_FileWithoutNoDirectory()
|
||||
{
|
||||
var path = "abc.log";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreSame(path, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_FileInRootDirectory()
|
||||
{
|
||||
var path = "/abc.log";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("abc.log", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_RootDirectoryOnly()
|
||||
{
|
||||
var path = "/";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual(string.Empty, actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_FileInNonRootDirectory()
|
||||
{
|
||||
var path = "/home/sshnet/xyz";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("xyz", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_BackslashIsNotConsideredDirectorySeparator()
|
||||
{
|
||||
var path = "/home\\abc.log";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("home\\abc.log", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_ColonIsNotConsideredPathSeparator()
|
||||
{
|
||||
var path = "/home:abc.log";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("home:abc.log", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_LeadingWhitespace()
|
||||
{
|
||||
var path = " / \tabc";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual(" \tabc", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_TrailingWhitespace()
|
||||
{
|
||||
var path = "/abc \t ";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("abc \t ", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_OnlyWhitespace()
|
||||
{
|
||||
var path = " ";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual(" ", actual);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Path_FileNameOnlyWhitespace()
|
||||
{
|
||||
var path = "/home/\t ";
|
||||
|
||||
var actual = PosixPath.GetFileName(path);
|
||||
|
||||
Assert.IsNotNull(actual);
|
||||
Assert.AreEqual("\t ", actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,206 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestClass]
|
||||
public partial class ScpClientTest : TestBase
|
||||
{
|
||||
private Random _random;
|
||||
|
||||
[TestInitialize]
|
||||
public void SetUp()
|
||||
{
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_ConnectionInfo_Null()
|
||||
{
|
||||
const ConnectionInfo connectionInfo = null;
|
||||
|
||||
try
|
||||
{
|
||||
new ScpClient(connectionInfo);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
Assert.IsNull(ex.InnerException);
|
||||
Assert.AreEqual("connectionInfo", ex.ParamName);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_ConnectionInfo_NotNull()
|
||||
{
|
||||
var connectionInfo = new ConnectionInfo("HOST", "USER", new PasswordAuthenticationMethod("USER", "PWD"));
|
||||
|
||||
var client = new ScpClient(connectionInfo);
|
||||
Assert.AreEqual(16 * 1024U, client.BufferSize);
|
||||
Assert.AreSame(connectionInfo, client.ConnectionInfo);
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.KeepAliveInterval);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.OperationTimeout);
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
Assert.IsNull(client.Session);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_HostAndPortAndUsernameAndPassword()
|
||||
{
|
||||
var host = _random.Next().ToString();
|
||||
var port = _random.Next(1, 100);
|
||||
var userName = _random.Next().ToString();
|
||||
var password = _random.Next().ToString();
|
||||
|
||||
var client = new ScpClient(host, port, userName, password);
|
||||
Assert.AreEqual(16 * 1024U, client.BufferSize);
|
||||
Assert.IsNotNull(client.ConnectionInfo);
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.KeepAliveInterval);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.OperationTimeout);
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
Assert.IsNull(client.Session);
|
||||
|
||||
var passwordConnectionInfo = client.ConnectionInfo as PasswordConnectionInfo;
|
||||
Assert.IsNotNull(passwordConnectionInfo);
|
||||
Assert.AreEqual(host, passwordConnectionInfo.Host);
|
||||
Assert.AreEqual(port, passwordConnectionInfo.Port);
|
||||
Assert.AreSame(userName, passwordConnectionInfo.Username);
|
||||
Assert.IsNotNull(passwordConnectionInfo.AuthenticationMethods);
|
||||
Assert.AreEqual(1, passwordConnectionInfo.AuthenticationMethods.Count);
|
||||
|
||||
var passwordAuthentication = passwordConnectionInfo.AuthenticationMethods[0] as PasswordAuthenticationMethod;
|
||||
Assert.IsNotNull(passwordAuthentication);
|
||||
Assert.AreEqual(userName, passwordAuthentication.Username);
|
||||
Assert.IsTrue(Encoding.UTF8.GetBytes(password).IsEqualTo(passwordAuthentication.Password));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_HostAndUsernameAndPassword()
|
||||
{
|
||||
var host = _random.Next().ToString();
|
||||
var userName = _random.Next().ToString();
|
||||
var password = _random.Next().ToString();
|
||||
|
||||
var client = new ScpClient(host, userName, password);
|
||||
Assert.AreEqual(16 * 1024U, client.BufferSize);
|
||||
Assert.IsNotNull(client.ConnectionInfo);
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.KeepAliveInterval);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.OperationTimeout);
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
Assert.IsNull(client.Session);
|
||||
|
||||
var passwordConnectionInfo = client.ConnectionInfo as PasswordConnectionInfo;
|
||||
Assert.IsNotNull(passwordConnectionInfo);
|
||||
Assert.AreEqual(host, passwordConnectionInfo.Host);
|
||||
Assert.AreEqual(22, passwordConnectionInfo.Port);
|
||||
Assert.AreSame(userName, passwordConnectionInfo.Username);
|
||||
Assert.IsNotNull(passwordConnectionInfo.AuthenticationMethods);
|
||||
Assert.AreEqual(1, passwordConnectionInfo.AuthenticationMethods.Count);
|
||||
|
||||
var passwordAuthentication = passwordConnectionInfo.AuthenticationMethods[0] as PasswordAuthenticationMethod;
|
||||
Assert.IsNotNull(passwordAuthentication);
|
||||
Assert.AreEqual(userName, passwordAuthentication.Username);
|
||||
Assert.IsTrue(Encoding.UTF8.GetBytes(password).IsEqualTo(passwordAuthentication.Password));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_HostAndPortAndUsernameAndPrivateKeys()
|
||||
{
|
||||
var host = _random.Next().ToString();
|
||||
var port = _random.Next(1, 100);
|
||||
var userName = _random.Next().ToString();
|
||||
var privateKeys = new[] {GetRsaKey(), GetDsaKey()};
|
||||
|
||||
var client = new ScpClient(host, port, userName, privateKeys);
|
||||
Assert.AreEqual(16 * 1024U, client.BufferSize);
|
||||
Assert.IsNotNull(client.ConnectionInfo);
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.KeepAliveInterval);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.OperationTimeout);
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
Assert.IsNull(client.Session);
|
||||
|
||||
var privateKeyConnectionInfo = client.ConnectionInfo as PrivateKeyConnectionInfo;
|
||||
Assert.IsNotNull(privateKeyConnectionInfo);
|
||||
Assert.AreEqual(host, privateKeyConnectionInfo.Host);
|
||||
Assert.AreEqual(port, privateKeyConnectionInfo.Port);
|
||||
Assert.AreSame(userName, privateKeyConnectionInfo.Username);
|
||||
Assert.IsNotNull(privateKeyConnectionInfo.AuthenticationMethods);
|
||||
Assert.AreEqual(1, privateKeyConnectionInfo.AuthenticationMethods.Count);
|
||||
|
||||
var privateKeyAuthentication = privateKeyConnectionInfo.AuthenticationMethods[0] as PrivateKeyAuthenticationMethod;
|
||||
Assert.IsNotNull(privateKeyAuthentication);
|
||||
Assert.AreEqual(userName, privateKeyAuthentication.Username);
|
||||
Assert.IsNotNull(privateKeyAuthentication.KeyFiles);
|
||||
Assert.AreEqual(privateKeys.Length, privateKeyAuthentication.KeyFiles.Count);
|
||||
Assert.IsTrue(privateKeyAuthentication.KeyFiles.Contains(privateKeys[0]));
|
||||
Assert.IsTrue(privateKeyAuthentication.KeyFiles.Contains(privateKeys[1]));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Ctor_HostAndUsernameAndPrivateKeys()
|
||||
{
|
||||
var host = _random.Next().ToString();
|
||||
var userName = _random.Next().ToString();
|
||||
var privateKeys = new[] { GetRsaKey(), GetDsaKey() };
|
||||
|
||||
var client = new ScpClient(host, userName, privateKeys);
|
||||
Assert.AreEqual(16 * 1024U, client.BufferSize);
|
||||
Assert.IsNotNull(client.ConnectionInfo);
|
||||
Assert.IsFalse(client.IsConnected);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.KeepAliveInterval);
|
||||
Assert.AreEqual(new TimeSpan(0, 0, 0, 0, -1), client.OperationTimeout);
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
Assert.IsNull(client.Session);
|
||||
|
||||
var privateKeyConnectionInfo = client.ConnectionInfo as PrivateKeyConnectionInfo;
|
||||
Assert.IsNotNull(privateKeyConnectionInfo);
|
||||
Assert.AreEqual(host, privateKeyConnectionInfo.Host);
|
||||
Assert.AreEqual(22, privateKeyConnectionInfo.Port);
|
||||
Assert.AreSame(userName, privateKeyConnectionInfo.Username);
|
||||
Assert.IsNotNull(privateKeyConnectionInfo.AuthenticationMethods);
|
||||
Assert.AreEqual(1, privateKeyConnectionInfo.AuthenticationMethods.Count);
|
||||
|
||||
var privateKeyAuthentication = privateKeyConnectionInfo.AuthenticationMethods[0] as PrivateKeyAuthenticationMethod;
|
||||
Assert.IsNotNull(privateKeyAuthentication);
|
||||
Assert.AreEqual(userName, privateKeyAuthentication.Username);
|
||||
Assert.IsNotNull(privateKeyAuthentication.KeyFiles);
|
||||
Assert.AreEqual(privateKeys.Length, privateKeyAuthentication.KeyFiles.Count);
|
||||
Assert.IsTrue(privateKeyAuthentication.KeyFiles.Contains(privateKeys[0]));
|
||||
Assert.IsTrue(privateKeyAuthentication.KeyFiles.Contains(privateKeys[1]));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemotePathTransformation_Value_NotNull()
|
||||
{
|
||||
var client = new ScpClient("HOST", 22, "USER", "PWD");
|
||||
|
||||
Assert.AreSame(RemotePathTransformation.DoubleQuote, client.RemotePathTransformation);
|
||||
client.RemotePathTransformation = RemotePathTransformation.ShellQuote;
|
||||
Assert.AreSame(RemotePathTransformation.ShellQuote, client.RemotePathTransformation);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RemotePathTransformation_Value_Null()
|
||||
{
|
||||
var client = new ScpClient("HOST", 22, "USER", "PWD")
|
||||
{
|
||||
RemotePathTransformation = RemotePathTransformation.ShellQuote
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
client.RemotePathTransformation = null;
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
Assert.IsNull(ex.InnerException);
|
||||
Assert.AreEqual("value", ex.ParamName);
|
||||
}
|
||||
|
||||
Assert.AreSame(RemotePathTransformation.ShellQuote, client.RemotePathTransformation);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[TestCategory("Scp")]
|
||||
[TestCategory("integration")]
|
||||
@@ -172,10 +372,12 @@ namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
scp.Connect();
|
||||
|
||||
var uploadDirectory = Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
var uploadDirectory =
|
||||
Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var subfolder = Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i));
|
||||
var subfolder =
|
||||
Directory.CreateDirectory(string.Format(@"{0}\folder_{1}", uploadDirectory.FullName, i));
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
this.CreateTestFile(string.Format(@"{0}\file_{1}", subfolder.FullName, j), 1);
|
||||
@@ -185,7 +387,8 @@ namespace Renci.SshNet.Tests.Classes
|
||||
|
||||
scp.Upload(uploadDirectory, "uploaded_dir");
|
||||
|
||||
var downloadDirectory = Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
var downloadDirectory =
|
||||
Directory.CreateDirectory(string.Format("{0}\\{1}", Path.GetTempPath(), Path.GetRandomFileName()));
|
||||
|
||||
scp.Download("uploaded_dir", downloadDirectory);
|
||||
|
||||
@@ -193,11 +396,12 @@ namespace Renci.SshNet.Tests.Classes
|
||||
var downloadFiles = downloadDirectory.GetFiles("*.*", System.IO.SearchOption.AllDirectories);
|
||||
|
||||
var result = from f1 in uploadedFiles
|
||||
from f2 in downloadFiles
|
||||
where
|
||||
f1.FullName.Substring(uploadDirectory.FullName.Length) == f2.FullName.Substring(downloadDirectory.FullName.Length)
|
||||
&& CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName)
|
||||
select f1;
|
||||
from f2 in downloadFiles
|
||||
where
|
||||
f1.FullName.Substring(uploadDirectory.FullName.Length) ==
|
||||
f2.FullName.Substring(downloadDirectory.FullName.Length)
|
||||
&& CalculateMD5(f1.FullName) == CalculateMD5(f2.FullName)
|
||||
select f1;
|
||||
|
||||
var counter = result.Count();
|
||||
|
||||
@@ -331,76 +535,6 @@ namespace Renci.SshNet.Tests.Classes
|
||||
Assert.Inconclusive("A method that does not return a value cannot be verified.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///A test for ScpClient Constructor
|
||||
///</summary>
|
||||
[TestMethod]
|
||||
[Ignore] // placeholder for actual test
|
||||
public void ScpClientConstructorTest()
|
||||
{
|
||||
string host = string.Empty; // TODO: Initialize to an appropriate value
|
||||
string username = string.Empty; // TODO: Initialize to an appropriate value
|
||||
PrivateKeyFile[] keyFiles = null; // TODO: Initialize to an appropriate value
|
||||
ScpClient target = new ScpClient(host, username, keyFiles);
|
||||
Assert.Inconclusive("TODO: Implement code to verify target");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///A test for ScpClient Constructor
|
||||
///</summary>
|
||||
[TestMethod]
|
||||
[Ignore] // placeholder for actual test
|
||||
public void ScpClientConstructorTest1()
|
||||
{
|
||||
string host = string.Empty; // TODO: Initialize to an appropriate value
|
||||
int port = 0; // TODO: Initialize to an appropriate value
|
||||
string username = string.Empty; // TODO: Initialize to an appropriate value
|
||||
PrivateKeyFile[] keyFiles = null; // TODO: Initialize to an appropriate value
|
||||
ScpClient target = new ScpClient(host, port, username, keyFiles);
|
||||
Assert.Inconclusive("TODO: Implement code to verify target");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///A test for ScpClient Constructor
|
||||
///</summary>
|
||||
[TestMethod]
|
||||
[Ignore] // placeholder for actual test
|
||||
public void ScpClientConstructorTest2()
|
||||
{
|
||||
string host = string.Empty; // TODO: Initialize to an appropriate value
|
||||
string username = string.Empty; // TODO: Initialize to an appropriate value
|
||||
string password = string.Empty; // TODO: Initialize to an appropriate value
|
||||
ScpClient target = new ScpClient(host, username, password);
|
||||
Assert.Inconclusive("TODO: Implement code to verify target");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///A test for ScpClient Constructor
|
||||
///</summary>
|
||||
[TestMethod]
|
||||
[Ignore] // placeholder for actual test
|
||||
public void ScpClientConstructorTest3()
|
||||
{
|
||||
string host = string.Empty; // TODO: Initialize to an appropriate value
|
||||
int port = 0; // TODO: Initialize to an appropriate value
|
||||
string username = string.Empty; // TODO: Initialize to an appropriate value
|
||||
string password = string.Empty; // TODO: Initialize to an appropriate value
|
||||
ScpClient target = new ScpClient(host, port, username, password);
|
||||
Assert.Inconclusive("TODO: Implement code to verify target");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///A test for ScpClient Constructor
|
||||
///</summary>
|
||||
[TestMethod]
|
||||
[Ignore] // placeholder for actual test
|
||||
public void ScpClientConstructorTest4()
|
||||
{
|
||||
ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
|
||||
ScpClient target = new ScpClient(connectionInfo);
|
||||
Assert.Inconclusive("TODO: Implement code to verify target");
|
||||
}
|
||||
|
||||
protected static string CalculateMD5(string fileName)
|
||||
{
|
||||
using (var file = new FileStream(fileName, FileMode.Open))
|
||||
@@ -427,5 +561,21 @@ namespace Renci.SshNet.Tests.Classes
|
||||
client.Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKeyFile GetRsaKey()
|
||||
{
|
||||
using (var stream = GetData("Key.RSA.txt"))
|
||||
{
|
||||
return new PrivateKeyFile(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKeyFile GetDsaKey()
|
||||
{
|
||||
using (var stream = GetData("Key.SSH2.DSA.txt"))
|
||||
{
|
||||
return new PrivateKeyFile(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
public abstract class ScpClientTestBase
|
||||
{
|
||||
internal Mock<IServiceFactory> _serviceFactoryMock;
|
||||
internal Mock<IRemotePathTransformation> _remotePathTransformationMock;
|
||||
internal Mock<ISession> _sessionMock;
|
||||
internal Mock<IChannelSession> _channelSessionMock;
|
||||
internal Mock<PipeStream> _pipeStreamMock;
|
||||
|
||||
protected abstract void SetupData();
|
||||
|
||||
protected void CreateMocks()
|
||||
{
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_remotePathTransformationMock = new Mock<IRemotePathTransformation>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
}
|
||||
|
||||
protected abstract void SetupMocks();
|
||||
|
||||
protected virtual void Arrange()
|
||||
{
|
||||
SetupData();
|
||||
CreateMocks();
|
||||
SetupMocks();
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
protected abstract void Act();
|
||||
}
|
||||
}
|
||||
+26
-26
@@ -4,66 +4,66 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private DirectoryInfo _directoryInfo;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_directoryInfo = new DirectoryInfo("destination");
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -prf {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -prf {0}", _transformedPath)))
|
||||
.Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -prf {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -prf {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
+25
-26
@@ -4,66 +4,65 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private FileInfo _fileInfo;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_fileInfo = new FileInfo("destination");
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -pf {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -pf {0}", _transformedPath))).Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -87,7 +86,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -pf {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -pf {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
+26
-26
@@ -4,33 +4,21 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private Stream _destination;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
@@ -40,39 +28,51 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_destination = new MemoryStream();
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath)))
|
||||
.Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -96,7 +96,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -f {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
+26
-26
@@ -4,66 +4,66 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private DirectoryInfo _directoryInfo;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_directoryInfo = new DirectoryInfo("source");
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -rt {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -rt {0}", _transformedPath)))
|
||||
.Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecREquestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -rt {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -rt {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
+27
-27
@@ -4,34 +4,22 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private FileInfo _fileInfo;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private string _fileName;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
@@ -42,40 +30,52 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
_fileName = CreateTemporaryFile(new byte[] {1});
|
||||
|
||||
_fileName = CreateTemporaryFile(new byte[] { 1 });
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_fileInfo = new FileInfo(_fileName);
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = _path.ShellQuote();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)))
|
||||
.Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -99,7 +99,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -6,36 +6,24 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Upload_FileInfoAndPath_Success
|
||||
public class ScpClientTest_Upload_FileInfoAndPath_Success : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private FileInfo _fileInfo;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private int _bufferSize;
|
||||
private byte[] _fileContent;
|
||||
private string _fileName;
|
||||
private int _fileSize;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
@@ -46,7 +34,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupData()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
@@ -57,30 +45,30 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_fileInfo = new FileInfo(_fileName);
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
private void CreateMocks()
|
||||
{
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
}
|
||||
|
||||
private void SetupMocks()
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath))).Returns(true);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)))
|
||||
.Returns(true);
|
||||
_pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.SendData(It.IsAny<byte[]>()));
|
||||
_pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0);
|
||||
@@ -102,11 +90,9 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void Arrange()
|
||||
{
|
||||
SetupData();
|
||||
CreateMocks();
|
||||
SetupMocks();
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object)
|
||||
{
|
||||
@@ -116,7 +102,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
_scpClient.Upload(_fileInfo, _path);
|
||||
}
|
||||
@@ -124,7 +110,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
+26
-26
@@ -4,33 +4,21 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using Renci.SshNet.Channels;
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
[TestClass]
|
||||
public class ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse
|
||||
public class ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse : ScpClientTestBase
|
||||
{
|
||||
private Mock<IServiceFactory> _serviceFactoryMock;
|
||||
private Mock<ISession> _sessionMock;
|
||||
private Mock<IChannelSession> _channelSessionMock;
|
||||
private Mock<PipeStream> _pipeStreamMock;
|
||||
private ConnectionInfo _connectionInfo;
|
||||
private ScpClient _scpClient;
|
||||
private Stream _source;
|
||||
private string _path;
|
||||
private string _quotedPath;
|
||||
private string _transformedPath;
|
||||
private IList<ScpUploadEventArgs> _uploadingRegister;
|
||||
private SshException _actualException;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
Arrange();
|
||||
Act();
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
@@ -40,39 +28,51 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
|
||||
protected void Arrange()
|
||||
protected override void SetupData()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_connectionInfo = new ConnectionInfo("host", 22, "user", new PasswordAuthenticationMethod("user", "pwd"));
|
||||
_source = new MemoryStream();
|
||||
_path = "/home/sshnet/" + random.Next().ToString(CultureInfo.InvariantCulture);
|
||||
_quotedPath = _path.ShellQuote();
|
||||
_transformedPath = random.Next().ToString();
|
||||
_uploadingRegister = new List<ScpUploadEventArgs>();
|
||||
}
|
||||
|
||||
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
|
||||
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
|
||||
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
|
||||
_pipeStreamMock = new Mock<PipeStream>(MockBehavior.Strict);
|
||||
|
||||
protected override void SetupMocks()
|
||||
{
|
||||
var sequence = new MockSequence();
|
||||
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
.Setup(p => p.CreateRemotePathDoubleQuoteTransformation())
|
||||
.Returns(_remotePathTransformationMock.Object);
|
||||
_serviceFactoryMock.InSequence(sequence)
|
||||
.Setup(p => p.CreateSession(_connectionInfo))
|
||||
.Returns(_sessionMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.Connect());
|
||||
_serviceFactoryMock.InSequence(sequence).Setup(p => p.CreatePipeStream()).Returns(_pipeStreamMock.Object);
|
||||
_sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Open());
|
||||
_remotePathTransformationMock.InSequence(sequence)
|
||||
.Setup(p => p.Transform(_path))
|
||||
.Returns(_transformedPath);
|
||||
_channelSessionMock.InSequence(sequence)
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath))).Returns(false);
|
||||
.Setup(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)))
|
||||
.Returns(false);
|
||||
_channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
|
||||
_pipeStreamMock.As<IDisposable>().InSequence(sequence).Setup(p => p.Dispose());
|
||||
}
|
||||
|
||||
protected override void Arrange()
|
||||
{
|
||||
base.Arrange();
|
||||
|
||||
_scpClient = new ScpClient(_connectionInfo, false, _serviceFactoryMock.Object);
|
||||
_scpClient.Uploading += (sender, args) => _uploadingRegister.Add(args);
|
||||
_scpClient.Connect();
|
||||
}
|
||||
|
||||
protected virtual void Act()
|
||||
protected override void Act()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -96,7 +96,7 @@ namespace Renci.SshNet.Tests.Classes
|
||||
[TestMethod]
|
||||
public void SendExecRequestOnChannelSessionShouldBeInvokedOnce()
|
||||
{
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _quotedPath)), Times.Once);
|
||||
_channelSessionMock.Verify(p => p.SendExecRequest(string.Format("scp -t {0}", _transformedPath)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
-1
@@ -19,7 +19,6 @@ namespace Renci.SshNet.Tests.Classes.Sftp
|
||||
private uint _readBufferSize;
|
||||
private uint _writeBufferSize;
|
||||
private byte[] _writeBytes;
|
||||
private byte[] _requestWriteBytes;
|
||||
|
||||
protected override void SetupData()
|
||||
{
|
||||
|
||||
-1
@@ -19,7 +19,6 @@ namespace Renci.SshNet.Tests.Classes.Sftp
|
||||
private byte[] _handle;
|
||||
private SftpFileStream _target;
|
||||
private int _offset;
|
||||
private EndOfStreamException _actualException;
|
||||
private long _actual;
|
||||
|
||||
protected override void SetupData()
|
||||
|
||||
@@ -144,6 +144,8 @@
|
||||
<Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SkipFailedAuthenticationMethod.cs" />
|
||||
<Compile Include="Classes\ClientAuthenticationTest_Success_SingleList_SameAllowedAuthenticationAfterPartialSuccess.cs" />
|
||||
<Compile Include="Classes\Common\CountdownEventTest.cs" />
|
||||
<Compile Include="Classes\Common\CountdownEventTest_Dispose_NotSet.cs" />
|
||||
<Compile Include="Classes\Common\CountdownEventTest_Dispose_Set.cs" />
|
||||
<Compile Include="Classes\Common\ExtensionsTest_Concat.cs" />
|
||||
<Compile Include="Classes\Common\ExtensionsTest_IsEqualTo_ByteArray.cs" />
|
||||
<Compile Include="Classes\Common\ExtensionsTest_Reverse.cs" />
|
||||
@@ -152,6 +154,7 @@
|
||||
<Compile Include="Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs" />
|
||||
<Compile Include="Classes\Common\ExtensionsTest_TrimLeadingZeros.cs" />
|
||||
<Compile Include="Classes\Common\PackTest.cs" />
|
||||
<Compile Include="Classes\Common\PosixPathTest_GetFileName.cs" />
|
||||
<Compile Include="Classes\ConnectionInfoTest_Authenticate_Failure.cs" />
|
||||
<Compile Include="Classes\ConnectionInfoTest_Authenticate_Success.cs" />
|
||||
<Compile Include="Classes\ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs" />
|
||||
@@ -211,6 +214,9 @@
|
||||
<Compile Include="Classes\NetConfClientTest_Dispose_Disposed.cs" />
|
||||
<Compile Include="Classes\NetConfClientTest_Finalize_Connected.cs" />
|
||||
<Compile Include="Classes\PipeStreamTest_Dispose.cs" />
|
||||
<Compile Include="Classes\RemotePathDoubleQuoteTransformationTest.cs" />
|
||||
<Compile Include="Classes\RemotePathShellQuoteTransformationTest.cs" />
|
||||
<Compile Include="Classes\ScpClientTestBase.cs" />
|
||||
<Compile Include="Classes\ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs" />
|
||||
<Compile Include="Classes\ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs" />
|
||||
<Compile Include="Classes\ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs" />
|
||||
|
||||
@@ -369,6 +369,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -642,6 +645,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -305,6 +305,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -563,6 +566,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
|
||||
@@ -352,6 +352,9 @@
|
||||
<Compile Include="..\Renci.SshNet\IForwardedPort.cs">
|
||||
<Link>IForwardedPort.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IRemotePathTransformation.cs">
|
||||
<Link>IRemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\IServiceFactory.cs">
|
||||
<Link>IServiceFactory.cs</Link>
|
||||
</Compile>
|
||||
@@ -610,6 +613,18 @@
|
||||
<Compile Include="..\Renci.SshNet\ProxyTypes.cs">
|
||||
<Link>ProxyTypes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathDoubleQuoteTransformation.cs">
|
||||
<Link>RemotePathDoubleQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathNoneTransformation.cs">
|
||||
<Link>RemotePathNoneTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathShellQuoteTransformation.cs">
|
||||
<Link>RemotePathShellQuoteTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\RemotePathTransformation.cs">
|
||||
<Link>RemotePathTransformation.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Renci.SshNet\ScpClient.cs">
|
||||
<Link>ScpClient.cs</Link>
|
||||
</Compile>
|
||||
@@ -982,7 +997,7 @@
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\$(TargetFrameworkIdentifier)\$(TargetFrameworkVersion)\Microsoft.$(TargetFrameworkIdentifier).CSharp.targets" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
|
||||
<UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -57,6 +57,24 @@ namespace Renci.SshNet.Common
|
||||
get { return _count == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="WaitHandle"/> that is used to wait for the event to be set.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A <see cref="WaitHandle"/> that is used to wait for the event to be set.
|
||||
/// </value>
|
||||
/// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
|
||||
public WaitHandle WaitHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
|
||||
return _event;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers a signal with the <see cref="CountdownEvent"/>, decrementing the value of <see cref="CurrentCount"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Renci.SshNet.Common
|
||||
using System;
|
||||
|
||||
namespace Renci.SshNet.Common
|
||||
{
|
||||
internal class PosixPath
|
||||
{
|
||||
@@ -9,15 +11,23 @@
|
||||
/// <returns>
|
||||
/// The file name part of <paramref name="path"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="NullReferenceException"><paramref name="path"/> is <c>null</c>.</exception>
|
||||
/// <remarks>
|
||||
/// If <paramref name="path"/> contains no forward slash or has a trailing
|
||||
/// forward slash, then <paramref name="path"/> is returned.
|
||||
/// <para>
|
||||
/// If <paramref name="path"/> contains no forward slash, then <paramref name="path"/>
|
||||
/// is returned.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If path has a trailing slash, but <see cref="GetFileName(string)"/> return a zero-length string.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static string GetFileName(string path)
|
||||
{
|
||||
var pathEnd = path.LastIndexOf('/');
|
||||
if (pathEnd == -1 || pathEnd == path.Length - 1)
|
||||
if (pathEnd == -1)
|
||||
return path;
|
||||
if (pathEnd == path.Length - 1)
|
||||
return string.Empty;
|
||||
return path.Substring(pathEnd + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a transformation that can be applied to a remote path.
|
||||
/// </summary>
|
||||
public interface IRemotePathTransformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms the specified remote path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to transform.</param>
|
||||
/// <returns>
|
||||
/// The transformed path.
|
||||
/// </returns>
|
||||
string Transform(string path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -62,5 +62,16 @@ namespace Renci.SshNet
|
||||
ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize);
|
||||
|
||||
ISftpResponseFactory CreateSftpResponseFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes
|
||||
/// any embedded double quote with a backslash.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes any
|
||||
/// embedded double quote with a backslash.
|
||||
/// with a shell.
|
||||
/// </returns>
|
||||
IRemotePathTransformation CreateRemotePathDoubleQuoteTransformation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,17 @@ namespace Renci.SshNet
|
||||
get { return _requestMessage.MethodName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password as a sequence of bytes.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The password as a sequence of bytes.
|
||||
/// </value>
|
||||
internal byte[] Password
|
||||
{
|
||||
get { return _password; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when user's password has expired and needs to be changed.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
|
||||
/// </summary>
|
||||
internal class RemotePathDoubleQuoteTransformation : IRemotePathTransformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to transform.</param>
|
||||
/// <returns>
|
||||
/// The transformed path.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
|
||||
/// <example>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Original</term>
|
||||
/// <term>Transformed</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>/var/log/auth.log</term>
|
||||
/// <term>"/var/log/auth.log"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/mp3/Guns N' Roses</term>
|
||||
/// <term>"/var/mp3/Guns N' Roses"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/garbage!/temp</term>
|
||||
/// <term>"/var/garbage!/temp"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/would be 'kewl'!/not?</term>
|
||||
/// <term>"/var/would be 'kewl'!, not?"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term></term>
|
||||
/// <term>""</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Hello "World"</term>
|
||||
/// <term>"Hello \"World"</term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
public string Transform(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
var transformed = new StringBuilder(path.Length);
|
||||
|
||||
transformed.Append('"');
|
||||
foreach (var c in path)
|
||||
{
|
||||
if (c == '"')
|
||||
transformed.Append('\\');
|
||||
transformed.Append(c);
|
||||
}
|
||||
transformed.Append('"');
|
||||
|
||||
return transformed.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs no transformation.
|
||||
/// </summary>
|
||||
internal class RemotePathNoneTransformation : IRemotePathTransformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the specified path without applying a transformation.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to transform.</param>
|
||||
/// <returns>
|
||||
/// The specified path as is.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
|
||||
/// <remarks>
|
||||
/// This transformation is recommended for servers that do not require any quoting to preserve the
|
||||
/// literal value of metacharacters, or when paths are guaranteed to never contain any such characters.
|
||||
/// </remarks>
|
||||
public string Transform(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Quotes a path in a way to be suitable to be used with a shell-based server.
|
||||
/// </summary>
|
||||
internal class RemotePathShellQuoteTransformation : IRemotePathTransformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Quotes a path in a way to be suitable to be used with a shell-based server.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to transform.</param>
|
||||
/// <returns>
|
||||
/// A quoted path.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If <paramref name="path"/> contains a single-quote, that character is embedded
|
||||
/// in quotation marks (eg. "'"). Sequences of single-quotes are grouped in a single
|
||||
/// pair of quotation marks.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// An exclamation mark in <paramref name="path"/> is escaped with a backslash. This is
|
||||
/// necessary because C Shell interprets it as a meta-character for history substitution
|
||||
/// even when enclosed in single quotes or quotation marks.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// All other characters are enclosed in single quotes. Sequences of such characters are grouped
|
||||
/// in a single pair of single quotes.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// References:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description><a href="http://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html">Shell Command Language</a></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><a href="https://earthsci.stanford.edu/computing/unix/shell/specialchars.php">Unix C-Shell special characters and their uses</a></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><a href="https://docstore.mik.ua/orelly/unix3/upt/ch27_13.htm">Differences Between Bourne and C Shell Quoting</a></description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Original</term>
|
||||
/// <term>Transformed</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>/var/log/auth.log</term>
|
||||
/// <term>'/var/log/auth.log'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/mp3/Guns N' Roses</term>
|
||||
/// <term>'/var/mp3/Guns N'"'"' Roses'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/garbage!/temp</term>
|
||||
/// <term>'/var/garbage'\!'/temp'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/would be 'kewl'!, not?</term>
|
||||
/// <term>'/var/would be '"'"'kewl'"'"\!', not?'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term></term>
|
||||
/// <term>''</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Hello "World"</term>
|
||||
/// <term>'Hello "World"'</term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
public string Transform(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
|
||||
// result is at least value and (likely) leading/trailing single-quotes
|
||||
var sb = new StringBuilder(path.Length + 2);
|
||||
var state = ShellQuoteState.Unquoted;
|
||||
|
||||
foreach (var c in path)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\'':
|
||||
// embed a single-quote in quotes
|
||||
switch (state)
|
||||
{
|
||||
case ShellQuoteState.Unquoted:
|
||||
// Start quoted string
|
||||
sb.Append('"');
|
||||
break;
|
||||
case ShellQuoteState.Quoted:
|
||||
// Continue quoted string
|
||||
break;
|
||||
case ShellQuoteState.SingleQuoted:
|
||||
// Close single-quoted string
|
||||
sb.Append('\'');
|
||||
// Start quoted string
|
||||
sb.Append('"');
|
||||
break;
|
||||
}
|
||||
state = ShellQuoteState.Quoted;
|
||||
break;
|
||||
case '!':
|
||||
// In C-Shell, an exclamatation point can only be protected from shell interpretation
|
||||
// when escaped by a backslash
|
||||
// Source:
|
||||
// https://earthsci.stanford.edu/computing/unix/shell/specialchars.php
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ShellQuoteState.Unquoted:
|
||||
sb.Append('\\');
|
||||
break;
|
||||
case ShellQuoteState.Quoted:
|
||||
// Close quoted string
|
||||
sb.Append('"');
|
||||
sb.Append('\\');
|
||||
break;
|
||||
case ShellQuoteState.SingleQuoted:
|
||||
// Close single quoted string
|
||||
sb.Append('\'');
|
||||
sb.Append('\\');
|
||||
break;
|
||||
}
|
||||
state = ShellQuoteState.Unquoted;
|
||||
break;
|
||||
default:
|
||||
switch (state)
|
||||
{
|
||||
case ShellQuoteState.Unquoted:
|
||||
// Start single-quoted string
|
||||
sb.Append('\'');
|
||||
break;
|
||||
case ShellQuoteState.Quoted:
|
||||
// Close quoted string
|
||||
sb.Append('"');
|
||||
// Start single-quoted string
|
||||
sb.Append('\'');
|
||||
break;
|
||||
case ShellQuoteState.SingleQuoted:
|
||||
// Continue single-quoted string
|
||||
break;
|
||||
}
|
||||
state = ShellQuoteState.SingleQuoted;
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ShellQuoteState.Unquoted:
|
||||
break;
|
||||
case ShellQuoteState.Quoted:
|
||||
// Close quoted string
|
||||
sb.Append('"');
|
||||
break;
|
||||
case ShellQuoteState.SingleQuoted:
|
||||
// Close single-quoted string
|
||||
sb.Append('\'');
|
||||
break;
|
||||
}
|
||||
|
||||
if (sb.Length == 0)
|
||||
{
|
||||
sb.Append("''");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private enum ShellQuoteState
|
||||
{
|
||||
Unquoted = 1,
|
||||
SingleQuoted = 2,
|
||||
Quoted = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to built-in remote path transformations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// References:
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// <description><a href="http://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html">Shell Command Language</a></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><a href="https://earthsci.stanford.edu/computing/unix/shell/specialchars.php">Unix C-Shell special characters and their uses</a></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><a href="https://docstore.mik.ua/orelly/unix3/upt/ch27_13.htm">Differences Between Bourne and C Shell Quoting</a></description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <description><a href="https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/">Everyone quotes command line arguments the wrong way</a></description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public static class RemotePathTransformation
|
||||
{
|
||||
private static readonly IRemotePathTransformation ShellQuoteTransformation = new RemotePathShellQuoteTransformation();
|
||||
private static readonly IRemotePathTransformation NoneTransformation = new RemotePathNoneTransformation();
|
||||
private static readonly IRemotePathTransformation DoubleQuoteTransformation = new RemotePathDoubleQuoteTransformation();
|
||||
|
||||
/// <summary>
|
||||
/// Quotes a path in a way to be suitable to be used with a shell-based server.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A quoted path.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a path contains a single-quote, that character is embedded in quotation marks (eg. "'").
|
||||
/// Sequences of single-quotes are grouped in a single pair of quotation marks.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// An exclamation mark in a path is escaped with a backslash. This is necessary because C Shell
|
||||
/// interprets it as a meta-character for history substitution even when enclosed in single quotes
|
||||
/// or quotation marks.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// All other characters are enclosed in single quotes. Sequences of such characters are grouped
|
||||
/// in a single pair of single quotes.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Original</term>
|
||||
/// <term>Transformed</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>/var/log/auth.log</term>
|
||||
/// <term>'/var/log/auth.log'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/mp3/Guns N' Roses</term>
|
||||
/// <term>'/var/mp3/Guns N'"'"' Roses'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/garbage!/temp</term>
|
||||
/// <term>'/var/garbage'\!'/temp'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/would be 'kewl'!, not?</term>
|
||||
/// <term>'/var/would be '"'"'kewl'"'"\!', not?'</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term></term>
|
||||
/// <term>''</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Hello "World"</term>
|
||||
/// <term>'Hello "World"'</term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
public static IRemotePathTransformation ShellQuote
|
||||
{
|
||||
get { return ShellQuoteTransformation; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs no transformation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Recommended for servers that do not require any character to be escaped or enclosed in quotes,
|
||||
/// or when paths are guaranteed to never contain any special characters (such as #, ", ', $, ...).
|
||||
/// </remarks>
|
||||
public static IRemotePathTransformation None
|
||||
{
|
||||
get { return NoneTransformation; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encloses a path in double quotes, and escapes any embedded double quote with a backslash.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A transformation that encloses a path in double quotes, and escapes any embedded double quote with
|
||||
/// a backslash.
|
||||
/// </value>
|
||||
/// <example>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>Original</term>
|
||||
/// <term>Transformed</term>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>/var/log/auth.log</term>
|
||||
/// <term>"/var/log/auth.log"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/mp3/Guns N' Roses</term>
|
||||
/// <term>"/var/mp3/Guns N' Roses"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/garbage!/temp</term>
|
||||
/// <term>"/var/garbage!/temp"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>/var/would be 'kewl'!, not?</term>
|
||||
/// <term>"/var/would be 'kewl'!, not?"</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term></term>
|
||||
/// <term>""</term>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Hello "World"</term>
|
||||
/// <term>"Hello \"World"</term>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </example>
|
||||
public static IRemotePathTransformation DoubleQuote
|
||||
{
|
||||
get { return DoubleQuoteTransformation; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,6 +153,7 @@
|
||||
<Compile Include="IClientAuthentication.cs" />
|
||||
<Compile Include="IConnectionInfo.cs" />
|
||||
<Compile Include="IForwardedPort.cs" />
|
||||
<Compile Include="IRemotePathTransformation.cs" />
|
||||
<Compile Include="IServiceFactory.cs" />
|
||||
<Compile Include="IServiceFactory.NET.cs" />
|
||||
<Compile Include="ISession.cs" />
|
||||
@@ -162,6 +163,10 @@
|
||||
<Compile Include="Messages\Transport\KeyExchangeEcdhInitMessage.cs" />
|
||||
<Compile Include="Messages\Transport\KeyExchangeEcdhReplyMessage.cs" />
|
||||
<Compile Include="Netconf\INetConfSession.cs" />
|
||||
<Compile Include="RemotePathDoubleQuoteTransformation.cs" />
|
||||
<Compile Include="RemotePathNoneTransformation.cs" />
|
||||
<Compile Include="RemotePathShellQuoteTransformation.cs" />
|
||||
<Compile Include="RemotePathTransformation.cs" />
|
||||
<Compile Include="Security\Cryptography\HMACMD5.cs" />
|
||||
<Compile Include="Security\Cryptography\HMACSHA1.cs" />
|
||||
<Compile Include="Security\Cryptography\HMACSHA256.cs" />
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Renci.SshNet
|
||||
channel.DataReceived += (sender, e) => input.Write(e.Data, 0, e.Data.Length);
|
||||
channel.Open();
|
||||
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", path.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", _remotePathTransformation.Transform(path))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace Renci.SshNet
|
||||
channel.Open();
|
||||
|
||||
// start recursive upload
|
||||
if (!channel.SendExecRequest(string.Format("scp -rt {0}", path.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -rt {0}", _remotePathTransformation.Transform(path))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
@@ -109,7 +109,7 @@ namespace Renci.SshNet
|
||||
channel.Open();
|
||||
|
||||
// Send channel command request
|
||||
if (!channel.SendExecRequest(string.Format("scp -pf {0}", filename.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -pf {0}", _remotePathTransformation.Transform(filename))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
@@ -143,7 +143,7 @@ namespace Renci.SshNet
|
||||
channel.Open();
|
||||
|
||||
// Send channel command request
|
||||
if (!channel.SendExecRequest(string.Format("scp -prf {0}", directoryName.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -prf {0}", _remotePathTransformation.Transform(directoryName))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Renci.SshNet
|
||||
private static readonly byte[] SuccessConfirmationCode = {0};
|
||||
private static readonly byte[] ErrorConfirmationCode = { 1 };
|
||||
|
||||
private IRemotePathTransformation _remotePathTransformation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the operation timeout.
|
||||
/// </summary>
|
||||
@@ -50,6 +52,34 @@ namespace Renci.SshNet
|
||||
/// </value>
|
||||
public uint BufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transformation to apply to remote paths.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The transformation to apply to remote paths. The default is <see cref="SshNet.RemotePathTransformation.DoubleQuote"/>.
|
||||
/// </value>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <c>null</c>.</exception>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This transformation is applied to the remote file or directory path that is passed to the
|
||||
/// <c>scp</c> command.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See <see cref="SshNet.RemotePathTransformation"/> for the transformations that are supplied
|
||||
/// out-of-the-box with SSH.NET.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public IRemotePathTransformation RemotePathTransformation
|
||||
{
|
||||
get { return _remotePathTransformation; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
_remotePathTransformation = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when downloading file.
|
||||
/// </summary>
|
||||
@@ -162,6 +192,7 @@ namespace Renci.SshNet
|
||||
{
|
||||
OperationTimeout = SshNet.Session.InfiniteTimeSpan;
|
||||
BufferSize = 1024 * 16;
|
||||
_remotePathTransformation = serviceFactory.CreateRemotePathDoubleQuoteTransformation();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -183,7 +214,7 @@ namespace Renci.SshNet
|
||||
|
||||
// pass the full path to ensure the server does not create the directory part
|
||||
// as a file in case the directory does not exist
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", path.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -t {0}", _remotePathTransformation.Transform(path))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
@@ -220,7 +251,7 @@ namespace Renci.SshNet
|
||||
channel.Open();
|
||||
|
||||
// Send channel command request
|
||||
if (!channel.SendExecRequest(string.Format("scp -f {0}", filename.ShellQuote())))
|
||||
if (!channel.SendExecRequest(string.Format("scp -f {0}", _remotePathTransformation.Transform(filename))))
|
||||
{
|
||||
throw SecureExecutionRequestRejectedException();
|
||||
}
|
||||
|
||||
@@ -136,5 +136,19 @@ namespace Renci.SshNet
|
||||
{
|
||||
return new SftpResponseFactory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes
|
||||
/// any embedded double quote with a backslash.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes any
|
||||
/// embedded double quote with a backslash.
|
||||
/// with a shell.
|
||||
/// </returns>
|
||||
public IRemotePathTransformation CreateRemotePathDoubleQuoteTransformation()
|
||||
{
|
||||
return RemotePathTransformation.DoubleQuote;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user