• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2008 Google Inc.  All rights reserved.
4 //
5 // Use of this source code is governed by a BSD-style
6 // license that can be found in the LICENSE file or at
7 // https://developers.google.com/open-source/licenses/bsd
8 #endregion
9 
10 using System.Buffers;
11 using pb = Google.Protobuf;
12 using pbr = Google.Protobuf.Reflection;
13 using NUnit.Framework;
14 using System.IO;
15 using Google.Protobuf.Buffers;
16 
17 namespace Google.Protobuf
18 {
19     public class LegacyGeneratedCodeTest
20     {
21         [Test]
IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()22         public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()
23         {
24             var message = new ParseContextEnabledMessageB
25             {
26                 A = new LegacyGeneratedCodeMessageA
27                 {
28                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
29                 },
30                 OptionalInt32 = 6789
31             };
32             var data = message.ToByteArray();
33 
34             // when parsing started using CodedInputStream and a message with legacy generated code
35             // is encountered somewhere in the parse tree, we still need to be able to use its
36             // MergeFrom(CodedInputStream) method to parse correctly.
37             var codedInput = new CodedInputStream(data);
38             var parsed = new ParseContextEnabledMessageB();
39             codedInput.ReadRawMessage(parsed);
40             Assert.IsTrue(codedInput.IsAtEnd);
41 
42             Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
43             Assert.AreEqual(6789, parsed.OptionalInt32);
44         }
45 
46         [Test]
LegacyGeneratedCodeThrowsWithReadOnlySequence()47         public void LegacyGeneratedCodeThrowsWithReadOnlySequence()
48         {
49             var message = new ParseContextEnabledMessageB
50             {
51                 A = new LegacyGeneratedCodeMessageA
52                 {
53                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
54                 },
55                 OptionalInt32 = 6789
56             };
57             var data = message.ToByteArray();
58 
59             // if parsing started using ReadOnlySequence and we don't have a CodedInputStream
60             // instance at hand, we cannot fall back to the legacy MergeFrom(CodedInputStream)
61             // method and parsing will fail. As a consequence, one can only use parsing
62             // from ReadOnlySequence if all the messages in the parsing tree have their generated
63             // code up to date.
64             var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
65             {
66                 ParseContext.Initialize(new ReadOnlySequence<byte>(data), out ParseContext parseCtx);
67                 var parsed = new ParseContextEnabledMessageB();
68                 ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed);
69             });
70             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);
71         }
72 
73         [Test]
IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedOutputStream()74         public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedOutputStream()
75         {
76             // when serialization started using CodedOutputStream and a message with legacy generated code
77             // is encountered somewhere in the parse tree, we still need to be able to use its
78             // WriteTo(CodedOutputStream) method to serialize correctly.
79             var ms = new MemoryStream();
80             var codedOutput = new CodedOutputStream(ms);
81             var message = new ParseContextEnabledMessageB
82             {
83                 A = new LegacyGeneratedCodeMessageA
84                 {
85                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
86                 },
87                 OptionalInt32 = 6789
88             };
89             message.WriteTo(codedOutput);
90             codedOutput.Flush();
91 
92             var codedInput = new CodedInputStream(ms.ToArray());
93             var parsed = new ParseContextEnabledMessageB();
94             codedInput.ReadRawMessage(parsed);
95             Assert.IsTrue(codedInput.IsAtEnd);
96 
97             Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
98             Assert.AreEqual(6789, parsed.OptionalInt32);
99         }
100 
101         [Test]
LegacyGeneratedCodeThrowsWithIBufferWriter()102         public void LegacyGeneratedCodeThrowsWithIBufferWriter()
103         {
104             // if serialization started using IBufferWriter and we don't have a CodedOutputStream
105             // instance at hand, we cannot fall back to the legacy WriteTo(CodedOutputStream)
106             // method and serializatin will fail. As a consequence, one can only use serialization
107             // to IBufferWriter if all the messages in the parsing tree have their generated
108             // code up to date.
109             var message = new ParseContextEnabledMessageB
110             {
111                 A = new LegacyGeneratedCodeMessageA
112                 {
113                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
114                 },
115                 OptionalInt32 = 6789
116             };
117             var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
118             {
119                 WriteContext.Initialize(new TestArrayBufferWriter<byte>(), out WriteContext writeCtx);
120                 ((IBufferMessage)message).InternalWriteTo(ref writeCtx);
121             });
122             Assert.AreEqual($"Message {typeof(LegacyGeneratedCodeMessageA).Name} doesn't provide the generated method that enables WriteContext-based serialization. You might need to regenerate the generated protobuf code.", exception.Message);
123         }
124 
125         // hand-modified version of a generated message that only provides the legacy
126         // MergeFrom(CodedInputStream) method and doesn't implement IBufferMessage.
127         private sealed partial class LegacyGeneratedCodeMessageA : pb::IMessage {
128           private pb::UnknownFieldSet _unknownFields;
129 
130           pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
131 
132           /// <summary>Field number for the "bb" field.</summary>
133           public const int BbFieldNumber = 1;
134           private ParseContextEnabledMessageB bb_;
135           public ParseContextEnabledMessageB Bb {
136             get { return bb_; }
137             set {
138               bb_ = value;
139             }
140           }
141 
WriteTo(pb::CodedOutputStream output)142           public void WriteTo(pb::CodedOutputStream output) {
143             if (bb_ != null) {
144               output.WriteRawTag(10);
145               output.WriteMessage(Bb);
146             }
147             if (_unknownFields != null) {
148               _unknownFields.WriteTo(output);
149             }
150           }
151 
CalculateSize()152           public int CalculateSize() {
153             int size = 0;
154             if (bb_ != null) {
155               size += 1 + pb::CodedOutputStream.ComputeMessageSize(Bb);
156             }
157             if (_unknownFields != null) {
158               size += _unknownFields.CalculateSize();
159             }
160             return size;
161           }
162 
MergeFrom(pb::CodedInputStream input)163           public void MergeFrom(pb::CodedInputStream input) {
164             uint tag;
165             while ((tag = input.ReadTag()) != 0) {
166               switch(tag) {
167                 default:
168                   _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
169                   break;
170                 case 10: {
171                   if (bb_ == null) {
172                     Bb = new ParseContextEnabledMessageB();
173                   }
174                   input.ReadMessage(Bb);
175                   break;
176                 }
177               }
178             }
179           }
180         }
181 
182         // hand-modified version of a generated message that does provide
183         // the new InternalMergeFrom(ref ParseContext) method.
184         private sealed partial class ParseContextEnabledMessageB : pb::IBufferMessage {
185           private pb::UnknownFieldSet _unknownFields;
186 
187           pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
188 
189           /// <summary>Field number for the "a" field.</summary>
190           public const int AFieldNumber = 1;
191           private LegacyGeneratedCodeMessageA a_;
192           public LegacyGeneratedCodeMessageA A {
193             get { return a_; }
194             set {
195               a_ = value;
196             }
197           }
198 
199           /// <summary>Field number for the "optional_int32" field.</summary>
200           public const int OptionalInt32FieldNumber = 2;
201           private int optionalInt32_;
202           public int OptionalInt32 {
203             get { return optionalInt32_; }
204             set {
205               optionalInt32_ = value;
206             }
207           }
208 
WriteTo(pb::CodedOutputStream output)209           public void WriteTo(pb::CodedOutputStream output) {
210             output.WriteRawMessage(this);
211           }
212 
IBufferMessage.InternalWriteTo(ref pb::WriteContext output)213           void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output)
214           {
215             if (a_ != null)
216             {
217               output.WriteRawTag(10);
218               output.WriteMessage(A);
219             }
220             if (OptionalInt32 != 0)
221             {
222               output.WriteRawTag(16);
223               output.WriteInt32(OptionalInt32);
224             }
225             if (_unknownFields != null)
226             {
227               _unknownFields.WriteTo(ref output);
228             }
229           }
230 
CalculateSize()231           public int CalculateSize() {
232             int size = 0;
233             if (a_ != null) {
234               size += 1 + pb::CodedOutputStream.ComputeMessageSize(A);
235             }
236             if (OptionalInt32 != 0) {
237               size += 1 + pb::CodedOutputStream.ComputeInt32Size(OptionalInt32);
238             }
239             if (_unknownFields != null) {
240               size += _unknownFields.CalculateSize();
241             }
242             return size;
243           }
MergeFrom(pb::CodedInputStream input)244           public void MergeFrom(pb::CodedInputStream input) {
245             input.ReadRawMessage(this);
246           }
247 
IBufferMessage.InternalMergeFrom(ref pb::ParseContext input)248           void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
249             uint tag;
250             while ((tag = input.ReadTag()) != 0) {
251               switch(tag) {
252                 default:
253                   _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
254                   break;
255                 case 10: {
256                   if (a_ == null) {
257                     A = new LegacyGeneratedCodeMessageA();
258                   }
259                   input.ReadMessage(A);
260                   break;
261                 }
262                 case 16: {
263                   OptionalInt32 = input.ReadInt32();
264                   break;
265                 }
266               }
267             }
268           }
269         }
270     }
271 }