blob: 12fa16f55cf473a88dcbde3c4a5e062c28aab0f2 [file] [log] [blame]
// Copyright (c) 2013, 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 'package:fixnum/fixnum.dart';
import 'package:protobuf/protobuf.dart';
import 'package:test/test.dart';
import '../out/protos/foo.pb.dart' as foo;
import '../out/protos/google/protobuf/unittest.pb.dart';
import '../out/protos/map_enum_value.pb.dart';
import 'test_util.dart';
void main() {
final testAllJsonTypes = '{"1":101,"2":"102","3":103,"4":"104",'
'"5":105,"6":"106","7":107,"8":"108","9":109,"10":"110","11":111,'
'"12":112,"13":true,"14":"115","15":"MTE2","16":{"17":117},'
'"18":{"1":118},"19":{"1":119},"20":{"1":120},"21":3,"22":6,"23":9,'
'"24":"124","25":"125","31":[201,301],"32":["202","302"],'
'"33":[203,303],"34":["204","304"],"35":[205,305],"36":["206","306"],'
'"37":[207,307],"38":["208","308"],"39":[209,309],"40":["210","310"],'
'"41":[211,311],"42":[212,312],"43":[true,false],'
'"44":["215","315"],"45":["MjE2","MzE2"],"46":[{"47":217},{"47":317}],'
'"48":[{"1":218},{"1":318}],"49":[{"1":219},{"1":319}],'
'"50":[{"1":220},{"1":320}],"51":[2,3],"52":[5,6],"53":[8,9],'
'"54":["224","324"],"55":["225","325"],"61":401,"62":"402","63":403,'
'"64":"404","65":405,"66":"406","67":407,"68":"408","69":409,'
'"70":"410","71":411,"72":412,"73":false,"74":"415","75":"NDE2",'
'"81":1,"82":4,"83":7,"84":"424","85":"425"}';
/// Checks that the message, once serialized to JSON, matches
/// [testAllJsonTypes] massaged with `replaceAll(from, to)`.
Matcher expectedJson(String from, String to) {
final expectedJson = testAllJsonTypes.replaceAll(from, to);
return predicate(
(GeneratedMessage message) => message.writeToJson() == expectedJson,
'Incorrect output');
}
test('testUnsignedOutput', () {
final message = TestAllTypes();
// These values selected because:
// (1) large enough to set the sign bit
// (2) don't set all of the first 10 bits under the sign bit
// (3) are near each other
message.optionalUint64 = Int64.parseHex('f0000000ffff0000');
message.optionalFixed64 = Int64.parseHex('f0000000ffff0001');
final expectedJsonValue =
'{"4":"17293822573397606400","8":"17293822573397606401"}';
expect(message.writeToJson(), expectedJsonValue);
});
test('testOutput', () {
expect(getAllSet().writeToJson(), testAllJsonTypes);
// Test empty list.
expect(getAllSet()..repeatedBool.clear(),
expectedJson('"43":[true,false],', ''));
// Test negative number.
expect(getAllSet()..optionalInt32 = -1234567,
expectedJson(':101,', ':-1234567,'));
// All 64-bit numbers are quoted.
expect(getAllSet()..optionalInt64 = make64(0, 0x200000),
expectedJson(':"102",', ':"9007199254740992",'));
expect(getAllSet()..optionalInt64 = make64(1, 0x200000),
expectedJson(':"102",', ':"9007199254740993",'));
expect(getAllSet()..optionalInt64 = -make64(0, 0x200000),
expectedJson(':"102",', ':"-9007199254740992",'));
expect(getAllSet()..optionalInt64 = -make64(1, 0x200000),
expectedJson(':"102",', ':"-9007199254740993",'));
// Quotes, backslashes, and control characters in strings are quoted.
expect(getAllSet()..optionalString = 'a\u0000b\u0001cd\\e"fg',
expectedJson(':"115",', ':"a\\u0000b\\u0001cd\\\\e\\"fg",'));
});
test('testBase64Encode', () {
expect(getAllSet()..optionalBytes = 'Hello, world'.codeUnits,
expectedJson(':"MTE2",', ':"SGVsbG8sIHdvcmxk",'));
expect(getAllSet()..optionalBytes = 'Hello, world!'.codeUnits,
expectedJson(':"MTE2",', ':"SGVsbG8sIHdvcmxkIQ==",'));
expect(getAllSet()..optionalBytes = 'Hello, world!!'.codeUnits,
expectedJson(':"MTE2",', ':"SGVsbG8sIHdvcmxkISE=",'));
// An empty list should not appear in the output.
expect(getAllSet()..optionalBytes = [], expectedJson('"15":"MTE2",', ''));
expect(getAllSet()..optionalBytes = 'a'.codeUnits,
expectedJson(':"MTE2",', ':"YQ==",'));
});
test('testBase64Decode', () {
String optionalBytes(String from, String to) {
final json = testAllJsonTypes.replaceAll(from, to);
return String.fromCharCodes(TestAllTypes.fromJson(json).optionalBytes);
}
expect(optionalBytes(':"MTE2",', ':"SGVsbG8sIHdvcmxk",'), 'Hello, world');
expect(
optionalBytes(':"MTE2",', ':"SGVsbG8sIHdvcmxkIQ==",'), 'Hello, world!');
expect(optionalBytes(':"MTE2",', ':"SGVsbG8sIHdvcmxkISE=",'),
'Hello, world!!');
// Remove optionalBytes tag, reads back as empty list, hence empty string.
expect(optionalBytes('"15":"MTE2",', ''), isEmpty);
// Keep optionalBytes tag, set data to empty string, get back empty list.
expect(optionalBytes(':"MTE2",', ':"",'), isEmpty);
expect(optionalBytes(':"MTE2",', ':"YQ==",'), 'a');
});
test('testParseUnsigned', () {
final parsed = TestAllTypes.fromJson(
'{"4":"17293822573397606400","8":"17293822573397606401"}');
final expected = TestAllTypes();
expected.optionalUint64 = Int64.parseHex('f0000000ffff0000');
expected.optionalFixed64 = Int64.parseHex('f0000000ffff0001');
expect(parsed, expected);
});
group('testConvertDouble', () {
test('WithDecimal', () {
final json = '{"12":1.2}';
final proto = TestAllTypes()..optionalDouble = 1.2;
expect(TestAllTypes.fromJson(json), proto);
expect(proto.writeToJson(), json);
});
test('WholeNumber', () {
final json = '{"12":5}';
final proto = TestAllTypes()..optionalDouble = 5.0;
expect(TestAllTypes.fromJson(json), proto);
expect(proto.writeToJson(), json);
});
test('Infinity', () {
final json = '{"12":"Infinity"}';
final proto = TestAllTypes()..optionalDouble = double.infinity;
expect(TestAllTypes.fromJson(json), proto);
expect(proto.writeToJson(), json);
});
test('NegativeInfinity', () {
final json = '{"12":"-Infinity"}';
final proto = TestAllTypes()..optionalDouble = double.negativeInfinity;
expect(TestAllTypes.fromJson(json), proto);
expect(proto.writeToJson(), json);
});
});
test('testParseUnsignedLegacy', () {
final parsed = TestAllTypes.fromJson(
'{"4":"-1152921500311945216","8":"-1152921500311945215"}');
final expected = TestAllTypes();
expected.optionalUint64 = Int64.parseHex('f0000000ffff0000');
expected.optionalFixed64 = Int64.parseHex('f0000000ffff0001');
expect(parsed, expected);
});
test('testFixed32IntNegative', () {
var message = foo.Inner.fromJson('{"5": -1}');
expect(message.count, 4294967295);
message = foo.Inner.fromJson('{"5": -2080294357}');
expect(message.count, 2214672939);
});
test('testUint32Negative', () {
var message = foo.Inner.fromJson('{"6": -1}');
expect(message.countUint32, 4294967295);
message = foo.Inner.fromJson('{"6": -2080294357}');
expect(message.countUint32, 2214672939);
});
test('testParse', () {
expect(TestAllTypes.fromJson(testAllJsonTypes), getAllSet());
});
test('testExtensionsOutput', () {
expect(getAllExtensionsSet().writeToJson(), testAllJsonTypes);
});
test('testExtensionsParse', () {
final registry = getExtensionRegistry();
expect(TestAllExtensions.fromJson(testAllJsonTypes, registry),
getAllExtensionsSet());
});
test('testUnknownEnumValueInOptionalField', () {
// optional NestedEnum optional_nested_enum = 21;
final message = TestAllTypes.fromJson('{"21": 4}');
// 4 is an unknown value.
expect(message.optionalNestedEnum, equals(TestAllTypes_NestedEnum.FOO));
});
test('testUnknownEnumValueInRepeatedField', () {
// repeated NestedEnum repeated_nested_enum = 51;
var message = TestAllTypes.fromJson('{"51": [4]}');
// 4 is an unknown value, which should default to the enum's default value.
expect(message.repeatedNestedEnum, equals([TestAllTypes_NestedEnum.FOO]));
// 1 (FOO) and 2 (BAR) are known values. All unknowns should fill in to
// the default enum value (FOO).
message = TestAllTypes.fromJson('{"51": [1, 4, 2, 4, 1, 4]}');
expect(
message.repeatedNestedEnum,
equals([
TestAllTypes_NestedEnum.FOO,
TestAllTypes_NestedEnum.FOO,
TestAllTypes_NestedEnum.BAR,
TestAllTypes_NestedEnum.FOO,
TestAllTypes_NestedEnum.FOO,
TestAllTypes_NestedEnum.FOO
]));
});
test('testUnknownEnumValueInMapField', () {
final key = 'new_field';
// Only 0 and 1 are known enum values.
final message = MapEnumValue()..mergeFromJson('{"1":[{"1":"$key","2":2}]}');
expect(message.values[key], equals(MapEnumValue_NestedEnum.UNKNOWN));
});
}