// // EncodingTestBase.cs // // Author: // Alexander Köplinger (alexander.koeplinger@xamarin.com) // // Copyright (C) 2017 Xamarin, Inc. // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Text; using NUnit.Framework; using NUnit.Framework.Constraints; namespace MonoTests.I18N { public class CodePageTestInfo { public int CodePage; public bool IsBrowserDisplay; public bool IsBrowserSave; public bool IsMailNewsDisplay; public bool IsMailNewsSave; public byte SuperscriptFiveReplacementChar; public byte InfinityReplacementChar; public byte FFReplacementChar; public char A0Char; public char? OneChar; public char A8Char; public int? CharsWritten; public byte? SupplementChar; public bool SkipGetBytes7Test; public bool SkipEncoderFallbackTest; public bool SkipEncoderFallback2Test; public override string ToString () => "Codepage " + Convert.ToString (CodePage); } // // NOTE: when adding/updating tests here consider updating // the following files as well since they have similar tests: // // - mcs/class/corlib/Test/System.Text/ASCIIEncodingTest.cs // - mcs/class/corlib/Test/System.Text/Latin1EncodingTest.cs // public abstract class EncodingTestBase { private char[] testchars; private byte[] testbytes; [SetUp] public void SetUp () { testchars = new char[4]; testchars[0] = 'T'; testchars[1] = 'e'; testchars[2] = 's'; testchars[3] = 't'; testbytes = new byte[4]; testbytes[0] = (byte) 'T'; testbytes[1] = (byte) 'e'; testbytes[2] = (byte) 's'; testbytes[3] = (byte) 't'; } [Test] [TestCaseSource ("codepageTestInfos")] public void IsBrowserDisplay (CodePageTestInfo cpInfo) { Assert.AreEqual (cpInfo.IsBrowserDisplay, Encoding.GetEncoding (cpInfo.CodePage).IsBrowserDisplay); } [Test] [TestCaseSource ("codepageTestInfos")] public void IsBrowserSave (CodePageTestInfo cpInfo) { Assert.AreEqual (cpInfo.IsBrowserSave, Encoding.GetEncoding (cpInfo.CodePage).IsBrowserSave); } [Test] [TestCaseSource ("codepageTestInfos")] public void IsMailNewsDisplay (CodePageTestInfo cpInfo) { Assert.AreEqual (cpInfo.IsMailNewsDisplay, Encoding.GetEncoding (cpInfo.CodePage).IsMailNewsDisplay); } [Test] [TestCaseSource ("codepageTestInfos")] public void IsMailNewsSave (CodePageTestInfo cpInfo) { Assert.AreEqual (cpInfo.IsMailNewsSave, Encoding.GetEncoding (cpInfo.CodePage).IsMailNewsSave); } [Test] // Test GetBytes(char[]) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes1 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = test_encoding.GetBytes(testchars); for (int i = 0; i < testchars.Length; i++) Assert.AreEqual (testchars[i], (char) bytes[i]); } [Test] // Test GetBytes(char[], int, int) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes2 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = test_encoding.GetBytes(testchars, 1, 1); Assert.AreEqual (1, bytes.Length, "#1"); Assert.AreEqual (testchars [1], (char) bytes [0], "#2"); } [Test] // Test non-Latin1 char in char[] [TestCaseSource ("codepageTestInfos")] public void TestGetBytes3 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); testchars[2] = (char) 0x100; byte[] bytes = test_encoding.GetBytes(testchars); Assert.AreEqual ('T', (char) bytes [0], "#1"); Assert.AreEqual ('e', (char) bytes [1], "#2"); Assert.AreEqual (cpInfo.FFReplacementChar, (char) bytes [2], "#3"); Assert.AreEqual ('t', (char) bytes [3], "#4"); } [Test] // Test GetBytes(char[], int, int, byte[], int) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes4 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = new Byte[1]; int cnt = test_encoding.GetBytes(testchars, 1, 1, bytes, 0); Assert.AreEqual (1, cnt, "#1"); Assert.AreEqual (testchars [1], (char) bytes [0], "#2"); } [Test] // Test GetBytes(string, int, int, byte[], int) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes5 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = new Byte[1]; int cnt = test_encoding.GetBytes("Test", 1, 1, bytes, 0); Assert.AreEqual ('e', (char) bytes [0], "#1"); } [Test] // Test GetBytes(string) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes6 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = test_encoding.GetBytes("Test"); for (int i = 0; i < testchars.Length; i++) Assert.AreEqual (testchars [i], (char) bytes [i]); } [Test] // Test GetBytes(string) [TestCaseSource ("codepageTestInfos")] public void TestGetBytes7 (CodePageTestInfo cpInfo) { if (cpInfo.SkipGetBytes7Test) Assert.Ignore ("Codepage indicates this test should be skipped."); var test_encoding = Encoding.GetEncoding (cpInfo.CodePage); var expected = new byte [] { 0x3F, 0x20, cpInfo.SuperscriptFiveReplacementChar, 0x20, cpInfo.InfinityReplacementChar }; if (cpInfo.SupplementChar.HasValue) { var expectedNew = new byte [expected.Length + 1]; expected.CopyTo (expectedNew, 0); expectedNew [expected.Length] = cpInfo.SupplementChar.Value; expected = expectedNew; } var actual = test_encoding.GetBytes("\u24c8 \u2075 \u221e"); // normal replacement Assert.AreEqual (expected, actual, "#1"); expected = new byte [] { 0x3F, 0x3F }; actual = test_encoding.GetBytes("\ud83d\ude0a"); // surrogate pair replacement Assert.AreEqual (expected, actual, "#2"); expected = new byte [] { 0x3F, 0x3F, 0x20 }; actual = test_encoding.GetBytes("\ud83d\ude0a "); // surrogate pair replacement Assert.AreEqual (expected, actual, "#3"); expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 }; actual = test_encoding.GetBytes(" \ud83d\ude0a "); // surrogate pair replacement Assert.AreEqual (expected, actual, "#4"); expected = new byte [] { 0x20, 0x20, 0x3F, 0x3F, 0x20, 0x20 }; actual = test_encoding.GetBytes(" \ud834\udd1e "); // surrogate pair replacement Assert.AreEqual (expected, actual, "#5"); expected = new byte [] { 0x41, 0x42, 0x43, 0x00, 0x41, 0x42, 0x43 }; actual = test_encoding.GetBytes("ABC\0ABC"); // embedded zero byte not replaced Assert.AreEqual (expected, actual, "#6"); expected = new byte [] { 0x20, 0x20, 0x3F, 0x20, 0x20 }; actual = test_encoding.GetBytes(" \ud834 "); // invalid surrogate pair replacement Assert.AreEqual (expected, actual, "#7"); } [Test] // Test GetChars(byte[]) [TestCaseSource ("codepageTestInfos")] public void TestGetChars1 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); char[] chars = test_encoding.GetChars(testbytes); for (int i = 0; i < testbytes.Length; i++) Assert.AreEqual (testbytes[i], (byte) chars[i]); } [Test] // Test GetChars(byte[], int, int) [TestCaseSource ("codepageTestInfos")] public void TestGetChars2 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); char[] chars = test_encoding.GetChars(testbytes, 1, 1); Assert.AreEqual (1, chars.Length, "#1"); Assert.AreEqual (testbytes [1], (byte) chars [0], "#2"); } [Test] // Test GetChars(byte[], int, int, char[], int) [TestCaseSource ("codepageTestInfos")] public void TestGetChars4 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); char[] chars = new char[1]; int cnt = test_encoding.GetChars(testbytes, 1, 1, chars, 0); Assert.AreEqual (1, cnt, "#1"); Assert.AreEqual (testbytes [1], (byte) chars [0], "#2"); } [Test] // Test GetString(char[]) [TestCaseSource ("codepageTestInfos")] public void TestGetString1 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); string str = test_encoding.GetString(testbytes); Assert.AreEqual ("Test", str); } [Test] // Test GetString(char[], int, int) [TestCaseSource ("codepageTestInfos")] public void TestGetString2 (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); string str = test_encoding.GetString(testbytes, 1, 2); Assert.AreEqual ("es", str); } [Test] // Test Decoder [TestCaseSource ("codepageTestInfos")] public void TestDecoder (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); char[] chars = new char[1]; int cnt = test_encoding.GetDecoder().GetChars(testbytes, 1, 1, chars, 0); Assert.AreEqual (1, cnt, "#1"); Assert.AreEqual (testbytes [1], (byte) chars [0], "#2"); } [Test] // Test Decoder [TestCaseSource ("codepageTestInfos")] public void TestEncoder (CodePageTestInfo cpInfo) { Encoding test_encoding = Encoding.GetEncoding (cpInfo.CodePage); byte[] bytes = new Byte[1]; int cnt = test_encoding.GetEncoder().GetBytes(testchars, 1, 1, bytes, 0, false); Assert.AreEqual (1, cnt, "#1"); Assert.AreEqual (testchars [1], (char) bytes [0], "#2"); } [Test] [TestCaseSource ("codepageTestInfos")] public void TestZero (CodePageTestInfo cpInfo) { Encoding encoding = Encoding.GetEncoding (cpInfo.CodePage); Assert.AreEqual (string.Empty, encoding.GetString (new byte [0]), "#1"); Assert.AreEqual (string.Empty, encoding.GetString (new byte [0], 0, 0), "#2"); } [Test] [ExpectedException (typeof (EncoderFallbackException))] [TestCaseSource ("codepageTestInfos")] public void EncoderFallback (CodePageTestInfo cpInfo) { if (cpInfo.SkipEncoderFallbackTest) Assert.Ignore ("Codepage indicates this test should be skipped."); Encoding e = Encoding.GetEncoding (cpInfo.CodePage).Clone () as Encoding; e.EncoderFallback = new EncoderExceptionFallback (); e.GetBytes ("\u24c8"); } [Test] [TestCaseSource ("codepageTestInfos")] public void EncoderFallback2 (CodePageTestInfo cpInfo) { if (cpInfo.SkipEncoderFallback2Test) Assert.Ignore ("Codepage indicates this test should be skipped."); Encoding e = Encoding.GetEncoding (cpInfo.CodePage).Clone () as Encoding; e.EncoderFallback = new BackslashEncoderReplaceFallback (); byte[] bytes = e.GetBytes ("a\xac\u1234\u20ac\u8000"); var expected = new byte[] { 0x61, 0xAC, 0x5C, 0x75, 0x31, 0x32, 0x33, 0x34, 0x5C, 0x75, 0x32, 0x30, 0x61, 0x63, 0x5C, 0x75, 0x38, 0x30, 0x30, 0x30 }; // FIXME: some codepages do have U+00AC (logical not sign) or U+20AC (euro sign), we need to adapt the tests // Assert.AreEqual (expected, bytes); bytes = e.GetBytes ("1\u04d92"); expected = new byte[] { 0x31, 0x5C, 0x75, 0x30, 0x34, 0x64, 0x39, 0x32 }; Assert.AreEqual (expected, bytes); e.EncoderFallback = new EncoderExceptionOnWrongIndexFallback ('\u04d9', 1); bytes = e.GetBytes ("1\u04d92"); expected = new byte[] { 0x31, 0x21, 0x32 }; Assert.AreEqual (expected, bytes); e.EncoderFallback = new EncoderExceptionOnWrongIndexFallback ('\u04d9', 0); bytes = e.GetBytes ("\u04d921"); expected = new byte[] { 0x21, 0x32, 0x31 }; Assert.AreEqual (expected, bytes); } [Test] // [ExpectedException (typeof (ArgumentException))] [TestCaseSource ("codepageTestInfos")] public void DecoderFallback2 (CodePageTestInfo cpInfo) { var bytes = new byte[] { 0x30, 0xa0, 0x31, 0xa8 }; var enc = (Encoding)Encoding.GetEncoding (cpInfo.CodePage).Clone (); enc.DecoderFallback = new TestFallbackDecoder (); var chars = new char [7]; var ret = enc.GetChars (bytes, 0, bytes.Length, chars, 0); } [Test] [TestCaseSource ("codepageTestInfos")] public void DecoderFallback3 (CodePageTestInfo cpInfo) { var bytes = new byte[] { 0x30, 0xa0, 0x31, 0xa8 }; var enc = (Encoding)Encoding.GetEncoding (cpInfo.CodePage).Clone (); enc.DecoderFallback = new TestFallbackDecoder (); var chars = new char[] { '9', '8', '7', '6', '5' }; var ret = enc.GetChars (bytes, 0, bytes.Length, chars, 0); Assert.That (ret, Is.EqualTo (cpInfo.CharsWritten ?? 4), "ret"); Assert.That (chars [0], Is.EqualTo ('0'), "chars[0]"); Assert.That (chars [1], Is.EqualTo (cpInfo.A0Char), "chars[1]"); Assert.That (chars [2], Is.EqualTo ((cpInfo).OneChar ?? '1'), "chars[2]"); Assert.That (chars [3], Is.EqualTo ((char)cpInfo.A8Char), "chars[3]"); Assert.That (chars [4], Is.EqualTo ('5'), "chars[4]"); } class TestFallbackDecoder : DecoderFallback { const int count = 2; public override int MaxCharCount { get { return count; } } public override DecoderFallbackBuffer CreateFallbackBuffer () { return new Buffer (); } class Buffer : DecoderFallbackBuffer { char[] queue; int index; public override int Remaining { get { return queue.Length - index; } } public override char GetNextChar () { return index < queue.Length ? queue [index++] : '\0'; } public override bool Fallback (byte[] bytes, int unused) { queue = new char[bytes.Length * count]; index = 0; for (int i = 0; i < bytes.Length; i++) { for (int j = 0; j < count; j++) queue [index++] = (char)(bytes [i]+j); } return true; } public override bool MovePrevious () { throw new NotImplementedException (); } public override void Reset () { base.Reset (); } } } class BackslashEncoderReplaceFallback : EncoderFallback { class BackslashReplaceFallbackBuffer : EncoderFallbackBuffer { List _buffer = new List (); int _index; public override bool Fallback (char charUnknownHigh, char charUnknownLow, int index) { throw new NotImplementedException (); return false; } public override bool Fallback (char charUnknown, int index) { _buffer.Add('\\'); int val = (int)charUnknown; if (val > 0xFF) { _buffer.Add ('u'); AddCharacter (val >> 8); AddCharacter (val & 0xFF); } else { _buffer.Add ('x'); AddCharacter (charUnknown); } return true; } private void AddCharacter (int val) { AddOneDigit (((val) & 0xF0) >> 4); AddOneDigit (val & 0x0F); } private void AddOneDigit (int val) { if (val > 9) { _buffer.Add ((char)('a' + val - 0x0A)); } else { _buffer.Add ((char)('0' + val)); } } public override char GetNextChar () { if (_index == _buffer.Count) return Char.MinValue; return _buffer[_index++]; } public override bool MovePrevious () { if (_index > 0){ _index--; return true; } return false; } public override int Remaining { get { return _buffer.Count - _index; } } } public override EncoderFallbackBuffer CreateFallbackBuffer () { return new BackslashReplaceFallbackBuffer (); } public override int MaxCharCount { get { throw new NotImplementedException (); } } } class EncoderExceptionOnWrongIndexFallback : EncoderFallback { char _expectedCharUnknown; int _expectedIndex; public EncoderExceptionOnWrongIndexFallback (char expectedCharUnknown, int expectedIndex) { _expectedCharUnknown = expectedCharUnknown; _expectedIndex = expectedIndex; } public override EncoderFallbackBuffer CreateFallbackBuffer () { return new EncoderExceptionOnWrongIndexFallbackBuffer (_expectedCharUnknown, _expectedIndex); } public override int MaxCharCount => 1; class EncoderExceptionOnWrongIndexFallbackBuffer : EncoderFallbackBuffer { char _expectedCharUnknown; int _expectedIndex; bool read; public EncoderExceptionOnWrongIndexFallbackBuffer (char expectedCharUnknown, int expectedIndex) { _expectedCharUnknown = expectedCharUnknown; _expectedIndex = expectedIndex; } public override int Remaining => read ? 0 : 1; public override bool Fallback (char charUnknown, int index) { Assert.AreEqual (_expectedCharUnknown, charUnknown); Assert.AreEqual (_expectedIndex, index); return true; } public override bool Fallback (char charUnknownHigh, char charUnknownLow, int index) { throw new NotImplementedException (); return true; } public override char GetNextChar () { if (!read) { read = true; return '!'; } return '\0'; } public override bool MovePrevious () { return false; } } } } }