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