diff --git a/.gitignore b/.gitignore index 666e87f..7eb1e1e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ doc/api/ # Dart 2 additions .dart_tool/ + +/coverage/ \ No newline at end of file diff --git a/lib/src/decoder/pretty_print.dart b/lib/src/decoder/pretty_print.dart index a4924bb..7b70e08 100644 --- a/lib/src/decoder/pretty_print.dart +++ b/lib/src/decoder/pretty_print.dart @@ -50,6 +50,14 @@ class _PrettyPrint implements Sink { @override void add(RawValue x) { + if (nested.isNotEmpty) { + var remainingItems = nested.last.remainingItems; + if (remainingItems != null) { + remainingItems -= 1; + nested.last.remainingItems = remainingItems; + } + } + final indentation = ' ' * indent * nested.length; writer.write(indentation); @@ -59,18 +67,6 @@ class _PrettyPrint implements Sink { .map((by) => '${by.toRadixString(CborConstants.hexRadix)} '), ); - if (nested.isNotEmpty) { - var remainingItems = nested.last.remainingItems; - if (remainingItems != null) { - remainingItems -= 1; - nested.last.remainingItems = remainingItems; - - if (remainingItems <= 0) { - nested.removeLast(); - } - } - } - switch (x.header.majorType) { case CborMajorType.uint: writer.write('(int ${x.header.arg.toBigInt()})'); @@ -115,9 +111,7 @@ class _PrettyPrint implements Sink { nested.add(_Nesting(null)); } else { writer.write('(map length ${length.toInt()})'); - nested.add( - _Nesting(length.toInt() * CborConstants.prettyPrintIndent), - ); + nested.add(_Nesting(length.toInt() * 2)); } break; case CborMajorType.tag: @@ -154,7 +148,9 @@ class _PrettyPrint implements Sink { break; case CborAdditionalInfo.breakStop: writer.write('(break)'); - nested.removeLast(); + if (nested.isNotEmpty) { + nested.removeLast(); + } break; default: @@ -164,6 +160,10 @@ class _PrettyPrint implements Sink { } writer.write('\n'); + + while (nested.isNotEmpty && nested.last.remainingItems == 0) { + nested.removeLast(); + } } } diff --git a/scripts/generate_coverage.sh b/scripts/generate_coverage.sh new file mode 100755 index 0000000..9102a4f --- /dev/null +++ b/scripts/generate_coverage.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# +# Generate test coverage +# +dart pub global run coverage:test_with_coverage +cd coverage || exit +genhtml lcov.info -o coverage --no-function-coverage -s -p "$(pwd)" diff --git a/test/int_test.dart b/test/int_test.dart deleted file mode 100644 index e92ed75..0000000 --- a/test/int_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Package : Cbor - * Author : Alex Dochioiu - * Date : 19/06/2024 - * Copyright : Alex Dochioiu - */ - -import 'package:cbor/cbor.dart'; -import 'package:test/test.dart'; - -void main() { - group("Int Numbers Test", () { - final numbersToTest = [ - BigInt.parse("0"), - BigInt.parse("1"), - BigInt.parse("2656159029"), - BigInt.parse("4294967295"), - BigInt.parse("4294967296"), - BigInt.parse("4294967299"), - BigInt.parse("5294967590"), - BigInt.parse("91234567890"), - BigInt.parse("912345678901234567890"), - ]; - - group("positive numbers", () { - for (final number in numbersToTest) { - test(number.toString(), () { - final cborInt = CborInt(number); - final serialized = cborEncode(cborInt); - final deserialized = cborDecode(serialized); - expect(cborInt.toBigInt(), number); - expect(deserialized, isA()); - expect((deserialized as CborInt).toBigInt(), number); - }); - } - }); - - group("negative numbers", () { - for (final positiveNumber in numbersToTest) { - final number = BigInt.zero - positiveNumber; - test(number.toString(), () { - final cborInt = CborInt(number); - final serialized = cborEncode(cborInt); - final deserialized = cborDecode(serialized); - expect(cborInt.toBigInt(), number); - expect(deserialized, isA()); - expect((deserialized as CborInt).toBigInt(), number); - }); - } - }); - }); -} diff --git a/test/json_encoder_test.dart b/test/json_encoder_test.dart index 6323191..f28a030 100644 --- a/test/json_encoder_test.dart +++ b/test/json_encoder_test.dart @@ -225,4 +225,20 @@ void main() { ); }); }); + group('CborJsonEncoder startChunkedConversion', () { + test('works as stream transformer', () async { + final encoder = const CborJsonEncoder(); + + final cborList = CborList([ + CborSmallInt(1), + CborString('hello'), + CborMap({CborString('a'): CborSmallInt(2)}), + ]); + + final stream = Stream.fromIterable([cborList]); + final jsonStrings = await stream.transform(encoder).toList(); + + expect(jsonStrings.join(), equals('[1,"hello",{"a":2}]')); + }); + }); } diff --git a/test/pretty_print_test.dart b/test/pretty_print_test.dart new file mode 100644 index 0000000..71c9c8c --- /dev/null +++ b/test/pretty_print_test.dart @@ -0,0 +1,270 @@ +/* + * Package : Cbor + * Author : S. Hamblett + * Date : 21/02/2026 + * Copyright : S.Hamblett + */ + +import 'package:cbor/cbor.dart'; +import 'package:test/test.dart'; + +void main() { + group('cborPrettyPrint', () { + test('positive integer', () { + final value = CborSmallInt(42); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('18 2a (int 42)')); + }); + + test('negative integer', () { + final value = CborSmallInt(-42); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('38 29 (int -42)')); + }); + + test('large integer', () { + final value = CborInt(BigInt.parse('18446744073709551615')); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals('1b ff ff ff ff ff ff ff ff (int 18446744073709551615)'), + ); + }); + + test('bytes', () { + final value = CborBytes([1, 2, 3]); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('43 1 2 3 (bytes)')); + }); + + test('indefinite length bytes', () { + final value = CborBytes.indefinite([ + [1], + [2, 3], + ]); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + '5f (indefinite length bytes)\n' + ' 41 1 (bytes)\n' + ' 42 2 3 (bytes)\n' + ' ff (break)', + ), + ); + }); + + test('string', () { + final value = CborString('hello'); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('65 68 65 6c 6c 6f (string "hello")')); + }); + + test('indefinite length string', () { + final value = CborString.indefinite(['hel', 'lo']); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + '7f (indefinite length string)\n' + ' 63 68 65 6c (string "hel")\n' + ' 62 6c 6f (string "lo")\n' + ' ff (break)', + ), + ); + }); + + test('array', () { + final value = CborList([CborSmallInt(1), CborSmallInt(2)]); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + '82 (array length 2)\n' + ' 1 (int 1)\n' + ' 2 (int 2)', + ), + ); + }); + + test('indefinite length array', () { + final value = CborList([ + CborSmallInt(1), + CborSmallInt(2), + ], type: CborLengthType.indefinite); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + '9f (indefinite length array)\n' + ' 1 (int 1)\n' + ' 2 (int 2)\n' + ' ff (break)', + ), + ); + }); + + test('map', () { + final value = CborMap({CborSmallInt(1): CborString('one')}); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + 'a1 (map length 1)\n' + ' 1 (int 1)\n' + ' 63 6f 6e 65 (string "one")', + ), + ); + }); + + test('indefinite length map', () { + final value = CborMap({ + CborSmallInt(1): CborString('one'), + }, type: CborLengthType.indefinite); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + 'bf (indefinite length map)\n' + ' 1 (int 1)\n' + ' 63 6f 6e 65 (string "one")\n' + ' ff (break)', + ), + ); + }); + + test('tag', () { + final value = CborDateTimeString( + DateTime.parse('2022-01-01T00:00:00.000Z'), + ); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + 'c0 (tag 0)\n' + '74 32 30 32 32 2d 30 31 2d 30 31 54 30 30 3a 30 30 3a 30 30 5a (string "2022-01-01T00:00:00Z")', + ), + ); + }); + + test('null', () { + final value = CborNull(); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('f6 (null)')); + }); + + test('true', () { + final value = CborBool(true); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('f5 (true)')); + }); + + test('false', () { + final value = CborBool(false); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('f4 (false)')); + }); + + test('undefined', () { + final value = CborUndefined(); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('f7 (undefined)')); + }); + + test('simple float (double)', () { + final value = CborFloat(3.14); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('fb 40 9 1e b8 51 eb 85 1f (3.14)')); + }); + + test('simple float (half and single)', () { + final bytes = [ + 0xf9, 0x3e, 0x00, // half precision 1.5 + 0xfa, 0x3f, 0xc0, 0x00, 0x00, // single precision 1.5 + ]; + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + 'f9 3e 0 (1.5)\n' + 'fa 3f c0 0 0 (1.5)', + ), + ); + }); + + test('simple value', () { + final value = CborSimpleValue(200); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect(pretty.trim(), equals('f8 c8 (simple 200)')); + }); + + test('indent setting', () { + final value = CborMap({CborSmallInt(1): CborString('one')}); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes, indent: 4); + expect( + pretty.trim(), + equals( + 'a1 (map length 1)\n' + ' 1 (int 1)\n' + ' 63 6f 6e 65 (string "one")', + ), + ); + }); + + test('nested map', () { + final value = CborMap({ + CborSmallInt(1): CborMap({CborSmallInt(2): CborString('two')}), + }); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + 'a1 (map length 1)\n' + ' 1 (int 1)\n' + ' a1 (map length 1)\n' + ' 2 (int 2)\n' + ' 63 74 77 6f (string "two")', + ), + ); + }); + + test('nested list', () { + final value = CborList([ + CborList([CborSmallInt(1), CborSmallInt(2)]), + CborString('three'), + ]); + final bytes = cbor.encode(value); + final pretty = cborPrettyPrint(bytes); + expect( + pretty.trim(), + equals( + '82 (array length 2)\n' + ' 82 (array length 2)\n' + ' 1 (int 1)\n' + ' 2 (int 2)\n' + ' 65 74 68 72 65 65 (string "three")', + ), + ); + }); + }); +} diff --git a/test/simple_decoder_rfc_test.dart b/test/simple_decoder_rfc_test.dart index d1a171d..b22cb65 100644 --- a/test/simple_decoder_rfc_test.dart +++ b/test/simple_decoder_rfc_test.dart @@ -851,4 +851,26 @@ void main() { expect(decoded, {'Fun': true, 'Amt': -2}); }); }); + group('CborSimpleDecoder startChunkedConversion', () { + test('works as stream transformer', () async { + final decoder = const CborSimpleDecoder(); + final encoder = const CborSimpleEncoder(); + final bytes1 = encoder.convert([1, 'hello']); + final bytes2 = encoder.convert({'a': 2}); + + final stream = Stream>.fromIterable([ + bytes1.sublist(0, 3), + bytes1.sublist(3), + bytes2, + ]); + final decodedObjects = await stream.transform(decoder).toList(); + expect( + decodedObjects, + equals([ + [1, 'hello'], + {'a': 2}, + ]), + ); + }); + }); } diff --git a/test/simple_encoder_rfc_test.dart b/test/simple_encoder_rfc_test.dart index 6374568..4b0182e 100644 --- a/test/simple_encoder_rfc_test.dart +++ b/test/simple_encoder_rfc_test.dart @@ -538,4 +538,21 @@ void main() { ]); }); }); + group('CborSimpleEncoder startChunkedConversion', () { + test('works as stream transformer', () async { + final encoder = const CborSimpleEncoder(); + final stream = Stream.fromIterable([1, 'hello']); + final encodedBytes = await stream.transform(encoder).toList(); + + final decoder = const CborSimpleDecoder(); + + final flatBytes = encodedBytes.expand((x) => x).toList(); + + final decodedObjects = + await Stream>.fromIterable([ + flatBytes, + ]).transform(decoder).toList(); + expect(decodedObjects, equals([1, 'hello'])); + }); + }); } diff --git a/test/typed_data_test.dart b/test/typed_data_test.dart new file mode 100644 index 0000000..530a3f7 --- /dev/null +++ b/test/typed_data_test.dart @@ -0,0 +1,314 @@ +/* + * Package : Cbor + * Author : S. Hamblett + * Date : 22/02/2026 + * Copyright : S.Hamblett + */ + +@TestOn('vm') +library; + +import 'dart:typed_data'; + +import 'package:cbor/cbor.dart'; +import 'package:test/test.dart'; + +void main() { + group('CborTypedArray', () { + test('CborUint8Array', () { + final arr = CborUint8Array.fromList([1, 2, 255]); + expect(arr.tags, equals([CborTag.uint8Array])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 255])); + }); + + test('CborUint8ClampedArray', () { + final arr = CborUint8ClampedArray.fromList([1, 2, 256, -1]); + expect(arr.tags, equals([CborTag.uint8ArrayClamped])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 255, 0])); + }); + + test('CborUint16BigEndianArray', () { + final arr = CborUint16BigEndianArray.fromList([1, 2, 65535]); + expect(arr.tags, equals([CborTag.uint16ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 65535])); + + expect( + () => CborUint16BigEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborUint16LittleEndianArray', () { + final arr = CborUint16LittleEndianArray.fromList([1, 2, 65535]); + expect(arr.tags, equals([CborTag.uint16ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 65535])); + + expect( + () => CborUint16LittleEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborUint32BigEndianArray', () { + final arr = CborUint32BigEndianArray.fromList([1, 2, 4294967295]); + expect(arr.tags, equals([CborTag.uint32ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 4294967295])); + + expect( + () => CborUint32BigEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborUint32LittleEndianArray', () { + final arr = CborUint32LittleEndianArray.fromList([1, 2, 4294967295]); + expect(arr.tags, equals([CborTag.uint32ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 4294967295])); + + expect( + () => CborUint32LittleEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborUint64BigEndianArray', () { + final arr = CborUint64BigEndianArray.fromList([ + 1, + 2, + 9223372036854775807, + ]); + expect(arr.tags, equals([CborTag.uint64ArrayBE])); + expect( + arr.toObject(), + isA(), + ); // could be Uint64List or List depending on platform + expect(arr.toObject(), equals([1, 2, 9223372036854775807])); + + expect( + () => CborUint64BigEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborUint64LittleEndianArray', () { + final arr = CborUint64LittleEndianArray.fromList([ + 1, + 2, + 9223372036854775807, + ]); + expect(arr.tags, equals([CborTag.uint64ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, 2, 9223372036854775807])); + + expect( + () => CborUint64LittleEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt8Array', () { + final arr = CborInt8Array.fromList([1, -2, 127, -128]); + expect(arr.tags, equals([CborTag.sint8Array])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, -2, 127, -128])); + }); + + test('CborInt16BigEndianArray', () { + final arr = CborInt16BigEndianArray.fromList([1, -2, 32767, -32768]); + expect(arr.tags, equals([CborTag.sint16ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, -2, 32767, -32768])); + + expect( + () => CborInt16BigEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt16LittleEndianArray', () { + final arr = CborInt16LittleEndianArray.fromList([1, -2, 32767, -32768]); + expect(arr.tags, equals([CborTag.sint16ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, -2, 32767, -32768])); + + expect( + () => CborInt16LittleEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt32BigEndianArray', () { + final arr = CborInt32BigEndianArray.fromList([ + 1, + -2, + 2147483647, + -2147483648, + ]); + expect(arr.tags, equals([CborTag.sint32ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, -2, 2147483647, -2147483648])); + + expect( + () => CborInt32BigEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt32LittleEndianArray', () { + final arr = CborInt32LittleEndianArray.fromList([ + 1, + -2, + 2147483647, + -2147483648, + ]); + expect(arr.tags, equals([CborTag.sint32ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1, -2, 2147483647, -2147483648])); + + expect( + () => CborInt32LittleEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt64BigEndianArray', () { + final arr = CborInt64BigEndianArray.fromList([ + 1, + -2, + 9223372036854775807, + -9223372036854775808, + ]); + expect(arr.tags, equals([CborTag.sint64ArrayBE])); + expect(arr.toObject(), isA()); // Int64List or List + expect( + arr.toObject(), + equals([1, -2, 9223372036854775807, -9223372036854775808]), + ); + + expect( + () => CborInt64BigEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborInt64LittleEndianArray', () { + final arr = CborInt64LittleEndianArray.fromList([ + 1, + -2, + 9223372036854775807, + -9223372036854775808, + ]); + expect(arr.tags, equals([CborTag.sint64ArrayLE])); + expect(arr.toObject(), isA()); + expect( + arr.toObject(), + equals([1, -2, 9223372036854775807, -9223372036854775808]), + ); + + expect( + () => CborInt64LittleEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat16BigEndianArray', () { + // 1.5 in half precision is 0x3E00 + final arr = CborFloat16BigEndianArray([0x3e, 0x00]); + expect(arr.toObject(), equals([1.5])); + + expect( + () => CborFloat16BigEndianArray([0x3e]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat16LittleEndianArray', () { + // 1.5 in half precision little endian is 0x003E + final arr = CborFloat16LittleEndianArray([0x00, 0x3e]); + expect(arr.toObject(), equals([1.5])); + + expect( + () => CborFloat16LittleEndianArray([0x00]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat32BigEndianArray', () { + final arr = CborFloat32BigEndianArray.fromList([1.5, -2.5]); + expect(arr.tags, equals([CborTag.float32ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1.5, -2.5])); + + expect( + () => CborFloat32BigEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat32LittleEndianArray', () { + final arr = CborFloat32LittleEndianArray.fromList([1.5, -2.5]); + expect(arr.tags, equals([CborTag.float32ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1.5, -2.5])); + + expect( + () => CborFloat32LittleEndianArray([0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat64BigEndianArray', () { + final arr = CborFloat64BigEndianArray.fromList([1.5, -2.5]); + expect(arr.tags, equals([CborTag.float64ArrayBE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1.5, -2.5])); + + expect( + () => CborFloat64BigEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat64LittleEndianArray', () { + final arr = CborFloat64LittleEndianArray.fromList([1.5, -2.5]); + expect(arr.tags, equals([CborTag.float64ArrayLE])); + expect(arr.toObject(), isA()); + expect(arr.toObject(), equals([1.5, -2.5])); + + expect( + () => CborFloat64LittleEndianArray([0, 0, 0, 0, 0, 0, 0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat128BigEndianArray', () { + expect( + () => CborFloat128BigEndianArray(List.filled(16, 0)).toObject(), + throwsA(isA()), + ); + + expect( + () => CborFloat128BigEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + + test('CborFloat128LittleEndianArray', () { + expect( + () => CborFloat128LittleEndianArray(List.filled(16, 0)).toObject(), + throwsA(isA()), + ); + + expect( + () => CborFloat128LittleEndianArray([0]).toObject(), + throwsA(isA()), + ); + }); + }); +} diff --git a/test/value_test.dart b/test/value_test.dart new file mode 100644 index 0000000..fa21d9b --- /dev/null +++ b/test/value_test.dart @@ -0,0 +1,345 @@ +/* + * Package : Cbor + * Author : S. Hamblett + * Date : 22/02/2026 + * Copyright : S.Hamblett + */ + +import 'dart:convert'; +import 'package:cbor/cbor.dart'; +import 'package:test/test.dart'; + +void main() { + group('multi_dimensional_array', () { + test('CborMultiDimensionalArray fromTypedArray', () { + final typedArray = CborUint8Array.fromList([1, 2, 3, 4, 5, 6]); + final arr = CborMultiDimensionalArray.fromTypedArray([2, 3], typedArray); + expect(arr.dimensions, [2, 3]); + expect(arr.data, isA()); + expect( + arr.toObject(), + equals([ + [2, 3], + [1, 2, 3, 4, 5, 6], + ]), + ); + expect(arr.toJson(), isNotNull); + }); + + test('CborMultiDimensionalArray fromValues', () { + final arr = CborMultiDimensionalArray.fromValues( + [2, -2], + [CborSmallInt(1), CborSmallInt(2)], + ); + expect(arr.dimensions, [2, -2]); + expect(arr.data, isA()); + expect( + arr.toObject(), + equals([ + [2, -2], + [1, 2], + ]), + ); + expect( + arr.toJson(), + equals([ + [2, -2], + [1, 2], + ]), + ); + + final bytes = cbor.encode(arr); + expect(bytes, isNotEmpty); + + final decoded = cbor.decode(bytes); + expect(decoded.tags, contains(CborTag.multiDimensionalArray)); + }); + }); + + group('simple_value', () { + test('CborSimpleValue basics', () { + const val = CborSimpleValue(10, tags: [123]); + expect(val.hashCode, const CborSimpleValue(10, tags: [123]).hashCode); + expect(val.toString(), '10'); + expect(val == const CborSimpleValue(10, tags: [123]), isTrue); + expect(val == const CborSimpleValue(11, tags: [123]), isFalse); + + final sinkObj = val.toObject(); + expect(sinkObj, 10); + + final sinkJson = val.toJson(); + expect(sinkJson, isNull); + }); + + test('CborUndefined toJson', () { + const undef = CborUndefined(); + expect(undef.toJson(), isNull); + }); + }); + + group('string', () { + test('CborString indefinite length', () { + final indefiniteString = CborString.indefinite( + ['hello', 'world'], + tags: [100], + ); + expect(indefiniteString.utf8Bytes, utf8.encode('helloworld')); + expect( + indefiniteString.hashCode, + CborString.indefinite(['hello', 'world'], tags: [100]).hashCode, + ); + expect( + indefiniteString == + CborString.indefinite(['hello', 'world'], tags: [100]), + isTrue, + ); + expect(indefiniteString.toString(allowMalformed: true), 'helloworld'); + expect(indefiniteString.toJson(), 'helloworld'); + + final encoded = cbor.encode(indefiniteString); + expect(encoded, isNotEmpty); + }); + + test('CborEncodeIndefiniteLengthString', () { + final encodeIndef = CborEncodeIndefiniteLengthString(['a', 'b']); + expect(encodeIndef.toObject(), 'ab'); + expect(encodeIndef.toJson(), 'ab'); + final encoded = cbor.encode(encodeIndef); + expect(encoded, isNotEmpty); + }); + + test('CborEncodeDefiniteLengthString', () { + final encodeDef = CborEncodeDefiniteLengthString(CborString('test')); + expect(encodeDef.toObject(), 'test'); + expect(encodeDef.toJson(), 'test'); + final encoded = cbor.encode(encodeDef); + expect(encoded, isNotEmpty); + }); + + test('CborBase64Url', () { + final base64Url = CborBase64Url.encode([1, 2, 3]); + expect(base64Url.toObject(decodeBase64: true), [1, 2, 3]); + expect(base64Url.toObject(decodeBase64: false), base64Url.toString()); + + final fromStr = CborBase64Url.fromString('AQID'); + expect(fromStr.decode(), [1, 2, 3]); + + final fromUtf8 = CborBase64Url.fromUtf8(utf8.encode('AQID')); + expect(fromUtf8.decode(), [1, 2, 3]); + }); + + test('CborRegex and CborMime fromUtf8', () { + final regex = CborRegex.fromUtf8(utf8.encode('.*')); + expect(regex.toString(), '.*'); + + final mime = CborMime.fromUtf8(utf8.encode('text/plain')); + expect(mime.toString(), 'text/plain'); + }); + + test('CborDateTimeString toObject without parse', () { + final dtStr = CborDateTimeString(DateTime(2022)); + expect(dtStr.toObject(parseDateTime: false), dtStr.toString()); + }); + }); + + group('bytes', () { + test('CborBytes basics', () { + final bytesBase64 = CborBytes( + [1, 2, 3], + tags: [CborTag.expectedConversionToBase64], + ); + expect( + bytesBase64.bytesList, + equals([ + [1, 2, 3], + ]), + ); + expect(bytesBase64.toString(), [1, 2, 3].toString()); + expect(bytesBase64.toJson(), base64.encode([1, 2, 3])); + + final bytesBase64Url = CborBytes( + [1, 2, 3], + tags: [CborTag.expectedConversionToBase64Url], + ); + expect( + bytesBase64Url.toJson(), + base64Url.encode([1, 2, 3]).replaceAll('=', ''), + ); + }); + + test('CborBytes indefinite length', () { + final indefBytes = CborBytes.indefinite( + [ + [1, 2], + [3], + ], + tags: [CborTag.expectedConversionToBase64Url], + ); + expect(indefBytes.bytes, [1, 2, 3]); + expect(indefBytes.hashCode, isA()); + expect( + indefBytes == + CborBytes.indefinite( + [ + [1, 2], + [3], + ], + tags: [CborTag.expectedConversionToBase64Url], + ), + isTrue, + ); + expect(indefBytes.toString(), [1, 2, 3].toString()); + expect(indefBytes.toObject(), [1, 2, 3]); + + expect( + indefBytes.toJson(), + base64Url.encode([1, 2, 3]).replaceAll('=', ''), + ); + + final indefBytesBase64 = CborBytes.indefinite( + [ + [1, 2], + [3], + ], + tags: [CborTag.expectedConversionToBase64], + ); + expect(indefBytesBase64.toJson(), base64.encode([1, 2, 3])); + + final encoded = cbor.encode(indefBytes); + expect(encoded, isNotEmpty); + }); + }); + + group('list', () { + test('CborList.generate', () { + final l = CborList.generate(3, (i) => CborSmallInt(i)); + expect(l.length, 3); + expect(l[0], CborSmallInt(0)); + expect(l[1], CborSmallInt(1)); + }); + + test('CborList cyclic', () { + final l = CborList.of([]); + l.add(l); + expect(() => l.toObject(), throwsA(isA())); + expect(() => l.toJson(), throwsA(isA())); + }); + + test('CborEncodeDefiniteLengthList', () { + final defList = CborEncodeDefiniteLengthList(CborList([CborSmallInt(1)])); + expect(defList.toJson(), equals([1])); + expect(defList.toObject(), equals([1])); + }); + + test('CborEncodeIndefiniteLengthList', () { + final indefList = CborEncodeIndefiniteLengthList( + CborList([CborSmallInt(1)]), + ); + expect(indefList.toJson(), equals([1])); + expect(indefList.toObject(), equals([1])); + }); + + test('CborDecimalFraction', () { + final decFrac = CborDecimalFraction( + exponent: CborSmallInt(-2), + mantissa: CborSmallInt(123), + ); + expect(decFrac.toObject(), equals([-2, BigInt.from(123)])); + expect(decFrac.toJson(), equals([-2, 123])); + + final encDef = cbor.encode( + CborDecimalFraction( + exponent: CborSmallInt(-2), + mantissa: CborSmallInt(123), + type: CborLengthType.definite, + ), + ); + expect(encDef, isNotEmpty); + + final encIndef = cbor.encode( + CborDecimalFraction( + exponent: CborSmallInt(-2), + mantissa: CborSmallInt(123), + type: CborLengthType.indefinite, + ), + ); + expect(encIndef, isNotEmpty); + }); + + test('CborRationalNumber', () { + final rational = CborRationalNumber( + numerator: CborSmallInt(1), + denominator: CborSmallInt(3), + ); + expect(rational.toObject(), equals([BigInt.from(1), BigInt.from(3)])); + expect(rational.toJson(), equals([1, 3])); + + final encIndef = cbor.encode( + CborRationalNumber( + numerator: CborSmallInt(1), + denominator: CborSmallInt(3), + type: CborLengthType.indefinite, + ), + ); + expect(encIndef, isNotEmpty); + }); + + test('CborBigFloat', () { + final bigFloat = CborBigFloat( + exponent: CborSmallInt(-2), + mantissa: CborSmallInt(123), + ); + expect(bigFloat.toObject(), equals([-2, BigInt.from(123)])); + expect(bigFloat.toJson(), equals([-2, 123])); + + final encIndef = cbor.encode( + CborBigFloat( + exponent: CborSmallInt(-2), + mantissa: CborSmallInt(123), + type: CborLengthType.indefinite, + ), + ); + expect(encIndef, isNotEmpty); + }); + }); + group("Int Numbers Test", () { + final numbersToTest = [ + BigInt.parse("0"), + BigInt.parse("1"), + BigInt.parse("2656159029"), + BigInt.parse("4294967295"), + BigInt.parse("4294967296"), + BigInt.parse("4294967299"), + BigInt.parse("5294967590"), + BigInt.parse("91234567890"), + BigInt.parse("912345678901234567890"), + ]; + + group("positive numbers", () { + for (final number in numbersToTest) { + test(number.toString(), () { + final cborInt = CborInt(number); + final serialized = cborEncode(cborInt); + final deserialized = cborDecode(serialized); + expect(cborInt.toBigInt(), number); + expect(deserialized, isA()); + expect((deserialized as CborInt).toBigInt(), number); + }); + } + }); + + group("negative numbers", () { + for (final positiveNumber in numbersToTest) { + final number = BigInt.zero - positiveNumber; + test(number.toString(), () { + final cborInt = CborInt(number); + final serialized = cborEncode(cborInt); + final deserialized = cborDecode(serialized); + expect(cborInt.toBigInt(), number); + expect(deserialized, isA()); + expect((deserialized as CborInt).toBigInt(), number); + }); + } + }); + }); +}