SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Threading;
  5. using Microsoft.VisualStudio.TestTools.UnitTesting;
  6. using Moq;
  7. using Renci.SshNet.Common;
  8. using Renci.SshNet.Sftp;
  9. using Renci.SshNet.Sftp.Responses;
  10. namespace Renci.SshNet.Tests.Classes.Sftp
  11. {
  12. [TestClass]
  13. public class SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize : SftpFileStreamTestBase
  14. {
  15. private SftpFileStream _target;
  16. private string _path;
  17. private byte[] _handle;
  18. private uint _bufferSize;
  19. private uint _readBufferSize;
  20. private uint _writeBufferSize;
  21. private byte[] _data;
  22. private int _count;
  23. private int _offset;
  24. private Random _random;
  25. private uint _expectedWrittenByteCount;
  26. private int _expectedBufferedByteCount;
  27. private byte[] _expectedBufferedBytes;
  28. protected override void SetupData()
  29. {
  30. base.SetupData();
  31. _random = new Random();
  32. _path = _random.Next().ToString(CultureInfo.InvariantCulture);
  33. _handle = GenerateRandom(5, _random);
  34. _bufferSize = (uint)_random.Next(1, 1000);
  35. _readBufferSize = (uint) _random.Next(0, 1000);
  36. _writeBufferSize = (uint) _random.Next(500, 1000);
  37. _data = new byte[(_writeBufferSize * 2) + 15];
  38. _random.NextBytes(_data);
  39. _offset = _random.Next(1, 5);
  40. // to get multiple SSH_FXP_WRITE messages (and verify the offset is updated correctly), we make sure
  41. // the number of bytes to write is at least two times the write buffer size; we write a few extra bytes to
  42. // ensure the buffer is not empty after the writes so we can verify whether Length, Dispose and Flush
  43. // flush the buffer
  44. _count = ((int) _writeBufferSize * 2) + _random.Next(1, 5);
  45. _expectedWrittenByteCount = (2 * _writeBufferSize);
  46. _expectedBufferedByteCount = (int)(_count - _expectedWrittenByteCount);
  47. _expectedBufferedBytes = _data.Take(_offset + (int)_expectedWrittenByteCount, _expectedBufferedByteCount);
  48. }
  49. protected override void SetupMocks()
  50. {
  51. SftpSessionMock.InSequence(MockSequence)
  52. .Setup(p => p.RequestOpen(_path, Flags.Write | Flags.Truncate, true))
  53. .Returns(_handle);
  54. SftpSessionMock.InSequence(MockSequence)
  55. .Setup(p => p.CalculateOptimalReadLength(_bufferSize))
  56. .Returns(_readBufferSize);
  57. SftpSessionMock.InSequence(MockSequence)
  58. .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
  59. .Returns(_writeBufferSize);
  60. SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
  61. SftpSessionMock.InSequence(MockSequence)
  62. .Setup(p => p.RequestWrite(_handle, 0, _data, _offset, (int)_writeBufferSize, It.IsAny<AutoResetEvent>(), null));
  63. SftpSessionMock.InSequence(MockSequence)
  64. .Setup(p => p.RequestWrite(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, It.IsAny<AutoResetEvent>(), null));
  65. }
  66. [TestCleanup]
  67. public void TearDown()
  68. {
  69. if (SftpSessionMock != null)
  70. {
  71. // allow Dispose to complete successfully
  72. SftpSessionMock.InSequence(MockSequence)
  73. .Setup(p => p.IsOpen)
  74. .Returns(true);
  75. SftpSessionMock.InSequence(MockSequence)
  76. .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null));
  77. SftpSessionMock.InSequence(MockSequence)
  78. .Setup(p => p.RequestClose(_handle));
  79. }
  80. }
  81. protected override void Arrange()
  82. {
  83. base.Arrange();
  84. _target = new SftpFileStream(SftpSessionMock.Object,
  85. _path,
  86. FileMode.Create,
  87. FileAccess.Write,
  88. (int) _bufferSize);
  89. }
  90. protected override void Act()
  91. {
  92. _target.Write(_data, _offset, _count);
  93. }
  94. [TestMethod]
  95. public void RequestWriteOnSftpSessionShouldBeInvokedTwice()
  96. {
  97. SftpSessionMock.Verify(p => p.RequestWrite(_handle, 0, _data, _offset, (int)_writeBufferSize, It.IsAny<AutoResetEvent>(), null), Times.Once);
  98. SftpSessionMock.Verify(p => p.RequestWrite(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, It.IsAny<AutoResetEvent>(), null), Times.Once);
  99. }
  100. [TestMethod]
  101. public void PositionShouldBeNumberOfBytesWrittenToFileAndNUmberOfBytesInBuffer()
  102. {
  103. SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
  104. Assert.AreEqual(_count, _target.Position);
  105. }
  106. [TestMethod]
  107. public void LengthShouldFlushBufferAndReturnSizeOfFile()
  108. {
  109. var lengthFileAttributes = new SftpFileAttributes(DateTime.UtcNow,
  110. DateTime.UtcNow,
  111. 123,
  112. 456,
  113. 789,
  114. 7,
  115. null);
  116. byte[] actualFlushedData = null;
  117. SftpSessionMock.InSequence(MockSequence)
  118. .Setup(p => p.IsOpen)
  119. .Returns(true);
  120. SftpSessionMock.InSequence(MockSequence)
  121. .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null))
  122. .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length));
  123. SftpSessionMock.InSequence(MockSequence)
  124. .Setup(p => p.RequestFStat(_handle, true))
  125. .Returns(lengthFileAttributes);
  126. Assert.AreEqual(lengthFileAttributes.Size, _target.Length);
  127. Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes));
  128. SftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null), Times.Once);
  129. }
  130. [TestMethod]
  131. public void LengthShouldThrowIOExceptionIfRequestFStatReturnsNull()
  132. {
  133. const SftpFileAttributes lengthFileAttributes = null;
  134. byte[] actualFlushedData = null;
  135. SftpSessionMock.InSequence(MockSequence)
  136. .Setup(p => p.IsOpen)
  137. .Returns(true);
  138. SftpSessionMock.InSequence(MockSequence)
  139. .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null))
  140. .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length));
  141. SftpSessionMock.InSequence(MockSequence)
  142. .Setup(p => p.RequestFStat(_handle, true))
  143. .Returns(lengthFileAttributes);
  144. try
  145. {
  146. var length = _target.Length;
  147. Assert.Fail("Length should have failed, but returned: " + length + ".");
  148. }
  149. catch (IOException ex)
  150. {
  151. Assert.IsNull(ex.InnerException);
  152. Assert.AreEqual("Seek operation failed.", ex.Message);
  153. }
  154. Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes));
  155. SftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null), Times.Once);
  156. }
  157. [TestMethod]
  158. public void DisposeShouldFlushBufferAndCloseRequest()
  159. {
  160. byte[] actualFlushedData = null;
  161. SftpSessionMock.InSequence(MockSequence)
  162. .Setup(p => p.IsOpen)
  163. .Returns(true);
  164. SftpSessionMock.InSequence(MockSequence)
  165. .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null))
  166. .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length));
  167. SftpSessionMock.InSequence(MockSequence)
  168. .Setup(p => p.RequestClose(_handle));
  169. _target.Dispose();
  170. Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes));
  171. SftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null), Times.Once);
  172. SftpSessionMock.Verify(p => p.RequestClose(_handle), Times.Once);
  173. }
  174. [TestMethod]
  175. public void FlushShouldFlushBuffer()
  176. {
  177. byte[] actualFlushedData = null;
  178. SftpSessionMock.InSequence(MockSequence)
  179. .Setup(p => p.IsOpen)
  180. .Returns(true);
  181. SftpSessionMock.InSequence(MockSequence)
  182. .Setup(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null))
  183. .Callback<byte[], ulong, byte[], int, int, AutoResetEvent, Action<SftpStatusResponse>>((handle, serverFileOffset, data, offset, length, wait, writeCompleted) => actualFlushedData = data.Take(offset, length));
  184. _target.Flush();
  185. Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes));
  186. SftpSessionMock.Verify(p => p.RequestWrite(_handle, _expectedWrittenByteCount, It.IsAny<byte[]>(), 0, _expectedBufferedByteCount, It.IsAny<AutoResetEvent>(), null), Times.Once);
  187. }
  188. }
  189. }