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