Quellcode durchsuchen

Fix a few issues with ShellStream (#1322)

* Fix a few issues with ShellStream

The main change is to replace the Queue<byte> with a byte[] and a couple of
variables which index the start and end of the data. The remainder is mainly
slightly more careful locking semantics.

It also implements Expect(string) separately so that it can work on the bytes
and skip a lot of encoding work (this is where I wish ShellStream derived from
StreamReader).

One possibly contentious point: in fixing the Write behaviour I chose to
remove the "outgoing" buffer and immediately send the data across the channel.
Write(string) and WriteLine(string) were already doing this, and I felt it
was better to change Write(byte[]) to match rather than changing the string
methods.

* Integrate expectSize (as "windowSize" parameter)

* Rename "windowSize" to "lookback"

---------

Co-authored-by: Wojciech Nagórski <wojtpl2@gmail.com>
Rob Hague vor 1 Jahr
Ursprung
Commit
06af2ec6c0
29 geänderte Dateien mit 795 neuen und 1814 gelöschten Zeilen
  1. 38 0
      src/Renci.SshNet/Common/Extensions.cs
  2. 178 0
      src/Renci.SshNet/Common/TaskToAsyncResult.cs
  3. 0 22
      src/Renci.SshNet/ExpectAsyncResult.cs
  4. 1 3
      src/Renci.SshNet/IServiceFactory.cs
  5. 2 3
      src/Renci.SshNet/ServiceFactory.cs
  6. 419 332
      src/Renci.SshNet/ShellStream.cs
  7. 2 65
      src/Renci.SshNet/SshClient.cs
  8. 6 0
      test/.editorconfig
  9. 2 2
      test/Renci.SshNet.IntegrationBenchmarks/SshClientBenchmark.cs
  10. 12 22
      test/Renci.SshNet.IntegrationTests/SshTests.cs
  11. 1 4
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs
  12. 1 4
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs
  13. 1 4
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs
  14. 1 4
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs
  15. 1 4
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs
  16. 1 10
      test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_Success.cs
  17. 39 7
      test/Renci.SshNet.Tests/Classes/ShellStreamTest.cs
  18. 85 59
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_ReadExpect.cs
  19. 0 138
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteLessBytesThanBufferSize.cs
  20. 0 145
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs
  21. 0 132
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteNumberOfBytesEqualToBufferSize.cs
  22. 0 131
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs
  23. 0 138
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferFullAndWriteLessBytesThanBufferSize.cs
  24. 0 136
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferFullAndWriteZeroBytes.cs
  25. 0 144
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteLessBytesThanBufferCanContain.cs
  26. 0 152
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteMoreBytesThanBufferCanContain.cs
  27. 0 136
      test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteZeroBytes.cs
  28. 2 8
      test/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs
  29. 3 9
      test/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs

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

@@ -261,6 +261,44 @@ namespace Renci.SshNet.Common
             return value;
         }
 
+#if NETFRAMEWORK || NETSTANDARD2_0
+        public static int IndexOf(this byte[] array, byte[] value, int startIndex, int count)
+        {
+            if (value.Length > count)
+            {
+                return -1;
+            }
+
+            if (value.Length == 0)
+            {
+                return 0;
+            }
+
+            for (var i = startIndex; i < startIndex + count - value.Length + 1; i++)
+            {
+                if (MatchesAtIndex(i))
+                {
+                    return i - startIndex;
+                }
+            }
+
+            return -1;
+
+            bool MatchesAtIndex(int i)
+            {
+                for (var j = 0; j < value.Length; j++)
+                {
+                    if (array[i + j] != value[j])
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+        }
+#endif
+
         /// <summary>
         /// Pads with leading zeros if needed.
         /// </summary>

+ 178 - 0
src/Renci.SshNet/Common/TaskToAsyncResult.cs

@@ -0,0 +1,178 @@
+#pragma warning disable
+#if !NET8_0_OR_GREATER
+// Copied verbatim from https://github.com/dotnet/runtime/blob/78bd7debe6d8b454294c673c9cb969c6b8a14692/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs
+
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+
+namespace System.Threading.Tasks
+{
+    /// <summary>
+    /// Provides methods for using <see cref="Task"/> to implement the Asynchronous Programming Model
+    /// pattern based on "Begin" and "End" methods.
+    /// </summary>
+#if SYSTEM_PRIVATE_CORELIB
+    public
+#else
+    internal
+#endif
+    static class TaskToAsyncResult
+    {
+        /// <summary>Creates a new <see cref="IAsyncResult"/> from the specified <see cref="Task"/>, optionally invoking <paramref name="callback"/> when the task has completed.</summary>
+        /// <param name="task">The <see cref="Task"/> to be wrapped in an <see cref="IAsyncResult"/>.</param>
+        /// <param name="callback">The callback to be invoked upon <paramref name="task"/>'s completion. If <see langword="null"/>, no callback will be invoked.</param>
+        /// <param name="state">The state to be stored in the <see cref="IAsyncResult"/>.</param>
+        /// <returns>An <see cref="IAsyncResult"/> to represent the task's asynchronous operation. This instance will also be passed to <paramref name="callback"/> when it's invoked.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="task"/> is null.</exception>
+        /// <remarks>
+        /// In conjunction with the <see cref="End(IAsyncResult)"/> or <see cref="End{TResult}(IAsyncResult)"/> methods, this method may be used
+        /// to implement the Begin/End pattern (also known as the Asynchronous Programming Model pattern, or APM). It is recommended to not expose this pattern
+        /// in new code; the methods on <see cref="TaskToAsyncResult"/> are intended only to help implement such Begin/End methods when they must be exposed, for example
+        /// because a base class provides virtual methods for the pattern, or when they've already been exposed and must remain for compatibility.  These methods enable
+        /// implementing all of the core asynchronous logic via <see cref="Task"/>s and then easily implementing Begin/End methods around that functionality.
+        /// </remarks>
+        public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state)
+        {
+#if NET6_0_OR_GREATER
+            ArgumentNullException.ThrowIfNull(task);
+#else
+            if (task is null)
+            {
+                throw new ArgumentNullException(nameof(task));
+            }
+#endif
+
+            return new TaskAsyncResult(task, state, callback);
+        }
+
+        /// <summary>Waits for the <see cref="Task"/> wrapped by the <see cref="IAsyncResult"/> returned by <see cref="Begin"/> to complete.</summary>
+        /// <param name="asyncResult">The <see cref="IAsyncResult"/> for which to wait.</param>
+        /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="asyncResult"/> was not produced by a call to <see cref="Begin"/>.</exception>
+        /// <remarks>This will propagate any exception stored in the wrapped <see cref="Task"/>.</remarks>
+        public static void End(IAsyncResult asyncResult) =>
+            Unwrap(asyncResult).GetAwaiter().GetResult();
+
+        /// <summary>Waits for the <see cref="Task{TResult}"/> wrapped by the <see cref="IAsyncResult"/> returned by <see cref="Begin"/> to complete.</summary>
+        /// <typeparam name="TResult">The type of the result produced.</typeparam>
+        /// <param name="asyncResult">The <see cref="IAsyncResult"/> for which to wait.</param>
+        /// <returns>The result of the <see cref="Task{TResult}"/> wrapped by the <see cref="IAsyncResult"/>.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="asyncResult"/> was not produced by a call to <see cref="Begin"/>.</exception>
+        /// <remarks>This will propagate any exception stored in the wrapped <see cref="Task{TResult}"/>.</remarks>
+        public static TResult End<TResult>(IAsyncResult asyncResult) =>
+            Unwrap<TResult>(asyncResult).GetAwaiter().GetResult();
+
+        /// <summary>Extracts the underlying <see cref="Task"/> from an <see cref="IAsyncResult"/> created by <see cref="Begin"/>.</summary>
+        /// <param name="asyncResult">The <see cref="IAsyncResult"/> created by <see cref="Begin"/>.</param>
+        /// <returns>The <see cref="Task"/> wrapped by the <see cref="IAsyncResult"/>.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is null.</exception>
+        /// <exception cref="ArgumentException"><paramref name="asyncResult"/> was not produced by a call to <see cref="Begin"/>.</exception>
+        public static Task Unwrap(IAsyncResult asyncResult)
+        {
+#if NET6_0_OR_GREATER
+            ArgumentNullException.ThrowIfNull(asyncResult);
+#else
+            if (asyncResult is null)
+            {
+                throw new ArgumentNullException(nameof(asyncResult));
+            }
+#endif
+
+            if ((asyncResult as TaskAsyncResult)?._task is not Task task)
+            {
+                throw new ArgumentException(null, nameof(asyncResult));
+            }
+
+            return task;
+        }
+
+        /// <summary>Extracts the underlying <see cref="Task{TResult}"/> from an <see cref="IAsyncResult"/> created by <see cref="Begin"/>.</summary>
+        /// <typeparam name="TResult">The type of the result produced by the returned task.</typeparam>
+        /// <param name="asyncResult">The <see cref="IAsyncResult"/> created by <see cref="Begin"/>.</param>
+        /// <returns>The <see cref="Task{TResult}"/> wrapped by the <see cref="IAsyncResult"/>.</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is null.</exception>
+        /// <exception cref="ArgumentException">
+        /// <paramref name="asyncResult"/> was not produced by a call to <see cref="Begin"/>,
+        /// or the <see cref="Task{TResult}"/> provided to <see cref="Begin"/> was used a generic type parameter
+        /// that's different from the <typeparamref name="TResult"/> supplied to this call.
+        /// </exception>
+        public static Task<TResult> Unwrap<TResult>(IAsyncResult asyncResult)
+        {
+#if NET6_0_OR_GREATER
+            ArgumentNullException.ThrowIfNull(asyncResult);
+#else
+            if (asyncResult is null)
+            {
+                throw new ArgumentNullException(nameof(asyncResult));
+            }
+#endif
+
+            if ((asyncResult as TaskAsyncResult)?._task is not Task<TResult> task)
+            {
+                throw new ArgumentException(null, nameof(asyncResult));
+            }
+
+            return task;
+        }
+
+        /// <summary>Provides a simple <see cref="IAsyncResult"/> that wraps a <see cref="Task"/>.</summary>
+        /// <remarks>
+        /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state,
+        /// but that's very rare, in particular in a situation where someone cares about allocation, and always
+        /// using TaskAsyncResult simplifies things and enables additional optimizations.
+        /// </remarks>
+        private sealed class TaskAsyncResult : IAsyncResult
+        {
+            /// <summary>The wrapped Task.</summary>
+            internal readonly Task _task;
+            /// <summary>Callback to invoke when the wrapped task completes.</summary>
+            private readonly AsyncCallback? _callback;
+
+            /// <summary>Initializes the IAsyncResult with the Task to wrap and the associated object state.</summary>
+            /// <param name="task">The Task to wrap.</param>
+            /// <param name="state">The new AsyncState value.</param>
+            /// <param name="callback">Callback to invoke when the wrapped task completes.</param>
+            internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
+            {
+                Debug.Assert(task is not null);
+
+                _task = task;
+                AsyncState = state;
+
+                if (task.IsCompleted)
+                {
+                    // The task has already completed.  Treat this as synchronous completion.
+                    // Invoke the callback; no need to store it.
+                    CompletedSynchronously = true;
+                    callback?.Invoke(this);
+                }
+                else if (callback is not null)
+                {
+                    // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in
+                    // order to avoid running synchronously if the task has already completed by the time we get here but still run
+                    // synchronously as part of the task's completion if the task completes after (the more common case).
+                    _callback = callback;
+                    _task.ConfigureAwait(continueOnCapturedContext: false)
+                         .GetAwaiter()
+                         .OnCompleted(() => _callback.Invoke(this));
+                }
+            }
+
+            /// <inheritdoc/>
+            public object? AsyncState { get; }
+
+            /// <inheritdoc/>
+            public bool CompletedSynchronously { get; }
+
+            /// <inheritdoc/>
+            public bool IsCompleted => _task.IsCompleted;
+
+            /// <inheritdoc/>
+            public WaitHandle AsyncWaitHandle => ((IAsyncResult) _task).AsyncWaitHandle;
+        }
+    }
+}
+#endif

+ 0 - 22
src/Renci.SshNet/ExpectAsyncResult.cs

@@ -1,22 +0,0 @@
-using System;
-
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet
-{
-    /// <summary>
-    /// Provides additional information for asynchronous command execution.
-    /// </summary>
-    public class ExpectAsyncResult : AsyncResult<string>
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ExpectAsyncResult" /> class.
-        /// </summary>
-        /// <param name="asyncCallback">The async callback.</param>
-        /// <param name="state">The state.</param>
-        internal ExpectAsyncResult(AsyncCallback asyncCallback, object state)
-            : base(asyncCallback, state)
-        {
-        }
-    }
-}

+ 1 - 3
src/Renci.SshNet/IServiceFactory.cs

@@ -114,7 +114,6 @@ namespace Renci.SshNet
         /// <param name="height">The terminal height in pixels.</param>
         /// <param name="terminalModeValues">The terminal mode values.</param>
         /// <param name="bufferSize">Size of the buffer.</param>
-        /// <param name="expectSize">Size of the expect buffer.</param>
         /// <returns>
         /// The created <see cref="ShellStream"/> instance.
         /// </returns>
@@ -136,8 +135,7 @@ namespace Renci.SshNet
                                       uint width,
                                       uint height,
                                       IDictionary<TerminalModes, uint> terminalModeValues,
-                                      int bufferSize,
-                                      int expectSize);
+                                      int bufferSize);
 
         /// <summary>
         /// Creates an <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes

+ 2 - 3
src/Renci.SshNet/ServiceFactory.cs

@@ -187,7 +187,6 @@ namespace Renci.SshNet
         /// <param name="height">The terminal height in pixels.</param>
         /// <param name="terminalModeValues">The terminal mode values.</param>
         /// <param name="bufferSize">The size of the buffer.</param>
-        /// <param name="expectSize">The size of the expect buffer.</param>
         /// <returns>
         /// The created <see cref="ShellStream"/> instance.
         /// </returns>
@@ -202,9 +201,9 @@ namespace Renci.SshNet
         /// to the drawable area of the window.
         /// </para>
         /// </remarks>
-        public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize, int expectSize)
+        public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize)
         {
-            return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize, expectSize);
+            return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize);
         }
 
         /// <summary>

Datei-Diff unterdrückt, da er zu groß ist
+ 419 - 332
src/Renci.SshNet/ShellStream.cs


+ 2 - 65
src/Renci.SshNet/SshClient.cs

@@ -416,38 +416,7 @@ namespace Renci.SshNet
         /// </remarks>
         public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize)
         {
-            return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, bufferSize * 2, terminalModeValues: null);
-        }
-
-        /// <summary>
-        /// Creates the shell stream.
-        /// </summary>
-        /// <param name="terminalName">The <c>TERM</c> environment variable.</param>
-        /// <param name="columns">The terminal width in columns.</param>
-        /// <param name="rows">The terminal width in rows.</param>
-        /// <param name="width">The terminal width in pixels.</param>
-        /// <param name="height">The terminal height in pixels.</param>
-        /// <param name="bufferSize">The size of the buffer.</param>
-        /// <param name="expectSize">The size of the expect buffer.</param>
-        /// <returns>
-        /// The created <see cref="ShellStream"/> instance.
-        /// </returns>
-        /// <exception cref="SshConnectionException">Client is not connected.</exception>
-        /// <remarks>
-        /// <para>
-        /// The <c>TERM</c> environment variable contains an identifier for the text window's capabilities.
-        /// You can get a detailed list of these capabilities by using the ‘infocmp’ command.
-        /// </para>
-        /// <para>
-        /// The column/row dimensions override the pixel dimensions(when non-zero). Pixel dimensions refer
-        /// to the drawable area of the window.
-        /// </para>
-        /// </remarks>
-        public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, int expectSize)
-        {
-            EnsureSessionIsOpen();
-
-            return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, expectSize, terminalModeValues: null);
+            return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, terminalModeValues: null);
         }
 
         /// <summary>
@@ -478,39 +447,7 @@ namespace Renci.SshNet
         {
             EnsureSessionIsOpen();
 
-            return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, bufferSize * 2, terminalModeValues);
-        }
-
-        /// <summary>
-        /// Creates the shell stream.
-        /// </summary>
-        /// <param name="terminalName">The <c>TERM</c> environment variable.</param>
-        /// <param name="columns">The terminal width in columns.</param>
-        /// <param name="rows">The terminal width in rows.</param>
-        /// <param name="width">The terminal width in pixels.</param>
-        /// <param name="height">The terminal height in pixels.</param>
-        /// <param name="bufferSize">The size of the buffer.</param>
-        /// <param name="expectSize">The size of the expect buffer.</param>
-        /// <param name="terminalModeValues">The terminal mode values.</param>
-        /// <returns>
-        /// The created <see cref="ShellStream"/> instance.
-        /// </returns>
-        /// <exception cref="SshConnectionException">Client is not connected.</exception>
-        /// <remarks>
-        /// <para>
-        /// The <c>TERM</c> environment variable contains an identifier for the text window's capabilities.
-        /// You can get a detailed list of these capabilities by using the ‘infocmp’ command.
-        /// </para>
-        /// <para>
-        /// The column/row dimensions override the pixel dimensions(when non-zero). Pixel dimensions refer
-        /// to the drawable area of the window.
-        /// </para>
-        /// </remarks>
-        public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, int expectSize, IDictionary<TerminalModes, uint> terminalModeValues)
-        {
-            EnsureSessionIsOpen();
-
-            return ServiceFactory.CreateShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize, expectSize);
+            return ServiceFactory.CreateShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize);
         }
 
         /// <summary>

+ 6 - 0
test/.editorconfig

@@ -22,6 +22,9 @@ dotnet_diagnostic.S1215.severity = none
 # For unit tests, we do not care about this diagnostic.
 dotnet_diagnostic.S1854.severity = none
 
+# S3966: Objects should not be disposed more than once
+dotnet_diagnostic.S3966.severity = suggestion
+
 #### Meziantou.Analyzer rules ####
 
 # MA0089: Optimize string method usage
@@ -144,3 +147,6 @@ dotnet_diagnostic.CA5351.severity = none
 #
 # We do not care about this for unit tests.
 dotnet_diagnostic.CA5394.severity = none
+
+# IDE0078: Use pattern matching (may change code meaning)
+dotnet_diagnostic.IDE0078.severity = none

+ 2 - 2
test/Renci.SshNet.IntegrationBenchmarks/SshClientBenchmark.cs

@@ -81,7 +81,7 @@ namespace Renci.SshNet.IntegrationBenchmarks
 
                 while (true)
                 {
-                    var line = shellStream.ReadLine();
+                    var line = shellStream.ReadLine()!;
 
                     if (line.EndsWith("500", StringComparison.Ordinal))
                     {
@@ -92,7 +92,7 @@ namespace Renci.SshNet.IntegrationBenchmarks
         }
 
         [Benchmark]
-        public string ShellStreamExpect()
+        public string? ShellStreamExpect()
         {
             using (var shellStream = _sshClient!.CreateShellStream("xterm", 80, 24, 800, 600, 1024, ShellStreamTerminalModes))
             {

+ 12 - 22
test/Renci.SshNet.IntegrationTests/SshTests.cs

@@ -71,16 +71,7 @@ namespace Renci.SshNet.IntegrationTests
                     Assert.IsNotNull(line);
                     Assert.IsTrue(line.EndsWith("Hello!"), line);
 
-                    // TODO: ReadLine should return null when the buffer is empty and the channel has been closed (issue #672)
-                    try
-                    {
-                        line = shellStream.ReadLine();
-                        Assert.Fail(line);
-                    }
-                    catch (NullReferenceException)
-                    {
-
-                    }
+                    Assert.IsTrue(shellStream.ReadLine() is null || shellStream.ReadLine() is null); // we might first get e.g. "renci-ssh-tests-server:~$"
                 }
             }
         }
@@ -90,18 +81,17 @@ namespace Renci.SshNet.IntegrationTests
         /// </summary>
         [TestMethod]
         [Category("Reproduction Tests")]
-        [Ignore]
         public void Ssh_ShellStream_IntermittendOutput()
         {
             const string remoteFile = "/home/sshnet/test.sh";
 
-            var expectedResult = string.Join("\n",
-                                             "Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-                                             "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-                                             "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-                                             "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-                                             "Line 5 ",
-                                             "Line 6");
+            List<string> expectedLines = ["renci-ssh-tests-server:~$ Line 1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                          "Line 2 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                          "Line 3 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                          "Line 4 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+                                          "Line 5 ",
+                                          "Line 6",
+                                          "renci-ssh-tests-server:~$ "]; // No idea how stable this is.
 
             var scriptBuilder = new StringBuilder();
             scriptBuilder.Append("#!/bin/sh\n");
@@ -131,8 +121,8 @@ namespace Renci.SshNet.IntegrationTests
                     using (var shellStream = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024, terminalModes))
                     {
                         shellStream.WriteLine(remoteFile);
-                        Thread.Sleep(1200);
-                        using (var reader = new StreamReader(shellStream, new UTF8Encoding(false), false, 10))
+                        shellStream.WriteLine("exit");
+                        using (var reader = new StreamReader(shellStream))
                         {
                             var lines = new List<string>();
                             string line = null;
@@ -140,8 +130,8 @@ namespace Renci.SshNet.IntegrationTests
                             {
                                 lines.Add(line);
                             }
-                            Assert.AreEqual(6, lines.Count, string.Join("\n", lines));
-                            Assert.AreEqual(expectedResult, string.Join("\n", lines));
+
+                            CollectionAssert.AreEqual(expectedLines, lines, string.Join("\n", lines));
                         }
                     }
                 }

+ 1 - 4
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private SshException _channelOpenException;
         private SshException _actualException;
 
@@ -37,7 +36,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint) random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
             _channelOpenException = new SshException();
 
             _actualException = null;
@@ -97,8 +95,7 @@ namespace Renci.SshNet.Tests.Classes
                                                   _width,
                                                   _height,
                                                   _terminalModeValues,
-                                                  _bufferSize,
-                                                  _expectSize);
+                                                  _bufferSize);
                 Assert.Fail();
             }
             catch (SshException ex)

+ 1 - 4
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private SshException _actualException;
 
         private void SetupData()
@@ -36,7 +35,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint)random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
             _actualException = null;
         }
 
@@ -96,8 +94,7 @@ namespace Renci.SshNet.Tests.Classes
                                                   _width,
                                                   _height,
                                                   _terminalModeValues,
-                                                  _bufferSize,
-                                                  _expectSize);
+                                                  _bufferSize);
                 Assert.Fail();
             }
             catch (SshException ex)

+ 1 - 4
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private SshException _sendPseudoTerminalRequestException;
         private SshException _actualException;
 
@@ -37,7 +36,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint) random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
             _sendPseudoTerminalRequestException = new SshException();
 
             _actualException = null;
@@ -99,8 +97,7 @@ namespace Renci.SshNet.Tests.Classes
                                                   _width,
                                                   _height,
                                                   _terminalModeValues,
-                                                  _bufferSize,
-                                                  _expectSize);
+                                                  _bufferSize);
                 Assert.Fail();
             }
             catch (SshException ex)

+ 1 - 4
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private SshException _actualException;
 
         private void SetupData()
@@ -36,7 +35,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint) random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
             _actualException = null;
         }
 
@@ -99,8 +97,7 @@ namespace Renci.SshNet.Tests.Classes
                                                   _width,
                                                   _height,
                                                   _terminalModeValues,
-                                                  _bufferSize,
-                                                  _expectSize);
+                                                  _bufferSize);
                 Assert.Fail();
             }
             catch (SshException ex)

+ 1 - 4
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private SshException _sendShellRequestException;
         private SshException _actualException;
 
@@ -37,7 +36,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint) random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
             _sendShellRequestException = new SshException();
             _actualException = null;
         }
@@ -101,8 +99,7 @@ namespace Renci.SshNet.Tests.Classes
                                                   _width,
                                                   _height,
                                                   _terminalModeValues,
-                                                  _bufferSize,
-                                                  _expectSize);
+                                                  _bufferSize);
                 Assert.Fail();
             }
             catch (SshException ex)

+ 1 - 10
test/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_Success.cs

@@ -22,7 +22,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _height;
         private IDictionary<TerminalModes, uint> _terminalModeValues;
         private int _bufferSize;
-        private int _expectSize;
         private ShellStream _shellStream;
 
         private void SetupData()
@@ -36,7 +35,6 @@ namespace Renci.SshNet.Tests.Classes
             _height = (uint) random.Next();
             _terminalModeValues = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next();
-            _expectSize = _bufferSize;
         }
 
         private void CreateMocks()
@@ -99,8 +97,7 @@ namespace Renci.SshNet.Tests.Classes
                                                              _width,
                                                              _height,
                                                              _terminalModeValues,
-                                                             _bufferSize,
-                                                             _expectSize);
+                                                             _bufferSize);
         }
 
         [TestMethod]
@@ -109,12 +106,6 @@ namespace Renci.SshNet.Tests.Classes
             Assert.IsNotNull(_shellStream);
         }
 
-        [TestMethod]
-        public void BufferSizeOfShellStreamShouldBeValuePassedToCreateShellStream()
-        {
-            Assert.AreEqual(_bufferSize, _shellStream.BufferSize);
-        }
-
         [TestMethod]
         public void SendPseudoTerminalRequestShouldHaveBeenInvokedOnce()
         {

+ 39 - 7
test/Renci.SshNet.Tests/Classes/ShellStreamTest.cs

@@ -27,7 +27,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _heightPixels;
         private Dictionary<TerminalModes, uint> _terminalModes;
         private int _bufferSize;
-        private int _expectSize;
         private Mock<IChannelSession> _channelSessionMock;
 
         protected override void OnInit()
@@ -42,7 +41,6 @@ namespace Renci.SshNet.Tests.Classes
             _heightPixels = (uint)random.Next();
             _terminalModes = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next(100, 500);
-            _expectSize = random.Next(100, _bufferSize);
 
             _encoding = Encoding.UTF8;
             _sessionMock = new Mock<ISession>(MockBehavior.Strict);
@@ -98,11 +96,46 @@ namespace Renci.SshNet.Tests.Classes
             const string line = null;
             var lineTerminator = _encoding.GetBytes("\r");
 
-            _channelSessionMock.Setup(p => p.SendData(lineTerminator));
+            _channelSessionMock.Setup(p => p.SendData(lineTerminator, 0, lineTerminator.Length));
 
             shellStream.WriteLine(line);
 
-            _channelSessionMock.Verify(p => p.SendData(lineTerminator), Times.Once);
+            _channelSessionMock.Verify(p => p.SendData(lineTerminator, 0, lineTerminator.Length), Times.Once);
+        }
+
+        [TestMethod]
+        public void Write_Bytes_SendsToChannel()
+        {
+            var shellStream = CreateShellStream();
+
+            var bytes1 = _encoding.GetBytes("Hello World!");
+            var bytes2 = _encoding.GetBytes("Some more bytes!");
+
+            _channelSessionMock.Setup(p => p.SendData(bytes1, 0, bytes1.Length));
+            _channelSessionMock.Setup(p => p.SendData(bytes2, 0, bytes2.Length));
+
+            shellStream.Write(bytes1, 0, bytes1.Length);
+
+            _channelSessionMock.Verify(p => p.SendData(bytes1, 0, bytes1.Length), Times.Once);
+
+            shellStream.Write(bytes2, 0, bytes2.Length);
+
+            _channelSessionMock.Verify(p => p.SendData(bytes1, 0, bytes1.Length), Times.Once);
+            _channelSessionMock.Verify(p => p.SendData(bytes2, 0, bytes2.Length), Times.Once);
+        }
+
+        [TestMethod]
+        public void Write_AfterDispose_ThrowsObjectDisposedException()
+        {
+            var shellStream = CreateShellStream();
+
+            _channelSessionMock.Setup(p => p.Dispose());
+
+            shellStream.Dispose();
+
+            var bytes = _encoding.GetBytes("Hello World!");
+
+            Assert.ThrowsException<ObjectDisposedException>(() => shellStream.Write(bytes, 0, bytes.Length));
         }
 
         private ShellStream CreateShellStream()
@@ -122,8 +155,7 @@ namespace Renci.SshNet.Tests.Classes
                                    _widthPixels,
                                    _heightPixels,
                                    _terminalModes,
-                                   _bufferSize,
-                                   _expectSize);
+                                   _bufferSize);
         }
     }
-}
+}

+ 85 - 59
test/Renci.SshNet.Tests/Classes/ShellStreamTest_ReadExpect.cs

@@ -18,7 +18,6 @@ namespace Renci.SshNet.Tests.Classes
     public class ShellStreamTest_ReadExpect
     {
         private const int BufferSize = 1024;
-        private const int ExpectSize = BufferSize * 2;
         private ShellStream _shellStream;
         private ChannelSessionStub _channelSessionStub;
 
@@ -44,8 +43,7 @@ namespace Renci.SshNet.Tests.Classes
                 width: 800,
                 height: 600,
                 terminalModeValues: null,
-                bufferSize: BufferSize,
-                expectSize: ExpectSize);
+                bufferSize: BufferSize);
         }
 
         [TestMethod]
@@ -74,26 +72,48 @@ namespace Renci.SshNet.Tests.Classes
 
         [DataTestMethod]
         [DataRow("\r\n")]
-        //[DataRow("\r")] These currently fail.
-        //[DataRow("\n")]
+        [DataRow("\r")]
+        [DataRow("\n")]
         public void ReadLine(string newLine)
         {
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello "));
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("World!"));
 
-            // We specify a nonzero timeout to avoid waiting infinitely.
-            Assert.IsNull(_shellStream.ReadLine(TimeSpan.FromTicks(1)));
+            // We specify a timeout to avoid waiting infinitely.
+            Assert.IsNull(_shellStream.ReadLine(TimeSpan.Zero));
 
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes(newLine));
 
-            Assert.AreEqual("Hello World!", _shellStream.ReadLine(TimeSpan.FromTicks(1)));
-            Assert.IsNull(_shellStream.ReadLine(TimeSpan.FromTicks(1)));
+            Assert.AreEqual("Hello World!", _shellStream.ReadLine(TimeSpan.Zero));
+            Assert.IsNull(_shellStream.ReadLine(TimeSpan.Zero));
 
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Second line!" + newLine + "Third line!" + newLine));
 
-            Assert.AreEqual("Second line!", _shellStream.ReadLine(TimeSpan.FromTicks(1)));
-            Assert.AreEqual("Third line!", _shellStream.ReadLine(TimeSpan.FromTicks(1)));
-            Assert.IsNull(_shellStream.ReadLine(TimeSpan.FromTicks(1)));
+            Assert.AreEqual("Second line!", _shellStream.ReadLine(TimeSpan.Zero));
+            Assert.AreEqual("Third line!", _shellStream.ReadLine(TimeSpan.Zero));
+            Assert.IsNull(_shellStream.ReadLine(TimeSpan.Zero));
+
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Last line!")); // no newLine at the end
+
+            Assert.IsNull(_shellStream.ReadLine(TimeSpan.Zero));
+
+            _channelSessionStub.Close();
+
+            Assert.AreEqual("Last line!", _shellStream.ReadLine(TimeSpan.Zero));
+        }
+
+        [TestMethod]
+        public void ReadLine_DifferentTerminators()
+        {
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello\rWorld!\nWhat's\r\ngoing\n\ron?\n"));
+
+            Assert.AreEqual("Hello", _shellStream.ReadLine());
+            Assert.AreEqual("World!", _shellStream.ReadLine());
+            Assert.AreEqual("What's", _shellStream.ReadLine());
+            Assert.AreEqual("going", _shellStream.ReadLine());
+            Assert.AreEqual("", _shellStream.ReadLine());
+            Assert.AreEqual("on?", _shellStream.ReadLine());
+            Assert.IsNull(_shellStream.ReadLine(TimeSpan.Zero));
         }
 
         [DataTestMethod]
@@ -111,54 +131,31 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        [Ignore] // Currently returns 0 immediately
-        public void Read_NonEmptyArray_OnlyReturnsZeroAfterClose()
+        public async Task Read_NonEmptyArray_OnlyReturnsZeroAfterClose()
         {
-            Task closeTask = Task.Run(async () =>
-            {
-                // For the test to have meaning, we should be in
-                // the call to Read before closing the channel.
-                // Impose a short delay to make that more likely.
-                await Task.Delay(50);
-
-                _channelSessionStub.Close();
-            });
+            Task<int> readTask = _shellStream.ReadAsync(new byte[16], 0, 16);
 
-            Assert.AreEqual(0, _shellStream.Read(new byte[16], 0, 16));
-            Assert.AreEqual(TaskStatus.RanToCompletion, closeTask.Status);
-        }
+            await Task.Delay(50);
 
-        [TestMethod]
-        [Ignore] // Currently returns 0 immediately
-        public void Read_EmptyArray_OnlyReturnsZeroWhenDataAvailable()
-        {
-            Task receiveTask = Task.Run(async () =>
-            {
-                // For the test to have meaning, we should be in
-                // the call to Read before receiving the data.
-                // Impose a short delay to make that more likely.
-                await Task.Delay(50);
+            Assert.IsFalse(readTask.IsCompleted);
 
-                _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello World!"));
-            });
+            _channelSessionStub.Close();
 
-            Assert.AreEqual(0, _shellStream.Read(Array.Empty<byte>(), 0, 0));
-            Assert.AreEqual(TaskStatus.RanToCompletion, receiveTask.Status);
+            Assert.AreEqual(0, await readTask);
         }
 
         [TestMethod]
-        [Ignore] // Currently hangs
-        public void ReadLine_NoData_ReturnsNullAfterClose()
+        public async Task Read_EmptyArray_OnlyReturnsZeroWhenDataAvailable()
         {
-            Task closeTask = Task.Run(async () =>
-            {
-                await Task.Delay(50);
+            Task<int> readTask = _shellStream.ReadAsync(Array.Empty<byte>(), 0, 0);
 
-                _channelSessionStub.Close();
-            });
+            await Task.Delay(50);
 
-            Assert.IsNull(_shellStream.ReadLine());
-            Assert.AreEqual(TaskStatus.RanToCompletion, closeTask.Status);
+            Assert.IsFalse(readTask.IsCompleted);
+
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello World!"));
+
+            Assert.AreEqual(0, await readTask);
         }
 
         [TestMethod]
@@ -167,14 +164,24 @@ namespace Renci.SshNet.Tests.Classes
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello "));
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("World!"));
 
-            Assert.IsNull(_shellStream.Expect("123", TimeSpan.FromTicks(1)));
+            Assert.IsNull(_shellStream.Expect("123", TimeSpan.Zero));
 
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("\r\n12345"));
 
-            // Both of these cases fail
-            // Case 1 above.
-            Assert.AreEqual("Hello World!\r\n123", _shellStream.Expect("123")); // Fails, returns "Hello World!\r\n12345"
-            Assert.AreEqual("45", _shellStream.Read()); // Passes, but should probably fail and return ""
+            Assert.AreEqual("Hello World!\r\n123", _shellStream.Expect("123"));
+            Assert.AreEqual("45", _shellStream.Read());
+        }
+
+        [TestMethod]
+        public void Read_AfterDispose_StillWorks()
+        {
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello World!"));
+
+            _shellStream.Dispose();
+            _shellStream.Dispose(); // Check that multiple Dispose is OK.
+
+            Assert.AreEqual("Hello World!", _shellStream.ReadLine());
+            Assert.IsNull(_shellStream.ReadLine());
         }
 
         [TestMethod]
@@ -221,7 +228,7 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void Expect_String_non_ASCII_characters()
+        public void Expect_Regex_non_ASCII_characters()
         {
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello, こんにちは, Bonjour"));
 
@@ -247,13 +254,12 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void Expect_String_DequeueChecks()
+        public void Expect_String_WithLookback()
         {
             const string expected = "ccccc";
 
             // Prime buffer
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes(new string(' ', BufferSize)));
-            _channelSessionStub.Receive(Encoding.UTF8.GetBytes(new string(' ', ExpectSize)));
 
             // Test data
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes(new string('a', 100)));
@@ -263,14 +269,34 @@ namespace Renci.SshNet.Tests.Classes
             _channelSessionStub.Receive(Encoding.UTF8.GetBytes(new string('e', 100)));
 
             // Expected result
-            var expectedResult = $"{new string(' ', BufferSize)}{new string(' ', ExpectSize)}{new string('a', 100)}{new string('b', 100)}{expected}";
+            var expectedResult = $"{new string(' ', BufferSize)}{new string('a', 100)}{new string('b', 100)}{expected}";
             var expectedRead = $"{new string('d', 100)}{new string('e', 100)}";
 
-            Assert.AreEqual(expectedResult, _shellStream.Expect(expected));
+            Assert.AreEqual(expectedResult, _shellStream.Expect(expected, TimeSpan.Zero, lookback: 250));
 
             Assert.AreEqual(expectedRead, _shellStream.Read());
         }
 
+        [TestMethod]
+        public void Expect_Regex_WithLookback()
+        {
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("0123456789"));
+
+            Assert.AreEqual("01234567", _shellStream.Expect(new Regex(@"\d"), TimeSpan.Zero, lookback: 3));
+
+            Assert.AreEqual("89", _shellStream.Read());
+        }
+
+        [TestMethod]
+        public void Expect_Regex_WithLookback_non_ASCII_characters()
+        {
+            _channelSessionStub.Receive(Encoding.UTF8.GetBytes("Hello, こんにちは, Bonjour"));
+
+            Assert.AreEqual("Hello, こんにち", _shellStream.Expect(new Regex(@"[^\u0000-\u007F]"), TimeSpan.Zero, lookback: 11));
+
+            Assert.AreEqual("は, Bonjour", _shellStream.Read());
+        }
+
         [TestMethod]
         public void Expect_Timeout()
         {

+ 0 - 138
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteLessBytesThanBufferSize.cs

@@ -1,138 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferEmptyAndWriteLessBytesThanBufferSize
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint) random.Next();
-            _heightRows = (uint) random.Next();
-            _widthPixels = (uint) random.Next();
-            _heightPixels = (uint) random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _data = CryptoAbstraction.GenerateRandom(_bufferSize - 10);
-            _offset = random.Next(1, 5);
-            _count = _data.Length - _offset - random.Next(1, 10);
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendWrittenBytesToServer()
-        {
-            byte[] bytesSent = null;
-
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(It.IsAny<byte[]>()))
-                               .Callback<byte[]>(data => bytesSent = data);
-
-            _shellStream.Flush();
-
-            Assert.IsNotNull(bytesSent);
-            Assert.IsTrue(_data.Take(_offset, _count).IsEqualTo(bytesSent));
-
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Once);
-        }
-    }
-}

+ 0 - 145
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize.cs

@@ -1,145 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferEmptyAndWriteMoreBytesThanBufferSize
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private MockSequence _mockSequence;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-
-        private byte[] _expectedBytesSent1;
-        private byte[] _expectedBytesSent2;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint)random.Next();
-            _heightRows = (uint)random.Next();
-            _widthPixels = (uint)random.Next();
-            _heightPixels = (uint)random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _data = CryptoAbstraction.GenerateRandom((_bufferSize * 2) + 10);
-            _offset = 0;
-            _count = _data.Length;
-
-            _expectedBytesSent1 = _data.Take(0, _bufferSize);
-            _expectedBytesSent2 = _data.Take(_bufferSize, _bufferSize);
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _ = _sessionMock.InSequence(_mockSequence)
-                            .Setup(p => p.ConnectionInfo)
-                            .Returns(_connectionInfoMock.Object);
-            _ = _connectionInfoMock.InSequence(_mockSequence)
-                                   .Setup(p => p.Encoding)
-                                   .Returns(new UTF8Encoding());
-            _ = _sessionMock.InSequence(_mockSequence)
-                            .Setup(p => p.CreateChannelSession())
-                            .Returns(_channelSessionMock.Object);
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.Open());
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                           _widthColumns,
-                                                                           _heightRows,
-                                                                           _widthPixels,
-                                                                           _heightPixels,
-                                                                           _terminalModes))
-                                   .Returns(true);
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendShellRequest())
-                                   .Returns(true);
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendData(_expectedBytesSent1));
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendData(_expectedBytesSent2));
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void BufferShouldHaveBeenFlushedTwice()
-        {
-            _channelSessionMock.Verify(p => p.SendData(_expectedBytesSent1), Times.Once);
-            _channelSessionMock.Verify(p => p.SendData(_expectedBytesSent2), Times.Once);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendRemaningBytesToServer()
-        {
-            var expectedBytesSent = _data.Take(_bufferSize * 2, _data.Length - (_bufferSize * 2));
-
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendData(expectedBytesSent));
-
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(expectedBytesSent), Times.Once);
-        }
-    }
-}

+ 0 - 132
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteNumberOfBytesEqualToBufferSize.cs

@@ -1,132 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferEmptyAndWriteNumberOfBytesEqualToBufferSize
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint)random.Next();
-            _heightRows = (uint)random.Next();
-            _widthPixels = (uint)random.Next();
-            _heightPixels = (uint)random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _data = CryptoAbstraction.GenerateRandom(_bufferSize);
-            _offset = 0;
-            _count = _data.Length;
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendWrittenBytesToServer()
-        {
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_data));
-
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(_data), Times.Once);
-        }
-    }
-}

+ 0 - 131
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes.cs

@@ -1,131 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using Moq;
-
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferEmptyAndWriteZeroBytes
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint)random.Next();
-            _heightRows = (uint)random.Next();
-            _widthPixels = (uint)random.Next();
-            _heightPixels = (uint)random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _data = new byte[0];
-            _offset = 0;
-            _count = _data.Length;
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _ = _sessionMock.InSequence(_mockSequence)
-                            .Setup(p => p.ConnectionInfo)
-                            .Returns(_connectionInfoMock.Object);
-            _ = _connectionInfoMock.InSequence(_mockSequence)
-                                   .Setup(p => p.Encoding)
-                                   .Returns(new UTF8Encoding());
-            _ = _sessionMock.InSequence(_mockSequence)
-                            .Setup(p => p.CreateChannelSession())
-                            .Returns(_channelSessionMock.Object);
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.Open());
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                    .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                            _widthColumns,
-                                                                            _heightRows,
-                                                                            _widthPixels,
-                                                                            _heightPixels,
-                                                                            _terminalModes))
-                                    .Returns(true);
-            _ = _channelSessionMock.InSequence(_mockSequence)
-                                   .Setup(p => p.SendShellRequest())
-                                   .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendNoBytesToServer()
-        {
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-    }
-}

+ 0 - 138
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferFullAndWriteLessBytesThanBufferSize.cs

@@ -1,138 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferFullAndWriteLessBytesThanBufferSize
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-        private byte[] _bufferData;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint)random.Next();
-            _heightRows = (uint)random.Next();
-            _widthPixels = (uint)random.Next();
-            _heightPixels = (uint)random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _bufferData = CryptoAbstraction.GenerateRandom(_bufferSize);
-            _data = CryptoAbstraction.GenerateRandom(_bufferSize - 10);
-            _offset = 0;
-            _count = _data.Length;
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_bufferData));
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-
-            _shellStream.Write(_bufferData, 0, _bufferData.Length);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void BufferShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(_bufferData), Times.Once);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendRemainingBytesInBufferToServer()
-        {
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_data));
-
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(_data), Times.Once);
-        }
-    }
-}

+ 0 - 136
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferFullAndWriteZeroBytes.cs

@@ -1,136 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferFullAndWriteZeroBytes
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-        private byte[] _bufferData;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint) random.Next();
-            _heightRows = (uint) random.Next();
-            _widthPixels = (uint) random.Next();
-            _heightPixels = (uint) random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _bufferData = CryptoAbstraction.GenerateRandom(_bufferSize);
-            _data = new byte[0];
-            _offset = 0;
-            _count = _data.Length;
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-
-            _shellStream.Write(_bufferData, 0, _bufferData.Length);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendBufferToServer()
-        {
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_bufferData));
-
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(_bufferData), Times.Once);
-        }
-    }
-}

+ 0 - 144
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteLessBytesThanBufferCanContain.cs

@@ -1,144 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferNotEmptyAndWriteLessBytesThanBufferCanContain
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-        private byte[] _bufferData;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint) random.Next();
-            _heightRows = (uint) random.Next();
-            _widthPixels = (uint) random.Next();
-            _heightPixels = (uint) random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _bufferData = CryptoAbstraction.GenerateRandom(_bufferSize - 60);
-            _data = CryptoAbstraction.GenerateRandom(_bufferSize + 100);
-            _offset = 0;
-            _count = _bufferSize - _bufferData.Length - random.Next(1, 10);
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-
-            _shellStream.Write(_bufferData, 0, _bufferData.Length);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendWrittenBytesToServer()
-        {
-            byte[] bytesSent = null;
-
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(It.IsAny<byte[]>()))
-                               .Callback<byte[]>(data => bytesSent = data);
-
-            _shellStream.Flush();
-
-            Assert.IsNotNull(bytesSent);
-            Assert.AreEqual(_bufferData.Length + _count, bytesSent.Length);
-            Assert.IsTrue(_bufferData.IsEqualTo(bytesSent.Take(_bufferData.Length)));
-            Assert.IsTrue(_data.Take(0, _count).IsEqualTo(bytesSent.Take(_bufferData.Length, _count)));
-
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Once);
-        }
-    }
-}

+ 0 - 152
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteMoreBytesThanBufferCanContain.cs

@@ -1,152 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-using Renci.SshNet.Tests.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferNotEmptyAndWriteMoreBytesThanBufferCanContain
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-        private byte[] _bufferData;
-        private byte[] _expectedBytesSent;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint) random.Next();
-            _heightRows = (uint) random.Next();
-            _widthPixels = (uint) random.Next();
-            _heightPixels = (uint) random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _bufferData = CryptoAbstraction.GenerateRandom(_bufferSize - 60);
-            _data = CryptoAbstraction.GenerateRandom(_bufferSize - _bufferData.Length + random.Next(1, 10));
-            _offset = 0;
-            _count = _data.Length;
-
-            _expectedBytesSent = new ArrayBuilder<byte>().Add(_bufferData)
-                                                         .Add(_data, 0, _bufferSize - _bufferData.Length)
-                                                         .Build();
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_expectedBytesSent));
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-
-            _shellStream.Write(_bufferData, 0, _bufferData.Length);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void BufferShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(_expectedBytesSent), Times.Once);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendRemainingBytesInBufferToServer()
-        {
-           var expectedBytesSent = _data.Take(_bufferSize - _bufferData.Length, _data.Length + _bufferData.Length - _bufferSize);
-            byte[] actualBytesSent = null;
-
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(It.IsAny<byte[]>()))
-                               .Callback<byte[]>(data => actualBytesSent = data);
-
-            _shellStream.Flush();
-
-            Assert.IsNotNull(actualBytesSent);
-            Assert.AreEqual(expectedBytesSent.Length, actualBytesSent.Length);
-            Assert.IsTrue(expectedBytesSent.IsEqualTo(actualBytesSent));
-
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Exactly(2));
-        }
-    }
-}

+ 0 - 136
test/Renci.SshNet.Tests/Classes/ShellStreamTest_Write_WriteBufferNotEmptyAndWriteZeroBytes.cs

@@ -1,136 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Renci.SshNet.Abstractions;
-using Renci.SshNet.Channels;
-using Renci.SshNet.Common;
-
-namespace Renci.SshNet.Tests.Classes
-{
-    [TestClass]
-    public class ShellStreamTest_Write_WriteBufferNotEmptyAndWriteZeroBytes
-    {
-        private Mock<ISession> _sessionMock;
-        private Mock<IConnectionInfo> _connectionInfoMock;
-        private Mock<IChannelSession> _channelSessionMock;
-        private string _terminalName;
-        private uint _widthColumns;
-        private uint _heightRows;
-        private uint _widthPixels;
-        private uint _heightPixels;
-        private Dictionary<TerminalModes, uint> _terminalModes;
-        private ShellStream _shellStream;
-        private int _bufferSize;
-        private int _expectSize;
-
-        private byte[] _data;
-        private int _offset;
-        private int _count;
-        private MockSequence _mockSequence;
-        private byte[] _bufferData;
-
-        [TestInitialize]
-        public void Initialize()
-        {
-            Arrange();
-            Act();
-        }
-
-        private void SetupData()
-        {
-            var random = new Random();
-
-            _terminalName = random.Next().ToString();
-            _widthColumns = (uint)random.Next();
-            _heightRows = (uint)random.Next();
-            _widthPixels = (uint)random.Next();
-            _heightPixels = (uint)random.Next();
-            _terminalModes = new Dictionary<TerminalModes, uint>();
-            _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
-
-            _bufferData = CryptoAbstraction.GenerateRandom(_bufferSize - 60);
-            _data = new byte[0];
-            _offset = 0;
-            _count = _data.Length;
-        }
-
-        private void CreateMocks()
-        {
-            _sessionMock = new Mock<ISession>(MockBehavior.Strict);
-            _connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
-            _channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
-        }
-
-        private void SetupMocks()
-        {
-            _mockSequence = new MockSequence();
-
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.ConnectionInfo)
-                        .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
-                               .Setup(p => p.Encoding)
-                               .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
-                        .Setup(p => p.CreateChannelSession())
-                        .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
-                                                                       _widthColumns,
-                                                                       _heightRows,
-                                                                       _widthPixels,
-                                                                       _heightPixels,
-                                                                       _terminalModes))
-                               .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendShellRequest())
-                               .Returns(true);
-        }
-
-        private void Arrange()
-        {
-            SetupData();
-            CreateMocks();
-            SetupMocks();
-
-            _shellStream = new ShellStream(_sessionMock.Object,
-                                           _terminalName,
-                                           _widthColumns,
-                                           _heightRows,
-                                           _widthPixels,
-                                           _heightPixels,
-                                           _terminalModes,
-                                           _bufferSize,
-                                           _expectSize);
-
-            _shellStream.Write(_bufferData, 0, _bufferData.Length);
-        }
-
-        private void Act()
-        {
-            _shellStream.Write(_data, _offset, _count);
-        }
-
-        [TestMethod]
-        public void NoDataShouldBeSentToServer()
-        {
-            _channelSessionMock.Verify(p => p.SendData(It.IsAny<byte[]>()), Times.Never);
-        }
-
-        [TestMethod]
-        public void FlushShouldSendWrittenBytesToServer()
-        {
-            _channelSessionMock.InSequence(_mockSequence)
-                               .Setup(p => p.SendData(_bufferData));
-
-            _shellStream.Flush();
-
-            _channelSessionMock.Verify(p => p.SendData(_bufferData), Times.Once);
-        }
-    }
-}

+ 2 - 8
test/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs

@@ -19,7 +19,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _heightPixels;
         private Dictionary<TerminalModes, uint> _terminalModes;
         private int _bufferSize;
-        private int _expectSize;
         private ShellStream _expected;
         private ShellStream _actual;
 
@@ -36,7 +35,6 @@ namespace Renci.SshNet.Tests.Classes
             _heightPixels = (uint) random.Next();
             _terminalModes = new Dictionary<TerminalModes, uint>();
             _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
 
             _expected = CreateShellStream();
         }
@@ -61,8 +59,7 @@ namespace Renci.SshNet.Tests.Classes
                                                                _widthPixels,
                                                                _heightPixels,
                                                                _terminalModes,
-                                                               _bufferSize,
-                                                               _expectSize))
+                                                               _bufferSize))
                                .Returns(_expected);
         }
 
@@ -82,7 +79,6 @@ namespace Renci.SshNet.Tests.Classes
                                                    _widthPixels,
                                                    _heightPixels,
                                                    _bufferSize,
-                                                   _expectSize,
                                                    _terminalModes);
         }
 
@@ -96,8 +92,7 @@ namespace Renci.SshNet.Tests.Classes
                                                                 _widthPixels,
                                                                 _heightPixels,
                                                                 _terminalModes,
-                                                                _bufferSize,
-                                                                _expectSize),
+                                                                _bufferSize),
                                        Times.Once);
         }
 
@@ -135,7 +130,6 @@ namespace Renci.SshNet.Tests.Classes
                                    _widthPixels,
                                    _heightPixels,
                                    _terminalModes,
-                                   1,
                                    1);
         }
     }

+ 3 - 9
test/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs

@@ -19,7 +19,6 @@ namespace Renci.SshNet.Tests.Classes
         private uint _widthPixels;
         private uint _heightPixels;
         private int _bufferSize;
-        private int _expectSize;
         private ShellStream _expected;
         private ShellStream _actual;
 
@@ -35,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes
             _widthPixels = (uint)random.Next();
             _heightPixels = (uint)random.Next();
             _bufferSize = random.Next(100, 1000);
-            _expectSize = random.Next(100, _bufferSize);
 
             _expected = CreateShellStream();
         }
@@ -60,8 +58,7 @@ namespace Renci.SshNet.Tests.Classes
                                                                    _widthPixels,
                                                                    _heightPixels,
                                                                    null,
-                                                                   _bufferSize,
-                                                                   _expectSize))
+                                                                   _bufferSize))
                                    .Returns(_expected);
         }
 
@@ -80,8 +77,7 @@ namespace Renci.SshNet.Tests.Classes
                                                    _heightRows,
                                                    _widthPixels,
                                                    _heightPixels,
-                                                   _bufferSize,
-                                                   _expectSize);
+                                                   _bufferSize);
         }
 
         [TestMethod]
@@ -94,8 +90,7 @@ namespace Renci.SshNet.Tests.Classes
                                                                 _widthPixels,
                                                                 _heightPixels,
                                                                 null,
-                                                                _bufferSize,
-                                                                _expectSize),
+                                                                _bufferSize),
                                        Times.Once);
         }
 
@@ -133,7 +128,6 @@ namespace Renci.SshNet.Tests.Classes
                                    _widthPixels,
                                    _heightPixels,
                                    null,
-                                   1,
                                    1);
         }
     }

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.