| // 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'); |
| }); |
| }); |
| } |