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