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