| #!/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 readonly_message_test; |
| |
| import 'package:test/test.dart'; |
| |
| import 'package:protobuf/protobuf.dart' |
| show |
| BuilderInfo, |
| GeneratedMessage, |
| PbFieldType, |
| UnknownFieldSetField, |
| frozenMessageModificationHandler, |
| defaultFrozenMessageModificationHandler; |
| |
| throwsError(Type expectedType, Matcher expectedMessage) => |
| throwsA(predicate((x) { |
| expect(x.runtimeType, expectedType); |
| expect(x.message, expectedMessage); |
| return true; |
| })); |
| |
| class Rec extends GeneratedMessage { |
| static Rec getDefault() => Rec()..freeze(); |
| static Rec create() => Rec(); |
| Rec createEmptyInstance() => Rec(); |
| |
| @override |
| BuilderInfo info_ = BuilderInfo('rec') |
| ..a(1, 'value', PbFieldType.O3) |
| ..pc<Rec>(2, 'sub', PbFieldType.PM, subBuilder: Rec.create) |
| ..p<int>(10, 'ints', PbFieldType.P3); |
| |
| int get value => $_get(0, 0); |
| set value(int v) { |
| $_setUnsignedInt32(0, v); |
| } |
| |
| bool hasValue() => $_has(0); |
| |
| List<Rec> get sub => $_getList<Rec>(1); |
| |
| List<int> get ints => $_getList<int>(2); |
| |
| @override |
| Rec clone() => Rec()..mergeFromMessage(this); |
| |
| Rec copyWith(void Function(Rec) updates) => |
| super.copyWith((message) => updates(message as Rec)); |
| } |
| |
| main() { |
| test('can write a read-only message', () { |
| expect(Rec.getDefault().writeToBuffer(), []); |
| expect(Rec.getDefault().writeToJson(), "{}"); |
| }); |
| |
| test("can't merge to a read-only message", () { |
| expect( |
| () => Rec.getDefault().mergeFromJson('{"1":1}'), |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| }); |
| |
| test("can't set a field on a read-only message", () { |
| expect( |
| () => Rec.getDefault().setField(1, 456), |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| }); |
| |
| test('can set a field on a read-only message with a custom read-only handler', |
| () { |
| try { |
| int called = 0; |
| |
| frozenMessageModificationHandler = |
| (String messageName, [String methodName]) { |
| expect(messageName, 'rec'); |
| expect(methodName, isNull); |
| called++; |
| }; |
| |
| Rec.getDefault().setField(1, 456); |
| expect(called, 1); |
| } finally { |
| frozenMessageModificationHandler = |
| defaultFrozenMessageModificationHandler; |
| } |
| }); |
| |
| test("can't clear a read-only message", () { |
| expect( |
| () => Rec.getDefault().clear(), |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| }); |
| |
| test("can't clear a field on a read-only message", () { |
| expect( |
| () => Rec.getDefault().clearField(1), |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| }); |
| |
| test("can't modify repeated fields on a read-only message", () { |
| expect(() => Rec.getDefault().sub.add(Rec.create()), |
| throwsError(UnsupportedError, contains('add'))); |
| var r = Rec.create() |
| ..ints.add(10) |
| ..freeze(); |
| expect( |
| () => r.ints.clear(), |
| throwsError(UnsupportedError, |
| equals('Cannot call clear on an unmodifiable list'))); |
| expect( |
| () => r.ints[0] = 2, |
| throwsError(UnsupportedError, |
| equals('Cannot call set on an unmodifiable list'))); |
| expect(() => r.sub.add(Rec.create()), |
| throwsError(UnsupportedError, contains('add'))); |
| |
| r = Rec.create() |
| ..sub.add(Rec.create()) |
| ..freeze(); |
| expect( |
| () => r.sub.add(Rec.create()), |
| throwsError(UnsupportedError, |
| equals('Cannot call add on an unmodifiable list'))); |
| expect(() => r.ints.length = 20, |
| throwsError(UnsupportedError, contains('length'))); |
| }); |
| |
| test("can't modify sub-messages on a read-only message", () { |
| var subMessage = Rec.create()..value = 1; |
| var r = Rec.create() |
| ..sub.add(Rec.create()..sub.add(subMessage)) |
| ..freeze(); |
| expect(r.sub[0].sub[0].value, 1); |
| expect( |
| () => subMessage.value = 2, |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| }); |
| |
| test("can't modify unknown fields on a read-only message", () { |
| expect( |
| () => Rec.getDefault().unknownFields.clear(), |
| throwsError( |
| UnsupportedError, |
| equals( |
| "Attempted to call clear on a read-only message (UnknownFieldSet)"))); |
| }); |
| |
| test("can rebuild a frozen message with merge", () { |
| final orig = Rec.create() |
| ..value = 10 |
| ..freeze(); |
| final rebuilt = orig.copyWith((m) => m.mergeFromJson('{"1": 7}')); |
| expect(identical(orig, rebuilt), false); |
| expect(orig.value, 10); |
| expect(rebuilt.value, 7); |
| }); |
| |
| test("can set a field while rebuilding a frozen message", () { |
| final orig = Rec.create() |
| ..value = 10 |
| ..freeze(); |
| final rebuilt = orig.copyWith((m) => m.value = 7); |
| expect(identical(orig, rebuilt), false); |
| expect(orig.value, 10); |
| expect(rebuilt.value, 7); |
| }); |
| |
| test("can clear while rebuilding a frozen message", () { |
| final orig = Rec.create() |
| ..value = 10 |
| ..freeze(); |
| final rebuilt = orig.copyWith((m) => m.clear()); |
| expect(identical(orig, rebuilt), false); |
| expect(orig.value, 10); |
| expect(orig.hasValue(), true); |
| expect(rebuilt.hasValue(), false); |
| }); |
| |
| test("can clear a field while rebuilding a frozen message", () { |
| final orig = Rec.create() |
| ..value = 10 |
| ..freeze(); |
| final rebuilt = orig.copyWith((m) => m.clearField(1)); |
| expect(identical(orig, rebuilt), false); |
| expect(orig.value, 10); |
| expect(orig.hasValue(), true); |
| expect(rebuilt.hasValue(), false); |
| }); |
| |
| test("can modify repeated fields while rebuilding a frozen message", () { |
| var orig = Rec.create() |
| ..ints.add(10) |
| ..freeze(); |
| var rebuilt = orig.copyWith((m) => m.ints.add(12)); |
| expect(identical(orig, rebuilt), false); |
| expect(orig.ints, [10]); |
| expect(rebuilt.ints, [10, 12]); |
| |
| rebuilt = orig.copyWith((m) => m.ints.clear()); |
| expect(orig.ints, [10]); |
| expect(rebuilt.ints, []); |
| |
| rebuilt = orig.copyWith((m) => m.ints[0] = 2); |
| expect(orig.ints, [10]); |
| expect(rebuilt.ints, [2]); |
| |
| orig = Rec.create() |
| ..sub.add(Rec.create()) |
| ..freeze(); |
| rebuilt = orig.copyWith((m) => m.sub.add(Rec.create())); |
| expect(orig.sub.length, 1); |
| expect(rebuilt.sub.length, 2); |
| }); |
| |
| test("cannot modify sub-messages while rebuilding a frozen message", () { |
| final subMessage = Rec.create()..value = 1; |
| final orig = Rec.create() |
| ..sub.add(Rec.create()..sub.add(subMessage)) |
| ..freeze(); |
| |
| final rebuilt = orig.copyWith((m) { |
| expect( |
| () => subMessage.value = 5, |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| expect( |
| () => m.sub[0].sub[0].value = 2, |
| throwsError(UnsupportedError, |
| equals('Attempted to change a read-only message (rec)'))); |
| m.sub[0] = m.sub[0].copyWith( |
| (m2) => m2.sub[0] = m2.sub[0].copyWith((m3) => m3.value = 2)); |
| }); |
| expect(identical(subMessage, orig.sub[0].sub[0]), true); |
| expect(identical(subMessage, rebuilt.sub[0].sub[0]), false); |
| expect(orig.sub[0].sub[0].value, 1); |
| expect(rebuilt.sub[0].sub[0].value, 2); |
| }); |
| |
| test("can modify unknown fields while rebuilding a frozen message", () { |
| final orig = Rec.create() |
| ..unknownFields.addField(20, UnknownFieldSetField()..fixed32s.add(1)); |
| final rebuilt = orig.copyWith((m) => m.unknownFields.clear()); |
| expect(orig.unknownFields.hasField(20), true); |
| expect(rebuilt.unknownFields.hasField(20), false); |
| }); |
| } |