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