|
@@ -0,0 +1,233 @@
|
|
|
|
+#region Copyright notice and license
|
|
|
|
+// Protocol Buffers - Google's data interchange format
|
|
|
|
+// Copyright 2008 Google Inc. All rights reserved.
|
|
|
|
+// https://developers.google.com/protocol-buffers/
|
|
|
|
+//
|
|
|
|
+// Redistribution and use in source and binary forms, with or without
|
|
|
|
+// modification, are permitted provided that the following conditions are
|
|
|
|
+// met:
|
|
|
|
+//
|
|
|
|
+// * Redistributions of source code must retain the above copyright
|
|
|
|
+// notice, this list of conditions and the following disclaimer.
|
|
|
|
+// * Redistributions in binary form must reproduce the above
|
|
|
|
+// copyright notice, this list of conditions and the following disclaimer
|
|
|
|
+// in the documentation and/or other materials provided with the
|
|
|
|
+// distribution.
|
|
|
|
+// * Neither the name of Google Inc. nor the names of its
|
|
|
|
+// contributors may be used to endorse or promote products derived from
|
|
|
|
+// this software without specific prior written permission.
|
|
|
|
+//
|
|
|
|
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
+#endregion
|
|
|
|
+using Google.Protobuf;
|
|
|
|
+using Google.Protobuf.Reflection;
|
|
|
|
+using System.Buffers;
|
|
|
|
+using pb = global::Google.Protobuf;
|
|
|
|
+using pbr = global::Google.Protobuf.Reflection;
|
|
|
|
+using NUnit.Framework;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+namespace Google.Protobuf
|
|
|
|
+{
|
|
|
|
+ public class LegacyGeneratedCodeTest
|
|
|
|
+ {
|
|
|
|
+ [Test]
|
|
|
|
+ public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()
|
|
|
|
+ {
|
|
|
|
+ var message = new ParseContextEnabledMessageB
|
|
|
|
+ {
|
|
|
|
+ A = new LegacyGeneratedCodeMessageA
|
|
|
|
+ {
|
|
|
|
+ Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
|
|
|
|
+ },
|
|
|
|
+ OptionalInt32 = 6789
|
|
|
|
+ };
|
|
|
|
+ var data = message.ToByteArray();
|
|
|
|
+
|
|
|
|
+ // when parsing started using CodedInputStream and a message with legacy generated code
|
|
|
|
+ // is encountered somewhere in the parse tree, we still need to be able to use its
|
|
|
|
+ // MergeFrom(CodedInputStream) method to parse correctly.
|
|
|
|
+ var codedInput = new CodedInputStream(data);
|
|
|
|
+ var parsed = new ParseContextEnabledMessageB();
|
|
|
|
+ codedInput.ReadRawMessage(parsed);
|
|
|
|
+ Assert.IsTrue(codedInput.IsAtEnd);
|
|
|
|
+
|
|
|
|
+ Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
|
|
|
|
+ Assert.AreEqual(6789, parsed.OptionalInt32);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ [Test]
|
|
|
|
+ public void LegacyGeneratedCodeThrowsWithReadOnlySequence()
|
|
|
|
+ {
|
|
|
|
+ var message = new ParseContextEnabledMessageB
|
|
|
|
+ {
|
|
|
|
+ A = new LegacyGeneratedCodeMessageA
|
|
|
|
+ {
|
|
|
|
+ Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
|
|
|
|
+ },
|
|
|
|
+ OptionalInt32 = 6789
|
|
|
|
+ };
|
|
|
|
+ var data = message.ToByteArray();
|
|
|
|
+
|
|
|
|
+ // if parsing started using ReadOnlySequence and we don't have a CodedInputStream
|
|
|
|
+ // instance at hand, we cannot fall back to the legacy MergeFrom(CodedInputStream)
|
|
|
|
+ // method and parsing will fail. As a consequence, one can only use parsing
|
|
|
|
+ // from ReadOnlySequence if all the messages in the parsing tree have their generated
|
|
|
|
+ // code up to date.
|
|
|
|
+ var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
|
|
|
|
+ {
|
|
|
|
+ ParseContext.Initialize(new ReadOnlySequence<byte>(data), out ParseContext parseCtx);
|
|
|
|
+ var parsed = new ParseContextEnabledMessageB();
|
|
|
|
+ ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed);
|
|
|
|
+ });
|
|
|
|
+ Assert.AreEqual($"Message {typeof(LegacyGeneratedCodeMessageA).Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.", exception.Message);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // hand-modified version of a generated message that only provides the legacy
|
|
|
|
+ // MergeFrom(CodedInputStream) method and doesn't implement IBufferMessage.
|
|
|
|
+ private sealed partial class LegacyGeneratedCodeMessageA : pb::IMessage {
|
|
|
|
+ private pb::UnknownFieldSet _unknownFields;
|
|
|
|
+
|
|
|
|
+ pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
|
|
|
|
+
|
|
|
|
+ /// <summary>Field number for the "bb" field.</summary>
|
|
|
|
+ public const int BbFieldNumber = 1;
|
|
|
|
+ private ParseContextEnabledMessageB bb_;
|
|
|
|
+ public ParseContextEnabledMessageB Bb {
|
|
|
|
+ get { return bb_; }
|
|
|
|
+ set {
|
|
|
|
+ bb_ = value;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void WriteTo(pb::CodedOutputStream output) {
|
|
|
|
+ if (bb_ != null) {
|
|
|
|
+ output.WriteRawTag(10);
|
|
|
|
+ output.WriteMessage(Bb);
|
|
|
|
+ }
|
|
|
|
+ if (_unknownFields != null) {
|
|
|
|
+ _unknownFields.WriteTo(output);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int CalculateSize() {
|
|
|
|
+ int size = 0;
|
|
|
|
+ if (bb_ != null) {
|
|
|
|
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(Bb);
|
|
|
|
+ }
|
|
|
|
+ if (_unknownFields != null) {
|
|
|
|
+ size += _unknownFields.CalculateSize();
|
|
|
|
+ }
|
|
|
|
+ return size;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void MergeFrom(pb::CodedInputStream input) {
|
|
|
|
+ uint tag;
|
|
|
|
+ while ((tag = input.ReadTag()) != 0) {
|
|
|
|
+ switch(tag) {
|
|
|
|
+ default:
|
|
|
|
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
|
|
|
|
+ break;
|
|
|
|
+ case 10: {
|
|
|
|
+ if (bb_ == null) {
|
|
|
|
+ Bb = new ParseContextEnabledMessageB();
|
|
|
|
+ }
|
|
|
|
+ input.ReadMessage(Bb);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // hand-modified version of a generated message that does provide
|
|
|
|
+ // the new InternalMergeFrom(ref ParseContext) method.
|
|
|
|
+ private sealed partial class ParseContextEnabledMessageB : pb::IBufferMessage {
|
|
|
|
+ private pb::UnknownFieldSet _unknownFields;
|
|
|
|
+
|
|
|
|
+ pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
|
|
|
|
+
|
|
|
|
+ /// <summary>Field number for the "a" field.</summary>
|
|
|
|
+ public const int AFieldNumber = 1;
|
|
|
|
+ private LegacyGeneratedCodeMessageA a_;
|
|
|
|
+ public LegacyGeneratedCodeMessageA A {
|
|
|
|
+ get { return a_; }
|
|
|
|
+ set {
|
|
|
|
+ a_ = value;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <summary>Field number for the "optional_int32" field.</summary>
|
|
|
|
+ public const int OptionalInt32FieldNumber = 2;
|
|
|
|
+ private int optionalInt32_;
|
|
|
|
+ public int OptionalInt32 {
|
|
|
|
+ get { return optionalInt32_; }
|
|
|
|
+ set {
|
|
|
|
+ optionalInt32_ = value;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public void WriteTo(pb::CodedOutputStream output) {
|
|
|
|
+ if (a_ != null) {
|
|
|
|
+ output.WriteRawTag(10);
|
|
|
|
+ output.WriteMessage(A);
|
|
|
|
+ }
|
|
|
|
+ if (OptionalInt32 != 0) {
|
|
|
|
+ output.WriteRawTag(16);
|
|
|
|
+ output.WriteInt32(OptionalInt32);
|
|
|
|
+ }
|
|
|
|
+ if (_unknownFields != null) {
|
|
|
|
+ _unknownFields.WriteTo(output);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ public int CalculateSize() {
|
|
|
|
+ int size = 0;
|
|
|
|
+ if (a_ != null) {
|
|
|
|
+ size += 1 + pb::CodedOutputStream.ComputeMessageSize(A);
|
|
|
|
+ }
|
|
|
|
+ if (OptionalInt32 != 0) {
|
|
|
|
+ size += 1 + pb::CodedOutputStream.ComputeInt32Size(OptionalInt32);
|
|
|
|
+ }
|
|
|
|
+ if (_unknownFields != null) {
|
|
|
|
+ size += _unknownFields.CalculateSize();
|
|
|
|
+ }
|
|
|
|
+ return size;
|
|
|
|
+ }
|
|
|
|
+ public void MergeFrom(pb::CodedInputStream input) {
|
|
|
|
+ input.ReadRawMessage(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
|
|
|
|
+ uint tag;
|
|
|
|
+ while ((tag = input.ReadTag()) != 0) {
|
|
|
|
+ switch(tag) {
|
|
|
|
+ default:
|
|
|
|
+ _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
|
|
|
|
+ break;
|
|
|
|
+ case 10: {
|
|
|
|
+ if (a_ == null) {
|
|
|
|
+ A = new LegacyGeneratedCodeMessageA();
|
|
|
|
+ }
|
|
|
|
+ input.ReadMessage(A);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ case 16: {
|
|
|
|
+ OptionalInt32 = input.ReadInt32();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|