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