Browse Source

Added more tests for SftpFileStream.SetLength(long).

Gert Driesen 8 years ago
parent
commit
9444f21461

+ 17 - 0
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;
+        }
     }
 }

+ 170 - 0
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
+{
+    /// <summary>
+    /// - In read mode
+    /// - Bytes in (read) buffer
+    /// - New length greater than client position and greater than server position
+    /// </summary>
+    [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<byte[], SftpFileAttributes>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null), Times.Once);
+            SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+        }
+    }
+}

+ 171 - 0
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
+{
+    /// <summary>
+    /// - In read mode
+    /// - Bytes in (read) buffer
+    /// - New length less than client position and greater than server position
+    /// </summary>
+    [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<byte[], SftpFileAttributes>((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<byte>.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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null), Times.Once);
+            SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
+        }
+    }
+}

+ 193 - 0
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
+{
+    /// <summary>
+    /// - In write mode
+    /// - Bytes in (write) buffer
+    /// - New length greater than client position and greater than server position
+    /// </summary>
+    [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<byte[]>(), 0, _writeBytes.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[], SftpFileAttributes>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null), Times.Once);
+            SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(5));
+        }
+    }
+}

+ 194 - 0
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
+{
+    /// <summary>
+    /// - In write mode
+    /// - Bytes in (write) buffer
+    /// - New length less than client position and less than server position
+    /// </summary>
+    [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<byte[]>(), 0, _writeBytes.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[], SftpFileAttributes>((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<byte>.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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null))
+                           .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((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<byte[]>(), 0, bytesToWrite.Length, It.IsAny<AutoResetEvent>(), null), Times.Once);
+            SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(5));
+        }
+    }
+}

+ 38 - 0
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<TKey, TValue>(IDictionary<TKey, TValue> expected, IDictionary<TKey, TValue> 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));
+                }
+            }
+        }
+    }
+}

+ 33 - 0
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;
         }
+
+        /// <summary>
+        /// Creates a deep clone of the current instance.
+        /// </summary>
+        /// <returns>
+        /// A deep clone of the current instance.
+        /// </returns>
+        internal static SftpFileAttributes Clone(this SftpFileAttributes value)
+        {
+            Dictionary<string, string> clonedExtensions;
+
+            if (value.Extensions != null)
+            {
+                clonedExtensions = new Dictionary<string, string>(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);
+        }
     }
 }

+ 7 - 4
src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs

@@ -14,6 +14,11 @@ namespace Renci.SshNet.Tests.Common
         private uint? _permissions;
         private IDictionary<string, string> _extensions;
 
+        public SftpFileAttributesBuilder()
+        {
+            _extensions = new Dictionary<string, string>();
+        }
+
         public SftpFileAttributesBuilder WithLastAccessTime(DateTime lastAccessTime)
         {
             _lastAccessTime = lastAccessTime;
@@ -50,9 +55,9 @@ namespace Renci.SshNet.Tests.Common
             return this;
         }
 
-        public SftpFileAttributesBuilder WithExtensions(IDictionary<string, string> 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);
         }
-
-
     }
 }

+ 5 - 0
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -444,6 +444,10 @@
     <Compile Include="Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_NoBuffering.cs" />
     <Compile Include="Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_ReadBuffer.cs" />
     <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_Closed.cs" />
+    <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs" />
+    <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs" />
+    <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs" />
+    <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs" />
     <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_Disposed.cs" />
     <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_SessionNotOpen.cs" />
     <Compile Include="Classes\Sftp\SftpFileStreamTest_SetLength_SessionOpen_FIleAccessRead.cs" />
@@ -513,6 +517,7 @@
     <Compile Include="Classes\SubsystemSession_SendData_Disposed.cs" />
     <Compile Include="Classes\SubsystemSession_SendData_NeverConnected.cs" />
     <Compile Include="Common\AsyncSocketListener.cs" />
+    <Compile Include="Common\DictionaryAssert.cs" />
     <Compile Include="Common\Extensions.cs" />
     <Compile Include="Common\HttpProxyStub.cs" />
     <Compile Include="Common\HttpRequest.cs" />