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