blob: ca34ac8aeb0e99c470fce7db9462b0114ff0a9ac [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);
213 }
214
215 /// <summary>
216 /// Asserts that the given protos are equal and have the same hash code.
217 /// </summary>
218 private static void CheckEqualsIsConsistent(IMessage message) {
219 // Object should be equal to itself.
220 Assert.AreEqual(message, message);
221
222 // Object should be equal to a dynamic copy of itself.
223 DynamicMessage dynamic = DynamicMessage.CreateBuilder(message).Build();
224 Assert.AreEqual(message, dynamic);
225 Assert.AreEqual(dynamic, message);
226 Assert.AreEqual(dynamic.GetHashCode(), message.GetHashCode());
227 }
228
229 /// <summary>
230 /// Asserts that the given protos are not equal and have different hash codes.
231 /// </summary>
232 /// <remarks>
233 /// It's valid for non-equal objects to have the same hash code, so
234 /// this test is stricter than it needs to be. However, this should happen
235 /// relatively rarely. (If this test fails, it's probably still due to a bug.)
236 /// </remarks>
237 private static void CheckNotEqual(IMessage m1, IMessage m2) {
238 String equalsError = string.Format("{0} should not be equal to {1}", m1, m2);
239 Assert.IsFalse(m1.Equals(m2), equalsError);
240 Assert.IsFalse(m2.Equals(m1), equalsError);
241
242 Assert.IsFalse(m1.GetHashCode() == m2.GetHashCode(),
243 string.Format("{0} should have a different hash code from {1}", m1, m2));
244 }
245
246 /// <summary>
247 /// Extends AbstractMessage and wraps some other message object. The methods
248 /// of the Message interface which aren't explicitly implemented by
249 /// AbstractMessage are forwarded to the wrapped object. This allows us to
250 /// test that AbstractMessage's implementations work even if the wrapped
251 /// object does not use them.
252 /// </summary>
253 private class AbstractMessageWrapper : AbstractMessage<AbstractMessageWrapper, AbstractMessageWrapper.Builder> {
254 private readonly IMessage wrappedMessage;
255
256 public IMessage WrappedMessage {
257 get { return wrappedMessage; }
258 }
259
260 public AbstractMessageWrapper(IMessage wrappedMessage) {
261 this.wrappedMessage = wrappedMessage;
262 }
263
264 public override MessageDescriptor DescriptorForType {
265 get { return wrappedMessage.DescriptorForType; }
266 }
267
268 public override AbstractMessageWrapper DefaultInstanceForType {
269 get { return new AbstractMessageWrapper(wrappedMessage.WeakDefaultInstanceForType); }
270 }
271
272 public override IDictionary<FieldDescriptor, object> AllFields {
273 get { return wrappedMessage.AllFields; }
274 }
275
276 public override bool HasField(FieldDescriptor field) {
277 return wrappedMessage.HasField(field);
278 }
279
280 public override object this[FieldDescriptor field] {
281 get { return wrappedMessage[field]; }
282 }
283
284 public override object this[FieldDescriptor field, int index] {
285 get { return wrappedMessage[field, index]; }
286 }
287
288 public override int GetRepeatedFieldCount(FieldDescriptor field) {
289 return wrappedMessage.GetRepeatedFieldCount(field);
290 }
291
292 public override UnknownFieldSet UnknownFields {
293 get { return wrappedMessage.UnknownFields; }
294 }
295
296 public override Builder CreateBuilderForType() {
297 return new Builder(wrappedMessage.WeakCreateBuilderForType());
298 }
299
Jon Skeete81a9d72009-02-24 16:50:56 +0000300 public override Builder ToBuilder() {
301 return new Builder(wrappedMessage.WeakToBuilder());
302 }
303
Jon Skeet68036862008-10-22 13:30:34 +0100304 internal class Builder : AbstractBuilder<AbstractMessageWrapper, Builder> {
305 private readonly IBuilder wrappedBuilder;
306
307 protected override Builder ThisBuilder {
308 get { return this; }
309 }
310
311 internal Builder(IBuilder wrappedBuilder) {
312 this.wrappedBuilder = wrappedBuilder;
313 }
314
315 public override Builder MergeFrom(AbstractMessageWrapper other) {
316 wrappedBuilder.WeakMergeFrom(other.wrappedMessage);
317 return this;
318 }
319
320 public override bool IsInitialized {
321 get { return wrappedBuilder.IsInitialized; }
322 }
323
324 public override IDictionary<FieldDescriptor, object> AllFields {
325 get { return wrappedBuilder.AllFields; }
326 }
327
328 public override object this[FieldDescriptor field] {
329 get { return wrappedBuilder[field]; }
330 set { wrappedBuilder[field] = value; }
331 }
332
333 public override MessageDescriptor DescriptorForType {
334 get { return wrappedBuilder.DescriptorForType; }
335 }
336
337 public override int GetRepeatedFieldCount(FieldDescriptor field) {
338 return wrappedBuilder.GetRepeatedFieldCount(field);
339 }
340
341 public override object this[FieldDescriptor field, int index] {
342 get { return wrappedBuilder[field, index]; }
343 set { wrappedBuilder[field, index] = value; }
344 }
345
346 public override bool HasField(FieldDescriptor field) {
347 return wrappedBuilder.HasField(field);
348 }
349
350 public override UnknownFieldSet UnknownFields {
351 get { return wrappedBuilder.UnknownFields; }
352 set { wrappedBuilder.UnknownFields = value; }
353 }
354
355 public override AbstractMessageWrapper Build() {
356 return new AbstractMessageWrapper(wrappedBuilder.WeakBuild());
357 }
358
359 public override AbstractMessageWrapper BuildPartial() {
360 return new AbstractMessageWrapper(wrappedBuilder.WeakBuildPartial());
361 }
362
363 public override Builder Clone() {
364 return new Builder(wrappedBuilder.WeakClone());
365 }
366
367 public override AbstractMessageWrapper DefaultInstanceForType {
368 get { return new AbstractMessageWrapper(wrappedBuilder.WeakDefaultInstanceForType); }
369 }
370
371 public override Builder ClearField(FieldDescriptor field) {
372 wrappedBuilder.WeakClearField(field);
373 return this;
374 }
375
376 public override Builder AddRepeatedField(FieldDescriptor field, object value) {
377 wrappedBuilder.WeakAddRepeatedField(field, value);
378 return this;
379 }
380
381 public override IBuilder CreateBuilderForField(FieldDescriptor field) {
382 wrappedBuilder.CreateBuilderForField(field);
383 return this;
384 }
385
386 public override Builder MergeFrom(IMessage other) {
387 wrappedBuilder.WeakMergeFrom(other);
388 return this;
389 }
390
391 public override Builder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
392 wrappedBuilder.WeakMergeFrom(input, extensionRegistry);
393 return this;
394 }
395 }
396 }
397 }
398}