using System; using System.Globalization; using System.IO; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Sftp; using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { [TestClass] public class SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize { private Mock _sftpSessionMock; private string _path; private SftpFileStream _sftpFileStream; private byte[] _handle; private SftpFileAttributes _fileAttributes; private uint _bufferSize; private uint _readBufferSize; private uint _writeBufferSize; private byte[] _data; private int _count; private int _offset; private MockSequence _sequence; private Random _random; private uint _expectedWrittenByteCount; private int _expectedBufferedByteCount; private byte[] _expectedBufferedBytes; [TestInitialize] public void Setup() { Arrange(); Act(); } [TestCleanup] public void TearDown() { if (_sftpSessionMock != null) { // allow Dispose to complete successfully _sftpSessionMock.InSequence(_sequence) .Setup(p => p.IsOpen) .Returns(true); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null)); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestClose(_handle)); } } protected void Arrange() { _random = new Random(); _path = _random.Next().ToString(CultureInfo.InvariantCulture); _handle = new[] {(byte) _random.Next(byte.MinValue, byte.MaxValue)}; _fileAttributes = SftpFileAttributes.Empty; _bufferSize = (uint) _random.Next(1, 1000); _readBufferSize = (uint) _random.Next(0, 1000); _writeBufferSize = (uint) _random.Next(500, 1000); _data = new byte[(_writeBufferSize * 2) + 15]; _random.NextBytes(_data); _offset = _random.Next(1, 5); // to get multiple SSH_FXP_WRITE messages (and verify the offset is updated correctly), we make sure // the number of bytes to write is at least two times the write buffer size; we write a few extra bytes to // ensure the buffer is not empty after the writes so we can verify whether Length, Dispose and Flush // flush the buffer _count = ((int) _writeBufferSize*2) + _random.Next(1, 5); _expectedWrittenByteCount = (2 * _writeBufferSize); _expectedBufferedByteCount = (int)(_count - _expectedWrittenByteCount); _expectedBufferedBytes = _data.Take(_offset + (int)_expectedWrittenByteCount, _expectedBufferedByteCount); _sftpSessionMock = new Mock(MockBehavior.Strict); _sequence = new MockSequence(); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Truncate, true)) .Returns(_handle); _sftpSessionMock.InSequence(_sequence).Setup(p => p.RequestFStat(_handle)).Returns(_fileAttributes); _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.RequestWrite(_handle, 0, _data, _offset, (int) _writeBufferSize, It.IsAny(), null)); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestWrite(_handle, _writeBufferSize, _data, _offset + (int) _writeBufferSize, (int)_writeBufferSize, It.IsAny(), null)); _sftpFileStream = new SftpFileStream(_sftpSessionMock.Object, _path, FileMode.Create, FileAccess.Write, (int) _bufferSize); } protected void Act() { _sftpFileStream.Write(_data, _offset, _count); } [TestMethod] public void RequestWriteOnSftpSessionShouldBeInvokedTwice() { _sftpSessionMock.Verify(p => p.RequestWrite(_handle, 0, _data, _offset, (int)_writeBufferSize, It.IsAny(), null), Times.Once); _sftpSessionMock.Verify(p => p.RequestWrite(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, It.IsAny(), null), Times.Once); } [TestMethod] public void PositionShouldBeNumberOfBytesWrittenToFileAndNUmberOfBytesInBuffer() { _sftpSessionMock.InSequence(_sequence) .Setup(p => p.IsOpen) .Returns(true); Assert.AreEqual(_count, _sftpFileStream.Position); } [TestMethod] public void LengthShouldFlushBufferAndReturnSizeOfFile() { var lengthFileAttributes = new SftpFileAttributes(DateTime.Now, DateTime.Now, _random.Next(), _random.Next(), _random.Next(), (uint) _random.Next(0, int.MaxValue), null); byte[] actualFlushedData = null; _sftpSessionMock.InSequence(_sequence) .Setup(p => p.IsOpen) .Returns(true); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null)) .Callback>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length)); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestFStat(_handle)) .Returns(lengthFileAttributes); Assert.AreEqual(lengthFileAttributes.Size, _sftpFileStream.Length); Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes)); _sftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null), Times.Once); } [TestMethod] public void DisposeShouldFlushBufferAndCloseRequest() { byte[] actualFlushedData = null; _sftpSessionMock.InSequence(_sequence) .Setup(p => p.IsOpen) .Returns(true); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null)) .Callback>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length)); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestClose(_handle)); _sftpFileStream.Dispose(); Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes)); _sftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null), Times.Once); _sftpSessionMock.Verify(p => p.RequestClose(_handle), Times.Once); } [TestMethod] public void FlushShouldFlushBuffer() { byte[] actualFlushedData = null; _sftpSessionMock.InSequence(_sequence) .Setup(p => p.IsOpen) .Returns(true); _sftpSessionMock.InSequence(_sequence) .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null)) .Callback>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length)); _sftpFileStream.Flush(); Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes)); _sftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, It.IsAny(), null), Times.Once); } } }