Skip to content

Commit

Permalink
Console.Unix: fix OpenStandardInput Stream sometimes throwing for Rea…
Browse files Browse the repository at this point in the history
…ds. (#62153)

* Console.Unix: fix OpenStandardInput Stream sometimes throwing for Reads.

When connected to a terminal reads happen line-by-line.

StdInReader caches the line in a StringBuilder.

Instead of returning when the destination buffer is full, an attempt
was made to decode the next chunk from the StringBuilder (if there is
one). This causes the encoder to throw for encoding into an empty buffer.
  • Loading branch information
tmds authored Dec 1, 2021
1 parent 4aa8167 commit e51c22a
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/libraries/System.Console/src/System/IO/StdInReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ public int ReadLine(Span<byte> buffer)
int charsUsedTotal = 0;
foreach (ReadOnlyMemory<char> chunk in _readLineSB.GetChunks())
{
Debug.Assert(!buffer.IsEmpty);

encoder.Convert(chunk.Span, buffer, flush: false, out int charsUsed, out int bytesUsed, out bool completed);
buffer = buffer.Slice(bytesUsed);
bytesUsedTotal += bytesUsed;
charsUsedTotal += charsUsed;

if (charsUsed == 0)
if (!completed || buffer.IsEmpty)
{
break;
}
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Console/tests/ManualTests/ManualTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Xunit;

namespace System
Expand Down Expand Up @@ -45,6 +46,23 @@ public static void ReadLineFromOpenStandardInput()
AssertUserExpectedResults("the characters you typed properly echoed as you typed");
}

[ConditionalFact(nameof(ManualTestsEnabled))]
public static void ReadFromOpenStandardInput()
{
// The implementation in StdInReader uses a StringBuilder for caching. We want this builder to use
// multiple chunks. So the expectedLine is longer than 16 characters (StringBuilder.DefaultCapacity).
string expectedLine = $"This is a test for ReadFromOpenStandardInput.";
Assert.True(expectedLine.Length > new StringBuilder().Capacity);
Console.WriteLine($"Please type the sentence (without the quotes): \"{expectedLine}\"");
using Stream inputStream = Console.OpenStandardInput();
for (int i = 0; i < expectedLine.Length; i++)
{
Assert.Equal((byte)expectedLine[i], inputStream.ReadByte());
}
Assert.Equal((byte)'\n', inputStream.ReadByte());
AssertUserExpectedResults("the characters you typed properly echoed as you typed");
}

[ConditionalFact(nameof(ManualTestsEnabled))]
public static void ConsoleReadSupportsBackspace()
{
Expand Down

0 comments on commit e51c22a

Please sign in to comment.