Эх сурвалжийг харах

add LegacyGeneratedCodeTest

Jan Tattermusch 5 жил өмнө
parent
commit
17ea4d932f

+ 233 - 0
csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs

@@ -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;
+                }
+              }
+            }
+          }
+        }
+    }
+}

+ 1 - 1
csharp/src/Google.Protobuf/ParsingPrimitivesMessages.cs

@@ -189,7 +189,7 @@ namespace Google.Protobuf
                     // That also means that one of the new parsing APIs was used at the top level
                     // and in such case it is reasonable to require that all the nested message provide
                     // up-to-date generated code with ParseContext support (and fail otherwise).
-                    throw new InvalidProtocolBufferException($"Message ${message.GetType()} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.");
+                    throw new InvalidProtocolBufferException($"Message {message.GetType().Name} doesn't provide the generated method that enables ParseContext-based parsing. You might need to regenerate the generated protobuf code.");
                 }
 
                 ctx.CopyStateTo(ctx.state.codedInputStream);