Explorar o código

Use ReadExactly in ReadAllBytes and yield in ReadLines (#1681)

* Use ReadExactly in ReadAllBytes and yield in ReadLines

* on second thought, open the file lazily in ReadLines
Rob Hague hai 2 meses
pai
achega
605ee7fe23

+ 21 - 0
src/Renci.SshNet/Common/Extensions.cs

@@ -1,6 +1,9 @@
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+#if !NET
+using System.IO;
+#endif
 using System.Net;
 using System.Net.Sockets;
 using System.Numerics;
@@ -388,6 +391,24 @@ namespace Renci.SshNet.Common
             Array.Copy(arraySegment.Array, arraySegment.Offset, array, 0, arraySegment.Count);
             return array;
         }
+
+#pragma warning disable CA1859 // Use concrete types for improved performance
+        internal static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count)
+#pragma warning restore CA1859
+        {
+            var totalRead = 0;
+
+            while (totalRead < count)
+            {
+                var read = stream.Read(buffer, offset + totalRead, count - totalRead);
+                if (read == 0)
+                {
+                    throw new EndOfStreamException();
+                }
+
+                totalRead += read;
+            }
+        }
 #endif
     }
 }

+ 41 - 35
src/Renci.SshNet/SftpClient.cs

@@ -5,6 +5,7 @@ using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.IO;
+using System.Linq;
 using System.Net;
 using System.Runtime.CompilerServices;
 using System.Runtime.ExceptionServices;
@@ -1727,7 +1728,7 @@ namespace Renci.SshNet
             using (var stream = OpenRead(path))
             {
                 var buffer = new byte[stream.Length];
-                _ = stream.Read(buffer, 0, buffer.Length);
+                stream.ReadExactly(buffer, 0, buffer.Length);
                 return buffer;
             }
         }
@@ -1760,23 +1761,7 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
         public string[] ReadAllLines(string path, Encoding encoding)
         {
-            /*
-             * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
-             * for the SftpFileStream. We may want to revisit this later.
-             */
-
-            var lines = new List<string>();
-
-            using (var stream = new StreamReader(OpenRead(path), encoding))
-            {
-                string? line;
-                while ((line = stream.ReadLine()) != null)
-                {
-                    lines.Add(line);
-                }
-            }
-
-            return lines.ToArray();
+            return ReadLines(path, encoding).ToArray();
         }
 
         /// <summary>
@@ -1807,15 +1792,8 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
         public string ReadAllText(string path, Encoding encoding)
         {
-            /*
-             * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
-             * for the SftpFileStream. We may want to revisit this later.
-             */
-
-            using (var stream = new StreamReader(OpenRead(path), encoding))
-            {
-                return stream.ReadToEnd();
-            }
+            using var sr = new StreamReader(OpenRead(path), encoding);
+            return sr.ReadToEnd();
         }
 
         /// <summary>
@@ -1825,12 +1803,16 @@ namespace Renci.SshNet
         /// <returns>
         /// The lines of the file.
         /// </returns>
-        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
-        /// <exception cref="SshConnectionException">Client is not connected.</exception>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        /// <remarks>
+        /// The lines are enumerated lazily. The opening of the file and any resulting exceptions occur
+        /// upon enumeration.
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>. Thrown eagerly.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected upon enumeration.</exception>
+        /// <exception cref="ObjectDisposedException">The return value is enumerated after the client is disposed.</exception>
         public IEnumerable<string> ReadLines(string path)
         {
-            return ReadAllLines(path);
+            return ReadLines(path, Encoding.UTF8);
         }
 
         /// <summary>
@@ -1841,12 +1823,36 @@ namespace Renci.SshNet
         /// <returns>
         /// The lines of the file.
         /// </returns>
-        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
-        /// <exception cref="SshConnectionException">Client is not connected.</exception>
-        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        /// <remarks>
+        /// The lines are enumerated lazily. The opening of the file and any resulting exceptions occur
+        /// upon enumeration.
+        /// </remarks>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>. Thrown eagerly.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected upon enumeration.</exception>
+        /// <exception cref="ObjectDisposedException">The return value is enumerated after the client is disposed.</exception>
         public IEnumerable<string> ReadLines(string path, Encoding encoding)
         {
-            return ReadAllLines(path, encoding);
+            // We allow this usage exception to throw eagerly...
+            ThrowHelper.ThrowIfNull(path);
+
+            // ... but other exceptions will throw lazily i.e. inside the state machine created
+            // by yield. We could choose to open the file eagerly as well in order to throw
+            // file-related exceptions eagerly (matching what File.ReadLines does), but this
+            // complicates double enumeration, and introduces the problem that File.ReadLines
+            // has whereby the file is not closed if the return value is not enumerated.
+            return Enumerate();
+
+            IEnumerable<string> Enumerate()
+            {
+                using var sr = new StreamReader(OpenRead(path), encoding);
+
+                string? line;
+
+                while ((line = sr.ReadLine()) != null)
+                {
+                    yield return line;
+                }
+            }
         }
 
         /// <summary>

+ 118 - 341
test/Renci.SshNet.IntegrationTests/SftpTests.cs

@@ -215,7 +215,7 @@ namespace Renci.SshNet.IntegrationTests
                     }
 
                     var actualContent1 = client.ReadAllBytes(remoteFile);
-                    Assert.IsTrue(newContent1Bytes.IsEqualTo(actualContent1));
+                    CollectionAssert.AreEqual(newContent1Bytes, actualContent1);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -228,7 +228,7 @@ namespace Renci.SshNet.IntegrationTests
                     }
 
                     var actualContent2 = client.ReadAllBytes(remoteFile);
-                    Assert.IsTrue(newContent2Bytes.IsEqualTo(actualContent2));
+                    CollectionAssert.AreEqual(newContent2Bytes, actualContent2);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -721,12 +721,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -802,12 +798,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -847,13 +839,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -928,12 +915,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -977,12 +960,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -1059,12 +1038,8 @@ namespace Renci.SshNet.IntegrationTests
                         sw.Write(contentToAppend);
                     }
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -1106,12 +1081,8 @@ namespace Renci.SshNet.IntegrationTests
                     }
 
                     // verify that original content is left untouched
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
 
                     // write content that is less bytes than original content
                     using (var sw = client.CreateText(remoteFile))
@@ -1123,12 +1094,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -1219,12 +1186,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile);
                     Assert.AreEqual(initialContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -1266,12 +1229,8 @@ namespace Renci.SshNet.IntegrationTests
                     }
 
                     // verify that original content is left untouched
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
 
                     // write content that is less bytes than original content
                     using (var sw = client.CreateText(remoteFile, encoding))
@@ -1283,12 +1242,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedContentBytes.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -1381,12 +1336,8 @@ namespace Renci.SshNet.IntegrationTests
                     var text = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(initialContent, text);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -1438,41 +1389,6 @@ namespace Renci.SshNet.IntegrationTests
             }
         }
 
-        [TestMethod]
-        public void Sftp_ReadAllBytes_ExistingFile()
-        {
-            var encoding = GetRandomEncoding();
-            var content = "\u0100ert & Ann";
-            var contentBytes = GetBytesWithPreamble(content, encoding);
-
-            using (var client = new SftpClient(_connectionInfoFactory.Create()))
-            {
-                client.Connect();
-
-                var remoteFile = GenerateUniqueRemoteFileName();
-
-                if (client.Exists(remoteFile))
-                {
-                    client.DeleteFile(remoteFile);
-                }
-
-                try
-                {
-                    client.WriteAllText(remoteFile, content, encoding);
-
-                    var actualBytes = client.ReadAllBytes(remoteFile);
-                    Assert.IsTrue(contentBytes.IsEqualTo(actualBytes));
-                }
-                finally
-                {
-                    if (client.Exists(remoteFile))
-                    {
-                        client.DeleteFile(remoteFile);
-                    }
-                }
-            }
-        }
-
         [TestMethod]
         public void Sftp_ReadAllBytes_FileDoesNotExist()
         {
@@ -1541,19 +1457,10 @@ namespace Renci.SshNet.IntegrationTests
 
                     var actualLines = client.ReadAllLines(remoteFile);
                     Assert.IsNotNull(actualLines);
-                    Assert.AreEqual(lines.Length, actualLines.Length);
-
-                    for (var i = 0; i < lines.Length; i++)
-                    {
-                        Assert.AreEqual(lines[i], actualLines[i]);
-                    }
+                    CollectionAssert.AreEqual(lines, actualLines);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesBytes, actualBytes);
                 }
                 finally
                 {
@@ -1633,19 +1540,10 @@ namespace Renci.SshNet.IntegrationTests
 
                     var actualLines = client.ReadAllLines(remoteFile, encoding);
                     Assert.IsNotNull(actualLines);
-                    Assert.AreEqual(lines.Length, actualLines.Length);
+                    CollectionAssert.AreEqual(lines, actualLines);
 
-                    for (var i = 0; i < lines.Length; i++)
-                    {
-                        Assert.AreEqual(lines[i], actualLines[i]);
-                    }
-
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesBytes, actualBytes);
                 }
                 finally
                 {
@@ -1728,12 +1626,8 @@ namespace Renci.SshNet.IntegrationTests
                     var actualText = client.ReadAllText(remoteFile);
                     Assert.AreEqual(expectedText, actualText);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -1814,12 +1708,8 @@ namespace Renci.SshNet.IntegrationTests
                     var actualText = client.ReadAllText(remoteFile, encoding);
                     Assert.AreEqual(expectedText, actualText);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes, actualBytes);
                 }
                 finally
                 {
@@ -1899,15 +1789,9 @@ namespace Renci.SshNet.IntegrationTests
                     var actualLines = client.ReadLines(remoteFile);
                     Assert.IsNotNull(actualLines);
 
-                    var actualLinesEnum = actualLines.GetEnumerator();
-                    for (var i = 0; i < lines.Length; i++)
-                    {
-                        Assert.IsTrue(actualLinesEnum.MoveNext());
-                        var actualLine = actualLinesEnum.Current;
-                        Assert.AreEqual(lines[i], actualLine);
-                    }
-
-                    Assert.IsFalse(actualLinesEnum.MoveNext());
+                    // These two lines together test double enumeration.
+                    Assert.AreEqual(lines[0], actualLines.First());
+                    CollectionAssert.AreEqual(lines, actualLines.ToArray());
                 }
                 finally
                 {
@@ -1920,7 +1804,9 @@ namespace Renci.SshNet.IntegrationTests
         }
 
         [TestMethod]
-        public void Sftp_ReadLines_NoEncoding_FileDoesNotExist()
+        [DataRow(false)]
+        [DataRow(true)]
+        public void Sftp_ReadLines_FileDoesNotExist(bool encoding)
         {
             using (var client = new SftpClient(_connectionInfoFactory.Create()))
             {
@@ -1933,13 +1819,28 @@ namespace Renci.SshNet.IntegrationTests
                     client.DeleteFile(remoteFile);
                 }
 
+                // This exception should bubble up immediately
+                var nullEx = encoding
+                    ? Assert.Throws<ArgumentNullException>(() => client.ReadLines(null, GetRandomEncoding()))
+                    : Assert.Throws<ArgumentNullException>(() => client.ReadLines(null));
+
+                Assert.AreEqual("path", nullEx.ParamName);
+
                 try
                 {
-                    client.ReadLines(remoteFile);
-                    Assert.Fail();
-                }
-                catch (SftpPathNotFoundException ex)
-                {
+                    var ex = Assert.ThrowsExactly<SftpPathNotFoundException>(() =>
+                    {
+                        // The PathNotFound exception is permitted to bubble up only upon
+                        // enumerating.
+                        var lines = encoding
+                            ? client.ReadLines(remoteFile, GetRandomEncoding())
+                            : client.ReadLines(remoteFile);
+
+                        using var enumerator = lines.GetEnumerator();
+
+                        _ = enumerator.MoveNext();
+                    });
+
                     Assert.IsNull(ex.InnerException);
                     Assert.AreEqual("No such file", ex.Message);
 
@@ -1986,57 +1887,9 @@ namespace Renci.SshNet.IntegrationTests
                     var actualLines = client.ReadLines(remoteFile, encoding);
                     Assert.IsNotNull(actualLines);
 
-                    using (var actualLinesEnum = actualLines.GetEnumerator())
-                    {
-                        for (var i = 0; i < lines.Length; i++)
-                        {
-                            Assert.IsTrue(actualLinesEnum.MoveNext());
-
-                            var actualLine = actualLinesEnum.Current;
-                            Assert.AreEqual(lines[i], actualLine);
-                        }
-
-                        Assert.IsFalse(actualLinesEnum.MoveNext());
-                    }
-                }
-                finally
-                {
-                    if (client.Exists(remoteFile))
-                    {
-                        client.DeleteFile(remoteFile);
-                    }
-                }
-            }
-        }
-
-        [TestMethod]
-        public void Sftp_ReadLines_Encoding_FileDoesNotExist()
-        {
-            var encoding = GetRandomEncoding();
-
-            using (var client = new SftpClient(_connectionInfoFactory.Create()))
-            {
-                client.Connect();
-
-                var remoteFile = GenerateUniqueRemoteFileName();
-
-                if (client.Exists(remoteFile))
-                {
-                    client.DeleteFile(remoteFile);
-                }
-
-                try
-                {
-                    client.ReadLines(remoteFile, encoding);
-                    Assert.Fail();
-                }
-                catch (SftpPathNotFoundException ex)
-                {
-                    Assert.IsNull(ex.InnerException);
-                    Assert.AreEqual("No such file", ex.Message);
-
-                    // ensure file was not created by us
-                    Assert.IsFalse(client.Exists(remoteFile));
+                    // These two lines together test double enumeration.
+                    Assert.AreEqual(lines[0], actualLines.First());
+                    CollectionAssert.AreEqual(lines, actualLines.ToArray());
                 }
                 finally
                 {
@@ -2117,7 +1970,7 @@ namespace Renci.SshNet.IntegrationTests
                     client.WriteAllBytes(remoteFile, newContent1);
 
                     var actualContent1 = client.ReadAllBytes(remoteFile);
-                    Assert.IsTrue(expectedContent1.IsEqualTo(actualContent1));
+                    CollectionAssert.AreEqual(expectedContent1, actualContent1);
 
                     #endregion Write less bytes than the initial content, overwriting part of that content
 
@@ -2126,7 +1979,7 @@ namespace Renci.SshNet.IntegrationTests
                     client.WriteAllBytes(remoteFile, newContent2);
 
                     var actualContent2 = client.ReadAllBytes(remoteFile);
-                    Assert.IsTrue(newContent2.IsEqualTo(actualContent2));
+                    CollectionAssert.AreEqual(newContent2, actualContent2);
 
                     #endregion Write less bytes than the initial content, overwriting part of that content
                 }
@@ -2160,12 +2013,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllBytes(remoteFile, content);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(content.IsEqualTo(actualBytes));
-                    }
+                    var actualContent1 = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(content, actualContent1);
                 }
                 finally
                 {
@@ -2253,12 +2102,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite1);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2266,12 +2111,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite2);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -2307,12 +2148,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllLines(remoteFile, linesToWrite);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesToWriteBytes, actualBytes);
                 }
                 finally
                 {
@@ -2399,12 +2236,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite1, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2412,12 +2245,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite2, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -2453,12 +2282,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllLines(remoteFile, linesToWrite, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesToWriteBytes, actualBytes);
                 }
                 finally
                 {
@@ -2541,12 +2366,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite1);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2554,12 +2375,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite2);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -2595,12 +2412,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllLines(remoteFile, linesToWrite);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesToWriteBytes, actualBytes);
                 }
                 finally
                 {
@@ -2685,12 +2498,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite1, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2698,12 +2507,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllLines(remoteFile, linesToWrite2, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -2739,12 +2544,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllLines(remoteFile, linesToWrite, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(linesToWriteBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(linesToWriteBytes, actualBytes);
                 }
                 finally
                 {
@@ -2828,12 +2629,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllText(remoteFile, newContent1);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2841,12 +2638,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllText(remoteFile, newContent2);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -2883,12 +2676,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllText(remoteFile, initialContent);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -2972,12 +2761,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllText(remoteFile, newContent1, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes1.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes1, actualBytes);
 
                     #endregion Write less bytes than the current content, overwriting part of that content
 
@@ -2985,12 +2770,8 @@ namespace Renci.SshNet.IntegrationTests
 
                     client.WriteAllText(remoteFile, newContent2, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(expectedBytes2.IsEqualTo(actualBytes));
-                    }
+                    actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(expectedBytes2, actualBytes);
 
                     #endregion Write more bytes than the current content, overwriting and appending to that content
                 }
@@ -3027,12 +2808,8 @@ namespace Renci.SshNet.IntegrationTests
                 {
                     client.WriteAllText(remoteFile, initialContent, encoding);
 
-                    using (var fs = client.OpenRead(remoteFile))
-                    {
-                        var actualBytes = new byte[fs.Length];
-                        _ = fs.Read(actualBytes, offset: 0, actualBytes.Length);
-                        Assert.IsTrue(initialContentBytes.IsEqualTo(actualBytes));
-                    }
+                    var actualBytes = client.ReadAllBytes(remoteFile);
+                    CollectionAssert.AreEqual(initialContentBytes, actualBytes);
                 }
                 finally
                 {
@@ -4630,7 +4407,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4674,7 +4451,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4711,7 +4488,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4751,7 +4528,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4876,7 +4653,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4918,7 +4695,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4958,7 +4735,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -4999,7 +4776,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -5086,7 +4863,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -5339,7 +5116,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -5381,7 +5158,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(readBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -5421,7 +5198,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());
@@ -5464,7 +5241,7 @@ namespace Renci.SshNet.IntegrationTests
 
                         var readBuffer = new byte[writeBuffer.Length];
                         Assert.AreEqual(writeBuffer.Length, fs.Read(readBuffer, offset: 0, readBuffer.Length));
-                        Assert.IsTrue(writeBuffer.IsEqualTo(readBuffer));
+                        CollectionAssert.AreEqual(writeBuffer, readBuffer);
 
                         // Ensure we've reached end of the stream
                         Assert.AreEqual(-1, fs.ReadByte());