#region Copyright notice and license | |
// Protocol Buffers - Google's data interchange format | |
// Copyright 2008 Google Inc. All rights reserved. | |
// http://github.com/jskeet/dotnet-protobufs/ | |
// Original C++/Java/Python code: | |
// http://code.google.com/p/protobuf/ | |
// | |
// 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 System; | |
using System.Collections.Generic; | |
using System.IO; | |
using Google.ProtocolBuffers.Descriptors; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using Google.ProtocolBuffers.TestProtos; | |
namespace Google.ProtocolBuffers | |
{ | |
[TestClass] | |
public class AbstractMessageTest | |
{ | |
[TestMethod] | |
public void Clear() | |
{ | |
AbstractMessageWrapper message = | |
new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(TestUtil.GetAllSet())).Clear().Build(); | |
TestUtil.AssertClear((TestAllTypes) message.WrappedMessage); | |
} | |
[TestMethod] | |
public void Copy() | |
{ | |
AbstractMessageWrapper message = | |
new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder()).MergeFrom(TestUtil.GetAllSet()).Build(); | |
TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage); | |
} | |
[TestMethod] | |
public void CreateAndBuild() | |
{ | |
TestAllTypes.CreateBuilder() | |
.Build(); | |
} | |
[TestMethod] | |
public void SerializedSize() | |
{ | |
TestAllTypes message = TestUtil.GetAllSet(); | |
IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet()); | |
Assert.AreEqual(message.SerializedSize, abstractMessage.SerializedSize); | |
} | |
[TestMethod] | |
public void Serialization() | |
{ | |
IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet()); | |
TestUtil.AssertAllFieldsSet(TestAllTypes.ParseFrom(abstractMessage.ToByteString())); | |
Assert.AreEqual(TestUtil.GetAllSet().ToByteString(), abstractMessage.ToByteString()); | |
} | |
[TestMethod] | |
public void Parsing() | |
{ | |
IBuilder builder = new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder()); | |
AbstractMessageWrapper message = | |
(AbstractMessageWrapper) builder.WeakMergeFrom(TestUtil.GetAllSet().ToByteString()).WeakBuild(); | |
TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage); | |
} | |
[TestMethod] | |
public void PackedSerialization() | |
{ | |
IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetPackedSet()); | |
TestUtil.AssertPackedFieldsSet(TestPackedTypes.ParseFrom(abstractMessage.ToByteString())); | |
Assert.AreEqual(TestUtil.GetPackedSet().ToByteString(), abstractMessage.ToByteString()); | |
} | |
[TestMethod] | |
public void PackedParsing() | |
{ | |
AbstractMessageWrapper.Builder builder = new AbstractMessageWrapper.Builder(TestPackedTypes.CreateBuilder()); | |
AbstractMessageWrapper message = builder.MergeFrom(TestUtil.GetPackedSet().ToByteString()).Build(); | |
TestUtil.AssertPackedFieldsSet((TestPackedTypes)message.WrappedMessage); | |
} | |
[TestMethod] | |
public void UnpackedParsingOfPackedInput() | |
{ | |
byte[] bytes = TestUtil.GetPackedSet().ToByteArray(); | |
TestUnpackedTypes message = TestUnpackedTypes.ParseFrom(bytes); | |
TestUtil.AssertUnpackedFieldsSet(message); | |
} | |
[TestMethod] | |
public void PackedParsingOfUnpackedInput() | |
{ | |
byte[] bytes = TestUnpackedTypes.ParseFrom(TestUtil.GetPackedSet().ToByteArray()).ToByteArray(); | |
TestPackedTypes message = TestPackedTypes.ParseFrom(bytes); | |
TestUtil.AssertPackedFieldsSet(message); | |
} | |
[TestMethod] | |
public void UnpackedParsingOfPackedInputExtensions() | |
{ | |
byte[] bytes = TestUtil.GetPackedSet().ToByteArray(); | |
ExtensionRegistry registry = ExtensionRegistry.CreateInstance(); | |
Unittest.RegisterAllExtensions(registry); | |
UnittestImport.RegisterAllExtensions(registry); | |
TestUnpackedExtensions message = TestUnpackedExtensions.ParseFrom(bytes, registry); | |
TestUtil.AssertUnpackedExtensionsSet(message); | |
} | |
[TestMethod] | |
public void PackedParsingOfUnpackedInputExtensions() | |
{ | |
byte[] bytes = TestUnpackedTypes.ParseFrom(TestUtil.GetPackedSet().ToByteArray()).ToByteArray(); | |
ExtensionRegistry registry = ExtensionRegistry.CreateInstance(); | |
Unittest.RegisterAllExtensions(registry); | |
TestPackedExtensions message = TestPackedExtensions.ParseFrom(bytes, registry); | |
TestUtil.AssertPackedExtensionsSet(message); | |
} | |
[TestMethod] | |
public void OptimizedForSize() | |
{ | |
// We're mostly only Checking that this class was compiled successfully. | |
TestOptimizedForSize message = TestOptimizedForSize.CreateBuilder().SetI(1).Build(); | |
message = TestOptimizedForSize.ParseFrom(message.ToByteString()); | |
Assert.AreEqual(2, message.SerializedSize); | |
} | |
// ----------------------------------------------------------------- | |
// Tests for isInitialized(). | |
private static readonly TestRequired TestRequiredUninitialized = TestRequired.DefaultInstance; | |
private static readonly TestRequired TestRequiredInitialized = | |
TestRequired.CreateBuilder().SetA(1).SetB(2).SetC(3).Build(); | |
[TestMethod] | |
public void IsInitialized() | |
{ | |
TestRequired.Builder builder = TestRequired.CreateBuilder(); | |
AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder); | |
Assert.IsFalse(abstractBuilder.IsInitialized); | |
builder.A = 1; | |
Assert.IsFalse(abstractBuilder.IsInitialized); | |
builder.B = 1; | |
Assert.IsFalse(abstractBuilder.IsInitialized); | |
builder.C = 1; | |
Assert.IsTrue(abstractBuilder.IsInitialized); | |
} | |
[TestMethod] | |
public void ForeignIsInitialized() | |
{ | |
TestRequiredForeign.Builder builder = TestRequiredForeign.CreateBuilder(); | |
AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder); | |
Assert.IsTrue(abstractBuilder.IsInitialized); | |
builder.SetOptionalMessage(TestRequiredUninitialized); | |
Assert.IsFalse(abstractBuilder.IsInitialized); | |
builder.SetOptionalMessage(TestRequiredInitialized); | |
Assert.IsTrue(abstractBuilder.IsInitialized); | |
builder.AddRepeatedMessage(TestRequiredUninitialized); | |
Assert.IsFalse(abstractBuilder.IsInitialized); | |
builder.SetRepeatedMessage(0, TestRequiredInitialized); | |
Assert.IsTrue(abstractBuilder.IsInitialized); | |
} | |
// ----------------------------------------------------------------- | |
// Tests for mergeFrom | |
private static readonly TestAllTypes MergeSource = TestAllTypes.CreateBuilder() | |
.SetOptionalInt32(1) | |
.SetOptionalString("foo") | |
.SetOptionalForeignMessage(ForeignMessage.DefaultInstance) | |
.AddRepeatedString("bar") | |
.Build(); | |
private static readonly TestAllTypes MergeDest = TestAllTypes.CreateBuilder() | |
.SetOptionalInt64(2) | |
.SetOptionalString("baz") | |
.SetOptionalForeignMessage(ForeignMessage.CreateBuilder().SetC(3).Build()) | |
.AddRepeatedString("qux") | |
.Build(); | |
private const string MergeResultText = "optional_int32: 1\n" + | |
"optional_int64: 2\n" + | |
"optional_string: \"foo\"\n" + | |
"optional_foreign_message {\n" + | |
" c: 3\n" + | |
"}\n" + | |
"repeated_string: \"qux\"\n" + | |
"repeated_string: \"bar\"\n"; | |
[TestMethod] | |
public void MergeFrom() | |
{ | |
AbstractMessageWrapper result = (AbstractMessageWrapper) | |
new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(MergeDest)) | |
.MergeFrom(MergeSource) | |
.Build(); | |
Assert.AreEqual(MergeResultText, result.ToString()); | |
} | |
// ----------------------------------------------------------------- | |
// Tests for equals and hashCode | |
[TestMethod] | |
public void EqualsAndHashCode() | |
{ | |
TestAllTypes a = TestUtil.GetAllSet(); | |
TestAllTypes b = TestAllTypes.CreateBuilder().Build(); | |
TestAllTypes c = TestAllTypes.CreateBuilder(b).AddRepeatedString("x").Build(); | |
TestAllTypes d = TestAllTypes.CreateBuilder(c).AddRepeatedString("y").Build(); | |
TestAllExtensions e = TestUtil.GetAllExtensionsSet(); | |
TestAllExtensions f = TestAllExtensions.CreateBuilder(e) | |
.AddExtension(Unittest.RepeatedInt32Extension, 999).Build(); | |
CheckEqualsIsConsistent(a); | |
CheckEqualsIsConsistent(b); | |
CheckEqualsIsConsistent(c); | |
CheckEqualsIsConsistent(d); | |
CheckEqualsIsConsistent(e); | |
CheckEqualsIsConsistent(f); | |
CheckNotEqual(a, b); | |
CheckNotEqual(a, c); | |
CheckNotEqual(a, d); | |
CheckNotEqual(a, e); | |
CheckNotEqual(a, f); | |
CheckNotEqual(b, c); | |
CheckNotEqual(b, d); | |
CheckNotEqual(b, e); | |
CheckNotEqual(b, f); | |
CheckNotEqual(c, d); | |
CheckNotEqual(c, e); | |
CheckNotEqual(c, f); | |
CheckNotEqual(d, e); | |
CheckNotEqual(d, f); | |
CheckNotEqual(e, f); | |
// Deserializing into the TestEmptyMessage such that every field is an UnknownFieldSet.Field | |
TestEmptyMessage eUnknownFields = TestEmptyMessage.ParseFrom(e.ToByteArray()); | |
TestEmptyMessage fUnknownFields = TestEmptyMessage.ParseFrom(f.ToByteArray()); | |
CheckNotEqual(eUnknownFields, fUnknownFields); | |
CheckEqualsIsConsistent(eUnknownFields); | |
CheckEqualsIsConsistent(fUnknownFields); | |
// Subseqent reconstitutions should be identical | |
TestEmptyMessage eUnknownFields2 = TestEmptyMessage.ParseFrom(e.ToByteArray()); | |
CheckEqualsIsConsistent(eUnknownFields, eUnknownFields2); | |
} | |
/// <summary> | |
/// Asserts that the given protos are equal and have the same hash code. | |
/// </summary> | |
private static void CheckEqualsIsConsistent(IMessage message) | |
{ | |
// Object should be equal to itself. | |
Assert.AreEqual(message, message); | |
// Object should be equal to a dynamic copy of itself. | |
DynamicMessage dynamic = DynamicMessage.CreateBuilder(message).Build(); | |
CheckEqualsIsConsistent(message, dynamic); | |
} | |
/// <summary> | |
/// Asserts that the given protos are equal and have the same hash code. | |
/// </summary> | |
private static void CheckEqualsIsConsistent(IMessage message1, IMessage message2) | |
{ | |
Assert.AreEqual(message1, message2); | |
Assert.AreEqual(message2, message1); | |
Assert.AreEqual(message2.GetHashCode(), message1.GetHashCode()); | |
} | |
/// <summary> | |
/// Asserts that the given protos are not equal and have different hash codes. | |
/// </summary> | |
/// <remarks> | |
/// It's valid for non-equal objects to have the same hash code, so | |
/// this test is stricter than it needs to be. However, this should happen | |
/// relatively rarely. (If this test fails, it's probably still due to a bug.) | |
/// </remarks> | |
private static void CheckNotEqual(IMessage m1, IMessage m2) | |
{ | |
String equalsError = string.Format("{0} should not be equal to {1}", m1, m2); | |
Assert.IsFalse(m1.Equals(m2), equalsError); | |
Assert.IsFalse(m2.Equals(m1), equalsError); | |
Assert.IsFalse(m1.GetHashCode() == m2.GetHashCode(), | |
string.Format("{0} should have a different hash code from {1}", m1, m2)); | |
} | |
/// <summary> | |
/// Extends AbstractMessage and wraps some other message object. The methods | |
/// of the Message interface which aren't explicitly implemented by | |
/// AbstractMessage are forwarded to the wrapped object. This allows us to | |
/// test that AbstractMessage's implementations work even if the wrapped | |
/// object does not use them. | |
/// </summary> | |
private class AbstractMessageWrapper : AbstractMessage<AbstractMessageWrapper, AbstractMessageWrapper.Builder> | |
{ | |
private readonly IMessage wrappedMessage; | |
public IMessage WrappedMessage | |
{ | |
get { return wrappedMessage; } | |
} | |
public AbstractMessageWrapper(IMessage wrappedMessage) | |
{ | |
this.wrappedMessage = wrappedMessage; | |
} | |
public override MessageDescriptor DescriptorForType | |
{ | |
get { return wrappedMessage.DescriptorForType; } | |
} | |
public override AbstractMessageWrapper DefaultInstanceForType | |
{ | |
get { return new AbstractMessageWrapper(wrappedMessage.WeakDefaultInstanceForType); } | |
} | |
public override IDictionary<FieldDescriptor, object> AllFields | |
{ | |
get { return wrappedMessage.AllFields; } | |
} | |
public override bool HasField(FieldDescriptor field) | |
{ | |
return wrappedMessage.HasField(field); | |
} | |
public override object this[FieldDescriptor field] | |
{ | |
get { return wrappedMessage[field]; } | |
} | |
public override object this[FieldDescriptor field, int index] | |
{ | |
get { return wrappedMessage[field, index]; } | |
} | |
public override int GetRepeatedFieldCount(FieldDescriptor field) | |
{ | |
return wrappedMessage.GetRepeatedFieldCount(field); | |
} | |
public override UnknownFieldSet UnknownFields | |
{ | |
get { return wrappedMessage.UnknownFields; } | |
} | |
public override Builder CreateBuilderForType() | |
{ | |
return new Builder(wrappedMessage.WeakCreateBuilderForType()); | |
} | |
public override Builder ToBuilder() | |
{ | |
return new Builder(wrappedMessage.WeakToBuilder()); | |
} | |
internal class Builder : AbstractBuilder<AbstractMessageWrapper, Builder> | |
{ | |
private readonly IBuilder wrappedBuilder; | |
protected override Builder ThisBuilder | |
{ | |
get { return this; } | |
} | |
internal Builder(IBuilder wrappedBuilder) | |
{ | |
this.wrappedBuilder = wrappedBuilder; | |
} | |
public override Builder MergeFrom(AbstractMessageWrapper other) | |
{ | |
wrappedBuilder.WeakMergeFrom(other.wrappedMessage); | |
return this; | |
} | |
public override bool IsInitialized | |
{ | |
get { return wrappedBuilder.IsInitialized; } | |
} | |
public override IDictionary<FieldDescriptor, object> AllFields | |
{ | |
get { return wrappedBuilder.AllFields; } | |
} | |
public override object this[FieldDescriptor field] | |
{ | |
get { return wrappedBuilder[field]; } | |
set { wrappedBuilder[field] = value; } | |
} | |
public override MessageDescriptor DescriptorForType | |
{ | |
get { return wrappedBuilder.DescriptorForType; } | |
} | |
public override int GetRepeatedFieldCount(FieldDescriptor field) | |
{ | |
return wrappedBuilder.GetRepeatedFieldCount(field); | |
} | |
public override object this[FieldDescriptor field, int index] | |
{ | |
get { return wrappedBuilder[field, index]; } | |
set { wrappedBuilder[field, index] = value; } | |
} | |
public override bool HasField(FieldDescriptor field) | |
{ | |
return wrappedBuilder.HasField(field); | |
} | |
public override UnknownFieldSet UnknownFields | |
{ | |
get { return wrappedBuilder.UnknownFields; } | |
set { wrappedBuilder.UnknownFields = value; } | |
} | |
public override AbstractMessageWrapper Build() | |
{ | |
return new AbstractMessageWrapper(wrappedBuilder.WeakBuild()); | |
} | |
public override AbstractMessageWrapper BuildPartial() | |
{ | |
return new AbstractMessageWrapper(wrappedBuilder.WeakBuildPartial()); | |
} | |
public override Builder Clone() | |
{ | |
return new Builder(wrappedBuilder.WeakClone()); | |
} | |
public override AbstractMessageWrapper DefaultInstanceForType | |
{ | |
get { return new AbstractMessageWrapper(wrappedBuilder.WeakDefaultInstanceForType); } | |
} | |
public override Builder ClearField(FieldDescriptor field) | |
{ | |
wrappedBuilder.WeakClearField(field); | |
return this; | |
} | |
public override Builder AddRepeatedField(FieldDescriptor field, object value) | |
{ | |
wrappedBuilder.WeakAddRepeatedField(field, value); | |
return this; | |
} | |
public override IBuilder CreateBuilderForField(FieldDescriptor field) | |
{ | |
wrappedBuilder.CreateBuilderForField(field); | |
return this; | |
} | |
public override Builder MergeFrom(IMessage other) | |
{ | |
wrappedBuilder.WeakMergeFrom(other); | |
return this; | |
} | |
public override Builder MergeFrom(ICodedInputStream input, ExtensionRegistry extensionRegistry) | |
{ | |
wrappedBuilder.WeakMergeFrom(input, extensionRegistry); | |
return this; | |
} | |
} | |
} | |
} | |
} |