blob: b06d75cb1e457ed35935726ff158a7680e22a71d [file] [log] [blame]
#!/usr/bin/env dart
// 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.
library extension_test;
import 'package:protobuf/protobuf.dart';
import 'package:test/test.dart';
import '../out/protos/google/protobuf/unittest.pb.dart';
import '../out/protos/enum_extension.pb.dart';
import '../out/protos/extend_unittest.pb.dart';
import '../out/protos/nested_extension.pb.dart';
import '../out/protos/non_nested_extension.pb.dart';
import '../out/protos/ExtensionNameConflict.pb.dart';
import '../out/protos/ExtensionEnumNameConflict.pb.dart';
import 'test_util.dart';
Matcher throwsArgError(String expectedMessage) => throwsA(predicate((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', () {
var message = TestAllExtensions();
setAllExtensions(message);
assertAllExtensionsSet(message);
});
test('can modify all repeated extension types', () {
var 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.
var message = TestAllExtensions();
message.setExtension(Unittest.optionalInt32Extension, 1);
message.clearExtension(Unittest.optionalInt32Extension);
expect(message.hasExtension(Unittest.optionalInt32Extension), isFalse);
});
test('can clear a repeated extension', () {
var message = TestAllExtensions();
message.addExtension(Unittest.repeatedInt32Extension, 1);
message.clearExtension(Unittest.repeatedInt32Extension);
expect(message.getExtension(Unittest.repeatedInt32Extension).length, 0);
});
test('can clone an extension field', () {
var original = TestAllExtensions();
original.setExtension(Unittest.optionalInt32Extension, 1);
var 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', () {
var nestedMessage = TestAllTypes_NestedMessage()..i = 42;
var mergeSource = TestAllExtensions()
..setExtension(Unittest.optionalNestedMessageExtension, nestedMessage);
var nestedMessage2 = TestAllTypes_NestedMessage()..bb = 43;
var mergeDest = TestAllExtensions()
..setExtension(Unittest.optionalNestedMessageExtension, nestedMessage2);
var 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", () {
var 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", () {
var 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', () {
var 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', () {
var 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', () {
var 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', () {
var 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', () {
var 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);
var 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);
var 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',
() {
var r1 = ExtensionRegistry();
Unittest.registerAllExtensions(r1);
var 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',
() {
var 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', () {
var 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);
List<String> strings =
withUnknownFields.getExtension(Unittest.repeatedStringExtension);
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, reparsed.stringMap.length);
expect(withUnknownFields.stringMap[0], reparsed.stringMap[0]);
});
}