diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTestBase.cs
index a77222cf..19497c6d 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTestBase.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTestBase.cs
@@ -38,11 +38,28 @@ namespace Renci.SshNet.Tests.Classes.Sftp
protected abstract void Act();
+ protected byte[] GenerateRandom(int length)
+ {
+ return GenerateRandom(length, new Random());
+ }
+
protected byte[] GenerateRandom(int length, Random random)
{
var buffer = new byte[length];
random.NextBytes(buffer);
return buffer;
}
+
+ protected byte[] GenerateRandom(uint length)
+ {
+ return GenerateRandom(length, new Random());
+ }
+
+ protected byte[] GenerateRandom(uint length, Random random)
+ {
+ var buffer = new byte[length];
+ random.NextBytes(buffer);
+ return buffer;
+ }
}
}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs
new file mode 100644
index 00000000..aafde5c2
--- /dev/null
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Globalization;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
+using System.Threading;
+using Renci.SshNet.Sftp.Responses;
+
+namespace Renci.SshNet.Tests.Classes.Sftp
+{
+ ///
+ /// - In read mode
+ /// - Bytes in (read) buffer
+ /// - New length greater than client position and greater than server position
+ ///
+ [TestClass]
+ public class SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition : SftpFileStreamTestBase
+ {
+ private string _path;
+ private SftpFileStream _sftpFileStream;
+ private byte[] _handle;
+ private uint _bufferSize;
+ private uint _readBufferSize;
+ private uint _writeBufferSize;
+ private MockSequence _sequence;
+ private long _length;
+
+ private SftpFileAttributes _fileAttributes;
+ private SftpFileAttributes _originalFileAttributes;
+ private SftpFileAttributes _newFileAttributes;
+ private byte[] _readBytes;
+ private byte[] _actualReadBytes;
+
+ protected override void SetupData()
+ {
+ var random = new Random();
+
+ _path = random.Next().ToString(CultureInfo.InvariantCulture);
+ _handle = GenerateRandom(random.Next(2, 6), random);
+ _bufferSize = (uint) random.Next(1, 1000);
+ _readBufferSize = (uint) random.Next(1, 1000);
+ _writeBufferSize = (uint) random.Next(100, 1000);
+ _readBytes = new byte[5];
+ _actualReadBytes = GenerateRandom(_readBytes.Length, random);
+ _length = _readBytes.Length + 2;
+
+ _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
+ .WithExtension("V", "VValue")
+ .WithGroupId(random.Next())
+ .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithPermissions((uint)random.Next())
+ .WithSize(_length + 100)
+ .WithUserId(random.Next())
+ .Build();
+ _originalFileAttributes = _fileAttributes.Clone();
+ _newFileAttributes = null;
+ }
+
+ protected override void SetupMocks()
+ {
+ _sequence = new MockSequence();
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false))
+ .Returns(_handle);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalReadLength(_bufferSize))
+ .Returns(_readBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
+ .Returns(_writeBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, 0, _readBufferSize))
+ .Returns(_actualReadBytes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFStat(_handle, false))
+ .Returns(_fileAttributes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFSetStat(_handle, _fileAttributes))
+ .Callback((bytes, attributes) => _newFileAttributes = attributes.Clone());
+ }
+
+ protected override void Arrange()
+ {
+ base.Arrange();
+
+ _sftpFileStream = new SftpFileStream(SftpSessionMock.Object, _path, FileMode.Open, FileAccess.ReadWrite, (int)_bufferSize);
+ _sftpFileStream.Read(_readBytes, 0, _readBytes.Length);
+ }
+
+ protected override void Act()
+ {
+ _sftpFileStream.SetLength(_length);
+ }
+
+ [TestMethod]
+ public void PositionShouldReturnSamePositionAsBeforeSetLength()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+
+ Assert.AreEqual(_readBytes.Length, _sftpFileStream.Position);
+
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3));
+ }
+
+ [TestMethod]
+ public void RequestFSetStatOnSftpSessionShouldBeInvokedOnce()
+ {
+ SftpSessionMock.Verify(p => p.RequestFSetStat(_handle, _fileAttributes), Times.Once);
+ }
+
+ [TestMethod]
+ public void SizeOfSftpFileAttributesShouldBeModifiedToNewLengthBeforePassedToRequestFSetStat()
+ {
+ DictionaryAssert.AreEqual(_originalFileAttributes.Extensions, _newFileAttributes.Extensions);
+ Assert.AreEqual(_originalFileAttributes.GroupId, _newFileAttributes.GroupId);
+ Assert.AreEqual(_originalFileAttributes.LastAccessTime, _newFileAttributes.LastAccessTime);
+ Assert.AreEqual(_originalFileAttributes.LastWriteTime, _newFileAttributes.LastWriteTime);
+ Assert.AreEqual(_originalFileAttributes.Permissions, _newFileAttributes.Permissions);
+ Assert.AreEqual(_originalFileAttributes.UserId, _newFileAttributes.UserId);
+
+ Assert.AreEqual(_length, _newFileAttributes.Size);
+ }
+
+ [TestMethod]
+ public void ReadShouldReadStartFromSamePositionAsBeforeSetLength()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, (uint) _readBytes.Length, _readBufferSize))
+ .Returns(new byte[] { 0x0f });
+
+ var byteRead = _sftpFileStream.ReadByte();
+
+ Assert.AreEqual(0x0f, byteRead);
+
+ SftpSessionMock.Verify(p => p.RequestRead(_handle, (uint) _readBytes.Length, _readBufferSize), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3));
+ }
+
+ [TestMethod]
+ public void WriteShouldStartFromSamePositionAsBeforeSetLength()
+ {
+ var bytesToWrite = GenerateRandom(5);
+
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint) _readBytes.Length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) =>
+ {
+ wait.Set();
+ });
+
+ _sftpFileStream.Write(bytesToWrite, 0, bytesToWrite.Length);
+ _sftpFileStream.Flush();
+
+ SftpSessionMock.Verify(p => p.RequestWrite(_handle, (uint) _readBytes.Length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs
new file mode 100644
index 00000000..ca2df880
--- /dev/null
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Globalization;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
+using System.Threading;
+using Renci.SshNet.Sftp.Responses;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Sftp
+{
+ ///
+ /// - In read mode
+ /// - Bytes in (read) buffer
+ /// - New length less than client position and greater than server position
+ ///
+ [TestClass]
+ public class SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition : SftpFileStreamTestBase
+ {
+ private string _path;
+ private SftpFileStream _sftpFileStream;
+ private byte[] _handle;
+ private uint _bufferSize;
+ private uint _readBufferSize;
+ private uint _writeBufferSize;
+ private MockSequence _sequence;
+ private long _length;
+
+ private SftpFileAttributes _fileAttributes;
+ private SftpFileAttributes _originalFileAttributes;
+ private SftpFileAttributes _newFileAttributes;
+ private byte[] _readBytes;
+ private byte[] _actualReadBytes;
+
+ protected override void SetupData()
+ {
+ var random = new Random();
+
+ _path = random.Next().ToString(CultureInfo.InvariantCulture);
+ _handle = GenerateRandom(random.Next(2, 6), random);
+ _bufferSize = (uint)random.Next(1, 1000);
+ _readBufferSize = (uint)random.Next(1, 1000);
+ _writeBufferSize = (uint)random.Next(100, 1000);
+ _readBytes = new byte[5];
+ _actualReadBytes = GenerateRandom(_readBytes.Length, random);
+ _length = _readBytes.Length - 2;
+
+ _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
+ .WithExtension("V", "VValue")
+ .WithGroupId(random.Next())
+ .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithPermissions((uint)random.Next())
+ .WithSize(_length + 100)
+ .WithUserId(random.Next())
+ .Build();
+ _originalFileAttributes = _fileAttributes.Clone();
+ _newFileAttributes = null;
+ }
+
+ protected override void SetupMocks()
+ {
+ _sequence = new MockSequence();
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false))
+ .Returns(_handle);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalReadLength(_bufferSize))
+ .Returns(_readBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
+ .Returns(_writeBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, 0, _readBufferSize))
+ .Returns(_actualReadBytes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFStat(_handle, false))
+ .Returns(_fileAttributes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFSetStat(_handle, _fileAttributes))
+ .Callback((bytes, attributes) => _newFileAttributes = attributes.Clone());
+ }
+
+ protected override void Arrange()
+ {
+ base.Arrange();
+
+ _sftpFileStream = new SftpFileStream(SftpSessionMock.Object, _path, FileMode.Open, FileAccess.ReadWrite, (int)_bufferSize);
+ _sftpFileStream.Read(_readBytes, 0, _readBytes.Length);
+ }
+
+ protected override void Act()
+ {
+ _sftpFileStream.SetLength(_length);
+ }
+
+ [TestMethod]
+ public void PositionShouldReturnLengthOfStream()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+
+ Assert.AreEqual(_length, _sftpFileStream.Position);
+
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3));
+ }
+
+ [TestMethod]
+ public void RequestFSetStatOnSftpSessionShouldBeInvokedOnce()
+ {
+ SftpSessionMock.Verify(p => p.RequestFSetStat(_handle, _fileAttributes), Times.Once);
+ }
+
+ [TestMethod]
+ public void SizeOfSftpFileAttributesShouldBeModifiedToNewLengthBeforePassedToRequestFSetStat()
+ {
+ DictionaryAssert.AreEqual(_originalFileAttributes.Extensions, _newFileAttributes.Extensions);
+ Assert.AreEqual(_originalFileAttributes.GroupId, _newFileAttributes.GroupId);
+ Assert.AreEqual(_originalFileAttributes.LastAccessTime, _newFileAttributes.LastAccessTime);
+ Assert.AreEqual(_originalFileAttributes.LastWriteTime, _newFileAttributes.LastWriteTime);
+ Assert.AreEqual(_originalFileAttributes.Permissions, _newFileAttributes.Permissions);
+ Assert.AreEqual(_originalFileAttributes.UserId, _newFileAttributes.UserId);
+
+ Assert.AreEqual(_length, _newFileAttributes.Size);
+ }
+
+ [TestMethod]
+ public void ReadShouldStartFromEndOfStream()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, (uint) _length, _readBufferSize))
+ .Returns(Array.Empty);
+
+ var byteRead = _sftpFileStream.ReadByte();
+
+ Assert.AreEqual(-1, byteRead);
+
+ SftpSessionMock.Verify(p => p.RequestRead(_handle, (uint) _length, _readBufferSize), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3));
+ }
+
+ [TestMethod]
+ public void WriteShouldStartFromEndOfStream()
+ {
+ var bytesToWrite = GenerateRandom(5);
+
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint) _length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) =>
+ {
+ wait.Set();
+ });
+
+ _sftpFileStream.Write(bytesToWrite, 0, bytesToWrite.Length);
+ _sftpFileStream.Flush();
+
+ SftpSessionMock.Verify(p => p.RequestWrite(_handle, (uint)_length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs
new file mode 100644
index 00000000..863bae29
--- /dev/null
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Globalization;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
+using System.Threading;
+using Renci.SshNet.Sftp.Responses;
+
+namespace Renci.SshNet.Tests.Classes.Sftp
+{
+ ///
+ /// - In write mode
+ /// - Bytes in (write) buffer
+ /// - New length greater than client position and greater than server position
+ ///
+ [TestClass]
+ public class SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreaterThanPosition : SftpFileStreamTestBase
+ {
+ private string _path;
+ private SftpFileStream _sftpFileStream;
+ private byte[] _handle;
+ private uint _bufferSize;
+ private uint _readBufferSize;
+ private uint _writeBufferSize;
+ private MockSequence _sequence;
+ private long _length;
+
+ private SftpFileAttributes _fileAttributes;
+ private SftpFileAttributes _originalFileAttributes;
+ private SftpFileAttributes _newFileAttributes;
+ private byte[] _readBytes;
+ private byte[] _actualReadBytes;
+ private byte[] _writeBytes;
+ private byte[] _actualWrittenBytes;
+
+ protected override void SetupData()
+ {
+ var random = new Random();
+
+ _path = random.Next().ToString(CultureInfo.InvariantCulture);
+ _handle = GenerateRandom(random.Next(2, 6), random);
+ _bufferSize = (uint) random.Next(1, 1000);
+ _readBufferSize = (uint) random.Next(1, 1000);
+ _writeBufferSize = (uint) random.Next(100, 1000);
+ _readBytes = new byte[5];
+ _actualReadBytes = GenerateRandom(_readBytes.Length, random);
+ _writeBytes = new byte[] { 0x01, 0x02, 0x03, 0x04 };
+ _length = _readBytes.Length + _writeBytes.Length + 2;
+ _actualWrittenBytes = null;
+
+ _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
+ .WithExtension("V", "VValue")
+ .WithGroupId(random.Next())
+ .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithPermissions((uint)random.Next())
+ .WithSize(_length + 100)
+ .WithUserId(random.Next())
+ .Build();
+ _originalFileAttributes = _fileAttributes.Clone();
+ _newFileAttributes = null;
+ }
+
+ protected override void SetupMocks()
+ {
+ _sequence = new MockSequence();
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false))
+ .Returns(_handle);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalReadLength(_bufferSize))
+ .Returns(_readBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
+ .Returns(_writeBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, 0, _readBufferSize))
+ .Returns(_actualReadBytes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint) _readBytes.Length, It.IsAny(), 0, _writeBytes.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted)
+ =>
+ {
+ _actualWrittenBytes = data.Take(0, _writeBytes.Length);
+ wait.Set();
+ });
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFStat(_handle, false))
+ .Returns(_fileAttributes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFSetStat(_handle, _fileAttributes))
+ .Callback((bytes, attributes) => _newFileAttributes = attributes.Clone());
+ }
+
+ protected override void Arrange()
+ {
+ base.Arrange();
+
+ _sftpFileStream = new SftpFileStream(SftpSessionMock.Object, _path, FileMode.Open, FileAccess.ReadWrite, (int)_bufferSize);
+ _sftpFileStream.Read(_readBytes, 0, _readBytes.Length);
+ _sftpFileStream.Write(new byte[] { 0x01, 0x02, 0x03, 0x04 }, 0, 4);
+ }
+
+ protected override void Act()
+ {
+ _sftpFileStream.SetLength(_length);
+ }
+
+ [TestMethod]
+ public void PositionShouldReturnSamePositionAsBeforeSetLength()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+
+ Assert.AreEqual(_readBytes.Length + _writeBytes.Length, _sftpFileStream.Position);
+
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+
+ [TestMethod]
+ public void RequestFSetStatOnSftpSessionShouldBeInvokedOnce()
+ {
+ SftpSessionMock.Verify(p => p.RequestFSetStat(_handle, _fileAttributes), Times.Once);
+ }
+
+ [TestMethod]
+ public void SizeOfSftpFileAttributesShouldBeModifiedToNewLengthBeforePassedToRequestFSetStat()
+ {
+ DictionaryAssert.AreEqual(_originalFileAttributes.Extensions, _newFileAttributes.Extensions);
+ Assert.AreEqual(_originalFileAttributes.GroupId, _newFileAttributes.GroupId);
+ Assert.AreEqual(_originalFileAttributes.LastAccessTime, _newFileAttributes.LastAccessTime);
+ Assert.AreEqual(_originalFileAttributes.LastWriteTime, _newFileAttributes.LastWriteTime);
+ Assert.AreEqual(_originalFileAttributes.Permissions, _newFileAttributes.Permissions);
+ Assert.AreEqual(_originalFileAttributes.UserId, _newFileAttributes.UserId);
+
+ Assert.AreEqual(_length, _newFileAttributes.Size);
+ }
+
+ [TestMethod]
+ public void WrittenBytesShouldByFlushedToServer()
+ {
+ Assert.IsNotNull(_actualWrittenBytes);
+ CollectionAssert.AreEqual(_writeBytes, _actualWrittenBytes);
+ }
+
+ [TestMethod]
+ public void ReadShouldReadStartFromSamePositionAsBeforeSetLength()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, (uint) (_readBytes.Length + _writeBytes.Length), _readBufferSize))
+ .Returns(new byte[] { 0x0f });
+
+ var byteRead = _sftpFileStream.ReadByte();
+
+ Assert.AreEqual(0x0f, byteRead);
+
+ SftpSessionMock.Verify(p => p.RequestRead(_handle, (uint) (_readBytes.Length + _writeBytes.Length), _readBufferSize), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+
+ [TestMethod]
+ public void WriteShouldStartFromSamePositionAsBeforeSetLength()
+ {
+ var bytesToWrite = GenerateRandom(5);
+
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint)(_readBytes.Length + _writeBytes.Length), It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) =>
+ {
+ wait.Set();
+ });
+
+ _sftpFileStream.Write(bytesToWrite, 0, bytesToWrite.Length);
+ _sftpFileStream.Flush();
+
+ SftpSessionMock.Verify(p => p.RequestWrite(_handle, (uint) (_readBytes.Length + _writeBytes.Length), It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(5));
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs
new file mode 100644
index 00000000..434fc0ab
--- /dev/null
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Globalization;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using Renci.SshNet.Sftp;
+using Renci.SshNet.Tests.Common;
+using System.Threading;
+using Renci.SshNet.Sftp.Responses;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Sftp
+{
+ ///
+ /// - In write mode
+ /// - Bytes in (write) buffer
+ /// - New length less than client position and less than server position
+ ///
+ [TestClass]
+ public class SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition : SftpFileStreamTestBase
+ {
+ private string _path;
+ private SftpFileStream _sftpFileStream;
+ private byte[] _handle;
+ private uint _bufferSize;
+ private uint _readBufferSize;
+ private uint _writeBufferSize;
+ private MockSequence _sequence;
+ private long _length;
+
+ private SftpFileAttributes _fileAttributes;
+ private SftpFileAttributes _originalFileAttributes;
+ private SftpFileAttributes _newFileAttributes;
+ private byte[] _readBytes;
+ private byte[] _actualReadBytes;
+ private byte[] _writeBytes;
+ private byte[] _actualWrittenBytes;
+
+ protected override void SetupData()
+ {
+ var random = new Random();
+
+ _path = random.Next().ToString(CultureInfo.InvariantCulture);
+ _handle = GenerateRandom(random.Next(2, 6), random);
+ _bufferSize = (uint) random.Next(1, 1000);
+ _readBufferSize = (uint) random.Next(1, 1000);
+ _writeBufferSize = (uint) random.Next(100, 1000);
+ _readBytes = new byte[5];
+ _actualReadBytes = GenerateRandom(_readBytes.Length, random);
+ _writeBytes = new byte[] { 0x01, 0x02, 0x03, 0x04 };
+ _length = _readBytes.Length - 2;
+ _actualWrittenBytes = null;
+
+ _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
+ .WithExtension("V", "VValue")
+ .WithGroupId(random.Next())
+ .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithPermissions((uint) random.Next())
+ .WithSize(_length + 100)
+ .WithUserId(random.Next())
+ .Build();
+ _originalFileAttributes = _fileAttributes.Clone();
+ _newFileAttributes = null;
+ }
+
+ protected override void SetupMocks()
+ {
+ _sequence = new MockSequence();
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write, false))
+ .Returns(_handle);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalReadLength(_bufferSize))
+ .Returns(_readBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
+ .Returns(_writeBufferSize);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, 0, _readBufferSize))
+ .Returns(_actualReadBytes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.IsOpen)
+ .Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint) _readBytes.Length, It.IsAny(), 0, _writeBytes.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted)
+ =>
+ {
+ _actualWrittenBytes = data.Take(0, _writeBytes.Length);
+ wait.Set();
+ });
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFStat(_handle, false))
+ .Returns(_fileAttributes);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestFSetStat(_handle, _fileAttributes))
+ .Callback((bytes, attributes) => _newFileAttributes = attributes.Clone());
+ }
+
+ protected override void Arrange()
+ {
+ base.Arrange();
+
+ _sftpFileStream = new SftpFileStream(SftpSessionMock.Object, _path, FileMode.Open, FileAccess.ReadWrite, (int) _bufferSize);
+ _sftpFileStream.Read(_readBytes, 0, _readBytes.Length);
+ _sftpFileStream.Write(new byte[] { 0x01, 0x02, 0x03, 0x04 }, 0, 4);
+ }
+
+ protected override void Act()
+ {
+ _sftpFileStream.SetLength(_length);
+ }
+
+ [TestMethod]
+ public void PositionShouldReturnLengthOfStream()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+
+ Assert.AreEqual(_length, _sftpFileStream.Position);
+
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+
+ [TestMethod]
+ public void RequestFSetStatOnSftpSessionShouldBeInvokedOnce()
+ {
+ SftpSessionMock.Verify(p => p.RequestFSetStat(_handle, _fileAttributes), Times.Once);
+ }
+
+ [TestMethod]
+ public void SizeOfSftpFileAttributesShouldBeModifiedToNewLengthBeforePassedToRequestFSetStat()
+ {
+ DictionaryAssert.AreEqual(_originalFileAttributes.Extensions, _newFileAttributes.Extensions);
+ Assert.AreEqual(_originalFileAttributes.GroupId, _newFileAttributes.GroupId);
+ Assert.AreEqual(_originalFileAttributes.LastAccessTime, _newFileAttributes.LastAccessTime);
+ Assert.AreEqual(_originalFileAttributes.LastWriteTime, _newFileAttributes.LastWriteTime);
+ Assert.AreEqual(_originalFileAttributes.Permissions, _newFileAttributes.Permissions);
+ Assert.AreEqual(_originalFileAttributes.UserId, _newFileAttributes.UserId);
+
+ Assert.AreEqual(_length, _newFileAttributes.Size);
+ }
+
+ [TestMethod]
+ public void WrittenBytesShouldByFlushedToServer()
+ {
+ Assert.IsNotNull(_actualWrittenBytes);
+ CollectionAssert.AreEqual(_writeBytes, _actualWrittenBytes);
+ }
+
+ [TestMethod]
+ public void ReadShouldStartFromEndOfStream()
+ {
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestRead(_handle, (uint) _length, _readBufferSize))
+ .Returns(Array.Empty);
+
+ var byteRead = _sftpFileStream.ReadByte();
+
+ Assert.AreEqual(-1, byteRead);
+
+ SftpSessionMock.Verify(p => p.RequestRead(_handle, (uint)_length, _readBufferSize), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+ }
+
+ [TestMethod]
+ public void WriteShouldStartFromEndOfStream()
+ {
+ var bytesToWrite = GenerateRandom(5);
+
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true);
+ SftpSessionMock.InSequence(_sequence)
+ .Setup(p => p.RequestWrite(_handle, (uint) _length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null))
+ .Callback>((handle, serverOffset, data, offset, length, wait, writeCompleted) =>
+ {
+ wait.Set();
+ });
+
+ _sftpFileStream.Write(bytesToWrite, 0, bytesToWrite.Length);
+ _sftpFileStream.Flush();
+
+ SftpSessionMock.Verify(p => p.RequestWrite(_handle, (uint) _length, It.IsAny(), 0, bytesToWrite.Length, It.IsAny(), null), Times.Once);
+ SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(5));
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs b/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs
new file mode 100644
index 00000000..0df1c504
--- /dev/null
+++ b/src/Renci.SshNet.Tests/Common/DictionaryAssert.cs
@@ -0,0 +1,38 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Collections.Generic;
+
+namespace Renci.SshNet.Tests.Common
+{
+ public static class DictionaryAssert
+ {
+ public static void AreEqual(IDictionary expected, IDictionary actual)
+ {
+ if (ReferenceEquals(expected, actual))
+ return;
+
+ if (expected == null)
+ throw new AssertFailedException("Expected dictionary to be null, but was not null.");
+
+ if (actual == null)
+ throw new AssertFailedException("Expected dictionary not to be null, but was null.");
+
+ if (expected.Count != actual.Count)
+ throw new AssertFailedException(string.Format("Expected dictionary to contain {0} entries, but was {1}.",
+ expected.Count, actual.Count));
+
+ foreach (var expectedEntry in expected)
+ {
+ TValue actualValue;
+ if (!actual.TryGetValue(expectedEntry.Key, out actualValue))
+ {
+ throw new AssertFailedException(string.Format("Dictionary contains no entry with key '{0}'.", expectedEntry.Key));
+ }
+
+ if (!Equals(expectedEntry.Value, actualValue))
+ {
+ throw new AssertFailedException(string.Format("Value for key '{0}' does not match.", expectedEntry.Key));
+ }
+ }
+ }
+ }
+}
diff --git a/src/Renci.SshNet.Tests/Common/Extensions.cs b/src/Renci.SshNet.Tests/Common/Extensions.cs
index 6bb1e45e..911f3413 100644
--- a/src/Renci.SshNet.Tests/Common/Extensions.cs
+++ b/src/Renci.SshNet.Tests/Common/Extensions.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Renci.SshNet.Common;
using System;
+using Renci.SshNet.Sftp;
namespace Renci.SshNet.Tests.Common
{
@@ -24,5 +25,37 @@ namespace Renci.SshNet.Tests.Common
Buffer.BlockCopy(buffer, 0, copy, 0, buffer.Length);
return copy;
}
+
+ ///
+ /// Creates a deep clone of the current instance.
+ ///
+ ///
+ /// A deep clone of the current instance.
+ ///
+ internal static SftpFileAttributes Clone(this SftpFileAttributes value)
+ {
+ Dictionary clonedExtensions;
+
+ if (value.Extensions != null)
+ {
+ clonedExtensions = new Dictionary(value.Extensions.Count);
+ foreach (var entry in value.Extensions)
+ {
+ clonedExtensions.Add(entry.Key, entry.Value);
+ }
+ }
+ else
+ {
+ clonedExtensions = null;
+ }
+
+ return new SftpFileAttributes(value.LastAccessTime,
+ value.LastWriteTime,
+ value.Size,
+ value.UserId,
+ value.GroupId,
+ value.Permissions,
+ clonedExtensions);
+ }
}
}
diff --git a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
index 9003fc3d..2468c827 100644
--- a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
+++ b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
@@ -14,6 +14,11 @@ namespace Renci.SshNet.Tests.Common
private uint? _permissions;
private IDictionary _extensions;
+ public SftpFileAttributesBuilder()
+ {
+ _extensions = new Dictionary();
+ }
+
public SftpFileAttributesBuilder WithLastAccessTime(DateTime lastAccessTime)
{
_lastAccessTime = lastAccessTime;
@@ -50,9 +55,9 @@ namespace Renci.SshNet.Tests.Common
return this;
}
- public SftpFileAttributesBuilder WithExtensions(IDictionary extensions)
+ public SftpFileAttributesBuilder WithExtension(string name, string value)
{
- _extensions = extensions;
+ _extensions.Add(name, value);
return this;
}
@@ -79,7 +84,5 @@ namespace Renci.SshNet.Tests.Common
_permissions.Value,
_extensions);
}
-
-
}
}
diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
index 9fc8daef..5076c4b4 100644
--- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
+++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
@@ -444,6 +444,10 @@
+
+
+
+
@@ -513,6 +517,7 @@
+