Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Add some unit tests for ASCIIEncoding #35331

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.Text.Tests
{
public class ASCIIEncodingDecode
public partial class ASCIIEncodingDecode
{
public static IEnumerable<object[]> Decode_TestData()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Linq;
using Xunit;

namespace System.Text.Tests
{
public partial class ASCIIEncodingDecode
{
[Theory]
[InlineData("hello!", 6)]
[InlineData("hello\u1234there!", 16)]
[InlineData("\ud800\udc00", 10)]
public void GetByteCount_WithReplacementFallback(string input, int expectedByteCount)
{
Encoding encoding = Encoding.GetEncoding("ascii", new EncoderReplacementFallback("abcde"), DecoderFallback.ExceptionFallback);
Assert.Equal(expectedByteCount, encoding.GetByteCount(input));
}

[Fact]
public void GetByteCount_WithSingleCharNonAsciiReplacementFallback_ValidatesAscii()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to reviewers: This test case currently fails due to a bug in the current implementation of ASCIIEncoding. The actual behavior is that GetByteCount returns 1, but correct behavior would instead be for it to throw ArgumentException.

I'm working on correcting this issue at the same time that the other ASCIIEncoding changes come online.

{
// Tests trying to replace one non-ASCII character with another, which should cause
// fallback logic to identify the invalid data and abort the operation.

Encoding encoding = Encoding.GetEncoding("ascii", new EncoderReplacementFallback("\u1234"), DecoderFallback.ExceptionFallback);
Assert.Throws<ArgumentException>("chars", () => encoding.GetByteCount("\u0080"));
}

[Theory]
[InlineData("hello!", "hello!")]
[InlineData("hello\u1234there!", "helloabcdethere!")]
[InlineData("\ud800\udc00", "abcdeabcde")]
public void GetBytes_WithReplacementFallback(string input, string expectedResult)
{
Encoding encoding = Encoding.GetEncoding("ascii", new EncoderReplacementFallback("abcde"), DecoderFallback.ExceptionFallback);
Assert.Equal(WideToAsciiStr(expectedResult), encoding.GetBytes(input));
}

[Fact]
public void GetBytes_WithNonAsciiInput_AndSingleCharNonAsciiReplacementFallback_Throws()
{
// Tests trying to replace one non-ASCII character with another, which should cause
// fallback logic to identify the invalid data and abort the operation.

Encoding encoding = Encoding.GetEncoding("ascii", new EncoderReplacementFallback("\u1234"), DecoderFallback.ExceptionFallback);
Assert.Throws<ArgumentException>("chars", () => encoding.GetBytes("\u0080"));
}

private static byte[] WideToAsciiStr(string input)
{
return input.Select(ch => (byte)checked((sbyte)ch)).ToArray(); // makes sure each char is 00..7F
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.Text.Tests
{
public class ASCIIEncodingEncode
public partial class ASCIIEncodingEncode
{
public static IEnumerable<object[]> Encode_TestData()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Linq;
using Xunit;

namespace System.Text.Tests
{
public partial class ASCIIEncodingEncode
{
[Theory]
[InlineData("hello!", 6)]
[InlineData("hello\u0080there!", 16)]
[InlineData("\u00ff\u00ff", 10)]
public void GetCharCount_WithReplacementFallback(string input, int expectedCharCount)
{
Encoding encoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new DecoderReplacementFallback("abcde"));
Assert.Equal(expectedCharCount, encoding.GetCharCount(WideToNarrowStr(input)));
}

[Fact]
public void GetCharCount_WithInvalidFallbackBuffer_ValidatesAscii()
{
// Internal fallback logic should notice that we're about to write out a standalone
// surrogate character and should abort the operation.

Encoding encoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new StandaloneLowSurrogateDecoderFallback());
Assert.Throws<ArgumentException>(() => encoding.GetCharCount(new byte[] { 0x80 }));
}

[Theory]
[InlineData("hello!", "hello!")]
[InlineData("hello\u0080there!", "helloabcdethere!")]
[InlineData("\u00ff\u00ff", "abcdeabcde")]
public void GetChars_WithReplacementFallback(string input, string expectedResult)
{
Encoding encoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new DecoderReplacementFallback("abcde"));
Assert.Equal(expectedResult, encoding.GetChars(WideToNarrowStr(input)));
}

[Fact]
public void GetChars_WithNonAsciiInput_AndSingleCharNonAsciiReplacementFallback_Throws()
{
// Internal fallback logic should notice that we're about to write out a standalone
// surrogate character and should abort the operation.

Encoding encoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new StandaloneLowSurrogateDecoderFallback());
Assert.Throws<ArgumentException>(() => encoding.GetChars(new byte[] { 0x80 }));
}

private static byte[] WideToNarrowStr(string input)
{
return input.Select(ch => checked((byte)ch)).ToArray(); // makes sure each char is 00..FF
}

private class StandaloneLowSurrogateDecoderFallback : DecoderFallback
{
public override int MaxCharCount => 1;

public override DecoderFallbackBuffer CreateFallbackBuffer()
{
return new InnerFallbackBuffer();
}

private class InnerFallbackBuffer : DecoderFallbackBuffer
{
private int _remaining;

public override int Remaining => _remaining;

public override bool Fallback(byte[] bytesUnknown, int index)
{
_remaining = 1;
return true;
}

public override char GetNextChar()
{
// Return a standalone low surrogate
return (--_remaining >= 0) ? '\udc00' : default;
}

public override bool MovePrevious()
{
throw new NotImplementedException();
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,48 @@ public class ASCIIEncodingGetMaxByteCount
public void GetMaxByteCount(int charCount)
{
Assert.Equal(charCount + 1, new ASCIIEncoding().GetMaxByteCount(charCount));

// Now test the input for an Encoding which has a zero or negative-length EncoderFallback.MaxCharCount.

Assert.Equal(charCount + 1, Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback).GetMaxByteCount(charCount));
Assert.Equal(charCount + 1, Encoding.GetEncoding("ascii", new CustomLengthEncoderFallback(-5), DecoderFallback.ExceptionFallback).GetMaxByteCount(charCount));
}

[Theory]
[InlineData(0, 5)]
[InlineData(10, 55)]
[InlineData(10_000, 50_005)]
public void GetMaxByteCount_WithLongEncoderFallback(int charCount, int expectedMaxByteCount)
{
Encoding asciiEncoding = Encoding.GetEncoding("ascii", new EncoderReplacementFallback("abcde"), DecoderFallback.ExceptionFallback);
Assert.Equal(expectedMaxByteCount, asciiEncoding.GetMaxByteCount(charCount));
}

[Theory]
[InlineData(-1)]
[InlineData(int.MaxValue)]
public void GetMaxByteCount_WithDefaultEncoder_InvalidArg(int charCount)
{
Assert.Throws<ArgumentOutOfRangeException>("charCount", () => Encoding.ASCII.GetMaxByteCount(charCount));
}

[Fact]
public void GetMaxByteCount_Overflow_WithLongEncoderFallbackMaxCharCount()
{
Encoding asciiEncoding = Encoding.GetEncoding("ascii", new CustomLengthEncoderFallback(1_000_000), DecoderFallback.ExceptionFallback);
Assert.Throws<ArgumentOutOfRangeException>("charCount", () => asciiEncoding.GetMaxByteCount(5_000_000));
}

private class CustomLengthEncoderFallback : EncoderFallback
{
public CustomLengthEncoderFallback(int maxCharCount) { MaxCharCount = maxCharCount; }

public override int MaxCharCount { get; }

public override EncoderFallbackBuffer CreateFallbackBuffer()
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,46 @@ public class ASCIIEncodingGetMaxCharCount
public void GetMaxCharCount(int byteCount)
{
Assert.Equal(byteCount, new ASCIIEncoding().GetMaxCharCount(byteCount));

// Now test the input for an Encoding which has a zero or negative-length DecoderFallback.MaxCharCount.

Assert.Equal(byteCount, Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback).GetMaxCharCount(byteCount));
Assert.Equal(byteCount, Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new CustomLengthDecoderFallback(-5)).GetMaxCharCount(byteCount));
}

[Theory]
[InlineData(0, 0)]
[InlineData(10, 50)]
[InlineData(10_000, 50_000)]
public void GetMaxCharCount_WithLongDecoderFallback(int byteCount, int expectedMaxCharCount)
{
Encoding asciiEncoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new DecoderReplacementFallback("abcde"));
Assert.Equal(expectedMaxCharCount, asciiEncoding.GetMaxCharCount(byteCount));
}

[Fact]
public void GetMaxCharCount_WithDefaultDecoder_InvalidArg()
{
Assert.Throws<ArgumentOutOfRangeException>("byteCount", () => Encoding.ASCII.GetMaxCharCount(-1));
}

[Fact]
public void GetMaxCharCount_Overflow_WithLongDecoderFallbackMaxCharCount()
{
Encoding asciiEncoding = Encoding.GetEncoding("ascii", EncoderFallback.ExceptionFallback, new CustomLengthDecoderFallback(1_000_000));
Assert.Throws<ArgumentOutOfRangeException>("byteCount", () => asciiEncoding.GetMaxCharCount(5_000_000));
}

private class CustomLengthDecoderFallback : DecoderFallback
{
public CustomLengthDecoderFallback(int maxCharCount) { MaxCharCount = maxCharCount; }

public override int MaxCharCount { get; }

public override DecoderFallbackBuffer CreateFallbackBuffer()
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectGuid>{3BB28F2F-51DF-49A3-A0BF-E1C5C0D7E3E0}</ProjectGuid>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand All @@ -8,6 +8,8 @@
<ItemGroup>
<Compile Include="ASCIIEncoding\ASCIIEncodingEncode.cs" />
<Compile Include="ASCIIEncoding\ASCIIEncodingDecode.cs" />
<Compile Include="ASCIIEncoding\ASCIIEncodingEncode.netcoreapp.cs" Condition="'$(TargetGroup)'=='netcoreapp'" />
<Compile Include="ASCIIEncoding\ASCIIEncodingDecode.netcoreapp.cs" Condition="'$(TargetGroup)'=='netcoreapp'" />
<Compile Include="ASCIIEncoding\ASCIIEncodingGetDecoder.cs" />
<Compile Include="ASCIIEncoding\ASCIIEncodingGetEncoder.cs" />
<Compile Include="ASCIIEncoding\ASCIIEncodingGetMaxByteCount.cs" />
Expand Down