import 'dart:convert'; import 'package:hashlib_codecs/src/codecs/utf8.dart'; import 'package:hashlib_codecs/src/utf8.dart'; import 'package:test/test.dart'; import './utils.dart'; void main() { group('utf8 test', () { test('ππππππππ', () { var test = r"ππππππππ"; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('encoding <-> decoding', () { for (int i = 0; i < 100; ++i) { var b = randomNumbers(i, stop: 0x0010FFFF); var test = String.fromCharCodes(b); var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual), reason: 'Encoding: #$i'); var decoded = fromUtf8(mine); expect(decoded, equals(test), reason: 'Decoding: #$i'); } }); test('ASCII characters', () { var test = "Hello, World!"; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('2-byte UTF-8 characters', () { var test = "éüçñ"; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('3-byte UTF-8 characters', () { var test = "हिन्दी中文"; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('4-byte UTF-8 characters (emoji)', () { var test = "😀💡🦄"; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('Maximum valid code point (0x10FFFF) encodes/decodes correctly', () { var test = String.fromCharCodes([0x10FFFF]); var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('Boundary values for each UTF-8 range', () { var codePoints = [0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF]; for (var cp in codePoints) { var test = String.fromCharCodes([cp]); var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual), reason: 'Encoding: $cp'); var decoded = fromUtf8(mine); expect(decoded, equals(test), reason: 'Decoding: $cp'); } }); test('Empty string', () { var test = ""; var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); test('Surrogate pairs are encoded/decoded correctly', () { // Dart uses UTF-16, so surrogate pairs for emoji var test = String.fromCharCodes([0xD83D, 0xDE00]); // 😀 var actual = Utf8Encoder().convert(test); var mine = toUtf8(test); expect(mine, equals(actual)); var decoded = fromUtf8(mine); expect(decoded, equals(test)); }); group("Encoder", () { final encoder = UTF8Codec.standard.encoder; test('encoder source', () { expect(encoder.source, 32); }); test('encoder target', () { expect(encoder.target, 8); }); test('Negative code unit throws FormatException', () { expect(() => encoder.convert([-1]), throwsFormatException); }); test('encoder with unpaired low surrogate', () { expect(() => encoder.convert([0xDC00]), throwsFormatException); }); test('encoder with unpaired high surrogate', () { expect(() => encoder.convert([0xD801]), throwsFormatException); }); test('encoder with invalid surrogate pair with low value', () { expect(() => encoder.convert([0xD805, 0xDB00]), throwsFormatException); }); test('encoder with invalid surrogate pair with high value', () { expect(() => encoder.convert([0xD805, 0xEFFF]), throwsFormatException); }); test('encoder with value exceeding range', () { expect(() => encoder.convert([0x110000]), throwsFormatException); }); test('Empty input for decoder returns empty list', () { expect(encoder.convert([]), isEmpty); }); }); group('Decoder', () { final decoder = UTF8Codec.standard.decoder; test('decoder source', () { expect(decoder.source, 8); }); test('decoder target', () { expect(decoder.target, 32); }); test('Insufficient input for 2-byte sequence', () { expect(() => decoder.convert([0xC2]), throwsFormatException); }); test('Invalid continuation byte at 1 for 2-byte sequence', () { expect(() => decoder.convert([0xC2, 0xC2]), throwsFormatException); }); test('Insufficient input for 3-byte sequence', () { expect(() => decoder.convert([0xE1, 0x80]), throwsFormatException); }); test('Invalid continuation byte at 1 for 3-byte sequence', () { expect( () => decoder.convert([0xE1, 0x41, 0x80]), throwsFormatException); }); test('Invalid continuation byte at 2 for 3-byte sequence', () { expect( () => decoder.convert([0xE1, 0x80, 0x41]), throwsFormatException); }); test('Overlong 3-byte sequence', () { expect( () => decoder.convert([0xE0, 0x80, 0xAF]), throwsFormatException); }); test('Invalid surrogate for 3-byte', () { expect( () => decoder.convert([0xED, 0xA1, 0x80]), throwsFormatException); }); test('Insufficient input for 4-byte sequence', () { expect( () => decoder.convert([0xF0, 0x90, 0x80]), throwsFormatException); }); test('Invalid 4-byte lead', () { expect(() => decoder.convert([0xF5, 0x80, 0x80, 0x80]), throwsFormatException); }); test('Overlong 4-byte sequence', () { expect(() => decoder.convert([0xF0, 0x8F, 0x80, 0x80]), throwsFormatException); }); test('Invalid continuation byte at 1 for 4-byte sequence', () { expect(() => decoder.convert([0xF0, 0x41, 0x80, 0x80]), throwsFormatException); }); test('Invalid continuation byte at 2 for 4-byte sequence', () { expect(() => decoder.convert([0xF0, 0x90, 0x41, 0x80]), throwsFormatException); }); test('Invalid continuation byte at 3 for 4-byte sequence', () { expect(() => decoder.convert([0xF0, 0x90, 0x80, 0x41]), throwsFormatException); }); test('Above U+10FFFF', () { expect(() => decoder.convert([0xF4, 0x90, 0x80, 0x80]), throwsFormatException); }); test('Invalid UTF-8 sequence throws FormatException', () { // Invalid continuation byte (should start with 10xxxxxx) var invalid = [0xC2, 0x41]; expect(() => decoder.convert(invalid), throwsFormatException); }); test('Overlong encoding is rejected', () { // Overlong encoding for ASCII 'A' (should be 0x41, not 0xC1 0x81) var overlong = [0xC1, 0x81]; expect(() => decoder.convert(overlong), throwsFormatException); }); test('Code points above Unicode range are rejected', () { // 0x11FFFF is above valid Unicode range var invalid = [0xF4, 0x90, 0x80, 0x80]; expect(() => decoder.convert(invalid), throwsFormatException); }); test('5 or more bytes', () { expect(() => decoder.convert([0xF8]), throwsFormatException); }); }); }); }