parent
f1d12ac768
commit
17ea4d932f
2 changed files with 234 additions and 1 deletions
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue