blob: 260ae28c18d71e53d5d01588590cb8bc50f026b6 [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]
77 public void OptimizedForSize() {
78 // We're mostly only Checking that this class was compiled successfully.
79 TestOptimizedForSize message = TestOptimizedForSize.CreateBuilder().SetI(1).Build();
80 message = TestOptimizedForSize.ParseFrom(message.ToByteString());
81 Assert.AreEqual(2, message.SerializedSize);
82 }
83
84 // -----------------------------------------------------------------
85 // Tests for isInitialized().
86
87 private static readonly TestRequired TestRequiredUninitialized = TestRequired.DefaultInstance;
88 private static readonly TestRequired TestRequiredInitialized = TestRequired.CreateBuilder().SetA(1).SetB(2).SetC(3).Build();
89
90 [Test]
91 public void IsInitialized() {
92 TestRequired.Builder builder = TestRequired.CreateBuilder();
93 AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
94
95 Assert.IsFalse(abstractBuilder.IsInitialized);
96 builder.A = 1;
97 Assert.IsFalse(abstractBuilder.IsInitialized);
98 builder.B = 1;
99 Assert.IsFalse(abstractBuilder.IsInitialized);
100 builder.C = 1;
101 Assert.IsTrue(abstractBuilder.IsInitialized);
102 }
103
104 [Test]
105 public void ForeignIsInitialized() {
106 TestRequiredForeign.Builder builder = TestRequiredForeign.CreateBuilder();
107 AbstractMessageWrapper.Builder abstractBuilder = new AbstractMessageWrapper.Builder(builder);
108
109 Assert.IsTrue(abstractBuilder.IsInitialized);
110
111 builder.SetOptionalMessage(TestRequiredUninitialized);
112 Assert.IsFalse(abstractBuilder.IsInitialized);
113
114 builder.SetOptionalMessage(TestRequiredInitialized);
115 Assert.IsTrue(abstractBuilder.IsInitialized);
116
117 builder.AddRepeatedMessage(TestRequiredUninitialized);
118 Assert.IsFalse(abstractBuilder.IsInitialized);
119
120 builder.SetRepeatedMessage(0, TestRequiredInitialized);
121 Assert.IsTrue(abstractBuilder.IsInitialized);
122 }
123
124 // -----------------------------------------------------------------
125 // Tests for mergeFrom
126
127 static readonly TestAllTypes MergeSource = TestAllTypes.CreateBuilder()
128 .SetOptionalInt32(1)
129 .SetOptionalString("foo")
130 .SetOptionalForeignMessage(ForeignMessage.DefaultInstance)
131 .AddRepeatedString("bar")
132 .Build();
133
134 static readonly TestAllTypes MergeDest = TestAllTypes.CreateBuilder()
135 .SetOptionalInt64(2)
136 .SetOptionalString("baz")
137 .SetOptionalForeignMessage(ForeignMessage.CreateBuilder().SetC(3).Build())
138 .AddRepeatedString("qux")
139 .Build();
140
141 const string MergeResultText = "optional_int32: 1\n" +
142 "optional_int64: 2\n" +
143 "optional_string: \"foo\"\n" +
144 "optional_foreign_message {\n" +
145 " c: 3\n" +
146 "}\n" +
147 "repeated_string: \"qux\"\n" +
148 "repeated_string: \"bar\"\n";
149
150 [Test]
151 public void MergeFrom() {
152 AbstractMessageWrapper result = (AbstractMessageWrapper)
153 new AbstractMessageWrapper.Builder(TestAllTypes.CreateBuilder(MergeDest))
154 .MergeFrom(MergeSource)
155 .Build();
156
157 Assert.AreEqual(MergeResultText, result.ToString());
158 }
159
160 // -----------------------------------------------------------------
161 // Tests for equals and hashCode
162
163 [Test]
164 public void EqualsAndHashCode() {
165 TestAllTypes a = TestUtil.GetAllSet();
166 TestAllTypes b = TestAllTypes.CreateBuilder().Build();
167 TestAllTypes c = TestAllTypes.CreateBuilder(b).AddRepeatedString("x").Build();
168 TestAllTypes d = TestAllTypes.CreateBuilder(c).AddRepeatedString("y").Build();
169 TestAllExtensions e = TestUtil.GetAllExtensionsSet();
170 TestAllExtensions f = TestAllExtensions.CreateBuilder(e)
171 .AddExtension(UnitTestProtoFile.RepeatedInt32Extension, 999).Build();
172
173 CheckEqualsIsConsistent(a);
174 CheckEqualsIsConsistent(b);
175 CheckEqualsIsConsistent(c);
176 CheckEqualsIsConsistent(d);
177 CheckEqualsIsConsistent(e);
178 CheckEqualsIsConsistent(f);
179
180 CheckNotEqual(a, b);
181 CheckNotEqual(a, c);
182 CheckNotEqual(a, d);
183 CheckNotEqual(a, e);
184 CheckNotEqual(a, f);
185
186 CheckNotEqual(b, c);
187 CheckNotEqual(b, d);
188 CheckNotEqual(b, e);
189 CheckNotEqual(b, f);
190
191 CheckNotEqual(c, d);
192 CheckNotEqual(c, e);
193 CheckNotEqual(c, f);
194
195 CheckNotEqual(d, e);
196 CheckNotEqual(d, f);
197
198 CheckNotEqual(e, f);
199 }
200
201 /// <summary>
202 /// Asserts that the given protos are equal and have the same hash code.
203 /// </summary>
204 private static void CheckEqualsIsConsistent(IMessage message) {
205 // Object should be equal to itself.
206 Assert.AreEqual(message, message);
207
208 // Object should be equal to a dynamic copy of itself.
209 DynamicMessage dynamic = DynamicMessage.CreateBuilder(message).Build();
210 Assert.AreEqual(message, dynamic);
211 Assert.AreEqual(dynamic, message);
212 Assert.AreEqual(dynamic.GetHashCode(), message.GetHashCode());
213 }
214
215 /// <summary>
216 /// Asserts that the given protos are not equal and have different hash codes.
217 /// </summary>
218 /// <remarks>
219 /// It's valid for non-equal objects to have the same hash code, so
220 /// this test is stricter than it needs to be. However, this should happen
221 /// relatively rarely. (If this test fails, it's probably still due to a bug.)
222 /// </remarks>
223 private static void CheckNotEqual(IMessage m1, IMessage m2) {
224 String equalsError = string.Format("{0} should not be equal to {1}", m1, m2);
225 Assert.IsFalse(m1.Equals(m2), equalsError);
226 Assert.IsFalse(m2.Equals(m1), equalsError);
227
228 Assert.IsFalse(m1.GetHashCode() == m2.GetHashCode(),
229 string.Format("{0} should have a different hash code from {1}", m1, m2));
230 }
231
232 /// <summary>
233 /// Extends AbstractMessage and wraps some other message object. The methods
234 /// of the Message interface which aren't explicitly implemented by
235 /// AbstractMessage are forwarded to the wrapped object. This allows us to
236 /// test that AbstractMessage's implementations work even if the wrapped
237 /// object does not use them.
238 /// </summary>
239 private class AbstractMessageWrapper : AbstractMessage<AbstractMessageWrapper, AbstractMessageWrapper.Builder> {
240 private readonly IMessage wrappedMessage;
241
242 public IMessage WrappedMessage {
243 get { return wrappedMessage; }
244 }
245
246 public AbstractMessageWrapper(IMessage wrappedMessage) {
247 this.wrappedMessage = wrappedMessage;
248 }
249
250 public override MessageDescriptor DescriptorForType {
251 get { return wrappedMessage.DescriptorForType; }
252 }
253
254 public override AbstractMessageWrapper DefaultInstanceForType {
255 get { return new AbstractMessageWrapper(wrappedMessage.WeakDefaultInstanceForType); }
256 }
257
258 public override IDictionary<FieldDescriptor, object> AllFields {
259 get { return wrappedMessage.AllFields; }
260 }
261
262 public override bool HasField(FieldDescriptor field) {
263 return wrappedMessage.HasField(field);
264 }
265
266 public override object this[FieldDescriptor field] {
267 get { return wrappedMessage[field]; }
268 }
269
270 public override object this[FieldDescriptor field, int index] {
271 get { return wrappedMessage[field, index]; }
272 }
273
274 public override int GetRepeatedFieldCount(FieldDescriptor field) {
275 return wrappedMessage.GetRepeatedFieldCount(field);
276 }
277
278 public override UnknownFieldSet UnknownFields {
279 get { return wrappedMessage.UnknownFields; }
280 }
281
282 public override Builder CreateBuilderForType() {
283 return new Builder(wrappedMessage.WeakCreateBuilderForType());
284 }
285
286 internal class Builder : AbstractBuilder<AbstractMessageWrapper, Builder> {
287 private readonly IBuilder wrappedBuilder;
288
289 protected override Builder ThisBuilder {
290 get { return this; }
291 }
292
293 internal Builder(IBuilder wrappedBuilder) {
294 this.wrappedBuilder = wrappedBuilder;
295 }
296
297 public override Builder MergeFrom(AbstractMessageWrapper other) {
298 wrappedBuilder.WeakMergeFrom(other.wrappedMessage);
299 return this;
300 }
301
302 public override bool IsInitialized {
303 get { return wrappedBuilder.IsInitialized; }
304 }
305
306 public override IDictionary<FieldDescriptor, object> AllFields {
307 get { return wrappedBuilder.AllFields; }
308 }
309
310 public override object this[FieldDescriptor field] {
311 get { return wrappedBuilder[field]; }
312 set { wrappedBuilder[field] = value; }
313 }
314
315 public override MessageDescriptor DescriptorForType {
316 get { return wrappedBuilder.DescriptorForType; }
317 }
318
319 public override int GetRepeatedFieldCount(FieldDescriptor field) {
320 return wrappedBuilder.GetRepeatedFieldCount(field);
321 }
322
323 public override object this[FieldDescriptor field, int index] {
324 get { return wrappedBuilder[field, index]; }
325 set { wrappedBuilder[field, index] = value; }
326 }
327
328 public override bool HasField(FieldDescriptor field) {
329 return wrappedBuilder.HasField(field);
330 }
331
332 public override UnknownFieldSet UnknownFields {
333 get { return wrappedBuilder.UnknownFields; }
334 set { wrappedBuilder.UnknownFields = value; }
335 }
336
337 public override AbstractMessageWrapper Build() {
338 return new AbstractMessageWrapper(wrappedBuilder.WeakBuild());
339 }
340
341 public override AbstractMessageWrapper BuildPartial() {
342 return new AbstractMessageWrapper(wrappedBuilder.WeakBuildPartial());
343 }
344
345 public override Builder Clone() {
346 return new Builder(wrappedBuilder.WeakClone());
347 }
348
349 public override AbstractMessageWrapper DefaultInstanceForType {
350 get { return new AbstractMessageWrapper(wrappedBuilder.WeakDefaultInstanceForType); }
351 }
352
353 public override Builder ClearField(FieldDescriptor field) {
354 wrappedBuilder.WeakClearField(field);
355 return this;
356 }
357
358 public override Builder AddRepeatedField(FieldDescriptor field, object value) {
359 wrappedBuilder.WeakAddRepeatedField(field, value);
360 return this;
361 }
362
363 public override IBuilder CreateBuilderForField(FieldDescriptor field) {
364 wrappedBuilder.CreateBuilderForField(field);
365 return this;
366 }
367
368 public override Builder MergeFrom(IMessage other) {
369 wrappedBuilder.WeakMergeFrom(other);
370 return this;
371 }
372
373 public override Builder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
374 wrappedBuilder.WeakMergeFrom(input, extensionRegistry);
375 return this;
376 }
377 }
378 }
379 }
380}