add LegacyGeneratedCodeTest
This commit is contained in:
parent
f1d12ac768
commit
17ea4d932f
233
csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs
Normal file
233
csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user