Skip to content

Commit a9a38d7

Browse files
authored
Merge pull request #199 from sshnet/sftpfilestream
SftpFileStream: No longer track write position separately
2 parents bdec2d2 + 55e31b1 commit a9a38d7

15 files changed

+873
-88
lines changed

src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<GenerateDocumentationFile>true</GenerateDocumentationFile>
77
<AssemblyName>Renci.SshNet</AssemblyName>
88
<AssemblyOriginatorKeyFile>../Renci.SshNet.snk</AssemblyOriginatorKeyFile>
9+
<LangVersion>5</LangVersion>
910
<SignAssembly>true</SignAssembly>
1011
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
1112
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>

src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,8 +1202,29 @@
12021202
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_ReadByte_ReadMode_NoDataInWriteBufferAndNoDataInReadBuffer_LessDataThanReadBufferSizeAvailable.cs">
12031203
<Link>Classes\Sftp\SftpFileStreamTest_ReadByte_ReadMode_NoDataInWriteBufferAndNoDataInReadBuffer_LessDataThanReadBufferSizeAvailable.cs</Link>
12041204
</Compile>
1205-
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesThanCount.cs">
1206-
<Link>Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesThanCount.cs</Link>
1205+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs">
1206+
<Link>Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs</Link>
1207+
</Compile>
1208+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs">
1209+
<Link>Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs</Link>
1210+
</Compile>
1211+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs">
1212+
<Link>Classes\Sftp\SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs</Link>
1213+
</Compile>
1214+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetNegative.cs">
1215+
<Link>Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetNegative.cs</Link>
1216+
</Compile>
1217+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetPositive.cs">
1218+
<Link>Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetPositive.cs</Link>
1219+
</Compile>
1220+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetZero.cs">
1221+
<Link>Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffsetZero.cs</Link>
1222+
</Compile>
1223+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_NoBuffering.cs">
1224+
<Link>Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_NoBuffering.cs</Link>
1225+
</Compile>
1226+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_ReadBuffer.cs">
1227+
<Link>Classes\Sftp\SftpFileStreamTest_Seek_PositionedAtMiddleOfStream_OriginBeginAndOffsetZero_ReadBuffer.cs</Link>
12071228
</Compile>
12081229
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_SetLength_Closed.cs">
12091230
<Link>Classes\Sftp\SftpFileStreamTest_SetLength_Closed.cs</Link>
@@ -1223,6 +1244,9 @@
12231244
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs">
12241245
<Link>Classes\Sftp\SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs</Link>
12251246
</Compile>
1247+
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs">
1248+
<Link>Classes\Sftp\SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs</Link>
1249+
</Compile>
12261250
<Compile Include="..\Renci.SshNet.Tests\Classes\Sftp\SftpFileSystemInformationTest.cs">
12271251
<Link>Classes\Sftp\SftpFileSystemInformationTest.cs</Link>
12281252
</Compile>

src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ public class BaseClientTest_Connected_KeepAlivesNotSentConcurrently
1111
{
1212
private Mock<IServiceFactory> _serviceFactoryMock;
1313
private Mock<ISession> _sessionMock;
14+
private MockSequence _mockSequence;
1415
private BaseClient _client;
1516
private ConnectionInfo _connectionInfo;
17+
private ManualResetEvent _keepAliveSent;
1618

1719
[TestInitialize]
1820
public void Setup()
@@ -24,20 +26,33 @@ public void Setup()
2426
[TestCleanup]
2527
public void Cleanup()
2628
{
29+
if (_client != null)
30+
{
31+
_sessionMock.InSequence(_mockSequence).Setup(p => p.OnDisconnecting());
32+
_sessionMock.InSequence(_mockSequence).Setup(p => p.Dispose());
33+
_client.Dispose();
34+
}
2735
}
2836

2937
protected void Arrange()
3038
{
3139
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
40+
_keepAliveSent = new ManualResetEvent(false);
3241

3342
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
3443
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
3544

36-
_serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo)).Returns(_sessionMock.Object);
37-
_sessionMock.Setup(p => p.Connect());
38-
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
45+
_mockSequence = new MockSequence();
46+
47+
_serviceFactoryMock.InSequence(_mockSequence).Setup(p => p.CreateSession(_connectionInfo)).Returns(_sessionMock.Object);
48+
_sessionMock.InSequence(_mockSequence).Setup(p => p.Connect());
49+
_sessionMock.InSequence(_mockSequence).Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
3950
.Returns(true)
40-
.Callback(() => Thread.Sleep(300));
51+
.Callback(() =>
52+
{
53+
Thread.Sleep(300);
54+
_keepAliveSent.Set();
55+
});
4156

4257
_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object)
4358
{
@@ -51,6 +66,12 @@ protected void Act()
5166
// should keep-alive message be sent concurrently, then multiple keep-alive
5267
// message would be sent during this sleep period
5368
Thread.Sleep(200);
69+
70+
// disable further keep-alives
71+
_client.KeepAliveInterval = Session.InfiniteTimeSpan;
72+
73+
// wait until keep-alive has been sent at least once
74+
Assert.IsTrue(_keepAliveSent.WaitOne(500));
5475
}
5576

5677
[TestMethod]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
using System;
2+
using System.IO;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using Moq;
5+
using Renci.SshNet.Common;
6+
using Renci.SshNet.Sftp;
7+
8+
namespace Renci.SshNet.Tests.Classes.Sftp
9+
{
10+
[TestClass]
11+
public class SftpFileStreamTest_Read_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize : SftpFileStreamTestBase
12+
{
13+
private string _path;
14+
private SftpFileStream _target;
15+
private byte[] _handle;
16+
private uint _bufferSize;
17+
private uint _readBufferSize;
18+
private uint _writeBufferSize;
19+
private int _actual;
20+
private byte[] _buffer;
21+
private byte[] _serverData1;
22+
private byte[] _serverData2;
23+
private int _serverData1Length;
24+
private int _serverData2Length;
25+
private int _numberOfBytesToRead;
26+
27+
protected override void SetupData()
28+
{
29+
base.SetupData();
30+
31+
var random = new Random();
32+
_path = random.Next().ToString();
33+
_handle = GenerateRandom(5, random);
34+
_bufferSize = (uint)random.Next(1, 1000);
35+
_readBufferSize = 20;
36+
_writeBufferSize = 500;
37+
38+
_numberOfBytesToRead = (int) _readBufferSize + 5; // greather than read buffer size
39+
_buffer = new byte[_numberOfBytesToRead];
40+
_serverData1Length = (int) _readBufferSize; // equal to read buffer size
41+
_serverData1 = GenerateRandom(_serverData1Length, random);
42+
_serverData2Length = (int) _readBufferSize; // equal to read buffer size
43+
_serverData2 = GenerateRandom(_serverData2Length, random);
44+
45+
Assert.IsTrue(_serverData1Length < _numberOfBytesToRead && _serverData1Length == _readBufferSize);
46+
}
47+
48+
protected override void SetupMocks()
49+
{
50+
SftpSessionMock.InSequence(MockSequence)
51+
.Setup(p => p.RequestOpen(_path, Flags.Read, false))
52+
.Returns(_handle);
53+
SftpSessionMock.InSequence(MockSequence)
54+
.Setup(p => p.CalculateOptimalReadLength(_bufferSize))
55+
.Returns(_readBufferSize);
56+
SftpSessionMock.InSequence(MockSequence)
57+
.Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle))
58+
.Returns(_writeBufferSize);
59+
SftpSessionMock.InSequence(MockSequence)
60+
.Setup(p => p.IsOpen)
61+
.Returns(true);
62+
SftpSessionMock.InSequence(MockSequence)
63+
.Setup(p => p.RequestRead(_handle, 0UL, _readBufferSize))
64+
.Returns(_serverData1);
65+
SftpSessionMock.InSequence(MockSequence)
66+
.Setup(p => p.RequestRead(_handle, (ulong)_serverData1.Length, _readBufferSize))
67+
.Returns(_serverData2);
68+
}
69+
70+
[TestCleanup]
71+
public void TearDown()
72+
{
73+
SftpSessionMock.InSequence(MockSequence)
74+
.Setup(p => p.RequestClose(_handle));
75+
}
76+
77+
protected override void Arrange()
78+
{
79+
base.Arrange();
80+
81+
_target = new SftpFileStream(SftpSessionMock.Object,
82+
_path,
83+
FileMode.Open,
84+
FileAccess.Read,
85+
(int)_bufferSize);
86+
}
87+
88+
protected override void Act()
89+
{
90+
_actual = _target.Read(_buffer, 0, _numberOfBytesToRead);
91+
}
92+
93+
[TestMethod]
94+
public void ReadShouldHaveReturnedTheNumberOfBytesRequested()
95+
{
96+
Assert.AreEqual(_numberOfBytesToRead, _actual);
97+
}
98+
99+
[TestMethod]
100+
public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBuffer()
101+
{
102+
Assert.IsTrue(_serverData1.IsEqualTo(_buffer.Take(_serverData1Length)));
103+
104+
var bytesWrittenFromSecondRead = _numberOfBytesToRead - _serverData1Length;
105+
Assert.IsTrue(_serverData2.Take(bytesWrittenFromSecondRead).IsEqualTo(_buffer.Take(_serverData1Length, bytesWrittenFromSecondRead)));
106+
}
107+
108+
[TestMethod]
109+
public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer()
110+
{
111+
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
112+
113+
Assert.AreEqual(_actual, _target.Position);
114+
115+
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2));
116+
}
117+
118+
[TestMethod]
119+
public void ReadShouldReturnAllRemaningBytesFromReadBufferWhenCountIsEqualToNumberOfRemainingBytes()
120+
{
121+
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
122+
123+
var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead;
124+
125+
_buffer = new byte[numberOfBytesRemainingInReadBuffer];
126+
127+
var actual = _target.Read(_buffer, 0, _buffer.Length);
128+
129+
Assert.AreEqual(_buffer.Length, actual);
130+
Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, _buffer.Length).IsEqualTo(_buffer));
131+
132+
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2));
133+
}
134+
135+
[TestMethod]
136+
public void ReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes()
137+
{
138+
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
139+
SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize)).Returns(Array<byte>.Empty);
140+
141+
var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead;
142+
143+
_buffer = new byte[numberOfBytesRemainingInReadBuffer + 1];
144+
145+
var actual = _target.Read(_buffer, 0, _buffer.Length);
146+
147+
Assert.AreEqual(numberOfBytesRemainingInReadBuffer, actual);
148+
Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, numberOfBytesRemainingInReadBuffer).IsEqualTo(_buffer.Take(numberOfBytesRemainingInReadBuffer)));
149+
Assert.AreEqual(0, _buffer[numberOfBytesRemainingInReadBuffer]);
150+
151+
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2));
152+
SftpSessionMock.Verify(p => p.RequestRead(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize));
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)