diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.cs index a603c7674057..7ceb0ea44f7c 100644 --- a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.cs +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.cs @@ -7,7 +7,7 @@ namespace System.Text.Tests { - public class ASCIIEncodingDecode + public partial class ASCIIEncodingDecode { public static IEnumerable Decode_TestData() { diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.netcoreapp.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.netcoreapp.cs new file mode 100644 index 000000000000..2c2983ae3631 --- /dev/null +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingDecode.netcoreapp.cs @@ -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() + { + // 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("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("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 + } + } +} diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.cs index d1d7bd648de2..d62afa70d2f7 100644 --- a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.cs +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.cs @@ -7,7 +7,7 @@ namespace System.Text.Tests { - public class ASCIIEncodingEncode + public partial class ASCIIEncodingEncode { public static IEnumerable Encode_TestData() { diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.netcoreapp.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.netcoreapp.cs new file mode 100644 index 000000000000..922d9e0acb39 --- /dev/null +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingEncode.netcoreapp.cs @@ -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(() => 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(() => 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(); + } + } + } + } +} diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxByteCount.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxByteCount.cs index 1d29ec8a4145..06c48da46ed0 100644 --- a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxByteCount.cs +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxByteCount.cs @@ -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("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("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(); + } } } } diff --git a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxCharCount.cs b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxCharCount.cs index 11c764100c9c..18b47842307e 100644 --- a/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxCharCount.cs +++ b/src/System.Text.Encoding/tests/ASCIIEncoding/ASCIIEncodingGetMaxCharCount.cs @@ -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("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("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(); + } } } } diff --git a/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj b/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj index b57fdc74f9f7..fc534d19cdc3 100644 --- a/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj +++ b/src/System.Text.Encoding/tests/System.Text.Encoding.Tests.csproj @@ -1,4 +1,4 @@ - + {3BB28F2F-51DF-49A3-A0BF-E1C5C0D7E3E0} true @@ -8,6 +8,8 @@ + +