• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 // Protocol Buffers - Google's data interchange format
3 // Copyright 2015 Google Inc.  All rights reserved.
4 // https://developers.google.com/protocol-buffers/
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are
8 // met:
9 //
10 //     * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //     * Redistributions in binary form must reproduce the above
13 // copyright notice, this list of conditions and the following disclaimer
14 // in the documentation and/or other materials provided with the
15 // distribution.
16 //     * Neither the name of Google Inc. nor the names of its
17 // contributors may be used to endorse or promote products derived from
18 // this software without specific prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #endregion
32 
33 using System;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
37 using System.IO;
38 using System.Linq;
39 using System.Text;
40 using Google.Protobuf.TestProtos;
41 using Google.Protobuf.WellKnownTypes;
42 using NUnit.Framework;
43 
44 namespace Google.Protobuf.Collections
45 {
46     public class RepeatedFieldTest
47     {
48         [Test]
NullValuesRejected()49         public void NullValuesRejected()
50         {
51             var list = new RepeatedField<string>();
52             Assert.Throws<ArgumentNullException>(() => list.Add((string)null));
53             Assert.Throws<ArgumentNullException>(() => list.Add((IEnumerable<string>)null));
54             Assert.Throws<ArgumentNullException>(() => list.Add((RepeatedField<string>)null));
55             Assert.Throws<ArgumentNullException>(() => list.Contains(null));
56             Assert.Throws<ArgumentNullException>(() => list.IndexOf(null));
57         }
58 
59         [Test]
Add_SingleItem()60         public void Add_SingleItem()
61         {
62             var list = new RepeatedField<string>();
63             list.Add("foo");
64             Assert.AreEqual(1, list.Count);
65             Assert.AreEqual("foo", list[0]);
66         }
67 
68         [Test]
Add_Sequence()69         public void Add_Sequence()
70         {
71             var list = new RepeatedField<string>();
72             list.Add(new[] { "foo", "bar" });
73             Assert.AreEqual(2, list.Count);
74             Assert.AreEqual("foo", list[0]);
75             Assert.AreEqual("bar", list[1]);
76         }
77 
78         [Test]
AddRange_SlowPath()79         public void AddRange_SlowPath()
80         {
81             var list = new RepeatedField<string>();
82             list.AddRange(new[] { "foo", "bar" }.Select(x => x));
83             Assert.AreEqual(2, list.Count);
84             Assert.AreEqual("foo", list[0]);
85             Assert.AreEqual("bar", list[1]);
86         }
87 
88         [Test]
AddRange_SlowPath_NullsProhibited_ReferenceType()89         public void AddRange_SlowPath_NullsProhibited_ReferenceType()
90         {
91             var list = new RepeatedField<string>();
92             // It's okay for this to throw ArgumentNullException if necessary.
93             // It's not ideal, but not awful.
94             Assert.Catch<ArgumentException>(() => list.AddRange(new[] { "foo", null }.Select(x => x)));
95         }
96 
97         [Test]
AddRange_SlowPath_NullsProhibited_NullableValueType()98         public void AddRange_SlowPath_NullsProhibited_NullableValueType()
99         {
100             var list = new RepeatedField<int?>();
101             // It's okay for this to throw ArgumentNullException if necessary.
102             // It's not ideal, but not awful.
103             Assert.Catch<ArgumentException>(() => list.AddRange(new[] { 20, (int?)null }.Select(x => x)));
104         }
105 
106         [Test]
AddRange_Optimized_NonNullableValueType()107         public void AddRange_Optimized_NonNullableValueType()
108         {
109             var list = new RepeatedField<int>();
110             list.AddRange(new List<int> { 20, 30 });
111             Assert.AreEqual(2, list.Count);
112             Assert.AreEqual(20, list[0]);
113             Assert.AreEqual(30, list[1]);
114         }
115 
116         [Test]
AddRange_Optimized_ReferenceType()117         public void AddRange_Optimized_ReferenceType()
118         {
119             var list = new RepeatedField<string>();
120             list.AddRange(new List<string> { "foo", "bar" });
121             Assert.AreEqual(2, list.Count);
122             Assert.AreEqual("foo", list[0]);
123             Assert.AreEqual("bar", list[1]);
124         }
125 
126         [Test]
AddRange_Optimized_NullableValueType()127         public void AddRange_Optimized_NullableValueType()
128         {
129             var list = new RepeatedField<int?>();
130             list.AddRange(new List<int?> { 20, 30 });
131             Assert.AreEqual(2, list.Count);
132             Assert.AreEqual((int?) 20, list[0]);
133             Assert.AreEqual((int?) 30, list[1]);
134         }
135 
136         [Test]
AddRange_Optimized_NullsProhibited_ReferenceType()137         public void AddRange_Optimized_NullsProhibited_ReferenceType()
138         {
139             // We don't just trust that a collection with a nullable element type doesn't contain nulls
140             var list = new RepeatedField<string>();
141             // It's okay for this to throw ArgumentNullException if necessary.
142             // It's not ideal, but not awful.
143             Assert.Catch<ArgumentException>(() => list.AddRange(new List<string> { "foo", null }));
144         }
145 
146         [Test]
AddRange_Optimized_NullsProhibited_NullableValueType()147         public void AddRange_Optimized_NullsProhibited_NullableValueType()
148         {
149             // We don't just trust that a collection with a nullable element type doesn't contain nulls
150             var list = new RepeatedField<int?>();
151             // It's okay for this to throw ArgumentNullException if necessary.
152             // It's not ideal, but not awful.
153             Assert.Catch<ArgumentException>(() => list.AddRange(new List<int?> { 20, null }));
154         }
155 
156         [Test]
AddRange_AlreadyNotEmpty()157         public void AddRange_AlreadyNotEmpty()
158         {
159             var list = new RepeatedField<int> { 1, 2, 3 };
160             list.AddRange(new List<int> { 4, 5, 6 });
161             CollectionAssert.AreEqual(new[] { 1, 2, 3, 4, 5, 6 }, list);
162         }
163 
164         [Test]
AddRange_RepeatedField()165         public void AddRange_RepeatedField()
166         {
167             var list = new RepeatedField<string> { "original" };
168             list.AddRange(new RepeatedField<string> { "foo", "bar" });
169             Assert.AreEqual(3, list.Count);
170             Assert.AreEqual("original", list[0]);
171             Assert.AreEqual("foo", list[1]);
172             Assert.AreEqual("bar", list[2]);
173         }
174 
175         [Test]
RemoveAt_Valid()176         public void RemoveAt_Valid()
177         {
178             var list = new RepeatedField<string> { "first", "second", "third" };
179             list.RemoveAt(1);
180             CollectionAssert.AreEqual(new[] { "first", "third" }, list);
181             // Just check that these don't throw...
182             list.RemoveAt(list.Count - 1); // Now the count will be 1...
183             list.RemoveAt(0);
184             Assert.AreEqual(0, list.Count);
185         }
186 
187         [Test]
RemoveAt_Invalid()188         public void RemoveAt_Invalid()
189         {
190             var list = new RepeatedField<string> { "first", "second", "third" };
191             Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(-1));
192             Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(3));
193         }
194 
195         [Test]
Insert_Valid()196         public void Insert_Valid()
197         {
198             var list = new RepeatedField<string> { "first", "second" };
199             list.Insert(1, "middle");
200             CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
201             list.Insert(3, "end");
202             CollectionAssert.AreEqual(new[] { "first", "middle", "second", "end" }, list);
203             list.Insert(0, "start");
204             CollectionAssert.AreEqual(new[] { "start", "first", "middle", "second", "end" }, list);
205         }
206 
207         [Test]
Insert_Invalid()208         public void Insert_Invalid()
209         {
210             var list = new RepeatedField<string> { "first", "second" };
211             Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(-1, "foo"));
212             Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(3, "foo"));
213             Assert.Throws<ArgumentNullException>(() => list.Insert(0, null));
214         }
215 
216         [Test]
Equals_RepeatedField()217         public void Equals_RepeatedField()
218         {
219             var list = new RepeatedField<string> { "first", "second" };
220             Assert.IsFalse(list.Equals((RepeatedField<string>) null));
221             Assert.IsTrue(list.Equals(list));
222             Assert.IsFalse(list.Equals(new RepeatedField<string> { "first", "third" }));
223             Assert.IsFalse(list.Equals(new RepeatedField<string> { "first" }));
224             Assert.IsTrue(list.Equals(new RepeatedField<string> { "first", "second" }));
225         }
226 
227         [Test]
Equals_Object()228         public void Equals_Object()
229         {
230             var list = new RepeatedField<string> { "first", "second" };
231             Assert.IsFalse(list.Equals((object) null));
232             Assert.IsTrue(list.Equals((object) list));
233             Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first", "third" }));
234             Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first" }));
235             Assert.IsTrue(list.Equals((object) new RepeatedField<string> { "first", "second" }));
236             Assert.IsFalse(list.Equals(new object()));
237         }
238 
239         [Test]
GetEnumerator_GenericInterface()240         public void GetEnumerator_GenericInterface()
241         {
242             IEnumerable<string> list = new RepeatedField<string> { "first", "second" };
243             // Select gets rid of the optimizations in ToList...
244             CollectionAssert.AreEqual(new[] { "first", "second" }, list.Select(x => x).ToList());
245         }
246 
247         [Test]
GetEnumerator_NonGenericInterface()248         public void GetEnumerator_NonGenericInterface()
249         {
250             IEnumerable list = new RepeatedField<string> { "first", "second" };
251             CollectionAssert.AreEqual(new[] { "first", "second" }, list.Cast<object>().ToList());
252         }
253 
254         [Test]
CopyTo()255         public void CopyTo()
256         {
257             var list = new RepeatedField<string> { "first", "second" };
258             string[] stringArray = new string[4];
259             list.CopyTo(stringArray, 1);
260             CollectionAssert.AreEqual(new[] { null, "first", "second", null }, stringArray);
261         }
262 
263         [Test]
Indexer_Get()264         public void Indexer_Get()
265         {
266             var list = new RepeatedField<string> { "first", "second" };
267             Assert.AreEqual("first", list[0]);
268             Assert.AreEqual("second", list[1]);
269             Assert.Throws<ArgumentOutOfRangeException>(() => list[-1].GetHashCode());
270             Assert.Throws<ArgumentOutOfRangeException>(() => list[2].GetHashCode());
271         }
272 
273         [Test]
Indexer_Set()274         public void Indexer_Set()
275         {
276             var list = new RepeatedField<string> { "first", "second" };
277             list[0] = "changed";
278             Assert.AreEqual("changed", list[0]);
279             Assert.Throws<ArgumentNullException>(() => list[0] = null);
280             Assert.Throws<ArgumentOutOfRangeException>(() => list[-1] = "bad");
281             Assert.Throws<ArgumentOutOfRangeException>(() => list[2] = "bad");
282         }
283 
284         [Test]
Clone_ReturnsMutable()285         public void Clone_ReturnsMutable()
286         {
287             var list = new RepeatedField<int> { 0 };
288             var clone = list.Clone();
289             clone[0] = 1;
290         }
291 
292         [Test]
Enumerator()293         public void Enumerator()
294         {
295             var list = new RepeatedField<string> { "first", "second" };
296             using (var enumerator = list.GetEnumerator())
297             {
298                 Assert.IsTrue(enumerator.MoveNext());
299                 Assert.AreEqual("first", enumerator.Current);
300                 Assert.IsTrue(enumerator.MoveNext());
301                 Assert.AreEqual("second", enumerator.Current);
302                 Assert.IsFalse(enumerator.MoveNext());
303                 Assert.IsFalse(enumerator.MoveNext());
304             }
305         }
306 
307         [Test]
AddEntriesFrom_PackedInt32()308         public void AddEntriesFrom_PackedInt32()
309         {
310             uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
311             var stream = new MemoryStream();
312             var output = new CodedOutputStream(stream);
313             var length = CodedOutputStream.ComputeInt32Size(10)
314                 + CodedOutputStream.ComputeInt32Size(999)
315                 + CodedOutputStream.ComputeInt32Size(-1000);
316             output.WriteTag(packedTag);
317             output.WriteRawVarint32((uint) length);
318             output.WriteInt32(10);
319             output.WriteInt32(999);
320             output.WriteInt32(-1000);
321             output.Flush();
322             stream.Position = 0;
323 
324             // Deliberately "expecting" a non-packed tag, but we detect that the data is
325             // actually packed.
326             uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
327             var field = new RepeatedField<int>();
328             var input = new CodedInputStream(stream);
329             input.AssertNextTag(packedTag);
330             field.AddEntriesFrom(input, FieldCodec.ForInt32(nonPackedTag));
331             CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
332             Assert.IsTrue(input.IsAtEnd);
333         }
334 
335         [Test]
AddEntriesFrom_NonPackedInt32()336         public void AddEntriesFrom_NonPackedInt32()
337         {
338             uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
339             var stream = new MemoryStream();
340             var output = new CodedOutputStream(stream);
341             output.WriteTag(nonPackedTag);
342             output.WriteInt32(10);
343             output.WriteTag(nonPackedTag);
344             output.WriteInt32(999);
345             output.WriteTag(nonPackedTag);
346             output.WriteInt32(-1000); // Just for variety...
347             output.Flush();
348             stream.Position = 0;
349 
350             // Deliberately "expecting" a packed tag, but we detect that the data is
351             // actually not packed.
352             uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
353             var field = new RepeatedField<int>();
354             var input = new CodedInputStream(stream);
355             input.AssertNextTag(nonPackedTag);
356             field.AddEntriesFrom(input, FieldCodec.ForInt32(packedTag));
357             CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
358             Assert.IsTrue(input.IsAtEnd);
359         }
360 
361         [Test]
AddEntriesFrom_String()362         public void AddEntriesFrom_String()
363         {
364             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
365             var stream = new MemoryStream();
366             var output = new CodedOutputStream(stream);
367             output.WriteTag(tag);
368             output.WriteString("Foo");
369             output.WriteTag(tag);
370             output.WriteString("");
371             output.WriteTag(tag);
372             output.WriteString("Bar");
373             output.Flush();
374             stream.Position = 0;
375 
376             var field = new RepeatedField<string>();
377             var input = new CodedInputStream(stream);
378             input.AssertNextTag(tag);
379             field.AddEntriesFrom(input, FieldCodec.ForString(tag));
380             CollectionAssert.AreEqual(new[] { "Foo", "", "Bar" }, field);
381             Assert.IsTrue(input.IsAtEnd);
382         }
383 
384         [Test]
AddEntriesFrom_Message()385         public void AddEntriesFrom_Message()
386         {
387             var message1 = new ForeignMessage { C = 2000 };
388             var message2 = new ForeignMessage { C = -250 };
389 
390             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
391             var stream = new MemoryStream();
392             var output = new CodedOutputStream(stream);
393             output.WriteTag(tag);
394             output.WriteMessage(message1);
395             output.WriteTag(tag);
396             output.WriteMessage(message2);
397             output.Flush();
398             stream.Position = 0;
399 
400             var field = new RepeatedField<ForeignMessage>();
401             var input = new CodedInputStream(stream);
402             input.AssertNextTag(tag);
403             field.AddEntriesFrom(input, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
404             CollectionAssert.AreEqual(new[] { message1, message2}, field);
405             Assert.IsTrue(input.IsAtEnd);
406         }
407 
408         [Test]
WriteTo_PackedInt32()409         public void WriteTo_PackedInt32()
410         {
411             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
412             var field = new RepeatedField<int> { 10, 1000, 1000000 };
413             var stream = new MemoryStream();
414             var output = new CodedOutputStream(stream);
415             field.WriteTo(output, FieldCodec.ForInt32(tag));
416             output.Flush();
417             stream.Position = 0;
418 
419             var input = new CodedInputStream(stream);
420             input.AssertNextTag(tag);
421             var length = input.ReadLength();
422             Assert.AreEqual(10, input.ReadInt32());
423             Assert.AreEqual(1000, input.ReadInt32());
424             Assert.AreEqual(1000000, input.ReadInt32());
425             Assert.IsTrue(input.IsAtEnd);
426             Assert.AreEqual(1 + CodedOutputStream.ComputeLengthSize(length) + length, stream.Length);
427         }
428 
429         [Test]
WriteTo_NonPackedInt32()430         public void WriteTo_NonPackedInt32()
431         {
432             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
433             var field = new RepeatedField<int> { 10, 1000, 1000000};
434             var stream = new MemoryStream();
435             var output = new CodedOutputStream(stream);
436             field.WriteTo(output, FieldCodec.ForInt32(tag));
437             output.Flush();
438             stream.Position = 0;
439 
440             var input = new CodedInputStream(stream);
441             input.AssertNextTag(tag);
442             Assert.AreEqual(10, input.ReadInt32());
443             input.AssertNextTag(tag);
444             Assert.AreEqual(1000, input.ReadInt32());
445             input.AssertNextTag(tag);
446             Assert.AreEqual(1000000, input.ReadInt32());
447             Assert.IsTrue(input.IsAtEnd);
448         }
449 
450         [Test]
WriteTo_String()451         public void WriteTo_String()
452         {
453             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
454             var field = new RepeatedField<string> { "Foo", "", "Bar" };
455             var stream = new MemoryStream();
456             var output = new CodedOutputStream(stream);
457             field.WriteTo(output, FieldCodec.ForString(tag));
458             output.Flush();
459             stream.Position = 0;
460 
461             var input = new CodedInputStream(stream);
462             input.AssertNextTag(tag);
463             Assert.AreEqual("Foo", input.ReadString());
464             input.AssertNextTag(tag);
465             Assert.AreEqual("", input.ReadString());
466             input.AssertNextTag(tag);
467             Assert.AreEqual("Bar", input.ReadString());
468             Assert.IsTrue(input.IsAtEnd);
469         }
470 
471         [Test]
WriteTo_Message()472         public void WriteTo_Message()
473         {
474             var message1 = new ForeignMessage { C = 20 };
475             var message2 = new ForeignMessage { C = 25 };
476             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
477             var field = new RepeatedField<ForeignMessage> { message1, message2 };
478             var stream = new MemoryStream();
479             var output = new CodedOutputStream(stream);
480             field.WriteTo(output, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
481             output.Flush();
482             stream.Position = 0;
483 
484             var input = new CodedInputStream(stream);
485             input.AssertNextTag(tag);
486             Assert.AreEqual(message1, input.ReadMessage(ForeignMessage.Parser));
487             input.AssertNextTag(tag);
488             Assert.AreEqual(message2, input.ReadMessage(ForeignMessage.Parser));
489             Assert.IsTrue(input.IsAtEnd);
490         }
491 
492         [Test]
CalculateSize_VariableSizeNonPacked()493         public void CalculateSize_VariableSizeNonPacked()
494         {
495             var list = new RepeatedField<int> { 1, 500, 1 };
496             var tag = WireFormat.MakeTag(1, WireFormat.WireType.Varint);
497             // 2 bytes for the first entry, 3 bytes for the second, 2 bytes for the third
498             Assert.AreEqual(7, list.CalculateSize(FieldCodec.ForInt32(tag)));
499         }
500 
501         [Test]
CalculateSize_FixedSizeNonPacked()502         public void CalculateSize_FixedSizeNonPacked()
503         {
504             var list = new RepeatedField<int> { 1, 500, 1 };
505             var tag = WireFormat.MakeTag(1, WireFormat.WireType.Fixed32);
506             // 5 bytes for the each entry
507             Assert.AreEqual(15, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
508         }
509 
510         [Test]
CalculateSize_VariableSizePacked()511         public void CalculateSize_VariableSizePacked()
512         {
513             var list = new RepeatedField<int> { 1, 500, 1};
514             var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
515             // 1 byte for the tag, 1 byte for the length,
516             // 1 byte for the first entry, 2 bytes for the second, 1 byte for the third
517             Assert.AreEqual(6, list.CalculateSize(FieldCodec.ForInt32(tag)));
518         }
519 
520         [Test]
CalculateSize_FixedSizePacked()521         public void CalculateSize_FixedSizePacked()
522         {
523             var list = new RepeatedField<int> { 1, 500, 1 };
524             var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
525             // 1 byte for the tag, 1 byte for the length, 4 bytes per entry
526             Assert.AreEqual(14, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
527         }
528 
529         [Test]
TestNegativeEnumArray()530         public void TestNegativeEnumArray()
531         {
532             int arraySize = 1 + 1 + (11 * 5);
533             int msgSize = arraySize;
534             byte[] bytes = new byte[msgSize];
535             CodedOutputStream output = new CodedOutputStream(bytes);
536             uint tag = WireFormat.MakeTag(8, WireFormat.WireType.Varint);
537             for (int i = 0; i >= -5; i--)
538             {
539                 output.WriteTag(tag);
540                 output.WriteEnum(i);
541             }
542 
543             Assert.AreEqual(0, output.SpaceLeft);
544 
545             CodedInputStream input = new CodedInputStream(bytes);
546             tag = input.ReadTag();
547 
548             RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
549             values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
550 
551             Assert.AreEqual(6, values.Count);
552             Assert.AreEqual(SampleEnum.None, values[0]);
553             Assert.AreEqual(((SampleEnum)(-1)), values[1]);
554             Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
555             Assert.AreEqual(((SampleEnum)(-3)), values[3]);
556             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
557             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
558         }
559 
560 
561         [Test]
TestNegativeEnumPackedArray()562         public void TestNegativeEnumPackedArray()
563         {
564             int arraySize = 1 + (10 * 5);
565             int msgSize = 1 + 1 + arraySize;
566             byte[] bytes = new byte[msgSize];
567             CodedOutputStream output = new CodedOutputStream(bytes);
568             // Length-delimited to show we want the packed representation
569             uint tag = WireFormat.MakeTag(8, WireFormat.WireType.LengthDelimited);
570             output.WriteTag(tag);
571             int size = 0;
572             for (int i = 0; i >= -5; i--)
573             {
574                 size += CodedOutputStream.ComputeEnumSize(i);
575             }
576             output.WriteRawVarint32((uint)size);
577             for (int i = 0; i >= -5; i--)
578             {
579                 output.WriteEnum(i);
580             }
581             Assert.AreEqual(0, output.SpaceLeft);
582 
583             CodedInputStream input = new CodedInputStream(bytes);
584             tag = input.ReadTag();
585 
586             RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
587             values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
588 
589             Assert.AreEqual(6, values.Count);
590             Assert.AreEqual(SampleEnum.None, values[0]);
591             Assert.AreEqual(((SampleEnum)(-1)), values[1]);
592             Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
593             Assert.AreEqual(((SampleEnum)(-3)), values[3]);
594             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
595             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
596         }
597 
598         [Test]
TestPackedRepeatedFieldCollectionNonDivisibleLength()599         public void TestPackedRepeatedFieldCollectionNonDivisibleLength()
600         {
601             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
602             var codec = FieldCodec.ForFixed32(tag);
603             var stream = new MemoryStream();
604             var output = new CodedOutputStream(stream);
605             output.WriteTag(tag);
606             output.WriteString("A long string");
607             output.WriteTag(codec.Tag);
608             output.WriteRawVarint32((uint)codec.FixedSize - 1); // Length not divisible by FixedSize
609             output.WriteFixed32(uint.MaxValue);
610             output.Flush();
611             stream.Position = 0;
612 
613             var input = new CodedInputStream(stream);
614             input.ReadTag();
615             input.ReadString();
616             input.ReadTag();
617             var field = new RepeatedField<uint>();
618             Assert.Throws<InvalidProtocolBufferException>(() => field.AddEntriesFrom(input, codec));
619 
620             // Collection was not pre-initialized
621             Assert.AreEqual(0, field.Count);
622         }
623 
624         [Test]
TestPackedRepeatedFieldCollectionNotAllocatedWhenLengthExceedsBuffer()625         public void TestPackedRepeatedFieldCollectionNotAllocatedWhenLengthExceedsBuffer()
626         {
627             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
628             var codec = FieldCodec.ForFixed32(tag);
629             var stream = new MemoryStream();
630             var output = new CodedOutputStream(stream);
631             output.WriteTag(tag);
632             output.WriteString("A long string");
633             output.WriteTag(codec.Tag);
634             output.WriteRawVarint32((uint)codec.FixedSize);
635             // Note that there is no content for the packed field.
636             // The field length exceeds the remaining length of content.
637             output.Flush();
638             stream.Position = 0;
639 
640             var input = new CodedInputStream(stream);
641             input.ReadTag();
642             input.ReadString();
643             input.ReadTag();
644             var field = new RepeatedField<uint>();
645             Assert.Throws<InvalidProtocolBufferException>(() => field.AddEntriesFrom(input, codec));
646 
647             // Collection was not pre-initialized
648             Assert.AreEqual(0, field.Count);
649         }
650 
651         [Test]
TestPackedRepeatedFieldCollectionNotAllocatedWhenLengthExceedsRemainingData()652         public void TestPackedRepeatedFieldCollectionNotAllocatedWhenLengthExceedsRemainingData()
653         {
654             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
655             var codec = FieldCodec.ForFixed32(tag);
656             var stream = new MemoryStream();
657             var output = new CodedOutputStream(stream);
658             output.WriteTag(tag);
659             output.WriteString("A long string");
660             output.WriteTag(codec.Tag);
661             output.WriteRawVarint32((uint)codec.FixedSize);
662             // Note that there is no content for the packed field.
663             // The field length exceeds the remaining length of the buffer.
664             output.Flush();
665             stream.Position = 0;
666 
667             var sequence = ReadOnlySequenceFactory.CreateWithContent(stream.ToArray());
668             ParseContext.Initialize(sequence, out ParseContext ctx);
669 
670             ctx.ReadTag();
671             ctx.ReadString();
672             ctx.ReadTag();
673             var field = new RepeatedField<uint>();
674             try
675             {
676                 field.AddEntriesFrom(ref ctx, codec);
677                 Assert.Fail();
678             }
679             catch (InvalidProtocolBufferException)
680             {
681             }
682 
683             // Collection was not pre-initialized
684             Assert.AreEqual(0, field.Count);
685         }
686 
687         // Fairly perfunctory tests for the non-generic IList implementation
688         [Test]
IList_Indexer()689         public void IList_Indexer()
690         {
691             var field = new RepeatedField<string> { "first", "second" };
692             IList list = field;
693             Assert.AreEqual("first", list[0]);
694             list[1] = "changed";
695             Assert.AreEqual("changed", field[1]);
696         }
697 
698         [Test]
IList_Contains()699         public void IList_Contains()
700         {
701             IList list = new RepeatedField<string> { "first", "second" };
702             Assert.IsTrue(list.Contains("second"));
703             Assert.IsFalse(list.Contains("third"));
704             Assert.IsFalse(list.Contains(new object()));
705         }
706 
707         [Test]
IList_Add()708         public void IList_Add()
709         {
710             IList list = new RepeatedField<string> { "first", "second" };
711             list.Add("third");
712             CollectionAssert.AreEqual(new[] { "first", "second", "third" }, list);
713         }
714 
715         [Test]
IList_Remove()716         public void IList_Remove()
717         {
718             IList list = new RepeatedField<string> { "first", "second" };
719             list.Remove("third"); // No-op, no exception
720             list.Remove(new object()); // No-op, no exception
721             list.Remove("first");
722             CollectionAssert.AreEqual(new[] { "second" }, list);
723         }
724 
725         [Test]
IList_IsFixedSize()726         public void IList_IsFixedSize()
727         {
728             var field = new RepeatedField<string> { "first", "second" };
729             IList list = field;
730             Assert.IsFalse(list.IsFixedSize);
731         }
732 
733         [Test]
IList_IndexOf()734         public void IList_IndexOf()
735         {
736             IList list = new RepeatedField<string> { "first", "second" };
737             Assert.AreEqual(1, list.IndexOf("second"));
738             Assert.AreEqual(-1, list.IndexOf("third"));
739             Assert.AreEqual(-1, list.IndexOf(new object()));
740         }
741 
742         [Test]
IList_SyncRoot()743         public void IList_SyncRoot()
744         {
745             IList list = new RepeatedField<string> { "first", "second" };
746             Assert.AreSame(list, list.SyncRoot);
747         }
748 
749         [Test]
IList_CopyTo()750         public void IList_CopyTo()
751         {
752             IList list = new RepeatedField<string> { "first", "second" };
753             string[] stringArray = new string[4];
754             list.CopyTo(stringArray, 1);
755             CollectionAssert.AreEqual(new[] { null, "first",  "second", null }, stringArray);
756 
757             object[] objectArray = new object[4];
758             list.CopyTo(objectArray, 1);
759             CollectionAssert.AreEqual(new[] { null, "first", "second", null }, objectArray);
760 
761             Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new StringBuilder[4], 1));
762             Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new int[4], 1));
763         }
764 
765         [Test]
IList_IsSynchronized()766         public void IList_IsSynchronized()
767         {
768             IList list = new RepeatedField<string> { "first", "second" };
769             Assert.IsFalse(list.IsSynchronized);
770         }
771 
772         [Test]
IList_Insert()773         public void IList_Insert()
774         {
775             IList list = new RepeatedField<string> { "first", "second" };
776             list.Insert(1, "middle");
777             CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
778         }
779 
780         [Test]
ToString_Integers()781         public void ToString_Integers()
782         {
783             var list = new RepeatedField<int> { 5, 10, 20 };
784             var text = list.ToString();
785             Assert.AreEqual("[ 5, 10, 20 ]", text);
786         }
787 
788         [Test]
ToString_Strings()789         public void ToString_Strings()
790         {
791             var list = new RepeatedField<string> { "x", "y", "z" };
792             var text = list.ToString();
793             Assert.AreEqual("[ \"x\", \"y\", \"z\" ]", text);
794         }
795 
796         [Test]
ToString_Messages()797         public void ToString_Messages()
798         {
799             var list = new RepeatedField<TestAllTypes> { new TestAllTypes { SingleDouble = 1.5 }, new TestAllTypes { SingleInt32 = 10 } };
800             var text = list.ToString();
801             Assert.AreEqual("[ { \"singleDouble\": 1.5 }, { \"singleInt32\": 10 } ]", text);
802         }
803 
804         [Test]
ToString_Empty()805         public void ToString_Empty()
806         {
807             var list = new RepeatedField<TestAllTypes> { };
808             var text = list.ToString();
809             Assert.AreEqual("[ ]", text);
810         }
811 
812         [Test]
ToString_InvalidElementType()813         public void ToString_InvalidElementType()
814         {
815             var list = new RepeatedField<decimal> { 15m };
816             Assert.Throws<ArgumentException>(() => list.ToString());
817         }
818 
819         [Test]
ToString_Timestamp()820         public void ToString_Timestamp()
821         {
822             var list = new RepeatedField<Timestamp> { Timestamp.FromDateTime(new DateTime(2015, 10, 1, 12, 34, 56, DateTimeKind.Utc)) };
823             var text = list.ToString();
824             Assert.AreEqual("[ \"2015-10-01T12:34:56Z\" ]", text);
825         }
826 
827         [Test]
ToString_Struct()828         public void ToString_Struct()
829         {
830             var message = new Struct { Fields = { { "foo", new Value { NumberValue = 20 } } } };
831             var list = new RepeatedField<Struct> { message };
832             var text = list.ToString();
833             Assert.AreEqual(text, "[ { \"foo\": 20 } ]", message.ToString());
834         }
835 
836         [Test]
NaNValuesComparedBitwise()837         public void NaNValuesComparedBitwise()
838         {
839             var list1 = new RepeatedField<double> { SampleNaNs.Regular, SampleNaNs.SignallingFlipped };
840             var list2 = new RepeatedField<double> { SampleNaNs.Regular, SampleNaNs.PayloadFlipped };
841             var list3 = new RepeatedField<double> { SampleNaNs.Regular, SampleNaNs.SignallingFlipped };
842 
843             // All SampleNaNs have the same hashcode under certain targets (e.g. netcoreapp2.1)
844             EqualityTester.AssertInequality(list1, list2, checkHashcode: false);
845             EqualityTester.AssertEquality(list1, list3);
846             Assert.True(list1.Contains(SampleNaNs.SignallingFlipped));
847             Assert.False(list2.Contains(SampleNaNs.SignallingFlipped));
848         }
849 
850         [Test]
Capacity_Increase()851         public void Capacity_Increase()
852         {
853             // Unfortunately this case tests implementation details of RepeatedField.  This is necessary
854 
855             var list = new RepeatedField<int>() { 1, 2, 3 };
856 
857             Assert.AreEqual(8, list.Capacity);
858             Assert.AreEqual(3, list.Count);
859 
860             list.Capacity = 10; // Set capacity to a larger value to trigger growth
861             Assert.AreEqual(10, list.Capacity, "Capacity increased");
862             Assert.AreEqual(3, list.Count);
863 
864             CollectionAssert.AreEqual(new int[] {1, 2, 3}, list.ToArray(), "We didn't lose our data in the resize");
865         }
866 
867         [Test]
Capacity_Decrease()868         public void Capacity_Decrease()
869         {
870             var list = new RepeatedField<int>() { 1, 2, 3 };
871 
872             Assert.AreEqual(8, list.Capacity);
873             Assert.DoesNotThrow(() => list.Capacity = 5, "Can decrease capacity if new capacity is greater than list.Count");
874             Assert.AreEqual(5, list.Capacity);
875 
876             Assert.DoesNotThrow(() => list.Capacity = 3, "Can set capacity exactly to list.Count" );
877 
878             Assert.Throws<ArgumentOutOfRangeException>(() => list.Capacity = 2, "Can't set the capacity smaller than list.Count" );
879 
880             Assert.Throws<ArgumentOutOfRangeException>(() => list.Capacity = 0, "Can't set the capacity to zero" );
881 
882             Assert.Throws<ArgumentOutOfRangeException>(() => list.Capacity = -1, "Can't set the capacity to negative" );
883         }
884 
885         [Test]
Capacity_Zero()886         public void Capacity_Zero()
887         {
888             var list = new RepeatedField<int>() { 1 };
889             list.RemoveAt(0);
890             Assert.AreEqual(0, list.Count);
891             Assert.AreEqual(8, list.Capacity);
892 
893             Assert.DoesNotThrow(() => list.Capacity = 0, "Can set Capacity to 0");
894             Assert.AreEqual(0, list.Capacity);
895         }
896     }
897 }
898