// Copyright (c) 2025, Google Inc. 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 'package:built_value/serializer.dart'; import 'package:test/test.dart'; void main() { group('Map with known specifiedType but missing builder', () { var data = {1: 'one', 2: 'two', 3: 'three'}; var specifiedType = const FullType(Map, [FullType(int), FullType(String)]); var serializers = Serializers(); var serialized = json.decode(json.encode([1, 'one', 2, 'two', 3, 'three'])) as Object; test('cannot be serialized', () { expect(() => serializers.serialize(data, specifiedType: specifiedType), throwsA(const TypeMatcher())); }); test('cannot be deserialized', () { expect( () => serializers.deserialize(serialized, specifiedType: specifiedType), throwsA(const TypeMatcher())); }); }); group('Map with known specifiedType and correct builder', () { var data = {1: 'one', 2: 'two', 3: 'three'}; var specifiedType = const FullType(Map, [FullType(int), FullType(String)]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(specifiedType, () => {})) .build(); var serialized = json.decode(json.encode([1, 'one', 2, 'two', 3, 'three'])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); test('keeps generic type when deserialized', () { expect( serializers .deserialize(serialized, specifiedType: specifiedType) .runtimeType, {}.runtimeType); }); }); group('Map nested left with known specifiedType', () { var data = , String>{ {1: 'one'}: 'one!', {2: 'two'}: 'two!' }; const innerTypeLeft = FullType(Map, [FullType(int), FullType(String)]); var specifiedType = const FullType(Map, [innerTypeLeft, FullType(String)]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(innerTypeLeft, () => {}) ..addBuilderFactory( specifiedType, () => , String>{})) .build(); var serialized = json.decode(json.encode([ [1, 'one'], 'one!', [2, 'two'], 'two!' ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { // `expect` does not deep compare `Map` by key, `toString` is close // enough. expect( serializers .deserialize(serialized, specifiedType: specifiedType) .toString(), data.toString()); }); }); group('Map nested right with known specifiedType', () { var data = >{ 1: {'one': 'one!'}, 2: {'two': 'two!'} }; const innerTypeRight = FullType(Map, [FullType(String), FullType(String)]); var specifiedType = const FullType(Map, [FullType(int), innerTypeRight]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(innerTypeRight, () => {}) ..addBuilderFactory( specifiedType, () => >{})) .build(); var serialized = json.decode(json.encode([ 1, ['one', 'one!'], 2, ['two', 'two!'] ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); }); group('Map nested both with known specifiedType', () { var data = , Map>{ {1: 1}: {'one': 'one!'}, {2: 2}: {'two': 'two!'} }; const MapOfIntIntGenericType = FullType(Map, [FullType(int), FullType(int)]); const MapOfStringStringGenericType = FullType(Map, [FullType(String), FullType(String)]); var specifiedType = const FullType( Map, [MapOfIntIntGenericType, MapOfStringStringGenericType]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(MapOfIntIntGenericType, () => {}) ..addBuilderFactory( MapOfStringStringGenericType, () => {}) ..addBuilderFactory( specifiedType, () => , Map>{})) .build(); var serialized = json.decode(json.encode([ [1, 1], ['one', 'one!'], [2, 2], ['two', 'two!'] ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { // `expect` does not deep compare `Map` by key, `toString` is close // enough. expect( serializers .deserialize(serialized, specifiedType: specifiedType) .toString(), data.toString()); }); test('keeps generic type on deserialization', () { final genericSerializer = (serializers.toBuilder() ..addBuilderFactory( specifiedType, () => , Map>{}) ..addBuilderFactory(MapOfIntIntGenericType, () => {}) ..addBuilderFactory( MapOfStringStringGenericType, () => {})) .build(); expect( genericSerializer .deserialize(serialized, specifiedType: specifiedType) .runtimeType, , Map>{}.runtimeType); }); }); group('Map with Object values', () { var data = {1: 'one', 2: 2, 3: 'three'}; var specifiedType = const FullType(Map, [FullType(int), FullType.unspecified]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(specifiedType, () => {})) .build(); var serialized = json.decode(json.encode([ 1, ['String', 'one'], 2, ['int', 2], 3, ['String', 'three'] ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); }); group('Map with Object keys', () { var data = {1: 'one', 'two': 'two', 3: 'three'}; var specifiedType = const FullType(Map, [FullType.unspecified, FullType(String)]); var serializers = (Serializers().toBuilder() ..addBuilderFactory(specifiedType, () => {})) .build(); var serialized = json.decode(json.encode([ ['int', 1], 'one', ['String', 'two'], 'two', ['int', 3], 'three' ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); }); group('Map with Object keys and values', () { var data = {1: 'one', 'two': 2, 3: 'three'}; var specifiedType = const FullType(Map); var serializers = Serializers(); var serialized = json.decode(json.encode([ ['int', 1], ['String', 'one'], ['String', 'two'], ['int', 2], ['int', 3], ['String', 'three'] ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); }); group('Map with unknown specifiedType', () { var data = {1: 'one', 'two': 2, 3: 'three'}; var specifiedType = FullType.unspecified; var serializers = Serializers(); var serialized = json.decode(json.encode([ 'Map', ['int', 1], ['String', 'one'], ['String', 'two'], ['int', 2], ['int', 3], ['String', 'three'] ])) as Object; test('can be serialized', () { expect(serializers.serialize(data, specifiedType: specifiedType), serialized); }); test('can be deserialized', () { expect(serializers.deserialize(serialized, specifiedType: specifiedType), data); }); }); }