| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'dart:convert'; | |
| import 'dart:core'; | |
| import 'dart:typed_data'; | |
| import 'package:convert/convert.dart'; | |
| import 'package:test/test.dart'; | |
| void main() { | |
| var bytes = Uint8List.fromList([for (var i = 0; i < 256; i++) i]); | |
| for (var cp in [ | |
| latin2, | |
| latin3, | |
| latin4, | |
| latin5, | |
| latin6, | |
| latin7, | |
| latin8, | |
| latin9, | |
| latin10, | |
| latinCyrillic, | |
| latinGreek, | |
| latinHebrew, | |
| latinThai, | |
| latinArabic, | |
| ]) { | |
| group('${cp.name} codepage', () { | |
| test('ascii compatible', () { | |
| for (var byte = 0x20; byte < 0x7f; byte++) { | |
| expect(cp[byte], byte); | |
| } | |
| }); | |
| test('bidirectional mapping', () { | |
| // Maps both directions. | |
| for (var byte = 0; byte < 256; byte++) { | |
| var char = cp[byte]; | |
| if (char != 0xFFFD) { | |
| var string = String.fromCharCode(char); | |
| expect(cp.encode(string), [byte]); | |
| expect(cp.decode([byte]), string); | |
| } | |
| } | |
| }); | |
| test('decode invalid characters not allowed', () { | |
| expect(() => cp.decode([0xfffd]), throwsA(isA<FormatException>())); | |
| }); | |
| test('decode invalid characters allowed', () { | |
| // Decode works like operator[]. | |
| expect( | |
| cp.decode(bytes, allowInvalid: true), | |
| String.fromCharCodes([for (var i = 0; i < 256; i++) cp[i]]), | |
| ); | |
| }); | |
| test('chunked conversion', () { | |
| late final String decodedString; | |
| final outputSink = StringConversionSink.withCallback( | |
| (accumulated) => decodedString = accumulated, | |
| ); | |
| final inputSink = cp.decoder.startChunkedConversion(outputSink); | |
| final expected = StringBuffer(); | |
| for (var byte = 0; byte < 256; byte++) { | |
| var char = cp[byte]; | |
| if (char != 0xFFFD) { | |
| inputSink.add([byte]); | |
| expected.writeCharCode(char); | |
| } | |
| } | |
| inputSink.close(); | |
| expect(decodedString, expected.toString()); | |
| }); | |
| }); | |
| } | |
| test('latin-2 roundtrip', () { | |
| // Data from http://www.columbia.edu/kermit/latin2.html | |
| var latin2text = '\xa0Ą˘Ł¤ĽŚ§¨ŠŞŤŹ\xadŽŻ°ą˛ł´ľśˇ¸šşťź˝žżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇ' | |
| 'ÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙'; | |
| expect(latin2.decode(latin2.encode(latin2text)), latin2text); | |
| }); | |
| test('latin-3 roundtrip', () { | |
| // Data from http://www.columbia.edu/kermit/latin3.html | |
| var latin2text = '\xa0Ħ˘£¤\u{FFFD}Ĥ§¨İŞĞĴ\xad\u{FFFD}ݰħ²³´µĥ·¸ışğĵ½' | |
| '\u{FFFD}żÀÁÂ\u{FFFD}ÄĊĈÇÈÉÊËÌÍÎÏ\u{FFFD}ÑÒÓÔĠÖ×ĜÙÚÛÜŬŜßàáâ' | |
| '\u{FFFD}äċĉçèéêëìíîï\u{FFFD}ñòóôġö÷ĝùúûüŭŝ˙'; | |
| var encoded = latin3.encode(latin2text, invalidCharacter: 0); | |
| var decoded = latin3.decode(encoded, allowInvalid: true); | |
| expect(decoded, latin2text); | |
| }); | |
| group('Custom code page', () { | |
| late final cp = CodePage('custom', "ABCDEF${"\uFFFD" * 250}"); | |
| test('simple encode', () { | |
| var result = cp.encode('BADCAFE'); | |
| expect(result, [1, 0, 3, 2, 0, 5, 4]); | |
| }); | |
| test('unencodable character', () { | |
| expect(() => cp.encode('GAD'), throwsFormatException); | |
| }); | |
| test('unencodable character with invalidCharacter', () { | |
| expect(cp.encode('GAD', invalidCharacter: 0x3F), [0x3F, 0, 3]); | |
| }); | |
| test('simple decode', () { | |
| expect(cp.decode([1, 0, 3, 2, 0, 5, 4]), 'BADCAFE'); | |
| }); | |
| test('undecodable byte', () { | |
| expect(() => cp.decode([6, 1, 255]), throwsFormatException); | |
| }); | |
| test('undecodable byte with allowInvalid', () { | |
| expect(cp.decode([6, 1, 255], allowInvalid: true), '\u{FFFD}B\u{FFFD}'); | |
| }); | |
| test('chunked conversion', () { | |
| late final String decodedString; | |
| final outputSink = StringConversionSink.withCallback( | |
| (accumulated) => decodedString = accumulated, | |
| ); | |
| final inputSink = cp.decoder.startChunkedConversion(outputSink); | |
| inputSink | |
| ..add([1]) | |
| ..add([0]) | |
| ..add([3]) | |
| ..close(); | |
| expect(decodedString, 'BAD'); | |
| }); | |
| test('chunked conversion - byte conversion sink', () { | |
| late final String decodedString; | |
| final outputSink = StringConversionSink.withCallback( | |
| (accumulated) => decodedString = accumulated, | |
| ); | |
| final bytes = [1, 0, 3, 2, 0, 5, 4]; | |
| final inputSink = cp.decoder.startChunkedConversion(outputSink); | |
| expect(inputSink, isA<ByteConversionSink>()); | |
| (inputSink as ByteConversionSink) | |
| ..addSlice(bytes, 1, 3, false) | |
| ..addSlice(bytes, 4, 5, false) | |
| ..addSlice(bytes, 6, 6, true); | |
| expect(decodedString, 'ADA'); | |
| }); | |
| }); | |
| } |