blob: 0e4864c1718e5c54aa9493eb6fc4e7648e3d0e47 [file] [log] [blame]
// Copyright (c) 2015, 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:protobuf/protobuf.dart';
import 'package:test/test.dart';
import 'gen/ExtensionEnumNameConflict.pb.dart';
import 'gen/ExtensionNameConflict.pb.dart';
import 'gen/enum_extension.pb.dart';
import 'gen/extend_unittest.pb.dart';
import 'gen/google/protobuf/unittest.pb.dart';
import 'gen/nested_extension.pb.dart';
import 'gen/non_nested_extension.pb.dart';
import 'src/test_util.dart';
Matcher throwsArgError(String expectedMessage) => throwsA(
predicate((dynamic x) {
expect(x, isArgumentError);
expect(x.message, expectedMessage);
return true;
}),
);
final withExtensions =
TestAllExtensions()
..setExtension(
Unittest.optionalForeignMessageExtension,
ForeignMessage()..c = 3,
)
..setExtension(Unittest.defaultStringExtension, 'bar')
..getExtension(Unittest.repeatedBytesExtension).add('pop'.codeUnits)
..setExtension(
Extend_unittest.outer,
Outer()
..inner = (Inner()..value = 'abc')
..setExtension(
Extend_unittest.extensionInner,
Inner()..value = 'def',
),
);
void main() {
test('can set all extension types', () {
final message = TestAllExtensions();
setAllExtensions(message);
assertAllExtensionsSet(message);
});
test('can modify all repeated extension types', () {
final message = TestAllExtensions();
setAllExtensions(message);
modifyRepeatedExtensions(message);
assertRepeatedExtensionsModified(message);
});
test('unset extensions return default values', () {
assertExtensionsClear(TestAllExtensions());
});
// void testExtensionReflectionGetters() {} // UNSUPPORTED -- reflection
// void testExtensionReflectionSetters() {} // UNSUPPORTED -- reflection
// void testExtensionReflectionSettersRejectNull() {} // UNSUPPORTED
// void testExtensionReflectionRepeatedSetters() {} // UNSUPPORTED
// void testExtensionReflectionRepeatedSettersRejectNull() // UNSUPPORTED
// void testExtensionReflectionDefaults() // UNSUPPORTED
test('can clear an optional extension', () {
// clearExtension() is not actually used in test_util, so try it manually.
final message = TestAllExtensions();
message.setExtension(Unittest.optionalInt32Extension, 1);
message.clearExtension(Unittest.optionalInt32Extension);
expect(message.hasExtension(Unittest.optionalInt32Extension), isFalse);
});
test('can clear a repeated extension', () {
final message = TestAllExtensions();
message.addExtension(Unittest.repeatedInt32Extension, 1);
message.clearExtension(Unittest.repeatedInt32Extension);
expect(message.getExtension(Unittest.repeatedInt32Extension).length, 0);
});
test('can clone an extension field', () {
final original = TestAllExtensions();
original.setExtension(Unittest.optionalInt32Extension, 1);
final clone = original.deepCopy();
expect(clone.hasExtension(Unittest.optionalInt32Extension), isTrue);
expect(clone.getExtension(Unittest.optionalInt32Extension), 1);
});
test('can clone all types of extension fields', () {
assertAllExtensionsSet(getAllExtensionsSet().deepCopy());
});
test('can merge extension', () {
final nestedMessage = TestAllTypes_NestedMessage()..i = 42;
final mergeSource =
TestAllExtensions()..setExtension(
Unittest.optionalNestedMessageExtension,
nestedMessage,
);
final nestedMessage2 = TestAllTypes_NestedMessage()..bb = 43;
final mergeDest =
TestAllExtensions()..setExtension(
Unittest.optionalNestedMessageExtension,
nestedMessage2,
);
final result =
TestAllExtensions()
..mergeFromMessage(mergeSource)
..mergeFromMessage(mergeDest);
expect(result.getExtension(Unittest.optionalNestedMessageExtension).i, 42);
expect(result.getExtension(Unittest.optionalNestedMessageExtension).bb, 43);
});
test("throws if field number isn't allowed for extension", () {
final message = TestAllTypes(); // does not allow extensions
expect(
() {
message.setExtension(Unittest.optionalInt32Extension, 0);
},
throwsArgError(
'Extension optionalInt32Extension not legal for message protobuf_unittest.TestAllTypes',
),
);
expect(
() {
message.getExtension(Unittest.optionalInt32Extension);
},
throwsArgError(
'Extension optionalInt32Extension not legal for message protobuf_unittest.TestAllTypes',
),
);
});
test('throws if an int32 extension is set to a bad value', () {
final message = TestAllExtensions();
expect(
() {
message.setExtension(Unittest.optionalInt32Extension, 'hello');
},
throwsArgError(
'Illegal to set field optionalInt32Extension (1) of protobuf_unittest.TestAllExtensions'
' to value (hello): not type int',
),
);
});
test('throws if an int64 extension is set to a bad value', () {
final message = TestAllExtensions();
expect(
() {
message.setExtension(Unittest.optionalInt64Extension, 123);
},
throwsArgError(
'Illegal to set field optionalInt64Extension (2) of protobuf_unittest.TestAllExtensions'
' to value (123): not Int64',
),
);
});
test('throws if a message extension is set to a bad value', () {
final message = TestAllExtensions();
// For a non-repeated message, we only check for a GeneratedMessage.
expect(
() {
message.setExtension(Unittest.optionalNestedMessageExtension, 123);
},
throwsArgError(
'Illegal to set field optionalNestedMessageExtension (18)'
' of protobuf_unittest.TestAllExtensions to value (123): not a GeneratedMessage',
),
);
// For a repeated message, the type check is exact.
expect(() {
message.addExtension(
Unittest.repeatedNestedMessageExtension,
TestAllTypes(),
);
}, throwsATypeError);
});
test('throws if an enum extension is set to a bad value', () {
final message = TestAllExtensions();
// For a non-repeated enum, we only check for a ProtobufEnum.
expect(
() {
message.setExtension(Unittest.optionalNestedEnumExtension, 123);
},
throwsArgError(
'Illegal to set field optionalNestedEnumExtension (21)'
' of protobuf_unittest.TestAllExtensions to value (123): not type ProtobufEnum',
),
);
// For a repeated enum, the type check is exact.
expect(() {
message.addExtension(
Unittest.repeatedForeignEnumExtension,
TestAllTypes_NestedEnum.FOO,
);
}, throwsATypeError);
});
test('can extend a message with a message field with a different type', () {
expect(
Non_nested_extension.nonNestedExtension.makeDefault!(),
TypeMatcher<MyNonNestedExtension>(),
);
expect(Non_nested_extension.nonNestedExtension.name, 'nonNestedExtension');
});
test('can extend a message with a message field of the same type', () {
expect(
MyNestedExtension.recursiveExtension.makeDefault!()
is MessageToBeExtended,
isTrue,
);
expect(MyNestedExtension.recursiveExtension.name, 'recursiveExtension');
});
test('can extend message with enum', () {
final msg = Extendable();
msg.setExtension(Enum_extension.animal, Animal.CAT);
});
test('extension class was renamed to avoid conflict with message', () {
expect(ExtensionNameConflictExt.someExtension.tagNumber, 1);
});
test('extension class was renamed to avoid conflict with enum', () {
expect(ExtensionEnumNameConflictExt.enumConflictExtension.tagNumber, 1);
});
test('to toDebugString', () {
final value =
TestAllExtensions()
..setExtension(Unittest.optionalInt32Extension, 1)
..addExtension(Unittest.repeatedStringExtension, 'hello')
..addExtension(Unittest.repeatedStringExtension, 'world')
..setExtension(
Unittest.optionalNestedMessageExtension,
TestAllTypes_NestedMessage()..i = 42,
)
..setExtension(
Unittest.optionalNestedEnumExtension,
TestAllTypes_NestedEnum.BAR,
);
final expected =
'[optionalInt32Extension]: 1\n'
'[optionalNestedMessageExtension]: {\n'
' i: 42\n'
'}\n'
'[optionalNestedEnumExtension]: BAR\n'
'[repeatedStringExtension]: hello\n'
'[repeatedStringExtension]: world\n';
expect(value.toString(), expected);
});
test('can compare messages with and without extensions', () {
final withExtension =
TestFieldOrderings()
..myString = 'foo'
..setExtension(Unittest.myExtensionString, 'bar');
final b = withExtension.writeToBuffer();
final withUnknownField = TestFieldOrderings.fromBuffer(b);
final r = ExtensionRegistry();
Unittest.registerAllExtensions(r);
final decodedWithExtension = TestFieldOrderings.fromBuffer(b, r);
final noExtension = TestFieldOrderings()..myString = 'foo';
expect(withExtension == decodedWithExtension, true);
expect(withUnknownField == decodedWithExtension, false);
expect(decodedWithExtension == withUnknownField, false);
expect(withUnknownField == noExtension, false);
expect(noExtension == withUnknownField, false);
decodedWithExtension.setExtension(Unittest.myExtensionInt, 42);
expect(withExtension == decodedWithExtension, false);
expect(decodedWithExtension == withExtension, false);
expect(decodedWithExtension == withExtension, false);
});
test(
'ExtensionRegistry.reparseMessage will preserve already registered extensions',
() {
final r1 = ExtensionRegistry();
Unittest.registerAllExtensions(r1);
final r2 = ExtensionRegistry();
Extend_unittest.registerAllExtensions(r2);
final withUnknownFields = TestAllExtensions.fromBuffer(
withExtensions.writeToBuffer(),
);
final reparsedR1 = r1.reparseMessage(withUnknownFields);
expect(
reparsedR1.getExtension(Unittest.optionalForeignMessageExtension).c,
3,
);
expect(
r2
.reparseMessage(reparsedR1)
.getExtension(Unittest.optionalForeignMessageExtension)
.c,
3,
);
},
);
test(
'ExtensionRegistry.reparseMessage reparses extensions that were not in the original registry',
() {
final r = ExtensionRegistry();
Unittest.registerAllExtensions(r);
Extend_unittest.registerAllExtensions(r);
final withUnknownFields = TestAllExtensions.fromBuffer(
withExtensions.writeToBuffer(),
);
final reparsed = r.reparseMessage(withUnknownFields);
expect(
() => withUnknownFields.getExtension(Unittest.defaultStringExtension),
throwsA(const TypeMatcher<StateError>()),
);
expect(reparsed.getExtension(Unittest.defaultStringExtension), 'bar');
expect(
reparsed.unknownFields.getField(
Unittest.defaultStringExtension.tagNumber,
),
null,
reason:
'ExtensionRegistry.reparseMessage does not leave reparsed fields in unknownFields',
);
expect(reparsed.getExtension(Unittest.repeatedBytesExtension), [
'pop'.codeUnits,
]);
expect(
reparsed.getExtension(Unittest.optionalForeignMessageExtension).c,
3,
);
expect(reparsed.getExtension(Extend_unittest.outer).inner.value, 'abc');
final onlyOuter = ExtensionRegistry()..add(Extend_unittest.outer);
final onlyOuterReparsed = onlyOuter.reparseMessage(withUnknownFields);
expect(
onlyOuterReparsed
.getExtension(Extend_unittest.outer)
.hasExtension(Extend_unittest.extensionInner),
isFalse,
);
expect(
() => onlyOuter
.reparseMessage(withUnknownFields)
.getExtension(Extend_unittest.outer)
.getExtension(Extend_unittest.extensionInner),
throwsA(const TypeMatcher<StateError>()),
);
expect(
reparsed
.getExtension(Extend_unittest.outer)
.hasExtension(Extend_unittest.extensionInner),
isTrue,
);
expect(
reparsed
.getExtension(Extend_unittest.outer)
.getExtension(Extend_unittest.extensionInner)
.value,
'def',
);
},
);
test('ExtensionRegistry.reparseMessage does not update the original', () {
final r = ExtensionRegistry();
Unittest.registerAllExtensions(r);
Extend_unittest.registerAllExtensions(r);
final withUnknownFields = TestAllExtensions.fromBuffer(
withExtensions.writeToBuffer(),
);
final reparsedWithEmpty = ExtensionRegistry().reparseMessage(
withUnknownFields,
);
expect(reparsedWithEmpty, same(withUnknownFields));
final reparsed = r.reparseMessage(withUnknownFields);
final strings =
withUnknownFields.getExtension(Unittest.repeatedStringExtension)
as List<String>;
expect(strings, []);
strings.add('pop2');
expect(reparsed.getExtension(Unittest.repeatedStringExtension), []);
});
test('ExtensionRegistry.reparseMessage will throw on malformed buffers', () {
final r = ExtensionRegistry();
Unittest.registerAllExtensions(r);
final r2 = ExtensionRegistry();
Extend_unittest.registerAllExtensions(r2);
// The message encoded in this buffer has an encoding error in the
// Extend_unittest.outer extension field.
final withMalformedExtensionEncoding = TestAllExtensions.fromBuffer([
154, 1, 2, 8, 3, 210, //
4, 3, 98, 97, 114, 194, 6, 14, 10, 5, 10, 4,
97, 98, 99, 18, 5, 10, 3, 100, 101, 102,
]);
expect(
r
.reparseMessage(withMalformedExtensionEncoding)
.getExtension(Unittest.defaultStringExtension),
'bar',
reason: 'this succeeds because it does not decode Extend_unittest.outer',
);
expect(
() => r2.reparseMessage(withMalformedExtensionEncoding),
throwsA(const TypeMatcher<InvalidProtocolBufferException>()),
);
});
test('ExtensionRegistry.reparseMessage preserves frozenness', () {
final r = ExtensionRegistry();
Unittest.registerAllExtensions(r);
final withUnknownFields = TestAllExtensions.fromBuffer(
withExtensions.writeToBuffer(),
);
withUnknownFields.freeze();
expect(
() => r
.reparseMessage(withUnknownFields)
.setExtension(Unittest.defaultStringExtension, 'blah'),
throwsA(TypeMatcher<UnsupportedError>()),
);
});
test(
'ExtensionRegistry.reparseMessage returns the same object when no new extensions are parsed',
() {
final original =
Outer()
..inner =
(Inner()
..innerMost =
(InnerMost()..setExtension(
Extend_unittest.innerMostExtensionString,
'a',
)))
..inners.addAll([
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a')),
Inner()..value = 'value',
])
..innerMap[0] =
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a'));
final reparsed = ExtensionRegistry().reparseMessage(original);
expect(identical(reparsed, original), isTrue);
},
);
test(
'ExtensionRegistry.reparseMessage reparses extension in deeply nested subfield',
() {
final original =
Outer()
..inner =
(Inner()
..innerMost =
(InnerMost()..setExtension(
Extend_unittest.innerMostExtensionString,
'a',
)));
final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
expect(
withUnknownFields.inner.innerMost.hasExtension(
Extend_unittest.innerMostExtensionString,
),
isFalse,
);
final r =
ExtensionRegistry()..add(Extend_unittest.innerMostExtensionString);
final reparsed = r.reparseMessage(withUnknownFields);
expect(identical(reparsed.inner, withUnknownFields.inner), isFalse);
expect(
identical(reparsed.inner.innerMost, withUnknownFields.inner.innerMost),
isFalse,
);
expect(
reparsed.inner.innerMost.hasExtension(
Extend_unittest.innerMostExtensionString,
),
isTrue,
);
},
);
test(
'ExtensionRegistry.reparseMessage reparses extensions in all nested subfields',
() {
final original =
Outer()
..inner =
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a')
..innerMost =
(InnerMost()..setExtension(
Extend_unittest.innerMostExtensionString,
'a',
)));
final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
expect(
withUnknownFields.inner.hasExtension(
Extend_unittest.innerExtensionString,
),
isFalse,
);
expect(
withUnknownFields.inner.innerMost.hasExtension(
Extend_unittest.innerMostExtensionString,
),
isFalse,
);
final r =
ExtensionRegistry()..addAll([
Extend_unittest.innerExtensionString,
Extend_unittest.innerMostExtensionString,
]);
final reparsed = r.reparseMessage(withUnknownFields);
expect(identical(reparsed.inner, withUnknownFields.inner), isFalse);
expect(
identical(reparsed.inner.innerMost, withUnknownFields.inner.innerMost),
isFalse,
);
expect(
reparsed.inner.hasExtension(Extend_unittest.innerExtensionString),
isTrue,
);
expect(
reparsed.inner.innerMost.hasExtension(
Extend_unittest.innerMostExtensionString,
),
isTrue,
);
},
);
test(
'ExtensionRegistry.reparseMessage doesn\'t copy deepest subfield without extensions',
() {
final original =
Outer()
..inner =
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a')
..innerMost = InnerMost());
final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
expect(
withUnknownFields.inner.hasExtension(
Extend_unittest.innerExtensionString,
),
false,
);
final r = ExtensionRegistry()..add(Extend_unittest.innerExtensionString);
final reparsed = r.reparseMessage(withUnknownFields);
expect(identical(reparsed.inner, withUnknownFields.inner), isFalse);
expect(
identical(reparsed.inner.innerMost, withUnknownFields.inner.innerMost),
isTrue,
);
expect(
reparsed.inner.hasExtension(Extend_unittest.innerExtensionString),
isTrue,
);
},
);
test(
'ExtensionRegistry.reparseMessage reparses extensions in repeated fields',
() {
final original =
Outer()
..inners.addAll([
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a')),
Inner()..value = 'value',
]);
final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
expect(
withUnknownFields.inners.first.hasExtension(
Extend_unittest.innerExtensionString,
),
isFalse,
);
final r = ExtensionRegistry()..add(Extend_unittest.innerExtensionString);
final reparsed = r.reparseMessage(withUnknownFields);
expect(
reparsed.inners[0].hasExtension(Extend_unittest.innerExtensionString),
isTrue,
);
expect(
identical(withUnknownFields.inners[1], reparsed.inners[1]),
isTrue,
);
},
);
test(
'ExtensionRegistry.reparseMessage reparses extensions in map fields',
() {
final original =
Outer()
..innerMap[0] =
(Inner()
..setExtension(Extend_unittest.innerExtensionString, 'a'))
..innerMap[1] = (Inner())
..stringMap['hello'] = 'world';
final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
expect(
withUnknownFields.innerMap[0]!.hasExtension(
Extend_unittest.innerExtensionString,
),
isFalse,
);
final r = ExtensionRegistry()..add(Extend_unittest.innerExtensionString);
final reparsed = r.reparseMessage(withUnknownFields);
expect(
reparsed.innerMap[0]!.hasExtension(
Extend_unittest.innerExtensionString,
),
isTrue,
);
expect(
identical(withUnknownFields.innerMap[1], reparsed.innerMap[1]),
isTrue,
);
expect(withUnknownFields.stringMap.length, 1);
expect(reparsed.stringMap.length, 1);
expect(
withUnknownFields.stringMap['hello']!,
reparsed.stringMap['hello']!,
);
},
);
test('consistent hashcode for reparsed messages with extensions', () {
final r = ExtensionRegistry()..add(Extend_unittest.outer);
final m =
TestAllExtensions()..setExtension(
Extend_unittest.outer,
Outer()..inner = (Inner()..value = 'hello'),
);
final b = m.writeToBuffer();
final c = TestAllExtensions.fromBuffer(b);
final d = r.reparseMessage(c);
expect(m.hashCode, d.hashCode);
});
}