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