• 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 // https://developers.google.com/protocol-buffers/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32 using Google.Protobuf;
33 using Google.Protobuf.Reflection;
34 using System.Buffers;
35 using pb = global::Google.Protobuf;
36 using pbr = global::Google.Protobuf.Reflection;
37 using NUnit.Framework;
38 using System.IO;
39 using System;
40 using Google.Protobuf.Buffers;
41 
42 namespace Google.Protobuf
43 {
44     public class LegacyGeneratedCodeTest
45     {
46         [Test]
IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()47         public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedInputStream()
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             // when parsing started using CodedInputStream and a message with legacy generated code
60             // is encountered somewhere in the parse tree, we still need to be able to use its
61             // MergeFrom(CodedInputStream) method to parse correctly.
62             var codedInput = new CodedInputStream(data);
63             var parsed = new ParseContextEnabledMessageB();
64             codedInput.ReadRawMessage(parsed);
65             Assert.IsTrue(codedInput.IsAtEnd);
66 
67             Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
68             Assert.AreEqual(6789, parsed.OptionalInt32);
69         }
70 
71         [Test]
LegacyGeneratedCodeThrowsWithReadOnlySequence()72         public void LegacyGeneratedCodeThrowsWithReadOnlySequence()
73         {
74             var message = new ParseContextEnabledMessageB
75             {
76                 A = new LegacyGeneratedCodeMessageA
77                 {
78                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
79                 },
80                 OptionalInt32 = 6789
81             };
82             var data = message.ToByteArray();
83 
84             // if parsing started using ReadOnlySequence and we don't have a CodedInputStream
85             // instance at hand, we cannot fall back to the legacy MergeFrom(CodedInputStream)
86             // method and parsing will fail. As a consequence, one can only use parsing
87             // from ReadOnlySequence if all the messages in the parsing tree have their generated
88             // code up to date.
89             var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
90             {
91                 ParseContext.Initialize(new ReadOnlySequence<byte>(data), out ParseContext parseCtx);
92                 var parsed = new ParseContextEnabledMessageB();
93                 ParsingPrimitivesMessages.ReadRawMessage(ref parseCtx, parsed);
94             });
95             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);
96         }
97 
98         [Test]
IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedOutputStream()99         public void IntermixingOfNewAndLegacyGeneratedCodeWorksWithCodedOutputStream()
100         {
101             // when serialization started using CodedOutputStream and a message with legacy generated code
102             // is encountered somewhere in the parse tree, we still need to be able to use its
103             // WriteTo(CodedOutputStream) method to serialize correctly.
104             var ms = new MemoryStream();
105             var codedOutput = new CodedOutputStream(ms);
106             var message = new ParseContextEnabledMessageB
107             {
108                 A = new LegacyGeneratedCodeMessageA
109                 {
110                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
111                 },
112                 OptionalInt32 = 6789
113             };
114             message.WriteTo(codedOutput);
115             codedOutput.Flush();
116 
117             var codedInput = new CodedInputStream(ms.ToArray());
118             var parsed = new ParseContextEnabledMessageB();
119             codedInput.ReadRawMessage(parsed);
120             Assert.IsTrue(codedInput.IsAtEnd);
121 
122             Assert.AreEqual(12345, parsed.A.Bb.OptionalInt32);
123             Assert.AreEqual(6789, parsed.OptionalInt32);
124         }
125 
126         [Test]
LegacyGeneratedCodeThrowsWithIBufferWriter()127         public void LegacyGeneratedCodeThrowsWithIBufferWriter()
128         {
129             // if serialization started using IBufferWriter and we don't have a CodedOutputStream
130             // instance at hand, we cannot fall back to the legacy WriteTo(CodedOutputStream)
131             // method and serializatin will fail. As a consequence, one can only use serialization
132             // to IBufferWriter if all the messages in the parsing tree have their generated
133             // code up to date.
134             var message = new ParseContextEnabledMessageB
135             {
136                 A = new LegacyGeneratedCodeMessageA
137                 {
138                     Bb = new ParseContextEnabledMessageB { OptionalInt32 = 12345 }
139                 },
140                 OptionalInt32 = 6789
141             };
142             var exception = Assert.Throws<InvalidProtocolBufferException>(() =>
143             {
144                 WriteContext.Initialize(new ArrayBufferWriter<byte>(), out WriteContext writeCtx);
145                 ((IBufferMessage)message).InternalWriteTo(ref writeCtx);
146             });
147             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);
148         }
149 
150         // hand-modified version of a generated message that only provides the legacy
151         // MergeFrom(CodedInputStream) method and doesn't implement IBufferMessage.
152         private sealed partial class LegacyGeneratedCodeMessageA : pb::IMessage {
153           private pb::UnknownFieldSet _unknownFields;
154 
155           pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
156 
157           /// <summary>Field number for the "bb" field.</summary>
158           public const int BbFieldNumber = 1;
159           private ParseContextEnabledMessageB bb_;
160           public ParseContextEnabledMessageB Bb {
161             get { return bb_; }
162             set {
163               bb_ = value;
164             }
165           }
166 
WriteTo(pb::CodedOutputStream output)167           public void WriteTo(pb::CodedOutputStream output) {
168             if (bb_ != null) {
169               output.WriteRawTag(10);
170               output.WriteMessage(Bb);
171             }
172             if (_unknownFields != null) {
173               _unknownFields.WriteTo(output);
174             }
175           }
176 
CalculateSize()177           public int CalculateSize() {
178             int size = 0;
179             if (bb_ != null) {
180               size += 1 + pb::CodedOutputStream.ComputeMessageSize(Bb);
181             }
182             if (_unknownFields != null) {
183               size += _unknownFields.CalculateSize();
184             }
185             return size;
186           }
187 
MergeFrom(pb::CodedInputStream input)188           public void MergeFrom(pb::CodedInputStream input) {
189             uint tag;
190             while ((tag = input.ReadTag()) != 0) {
191               switch(tag) {
192                 default:
193                   _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
194                   break;
195                 case 10: {
196                   if (bb_ == null) {
197                     Bb = new ParseContextEnabledMessageB();
198                   }
199                   input.ReadMessage(Bb);
200                   break;
201                 }
202               }
203             }
204           }
205         }
206 
207         // hand-modified version of a generated message that does provide
208         // the new InternalMergeFrom(ref ParseContext) method.
209         private sealed partial class ParseContextEnabledMessageB : pb::IBufferMessage {
210           private pb::UnknownFieldSet _unknownFields;
211 
212           pbr::MessageDescriptor pb::IMessage.Descriptor => throw new System.NotImplementedException();
213 
214           /// <summary>Field number for the "a" field.</summary>
215           public const int AFieldNumber = 1;
216           private LegacyGeneratedCodeMessageA a_;
217           public LegacyGeneratedCodeMessageA A {
218             get { return a_; }
219             set {
220               a_ = value;
221             }
222           }
223 
224           /// <summary>Field number for the "optional_int32" field.</summary>
225           public const int OptionalInt32FieldNumber = 2;
226           private int optionalInt32_;
227           public int OptionalInt32 {
228             get { return optionalInt32_; }
229             set {
230               optionalInt32_ = value;
231             }
232           }
233 
WriteTo(pb::CodedOutputStream output)234           public void WriteTo(pb::CodedOutputStream output) {
235             output.WriteRawMessage(this);
236           }
237 
IBufferMessage.InternalWriteTo(ref pb::WriteContext output)238           void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output)
239           {
240             if (a_ != null)
241             {
242               output.WriteRawTag(10);
243               output.WriteMessage(A);
244             }
245             if (OptionalInt32 != 0)
246             {
247               output.WriteRawTag(16);
248               output.WriteInt32(OptionalInt32);
249             }
250             if (_unknownFields != null)
251             {
252               _unknownFields.WriteTo(ref output);
253             }
254           }
255 
CalculateSize()256           public int CalculateSize() {
257             int size = 0;
258             if (a_ != null) {
259               size += 1 + pb::CodedOutputStream.ComputeMessageSize(A);
260             }
261             if (OptionalInt32 != 0) {
262               size += 1 + pb::CodedOutputStream.ComputeInt32Size(OptionalInt32);
263             }
264             if (_unknownFields != null) {
265               size += _unknownFields.CalculateSize();
266             }
267             return size;
268           }
MergeFrom(pb::CodedInputStream input)269           public void MergeFrom(pb::CodedInputStream input) {
270             input.ReadRawMessage(this);
271           }
272 
IBufferMessage.InternalMergeFrom(ref pb::ParseContext input)273           void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
274             uint tag;
275             while ((tag = input.ReadTag()) != 0) {
276               switch(tag) {
277                 default:
278                   _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
279                   break;
280                 case 10: {
281                   if (a_ == null) {
282                     A = new LegacyGeneratedCodeMessageA();
283                   }
284                   input.ReadMessage(A);
285                   break;
286                 }
287                 case 16: {
288                   OptionalInt32 = input.ReadInt32();
289                   break;
290                 }
291               }
292             }
293           }
294         }
295     }
296 }