-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'newline-fix' into HEAD
- Loading branch information
Showing
5 changed files
with
267 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
2.6.0.0 | ||
2.6.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace GitCredentialManager.Tests; | ||
|
||
public class GitStreamReaderTests | ||
{ | ||
#region ReadLineAsync | ||
|
||
[Fact] | ||
public async Task GitStreamReader_ReadLineAsync_LF() | ||
{ | ||
// hello\n | ||
// world\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\nworld\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = await reader.ReadLineAsync(); | ||
string actual2 = await reader.ReadLineAsync(); | ||
string actual3 = await reader.ReadLineAsync(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world", actual2); | ||
Assert.Null(actual3); | ||
} | ||
|
||
[Fact] | ||
public async Task GitStreamReader_ReadLineAsync_CR() | ||
{ | ||
// hello\rworld\r | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\rworld\r"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = await reader.ReadLineAsync(); | ||
string actual2 = await reader.ReadLineAsync(); | ||
|
||
Assert.Equal("hello\rworld\r", actual1); | ||
Assert.Null(actual2); | ||
} | ||
|
||
[Fact] | ||
public async Task GitStreamReader_ReadLineAsync_CRLF() | ||
{ | ||
// hello\r\n | ||
// world\r\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\r\nworld\r\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = await reader.ReadLineAsync(); | ||
string actual2 = await reader.ReadLineAsync(); | ||
string actual3 = await reader.ReadLineAsync(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world", actual2); | ||
Assert.Null(actual3); | ||
} | ||
|
||
[Fact] | ||
public async Task GitStreamReader_ReadLineAsync_Mixed() | ||
{ | ||
// hello\r\n | ||
// world\rthis\n | ||
// is\n | ||
// a\n | ||
// \rmixed\rnewline\r\n | ||
// \n | ||
// string\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\r\nworld\rthis\nis\na\n\rmixed\rnewline\r\n\nstring\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = await reader.ReadLineAsync(); | ||
string actual2 = await reader.ReadLineAsync(); | ||
string actual3 = await reader.ReadLineAsync(); | ||
string actual4 = await reader.ReadLineAsync(); | ||
string actual5 = await reader.ReadLineAsync(); | ||
string actual6 = await reader.ReadLineAsync(); | ||
string actual7 = await reader.ReadLineAsync(); | ||
string actual8 = await reader.ReadLineAsync(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world\rthis", actual2); | ||
Assert.Equal("is", actual3); | ||
Assert.Equal("a", actual4); | ||
Assert.Equal("\rmixed\rnewline", actual5); | ||
Assert.Equal("", actual6); | ||
Assert.Equal("string", actual7); | ||
Assert.Null(actual8); | ||
} | ||
|
||
#endregion | ||
|
||
#region ReadLine | ||
|
||
[Fact] | ||
public void GitStreamReader_ReadLine_LF() | ||
{ | ||
// hello\n | ||
// world\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\nworld\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = reader.ReadLine(); | ||
string actual2 = reader.ReadLine(); | ||
string actual3 = reader.ReadLine(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world", actual2); | ||
Assert.Null(actual3); | ||
} | ||
|
||
[Fact] | ||
public void GitStreamReader_ReadLine_CR() | ||
{ | ||
// hello\rworld\r | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\rworld\r"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = reader.ReadLine(); | ||
string actual2 = reader.ReadLine(); | ||
|
||
Assert.Equal("hello\rworld\r", actual1); | ||
Assert.Null(actual2); | ||
} | ||
|
||
[Fact] | ||
public void GitStreamReader_ReadLine_CRLF() | ||
{ | ||
// hello\r\n | ||
// world\r\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\r\nworld\r\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = reader.ReadLine(); | ||
string actual2 = reader.ReadLine(); | ||
string actual3 = reader.ReadLine(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world", actual2); | ||
Assert.Null(actual3); | ||
} | ||
|
||
[Fact] | ||
public void GitStreamReader_ReadLine_Mixed() | ||
{ | ||
// hello\r\n | ||
// world\rthis\n | ||
// is\n | ||
// a\n | ||
// \rmixed\rnewline\r\n | ||
// \n | ||
// string\n | ||
|
||
byte[] buffer = Encoding.UTF8.GetBytes("hello\r\nworld\rthis\nis\na\n\rmixed\rnewline\r\n\nstring\n"); | ||
using var stream = new MemoryStream(buffer); | ||
var reader = new GitStreamReader(stream, Encoding.UTF8); | ||
|
||
string actual1 = reader.ReadLine(); | ||
string actual2 = reader.ReadLine(); | ||
string actual3 = reader.ReadLine(); | ||
string actual4 = reader.ReadLine(); | ||
string actual5 = reader.ReadLine(); | ||
string actual6 = reader.ReadLine(); | ||
string actual7 = reader.ReadLine(); | ||
string actual8 = reader.ReadLine(); | ||
|
||
Assert.Equal("hello", actual1); | ||
Assert.Equal("world\rthis", actual2); | ||
Assert.Equal("is", actual3); | ||
Assert.Equal("a", actual4); | ||
Assert.Equal("\rmixed\rnewline", actual5); | ||
Assert.Equal("", actual6); | ||
Assert.Equal("string", actual7); | ||
Assert.Null(actual8); | ||
} | ||
|
||
#endregion | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System.IO; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace GitCredentialManager; | ||
|
||
/// <summary> | ||
/// StreamReader that does NOT consider a lone carriage-return as a new-line character, | ||
/// only a line-feed or carriage-return immediately followed by a line-feed. | ||
/// <para/> | ||
/// The only major operating system that uses a lone carriage-return as a new-line character | ||
/// is the classic Macintosh OS (before OS X), which is not supported by Git. | ||
/// </summary> | ||
public class GitStreamReader : StreamReader | ||
{ | ||
public GitStreamReader(Stream stream, Encoding encoding) : base(stream, encoding) { } | ||
|
||
public override string ReadLine() | ||
{ | ||
#if NETFRAMEWORK | ||
return ReadLineAsync().ConfigureAwait(false).GetAwaiter().GetResult(); | ||
#else | ||
return ReadLineAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); | ||
#endif | ||
} | ||
|
||
#if NETFRAMEWORK | ||
public override async Task<string> ReadLineAsync() | ||
#else | ||
public override async ValueTask<string> ReadLineAsync(CancellationToken cancellationToken) | ||
#endif | ||
{ | ||
int nr; | ||
var sb = new StringBuilder(); | ||
var buffer = new char[1]; | ||
bool lastWasCR = false; | ||
|
||
while ((nr = await base.ReadAsync(buffer, 0, 1).ConfigureAwait(false)) > 0) | ||
{ | ||
char c = buffer[0]; | ||
|
||
// Only treat a line-feed as a new-line character. | ||
// Carriage-returns alone are NOT considered new-line characters. | ||
if (c == '\n') | ||
{ | ||
if (lastWasCR) | ||
{ | ||
// If the last character was a carriage-return we should remove it from the string builder | ||
// since together with this line-feed it is considered a new-line character. | ||
sb.Length--; | ||
} | ||
|
||
// We have a new-line character, so we should stop reading. | ||
break; | ||
} | ||
|
||
lastWasCR = c == '\r'; | ||
|
||
sb.Append(c); | ||
} | ||
|
||
if (sb.Length == 0 && nr == 0) | ||
{ | ||
return null; | ||
} | ||
|
||
return sb.ToString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters