blob: 3f7b8648d1636084f8d8c9ceb3b41aea74a8886b [file] [log] [blame]
Jon Skeet0aac0e42009-09-09 18:48:02 +01001#region Copyright notice and license
Jon Skeet60c059b2008-10-23 21:17:56 +01002// Protocol Buffers - Google's data interchange format
3// Copyright 2008 Google Inc. All rights reserved.
4// http://github.com/jskeet/dotnet-protobufs/
5// Original C++/Java/Python code:
Jon Skeet68036862008-10-22 13:30:34 +01006// http://code.google.com/p/protobuf/
7//
Jon Skeet60c059b2008-10-23 21:17:56 +01008// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
Jon Skeet68036862008-10-22 13:30:34 +010011//
Jon Skeet60c059b2008-10-23 21:17:56 +010012// * Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// * Redistributions in binary form must reproduce the above
15// copyright notice, this list of conditions and the following disclaimer
16// in the documentation and/or other materials provided with the
17// distribution.
18// * Neither the name of Google Inc. nor the names of its
19// contributors may be used to endorse or promote products derived from
20// this software without specific prior written permission.
Jon Skeet68036862008-10-22 13:30:34 +010021//
Jon Skeet60c059b2008-10-23 21:17:56 +010022// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Jon Skeet0aac0e42009-09-09 18:48:02 +010033#endregion
34
Jon Skeet68036862008-10-22 13:30:34 +010035using System;
36using System.Collections.Generic;
37using Google.ProtocolBuffers.Descriptors;
38using NUnit.Framework;
39using Google.ProtocolBuffers.TestProtos;
40
41namespace Google.ProtocolBuffers {
42 [TestFixture]
43 public class AbstractMessageTest {
44
45 [Test]
46 public void Clear() {
47 AbstractMessageWrapper message = new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(TestUtil.GetAllSet())).Clear().Build();
48 TestUtil.AssertClear((TestAllTypes) message.WrappedMessage);
49 }
50
51 [Test]
52 public void Copy() {
53 AbstractMessageWrapper message = new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder()).MergeFrom(TestUtil.GetAllSet()).Build();
54 TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage);
55 }
56
57 [Test]
58 public void SerializedSize() {
59 TestAllTypes message = TestUtil.GetAllSet();
60 IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet());
61
62 Assert.AreEqual(message.SerializedSize, abstractMessage.SerializedSize);
63 }
64
65 [Test]
66 public void Serialization() {
67 IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetAllSet());
68 TestUtil.AssertAllFieldsSet(TestAllTypes.ParseFrom(abstractMessage.ToByteString()));
69 Assert.AreEqual(TestUtil.GetAllSet().ToByteString(), abstractMessage.ToByteString());
70 }
71
72 [Test]
73 public void Parsing() {
74 IBuilder builder = new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder());
75 AbstractMessageWrapper message = (AbstractMessageWrapper) builder.WeakMergeFrom(TestUtil.GetAllSet().ToByteString()).WeakBuild();
76 TestUtil.AssertAllFieldsSet((TestAllTypes) message.WrappedMessage);
77 }
78
79 [Test]
Jon Skeet25a28582009-02-18 16:06:22 +000080 public void PackedSerialization() {
81 IMessage abstractMessage = new AbstractMessageWrapper(TestUtil.GetPackedSet());
82 TestUtil.AssertPackedFieldsSet(TestPackedTypes.ParseFrom(abstractMessage.ToByteString()));
83 Assert.AreEqual(TestUtil.GetPackedSet().ToByteString(), abstractMessage.ToByteString());
84 }
85
86 [Test]
87 public void PackedParsing() {
88 AbstractMessageWrapper.Builder builder = new AbstractMessageWrapper.Builder(TestPackedTypes.CreateBuilder());
89 AbstractMessageWrapper message = builder.MergeFrom(TestUtil.GetPackedSet().ToByteString()).Build();
90 TestUtil.AssertPackedFieldsSet((TestPackedTypes)message.WrappedMessage);
91 }
92
93 [Test]
Jon Skeet68036862008-10-22 13:30:34 +010094 public void OptimizedForSize() {
95 // We're mostly only Checking that this class was compiled successfully.
96 TestOptimizedForSize message = TestOptimizedForSize.CreateBuilder().SetI(1).Build();
97 message = TestOptimizedForSize.ParseFrom(message.ToByteString());
98 Assert.AreEqual(2, message.SerializedSize);
99 }
100
101 // -----------------------------------------------------------------
102 // Tests for isInitialized().
103
104 private static readonly TestRequired TestRequiredUninitialized = TestRequired.DefaultInstance;
105 private static readonly TestRequired TestRequiredInitialized = TestRequired.CreateBuilder().SetA(1).SetB(2).SetC(3).Build();
106
107 [Test]
108 public void IsInitialized() {
109 TestRequired.Builder builder = TestRequired.CreateBuilder();
110 AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
111
112 Assert.IsFalse(abstractBuilder.IsInitialized);
113 builder.A = 1;
114 Assert.IsFalse(abstractBuilder.IsInitialized);
115 builder.B = 1;
116 Assert.IsFalse(abstractBuilder.IsInitialized);
117 builder.C = 1;
118 Assert.IsTrue(abstractBuilder.IsInitialized);
119 }
120
121 [Test]
122 public void ForeignIsInitialized() {
123 TestRequiredForeign.Builder builder = TestRequiredForeign.CreateBuilder();
124 AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
125
126 Assert.IsTrue(abstractBuilder.IsInitialized);
127
128 builder.SetOptionalMessage(TestRequiredUninitialized);
129 Assert.IsFalse(abstractBuilder.IsInitialized);
130
131 builder.SetOptionalMessage(TestRequiredInitialized);
132 Assert.IsTrue(abstractBuilder.IsInitialized);
133
134 builder.AddRepeatedMessage(TestRequiredUninitialized);
135 Assert.IsFalse(abstractBuilder.IsInitialized);
136
137 builder.SetRepeatedMessage(0, TestRequiredInitialized);
138 Assert.IsTrue(abstractBuilder.IsInitialized);
139 }
140
141 // -----------------------------------------------------------------
142 // Tests for mergeFrom
143
144 static readonly TestAllTypes MergeSource = TestAllTypes.CreateBuilder()
145 .SetOptionalInt32(1)
146 .SetOptionalString("foo")
147 .SetOptionalForeignMessage(ForeignMessage.DefaultInstance)
148 .AddRepeatedString("bar")
149 .Build();
150
151 static readonly TestAllTypes MergeDest = TestAllTypes.CreateBuilder()
152 .SetOptionalInt64(2)
153 .SetOptionalString("baz")
154 .SetOptionalForeignMessage(ForeignMessage.CreateBuilder().SetC(3).Build())
155 .AddRepeatedString("qux")
156 .Build();
157
158 const string MergeResultText = "optional_int32: 1\n" +
159 "optional_int64: 2\n" +
160 "optional_string: \"foo\"\n" +
161 "optional_foreign_message {\n" +
162 " c: 3\n" +
163 "}\n" +
164 "repeated_string: \"qux\"\n" +
165 "repeated_string: \"bar\"\n";
166
167 [Test]
168 public void MergeFrom() {
169 AbstractMessageWrapper result = (AbstractMessageWrapper)
170 new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(MergeDest))
171 .MergeFrom(MergeSource)
172 .Build();
173
174 Assert.AreEqual(MergeResultText, result.ToString());
175 }
176
177 // -----------------------------------------------------------------
178 // Tests for equals and hashCode
179
180 [Test]
181 public void EqualsAndHashCode() {
182 TestAllTypes a = TestUtil.GetAllSet();
183 TestAllTypes b = TestAllTypes.CreateBuilder().Build();
184 TestAllTypes c = TestAllTypes.CreateBuilder(b).AddRepeatedString("x").Build();
185 TestAllTypes d = TestAllTypes.CreateBuilder(c).AddRepeatedString("y").Build();
186 TestAllExtensions e = TestUtil.GetAllExtensionsSet();
187 TestAllExtensions f = TestAllExtensions.CreateBuilder(e)
188 .AddExtension(UnitTestProtoFile.RepeatedInt32Extension, 999).Build();
189
190 CheckEqualsIsConsistent(a);
191 CheckEqualsIsConsistent(b);
192 CheckEqualsIsConsistent(c);
193 CheckEqualsIsConsistent(d);
194 CheckEqualsIsConsistent(e);
195 CheckEqualsIsConsistent(f);
196
197 CheckNotEqual(a, b);
198 CheckNotEqual(a, c);
199 CheckNotEqual(a, d);
200 CheckNotEqual(a, e);
201 CheckNotEqual(a, f);
202
203 CheckNotEqual(b, c);
204 CheckNotEqual(b, d);
205 CheckNotEqual(b, e);
206 CheckNotEqual(b, f);
207
208 CheckNotEqual(c, d);
209 CheckNotEqual(c, e);
210 CheckNotEqual(c, f);
211
212 CheckNotEqual(d, e);
213 CheckNotEqual(d, f);
214
215 CheckNotEqual(e, f);
Jon Skeet43da7ae2009-05-28 21:45:43 +0100216
217 // Deserializing into the TestEmptyMessage such that every field is an UnknownFieldSet.Field
218 TestEmptyMessage eUnknownFields = TestEmptyMessage.ParseFrom(e.ToByteArray());
219 TestEmptyMessage fUnknownFields = TestEmptyMessage.ParseFrom(f.ToByteArray());
220 CheckNotEqual(eUnknownFields, fUnknownFields);
221 CheckEqualsIsConsistent(eUnknownFields);
222 CheckEqualsIsConsistent(fUnknownFields);
223
224 // Subseqent reconstitutions should be identical
225 TestEmptyMessage eUnknownFields2 = TestEmptyMessage.ParseFrom(e.ToByteArray());
226 CheckEqualsIsConsistent(eUnknownFields, eUnknownFields2);
Jon Skeet68036862008-10-22 13:30:34 +0100227 }
228
229 /// <summary>
230 /// Asserts that the given protos are equal and have the same hash code.
231 /// </summary>
232 private static void CheckEqualsIsConsistent(IMessage message) {
233 // Object should be equal to itself.
234 Assert.AreEqual(message, message);
235
236 // Object should be equal to a dynamic copy of itself.
237 DynamicMessage dynamic = DynamicMessage.CreateBuilder(message).Build();
Jon Skeet43da7ae2009-05-28 21:45:43 +0100238 CheckEqualsIsConsistent(message, dynamic);
239 }
240
241 /// <summary>
242 /// Asserts that the given protos are equal and have the same hash code.
243 /// </summary>
244 private static void CheckEqualsIsConsistent(IMessage message1, IMessage message2) {
245 Assert.AreEqual(message1, message2);
246 Assert.AreEqual(message2, message1);
247 Assert.AreEqual(message2.GetHashCode(), message1.GetHashCode());
Jon Skeet68036862008-10-22 13:30:34 +0100248 }
249
250 /// <summary>
251 /// Asserts that the given protos are not equal and have different hash codes.
252 /// </summary>
253 /// <remarks>
254 /// It's valid for non-equal objects to have the same hash code, so
255 /// this test is stricter than it needs to be. However, this should happen
256 /// relatively rarely. (If this test fails, it's probably still due to a bug.)
257 /// </remarks>
258 private static void CheckNotEqual(IMessage m1, IMessage m2) {
259 String equalsError = string.Format("{0} should not be equal to {1}", m1, m2);
260 Assert.IsFalse(m1.Equals(m2), equalsError);
261 Assert.IsFalse(m2.Equals(m1), equalsError);
262
263 Assert.IsFalse(m1.GetHashCode() == m2.GetHashCode(),
264 string.Format("{0} should have a different hash code from {1}", m1, m2));
265 }
266
267 /// <summary>
268 /// Extends AbstractMessage and wraps some other message object. The methods
269 /// of the Message interface which aren't explicitly implemented by
270 /// AbstractMessage are forwarded to the wrapped object. This allows us to
271 /// test that AbstractMessage's implementations work even if the wrapped
272 /// object does not use them.
273 /// </summary>
274 private class AbstractMessageWrapper : AbstractMessage<AbstractMessageWrapper, AbstractMessageWrapper.Builder> {
275 private readonly IMessage wrappedMessage;
276
277 public IMessage WrappedMessage {
278 get { return wrappedMessage; }
279 }
280
281 public AbstractMessageWrapper(IMessage wrappedMessage) {
282 this.wrappedMessage = wrappedMessage;
283 }
284
285 public override MessageDescriptor DescriptorForType {
286 get { return wrappedMessage.DescriptorForType; }
287 }
288
289 public override AbstractMessageWrapper DefaultInstanceForType {
290 get { return new AbstractMessageWrapper(wrappedMessage.WeakDefaultInstanceForType); }
291 }
292
293 public override IDictionary<FieldDescriptor, object> AllFields {
294 get { return wrappedMessage.AllFields; }
295 }
296
297 public override bool HasField(FieldDescriptor field) {
298 return wrappedMessage.HasField(field);
299 }
300
301 public override object this[FieldDescriptor field] {
302 get { return wrappedMessage[field]; }
303 }
304
305 public override object this[FieldDescriptor field, int index] {
306 get { return wrappedMessage[field, index]; }
307 }
308
309 public override int GetRepeatedFieldCount(FieldDescriptor field) {
310 return wrappedMessage.GetRepeatedFieldCount(field);
311 }
312
313 public override UnknownFieldSet UnknownFields {
314 get { return wrappedMessage.UnknownFields; }
315 }
316
317 public override Builder CreateBuilderForType() {
318 return new Builder(wrappedMessage.WeakCreateBuilderForType());
319 }
320
Jon Skeete81a9d72009-02-24 16:50:56 +0000321 public override Builder ToBuilder() {
322 return new Builder(wrappedMessage.WeakToBuilder());
323 }
324
Jon Skeet68036862008-10-22 13:30:34 +0100325 internal class Builder : AbstractBuilder<AbstractMessageWrapper, Builder> {
326 private readonly IBuilder wrappedBuilder;
327
328 protected override Builder ThisBuilder {
329 get { return this; }
330 }
331
332 internal Builder(IBuilder wrappedBuilder) {
333 this.wrappedBuilder = wrappedBuilder;
334 }
335
336 public override Builder MergeFrom(AbstractMessageWrapper other) {
337 wrappedBuilder.WeakMergeFrom(other.wrappedMessage);
338 return this;
339 }
340
341 public override bool IsInitialized {
342 get { return wrappedBuilder.IsInitialized; }
343 }
344
345 public override IDictionary<FieldDescriptor, object> AllFields {
346 get { return wrappedBuilder.AllFields; }
347 }
348
349 public override object this[FieldDescriptor field] {
350 get { return wrappedBuilder[field]; }
351 set { wrappedBuilder[field] = value; }
352 }
353
354 public override MessageDescriptor DescriptorForType {
355 get { return wrappedBuilder.DescriptorForType; }
356 }
357
358 public override int GetRepeatedFieldCount(FieldDescriptor field) {
359 return wrappedBuilder.GetRepeatedFieldCount(field);
360 }
361
362 public override object this[FieldDescriptor field, int index] {
363 get { return wrappedBuilder[field, index]; }
364 set { wrappedBuilder[field, index] = value; }
365 }
366
367 public override bool HasField(FieldDescriptor field) {
368 return wrappedBuilder.HasField(field);
369 }
370
371 public override UnknownFieldSet UnknownFields {
372 get { return wrappedBuilder.UnknownFields; }
373 set { wrappedBuilder.UnknownFields = value; }
374 }
375
376 public override AbstractMessageWrapper Build() {
377 return new AbstractMessageWrapper(wrappedBuilder.WeakBuild());
378 }
379
380 public override AbstractMessageWrapper BuildPartial() {
381 return new AbstractMessageWrapper(wrappedBuilder.WeakBuildPartial());
382 }
383
384 public override Builder Clone() {
385 return new Builder(wrappedBuilder.WeakClone());
386 }
387
388 public override AbstractMessageWrapper DefaultInstanceForType {
389 get { return new AbstractMessageWrapper(wrappedBuilder.WeakDefaultInstanceForType); }
390 }
391
392 public override Builder ClearField(FieldDescriptor field) {
393 wrappedBuilder.WeakClearField(field);
394 return this;
395 }
396
397 public override Builder AddRepeatedField(FieldDescriptor field, object value) {
398 wrappedBuilder.WeakAddRepeatedField(field, value);
399 return this;
400 }
401
402 public override IBuilder CreateBuilderForField(FieldDescriptor field) {
403 wrappedBuilder.CreateBuilderForField(field);
404 return this;
405 }
406
407 public override Builder MergeFrom(IMessage other) {
408 wrappedBuilder.WeakMergeFrom(other);
409 return this;
410 }
411
412 public override Builder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
413 wrappedBuilder.WeakMergeFrom(input, extensionRegistry);
414 return this;
415 }
416 }
417 }
418 }
419}