• 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.IO;
37 using System.Linq;
38 using System.Text;
39 using Google.Protobuf.TestProtos;
40 using Google.Protobuf.WellKnownTypes;
41 using NUnit.Framework;
42 
43 namespace Google.Protobuf.Collections
44 {
45     public class RepeatedFieldTest
46     {
47         [Test]
NullValuesRejected()48         public void NullValuesRejected()
49         {
50             var list = new RepeatedField<string>();
51             Assert.Throws<ArgumentNullException>(() => list.Add((string)null));
52             Assert.Throws<ArgumentNullException>(() => list.Add((IEnumerable<string>)null));
53             Assert.Throws<ArgumentNullException>(() => list.Add((RepeatedField<string>)null));
54             Assert.Throws<ArgumentNullException>(() => list.Contains(null));
55             Assert.Throws<ArgumentNullException>(() => list.IndexOf(null));
56         }
57 
58         [Test]
Add_SingleItem()59         public void Add_SingleItem()
60         {
61             var list = new RepeatedField<string>();
62             list.Add("foo");
63             Assert.AreEqual(1, list.Count);
64             Assert.AreEqual("foo", list[0]);
65         }
66 
67         [Test]
Add_Sequence()68         public void Add_Sequence()
69         {
70             var list = new RepeatedField<string>();
71             list.Add(new[] { "foo", "bar" });
72             Assert.AreEqual(2, list.Count);
73             Assert.AreEqual("foo", list[0]);
74             Assert.AreEqual("bar", list[1]);
75         }
76 
77         [Test]
Add_RepeatedField()78         public void Add_RepeatedField()
79         {
80             var list = new RepeatedField<string> { "original" };
81             list.Add(new RepeatedField<string> { "foo", "bar" });
82             Assert.AreEqual(3, list.Count);
83             Assert.AreEqual("original", list[0]);
84             Assert.AreEqual("foo", list[1]);
85             Assert.AreEqual("bar", list[2]);
86         }
87 
88         [Test]
RemoveAt_Valid()89         public void RemoveAt_Valid()
90         {
91             var list = new RepeatedField<string> { "first", "second", "third" };
92             list.RemoveAt(1);
93             CollectionAssert.AreEqual(new[] { "first", "third" }, list);
94             // Just check that these don't throw...
95             list.RemoveAt(list.Count - 1); // Now the count will be 1...
96             list.RemoveAt(0);
97             Assert.AreEqual(0, list.Count);
98         }
99 
100         [Test]
RemoveAt_Invalid()101         public void RemoveAt_Invalid()
102         {
103             var list = new RepeatedField<string> { "first", "second", "third" };
104             Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(-1));
105             Assert.Throws<ArgumentOutOfRangeException>(() => list.RemoveAt(3));
106         }
107 
108         [Test]
Insert_Valid()109         public void Insert_Valid()
110         {
111             var list = new RepeatedField<string> { "first", "second" };
112             list.Insert(1, "middle");
113             CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
114             list.Insert(3, "end");
115             CollectionAssert.AreEqual(new[] { "first", "middle", "second", "end" }, list);
116             list.Insert(0, "start");
117             CollectionAssert.AreEqual(new[] { "start", "first", "middle", "second", "end" }, list);
118         }
119 
120         [Test]
Insert_Invalid()121         public void Insert_Invalid()
122         {
123             var list = new RepeatedField<string> { "first", "second" };
124             Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(-1, "foo"));
125             Assert.Throws<ArgumentOutOfRangeException>(() => list.Insert(3, "foo"));
126             Assert.Throws<ArgumentNullException>(() => list.Insert(0, null));
127         }
128 
129         [Test]
Equals_RepeatedField()130         public void Equals_RepeatedField()
131         {
132             var list = new RepeatedField<string> { "first", "second" };
133             Assert.IsFalse(list.Equals((RepeatedField<string>) null));
134             Assert.IsTrue(list.Equals(list));
135             Assert.IsFalse(list.Equals(new RepeatedField<string> { "first", "third" }));
136             Assert.IsFalse(list.Equals(new RepeatedField<string> { "first" }));
137             Assert.IsTrue(list.Equals(new RepeatedField<string> { "first", "second" }));
138         }
139 
140         [Test]
Equals_Object()141         public void Equals_Object()
142         {
143             var list = new RepeatedField<string> { "first", "second" };
144             Assert.IsFalse(list.Equals((object) null));
145             Assert.IsTrue(list.Equals((object) list));
146             Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first", "third" }));
147             Assert.IsFalse(list.Equals((object) new RepeatedField<string> { "first" }));
148             Assert.IsTrue(list.Equals((object) new RepeatedField<string> { "first", "second" }));
149             Assert.IsFalse(list.Equals(new object()));
150         }
151 
152         [Test]
GetEnumerator_GenericInterface()153         public void GetEnumerator_GenericInterface()
154         {
155             IEnumerable<string> list = new RepeatedField<string> { "first", "second" };
156             // Select gets rid of the optimizations in ToList...
157             CollectionAssert.AreEqual(new[] { "first", "second" }, list.Select(x => x).ToList());
158         }
159 
160         [Test]
GetEnumerator_NonGenericInterface()161         public void GetEnumerator_NonGenericInterface()
162         {
163             IEnumerable list = new RepeatedField<string> { "first", "second" };
164             CollectionAssert.AreEqual(new[] { "first", "second" }, list.Cast<object>().ToList());
165         }
166 
167         [Test]
CopyTo()168         public void CopyTo()
169         {
170             var list = new RepeatedField<string> { "first", "second" };
171             string[] stringArray = new string[4];
172             list.CopyTo(stringArray, 1);
173             CollectionAssert.AreEqual(new[] { null, "first", "second", null }, stringArray);
174         }
175 
176         [Test]
Indexer_Get()177         public void Indexer_Get()
178         {
179             var list = new RepeatedField<string> { "first", "second" };
180             Assert.AreEqual("first", list[0]);
181             Assert.AreEqual("second", list[1]);
182             Assert.Throws<ArgumentOutOfRangeException>(() => list[-1].GetHashCode());
183             Assert.Throws<ArgumentOutOfRangeException>(() => list[2].GetHashCode());
184         }
185 
186         [Test]
Indexer_Set()187         public void Indexer_Set()
188         {
189             var list = new RepeatedField<string> { "first", "second" };
190             list[0] = "changed";
191             Assert.AreEqual("changed", list[0]);
192             Assert.Throws<ArgumentNullException>(() => list[0] = null);
193             Assert.Throws<ArgumentOutOfRangeException>(() => list[-1] = "bad");
194             Assert.Throws<ArgumentOutOfRangeException>(() => list[2] = "bad");
195         }
196 
197         [Test]
Clone_ReturnsMutable()198         public void Clone_ReturnsMutable()
199         {
200             var list = new RepeatedField<int> { 0 };
201             var clone = list.Clone();
202             clone[0] = 1;
203         }
204 
205         [Test]
Enumerator()206         public void Enumerator()
207         {
208             var list = new RepeatedField<string> { "first", "second" };
209             using (var enumerator = list.GetEnumerator())
210             {
211                 Assert.IsTrue(enumerator.MoveNext());
212                 Assert.AreEqual("first", enumerator.Current);
213                 Assert.IsTrue(enumerator.MoveNext());
214                 Assert.AreEqual("second", enumerator.Current);
215                 Assert.IsFalse(enumerator.MoveNext());
216                 Assert.IsFalse(enumerator.MoveNext());
217             }
218         }
219 
220         [Test]
AddEntriesFrom_PackedInt32()221         public void AddEntriesFrom_PackedInt32()
222         {
223             uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
224             var stream = new MemoryStream();
225             var output = new CodedOutputStream(stream);
226             var length = CodedOutputStream.ComputeInt32Size(10)
227                 + CodedOutputStream.ComputeInt32Size(999)
228                 + CodedOutputStream.ComputeInt32Size(-1000);
229             output.WriteTag(packedTag);
230             output.WriteRawVarint32((uint) length);
231             output.WriteInt32(10);
232             output.WriteInt32(999);
233             output.WriteInt32(-1000);
234             output.Flush();
235             stream.Position = 0;
236 
237             // Deliberately "expecting" a non-packed tag, but we detect that the data is
238             // actually packed.
239             uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
240             var field = new RepeatedField<int>();
241             var input = new CodedInputStream(stream);
242             input.AssertNextTag(packedTag);
243             field.AddEntriesFrom(input, FieldCodec.ForInt32(nonPackedTag));
244             CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
245             Assert.IsTrue(input.IsAtEnd);
246         }
247 
248         [Test]
AddEntriesFrom_NonPackedInt32()249         public void AddEntriesFrom_NonPackedInt32()
250         {
251             uint nonPackedTag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
252             var stream = new MemoryStream();
253             var output = new CodedOutputStream(stream);
254             output.WriteTag(nonPackedTag);
255             output.WriteInt32(10);
256             output.WriteTag(nonPackedTag);
257             output.WriteInt32(999);
258             output.WriteTag(nonPackedTag);
259             output.WriteInt32(-1000); // Just for variety...
260             output.Flush();
261             stream.Position = 0;
262 
263             // Deliberately "expecting" a packed tag, but we detect that the data is
264             // actually not packed.
265             uint packedTag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
266             var field = new RepeatedField<int>();
267             var input = new CodedInputStream(stream);
268             input.AssertNextTag(nonPackedTag);
269             field.AddEntriesFrom(input, FieldCodec.ForInt32(packedTag));
270             CollectionAssert.AreEqual(new[] { 10, 999, -1000 }, field);
271             Assert.IsTrue(input.IsAtEnd);
272         }
273 
274         [Test]
AddEntriesFrom_String()275         public void AddEntriesFrom_String()
276         {
277             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
278             var stream = new MemoryStream();
279             var output = new CodedOutputStream(stream);
280             output.WriteTag(tag);
281             output.WriteString("Foo");
282             output.WriteTag(tag);
283             output.WriteString("");
284             output.WriteTag(tag);
285             output.WriteString("Bar");
286             output.Flush();
287             stream.Position = 0;
288 
289             var field = new RepeatedField<string>();
290             var input = new CodedInputStream(stream);
291             input.AssertNextTag(tag);
292             field.AddEntriesFrom(input, FieldCodec.ForString(tag));
293             CollectionAssert.AreEqual(new[] { "Foo", "", "Bar" }, field);
294             Assert.IsTrue(input.IsAtEnd);
295         }
296 
297         [Test]
AddEntriesFrom_Message()298         public void AddEntriesFrom_Message()
299         {
300             var message1 = new ForeignMessage { C = 2000 };
301             var message2 = new ForeignMessage { C = -250 };
302 
303             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
304             var stream = new MemoryStream();
305             var output = new CodedOutputStream(stream);
306             output.WriteTag(tag);
307             output.WriteMessage(message1);
308             output.WriteTag(tag);
309             output.WriteMessage(message2);
310             output.Flush();
311             stream.Position = 0;
312 
313             var field = new RepeatedField<ForeignMessage>();
314             var input = new CodedInputStream(stream);
315             input.AssertNextTag(tag);
316             field.AddEntriesFrom(input, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
317             CollectionAssert.AreEqual(new[] { message1, message2}, field);
318             Assert.IsTrue(input.IsAtEnd);
319         }
320 
321         [Test]
WriteTo_PackedInt32()322         public void WriteTo_PackedInt32()
323         {
324             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
325             var field = new RepeatedField<int> { 10, 1000, 1000000 };
326             var stream = new MemoryStream();
327             var output = new CodedOutputStream(stream);
328             field.WriteTo(output, FieldCodec.ForInt32(tag));
329             output.Flush();
330             stream.Position = 0;
331 
332             var input = new CodedInputStream(stream);
333             input.AssertNextTag(tag);
334             var length = input.ReadLength();
335             Assert.AreEqual(10, input.ReadInt32());
336             Assert.AreEqual(1000, input.ReadInt32());
337             Assert.AreEqual(1000000, input.ReadInt32());
338             Assert.IsTrue(input.IsAtEnd);
339             Assert.AreEqual(1 + CodedOutputStream.ComputeLengthSize(length) + length, stream.Length);
340         }
341 
342         [Test]
WriteTo_NonPackedInt32()343         public void WriteTo_NonPackedInt32()
344         {
345             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.Varint);
346             var field = new RepeatedField<int> { 10, 1000, 1000000};
347             var stream = new MemoryStream();
348             var output = new CodedOutputStream(stream);
349             field.WriteTo(output, FieldCodec.ForInt32(tag));
350             output.Flush();
351             stream.Position = 0;
352 
353             var input = new CodedInputStream(stream);
354             input.AssertNextTag(tag);
355             Assert.AreEqual(10, input.ReadInt32());
356             input.AssertNextTag(tag);
357             Assert.AreEqual(1000, input.ReadInt32());
358             input.AssertNextTag(tag);
359             Assert.AreEqual(1000000, input.ReadInt32());
360             Assert.IsTrue(input.IsAtEnd);
361         }
362 
363         [Test]
WriteTo_String()364         public void WriteTo_String()
365         {
366             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
367             var field = new RepeatedField<string> { "Foo", "", "Bar" };
368             var stream = new MemoryStream();
369             var output = new CodedOutputStream(stream);
370             field.WriteTo(output, FieldCodec.ForString(tag));
371             output.Flush();
372             stream.Position = 0;
373 
374             var input = new CodedInputStream(stream);
375             input.AssertNextTag(tag);
376             Assert.AreEqual("Foo", input.ReadString());
377             input.AssertNextTag(tag);
378             Assert.AreEqual("", input.ReadString());
379             input.AssertNextTag(tag);
380             Assert.AreEqual("Bar", input.ReadString());
381             Assert.IsTrue(input.IsAtEnd);
382         }
383 
384         [Test]
WriteTo_Message()385         public void WriteTo_Message()
386         {
387             var message1 = new ForeignMessage { C = 20 };
388             var message2 = new ForeignMessage { C = 25 };
389             uint tag = WireFormat.MakeTag(10, WireFormat.WireType.LengthDelimited);
390             var field = new RepeatedField<ForeignMessage> { message1, message2 };
391             var stream = new MemoryStream();
392             var output = new CodedOutputStream(stream);
393             field.WriteTo(output, FieldCodec.ForMessage(tag, ForeignMessage.Parser));
394             output.Flush();
395             stream.Position = 0;
396 
397             var input = new CodedInputStream(stream);
398             input.AssertNextTag(tag);
399             Assert.AreEqual(message1, input.ReadMessage(ForeignMessage.Parser));
400             input.AssertNextTag(tag);
401             Assert.AreEqual(message2, input.ReadMessage(ForeignMessage.Parser));
402             Assert.IsTrue(input.IsAtEnd);
403         }
404 
405         [Test]
CalculateSize_VariableSizeNonPacked()406         public void CalculateSize_VariableSizeNonPacked()
407         {
408             var list = new RepeatedField<int> { 1, 500, 1 };
409             var tag = WireFormat.MakeTag(1, WireFormat.WireType.Varint);
410             // 2 bytes for the first entry, 3 bytes for the second, 2 bytes for the third
411             Assert.AreEqual(7, list.CalculateSize(FieldCodec.ForInt32(tag)));
412         }
413 
414         [Test]
CalculateSize_FixedSizeNonPacked()415         public void CalculateSize_FixedSizeNonPacked()
416         {
417             var list = new RepeatedField<int> { 1, 500, 1 };
418             var tag = WireFormat.MakeTag(1, WireFormat.WireType.Fixed32);
419             // 5 bytes for the each entry
420             Assert.AreEqual(15, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
421         }
422 
423         [Test]
CalculateSize_VariableSizePacked()424         public void CalculateSize_VariableSizePacked()
425         {
426             var list = new RepeatedField<int> { 1, 500, 1};
427             var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
428             // 1 byte for the tag, 1 byte for the length,
429             // 1 byte for the first entry, 2 bytes for the second, 1 byte for the third
430             Assert.AreEqual(6, list.CalculateSize(FieldCodec.ForInt32(tag)));
431         }
432 
433         [Test]
CalculateSize_FixedSizePacked()434         public void CalculateSize_FixedSizePacked()
435         {
436             var list = new RepeatedField<int> { 1, 500, 1 };
437             var tag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited);
438             // 1 byte for the tag, 1 byte for the length, 4 bytes per entry
439             Assert.AreEqual(14, list.CalculateSize(FieldCodec.ForSFixed32(tag)));
440         }
441 
442         [Test]
TestNegativeEnumArray()443         public void TestNegativeEnumArray()
444         {
445             int arraySize = 1 + 1 + (11 * 5);
446             int msgSize = arraySize;
447             byte[] bytes = new byte[msgSize];
448             CodedOutputStream output = new CodedOutputStream(bytes);
449             uint tag = WireFormat.MakeTag(8, WireFormat.WireType.Varint);
450             for (int i = 0; i >= -5; i--)
451             {
452                 output.WriteTag(tag);
453                 output.WriteEnum(i);
454             }
455 
456             Assert.AreEqual(0, output.SpaceLeft);
457 
458             CodedInputStream input = new CodedInputStream(bytes);
459             tag = input.ReadTag();
460 
461             RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
462             values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
463 
464             Assert.AreEqual(6, values.Count);
465             Assert.AreEqual(SampleEnum.None, values[0]);
466             Assert.AreEqual(((SampleEnum)(-1)), values[1]);
467             Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
468             Assert.AreEqual(((SampleEnum)(-3)), values[3]);
469             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
470             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
471         }
472 
473 
474         [Test]
TestNegativeEnumPackedArray()475         public void TestNegativeEnumPackedArray()
476         {
477             int arraySize = 1 + (10 * 5);
478             int msgSize = 1 + 1 + arraySize;
479             byte[] bytes = new byte[msgSize];
480             CodedOutputStream output = new CodedOutputStream(bytes);
481             // Length-delimited to show we want the packed representation
482             uint tag = WireFormat.MakeTag(8, WireFormat.WireType.LengthDelimited);
483             output.WriteTag(tag);
484             int size = 0;
485             for (int i = 0; i >= -5; i--)
486             {
487                 size += CodedOutputStream.ComputeEnumSize(i);
488             }
489             output.WriteRawVarint32((uint)size);
490             for (int i = 0; i >= -5; i--)
491             {
492                 output.WriteEnum(i);
493             }
494             Assert.AreEqual(0, output.SpaceLeft);
495 
496             CodedInputStream input = new CodedInputStream(bytes);
497             tag = input.ReadTag();
498 
499             RepeatedField<SampleEnum> values = new RepeatedField<SampleEnum>();
500             values.AddEntriesFrom(input, FieldCodec.ForEnum(tag, x => (int)x, x => (SampleEnum)x));
501 
502             Assert.AreEqual(6, values.Count);
503             Assert.AreEqual(SampleEnum.None, values[0]);
504             Assert.AreEqual(((SampleEnum)(-1)), values[1]);
505             Assert.AreEqual(SampleEnum.NegativeValue, values[2]);
506             Assert.AreEqual(((SampleEnum)(-3)), values[3]);
507             Assert.AreEqual(((SampleEnum)(-4)), values[4]);
508             Assert.AreEqual(((SampleEnum)(-5)), values[5]);
509         }
510 
511         // Fairly perfunctory tests for the non-generic IList implementation
512         [Test]
IList_Indexer()513         public void IList_Indexer()
514         {
515             var field = new RepeatedField<string> { "first", "second" };
516             IList list = field;
517             Assert.AreEqual("first", list[0]);
518             list[1] = "changed";
519             Assert.AreEqual("changed", field[1]);
520         }
521 
522         [Test]
IList_Contains()523         public void IList_Contains()
524         {
525             IList list = new RepeatedField<string> { "first", "second" };
526             Assert.IsTrue(list.Contains("second"));
527             Assert.IsFalse(list.Contains("third"));
528             Assert.IsFalse(list.Contains(new object()));
529         }
530 
531         [Test]
IList_Add()532         public void IList_Add()
533         {
534             IList list = new RepeatedField<string> { "first", "second" };
535             list.Add("third");
536             CollectionAssert.AreEqual(new[] { "first", "second", "third" }, list);
537         }
538 
539         [Test]
IList_Remove()540         public void IList_Remove()
541         {
542             IList list = new RepeatedField<string> { "first", "second" };
543             list.Remove("third"); // No-op, no exception
544             list.Remove(new object()); // No-op, no exception
545             list.Remove("first");
546             CollectionAssert.AreEqual(new[] { "second" }, list);
547         }
548 
549         [Test]
IList_IsFixedSize()550         public void IList_IsFixedSize()
551         {
552             var field = new RepeatedField<string> { "first", "second" };
553             IList list = field;
554             Assert.IsFalse(list.IsFixedSize);
555         }
556 
557         [Test]
IList_IndexOf()558         public void IList_IndexOf()
559         {
560             IList list = new RepeatedField<string> { "first", "second" };
561             Assert.AreEqual(1, list.IndexOf("second"));
562             Assert.AreEqual(-1, list.IndexOf("third"));
563             Assert.AreEqual(-1, list.IndexOf(new object()));
564         }
565 
566         [Test]
IList_SyncRoot()567         public void IList_SyncRoot()
568         {
569             IList list = new RepeatedField<string> { "first", "second" };
570             Assert.AreSame(list, list.SyncRoot);
571         }
572 
573         [Test]
IList_CopyTo()574         public void IList_CopyTo()
575         {
576             IList list = new RepeatedField<string> { "first", "second" };
577             string[] stringArray = new string[4];
578             list.CopyTo(stringArray, 1);
579             CollectionAssert.AreEqual(new[] { null, "first",  "second", null }, stringArray);
580 
581             object[] objectArray = new object[4];
582             list.CopyTo(objectArray, 1);
583             CollectionAssert.AreEqual(new[] { null, "first", "second", null }, objectArray);
584 
585             Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new StringBuilder[4], 1));
586             Assert.Throws<ArrayTypeMismatchException>(() => list.CopyTo(new int[4], 1));
587         }
588 
589         [Test]
IList_IsSynchronized()590         public void IList_IsSynchronized()
591         {
592             IList list = new RepeatedField<string> { "first", "second" };
593             Assert.IsFalse(list.IsSynchronized);
594         }
595 
596         [Test]
IList_Insert()597         public void IList_Insert()
598         {
599             IList list = new RepeatedField<string> { "first", "second" };
600             list.Insert(1, "middle");
601             CollectionAssert.AreEqual(new[] { "first", "middle", "second" }, list);
602         }
603 
604         [Test]
ToString_Integers()605         public void ToString_Integers()
606         {
607             var list = new RepeatedField<int> { 5, 10, 20 };
608             var text = list.ToString();
609             Assert.AreEqual("[ 5, 10, 20 ]", text);
610         }
611 
612         [Test]
ToString_Strings()613         public void ToString_Strings()
614         {
615             var list = new RepeatedField<string> { "x", "y", "z" };
616             var text = list.ToString();
617             Assert.AreEqual("[ \"x\", \"y\", \"z\" ]", text);
618         }
619 
620         [Test]
ToString_Messages()621         public void ToString_Messages()
622         {
623             var list = new RepeatedField<TestAllTypes> { new TestAllTypes { SingleDouble = 1.5 }, new TestAllTypes { SingleInt32 = 10 } };
624             var text = list.ToString();
625             Assert.AreEqual("[ { \"singleDouble\": 1.5 }, { \"singleInt32\": 10 } ]", text);
626         }
627 
628         [Test]
ToString_Empty()629         public void ToString_Empty()
630         {
631             var list = new RepeatedField<TestAllTypes> { };
632             var text = list.ToString();
633             Assert.AreEqual("[ ]", text);
634         }
635 
636         [Test]
ToString_InvalidElementType()637         public void ToString_InvalidElementType()
638         {
639             var list = new RepeatedField<decimal> { 15m };
640             Assert.Throws<ArgumentException>(() => list.ToString());
641         }
642 
643         [Test]
ToString_Timestamp()644         public void ToString_Timestamp()
645         {
646             var list = new RepeatedField<Timestamp> { Timestamp.FromDateTime(new DateTime(2015, 10, 1, 12, 34, 56, DateTimeKind.Utc)) };
647             var text = list.ToString();
648             Assert.AreEqual("[ \"2015-10-01T12:34:56Z\" ]", text);
649         }
650 
651         [Test]
ToString_Struct()652         public void ToString_Struct()
653         {
654             var message = new Struct { Fields = { { "foo", new Value { NumberValue = 20 } } } };
655             var list = new RepeatedField<Struct> { message };
656             var text = list.ToString();
657             Assert.AreEqual(text, "[ { \"foo\": 20 } ]", message.ToString());
658         }
659     }
660 }
661