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.IO; 12 using System.Collections.Generic; 13 using Google.Protobuf.TestProtos; 14 using NUnit.Framework; 15 using System.Collections; 16 using System.Linq; 17 18 namespace Google.Protobuf.Collections 19 { 20 /// <summary> 21 /// Tests for MapField which aren't reliant on the encoded format - 22 /// tests for serialization/deserialization are part of GeneratedMessageTest. 23 /// </summary> 24 public class MapFieldTest 25 { 26 [Test] Clone_ClonesMessages()27 public void Clone_ClonesMessages() 28 { 29 var message = new ForeignMessage { C = 20 }; 30 var map = new MapField<string, ForeignMessage> { { "x", message } }; 31 var clone = map.Clone(); 32 map["x"].C = 30; 33 Assert.AreEqual(20, clone["x"].C); 34 } 35 36 [Test] NullValuesProhibited()37 public void NullValuesProhibited() 38 { 39 TestNullValues<int?>(0); 40 TestNullValues(""); 41 TestNullValues(new TestAllTypes()); 42 } 43 TestNullValues(T nonNullValue)44 private void TestNullValues<T>(T nonNullValue) 45 { 46 var map = new MapField<int, T>(); 47 var nullValue = (T) (object) null; 48 Assert.Throws<ArgumentNullException>(() => map.Add(0, nullValue)); 49 Assert.Throws<ArgumentNullException>(() => map[0] = nullValue); 50 map.Add(1, nonNullValue); 51 map[1] = nonNullValue; 52 } 53 54 [Test] Add_ForbidsNullKeys()55 public void Add_ForbidsNullKeys() 56 { 57 var map = new MapField<string, ForeignMessage>(); 58 Assert.Throws<ArgumentNullException>(() => map.Add(null, new ForeignMessage())); 59 } 60 61 [Test] Indexer_ForbidsNullKeys()62 public void Indexer_ForbidsNullKeys() 63 { 64 var map = new MapField<string, ForeignMessage>(); 65 Assert.Throws<ArgumentNullException>(() => map[null] = new ForeignMessage()); 66 } 67 68 [Test] AddPreservesInsertionOrder()69 public void AddPreservesInsertionOrder() 70 { 71 var map = new MapField<string, string> 72 { 73 { "a", "v1" }, 74 { "b", "v2" }, 75 { "c", "v3" } 76 }; 77 map.Remove("b"); 78 map.Add("d", "v4"); 79 CollectionAssert.AreEqual(new[] { "a", "c", "d" }, map.Keys); 80 CollectionAssert.AreEqual(new[] { "v1", "v3", "v4" }, map.Values); 81 } 82 83 [Test] EqualityIsOrderInsensitive()84 public void EqualityIsOrderInsensitive() 85 { 86 var map1 = new MapField<string, string> 87 { 88 { "a", "v1" }, 89 { "b", "v2" } 90 }; 91 92 var map2 = new MapField<string, string> 93 { 94 { "b", "v2" }, 95 { "a", "v1" } 96 }; 97 98 EqualityTester.AssertEquality(map1, map2); 99 } 100 101 [Test] EqualityIsKeySensitive()102 public void EqualityIsKeySensitive() 103 { 104 var map1 = new MapField<string, string> 105 { 106 { "first key", "v1" }, 107 { "second key", "v2" } 108 }; 109 110 var map2 = new MapField<string, string> 111 { 112 { "third key", "v1" }, 113 { "fourth key", "v2" } 114 }; 115 116 EqualityTester.AssertInequality(map1, map2); 117 } 118 119 [Test] Equality_Simple()120 public void Equality_Simple() 121 { 122 var map = new MapField<string, string>(); 123 EqualityTester.AssertEquality(map, map); 124 EqualityTester.AssertInequality(map, null); 125 Assert.IsFalse(map.Equals(new object())); 126 } 127 128 [Test] EqualityIsValueSensitive()129 public void EqualityIsValueSensitive() 130 { 131 // Note: Without some care, it's a little easier than one might 132 // hope to see hash collisions, but only in some environments... 133 var map1 = new MapField<string, string> 134 { 135 { "a", "first value" }, 136 { "b", "second value" } 137 }; 138 139 var map2 = new MapField<string, string> 140 { 141 { "a", "third value" }, 142 { "b", "fourth value" } 143 }; 144 145 EqualityTester.AssertInequality(map1, map2); 146 } 147 148 [Test] Add_Dictionary()149 public void Add_Dictionary() 150 { 151 var map1 = new MapField<string, string> 152 { 153 { "x", "y" }, 154 { "a", "b" } 155 }; 156 var map2 = new MapField<string, string> 157 { 158 { "before", "" }, 159 map1, 160 { "after", "" } 161 }; 162 var expected = new MapField<string, string> 163 { 164 { "before", "" }, 165 { "x", "y" }, 166 { "a", "b" }, 167 { "after", "" } 168 }; 169 Assert.AreEqual(expected, map2); 170 CollectionAssert.AreEqual(new[] { "before", "x", "a", "after" }, map2.Keys); 171 } 172 173 // General IDictionary<TKey, TValue> behavior tests 174 [Test] Add_KeyAlreadyExists()175 public void Add_KeyAlreadyExists() 176 { 177 var map = new MapField<string, string> { { "foo", "bar" } }; 178 Assert.Throws<ArgumentException>(() => map.Add("foo", "baz")); 179 } 180 181 [Test] Add_Pair()182 public void Add_Pair() 183 { 184 var map = new MapField<string, string>(); 185 ICollection<KeyValuePair<string, string>> collection = map; 186 collection.Add(NewKeyValuePair("x", "y")); 187 Assert.AreEqual("y", map["x"]); 188 Assert.Throws<ArgumentException>(() => collection.Add(NewKeyValuePair("x", "z"))); 189 } 190 191 [Test] Contains_Pair()192 public void Contains_Pair() 193 { 194 var map = new MapField<string, string> { { "x", "y" } }; 195 ICollection<KeyValuePair<string, string>> collection = map; 196 Assert.IsTrue(collection.Contains(NewKeyValuePair("x", "y"))); 197 Assert.IsFalse(collection.Contains(NewKeyValuePair("x", "z"))); 198 Assert.IsFalse(collection.Contains(NewKeyValuePair("z", "y"))); 199 } 200 201 [Test] Remove_Key()202 public void Remove_Key() 203 { 204 var map = new MapField<string, string> { { "foo", "bar" } }; 205 Assert.AreEqual(1, map.Count); 206 Assert.IsFalse(map.Remove("missing")); 207 Assert.AreEqual(1, map.Count); 208 Assert.IsTrue(map.Remove("foo")); 209 Assert.AreEqual(0, map.Count); 210 Assert.Throws<ArgumentNullException>(() => map.Remove(null)); 211 } 212 213 [Test] Remove_Pair()214 public void Remove_Pair() 215 { 216 var map = new MapField<string, string> { { "foo", "bar" } }; 217 ICollection<KeyValuePair<string, string>> collection = map; 218 Assert.AreEqual(1, map.Count); 219 Assert.IsFalse(collection.Remove(NewKeyValuePair("wrong key", "bar"))); 220 Assert.AreEqual(1, map.Count); 221 Assert.IsFalse(collection.Remove(NewKeyValuePair("foo", "wrong value"))); 222 Assert.AreEqual(1, map.Count); 223 Assert.IsTrue(collection.Remove(NewKeyValuePair("foo", "bar"))); 224 Assert.AreEqual(0, map.Count); 225 Assert.Throws<ArgumentException>(() => collection.Remove(new KeyValuePair<string, string>(null, ""))); 226 } 227 228 [Test] CopyTo_Pair()229 public void CopyTo_Pair() 230 { 231 var map = new MapField<string, string> { { "foo", "bar" } }; 232 ICollection<KeyValuePair<string, string>> collection = map; 233 KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[3]; 234 collection.CopyTo(array, 1); 235 Assert.AreEqual(NewKeyValuePair("foo", "bar"), array[1]); 236 } 237 238 [Test] Clear()239 public void Clear() 240 { 241 var map = new MapField<string, string> { { "x", "y" } }; 242 Assert.AreEqual(1, map.Count); 243 map.Clear(); 244 Assert.AreEqual(0, map.Count); 245 map.Add("x", "y"); 246 Assert.AreEqual(1, map.Count); 247 } 248 249 [Test] Indexer_Get()250 public void Indexer_Get() 251 { 252 var map = new MapField<string, string> { { "x", "y" } }; 253 Assert.AreEqual("y", map["x"]); 254 Assert.Throws<KeyNotFoundException>(() => { var ignored = map["z"]; }); 255 } 256 257 [Test] Indexer_Set()258 public void Indexer_Set() 259 { 260 var map = new MapField<string, string> { ["x"] = "y" }; 261 Assert.AreEqual("y", map["x"]); 262 map["x"] = "z"; // This won't throw, unlike Add. 263 Assert.AreEqual("z", map["x"]); 264 } 265 266 [Test] GetEnumerator_NonGeneric()267 public void GetEnumerator_NonGeneric() 268 { 269 IEnumerable map = new MapField<string, string> { { "x", "y" } }; 270 CollectionAssert.AreEqual(new[] { new KeyValuePair<string, string>("x", "y") }, 271 map.Cast<object>().ToList()); 272 } 273 274 // Test for the explicitly-implemented non-generic IDictionary interface 275 [Test] IDictionary_GetEnumerator()276 public void IDictionary_GetEnumerator() 277 { 278 IDictionary map = new MapField<string, string> { { "x", "y" } }; 279 var enumerator = map.GetEnumerator(); 280 281 // Commented assertions show an ideal situation - it looks like 282 // the LinkedList enumerator doesn't throw when you ask for the current entry 283 // at an inappropriate time; fixing this would be more work than it's worth. 284 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 285 Assert.IsTrue(enumerator.MoveNext()); 286 Assert.AreEqual("x", enumerator.Key); 287 Assert.AreEqual("y", enumerator.Value); 288 Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Current); 289 Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Entry); 290 Assert.IsFalse(enumerator.MoveNext()); 291 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 292 enumerator.Reset(); 293 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 294 Assert.IsTrue(enumerator.MoveNext()); 295 Assert.AreEqual("x", enumerator.Key); // Assume the rest are okay 296 } 297 298 [Test] IDictionary_Add()299 public void IDictionary_Add() 300 { 301 var map = new MapField<string, string> { { "x", "y" } }; 302 IDictionary dictionary = map; 303 dictionary.Add("a", "b"); 304 Assert.AreEqual("b", map["a"]); 305 Assert.Throws<ArgumentException>(() => dictionary.Add("a", "duplicate")); 306 Assert.Throws<InvalidCastException>(() => dictionary.Add(new object(), "key is bad")); 307 Assert.Throws<InvalidCastException>(() => dictionary.Add("value is bad", new object())); 308 } 309 310 [Test] IDictionary_Contains()311 public void IDictionary_Contains() 312 { 313 var map = new MapField<string, string> { { "x", "y" } }; 314 IDictionary dictionary = map; 315 316 Assert.IsFalse(dictionary.Contains("a")); 317 Assert.IsFalse(dictionary.Contains(5)); 318 // Surprising, but IDictionary.Contains is only about keys. 319 Assert.IsFalse(dictionary.Contains(new DictionaryEntry("x", "y"))); 320 Assert.IsTrue(dictionary.Contains("x")); 321 } 322 323 [Test] IDictionary_Remove()324 public void IDictionary_Remove() 325 { 326 var map = new MapField<string, string> { { "x", "y" } }; 327 IDictionary dictionary = map; 328 dictionary.Remove("a"); 329 Assert.AreEqual(1, dictionary.Count); 330 dictionary.Remove(5); 331 Assert.AreEqual(1, dictionary.Count); 332 dictionary.Remove(new DictionaryEntry("x", "y")); 333 Assert.AreEqual(1, dictionary.Count); 334 dictionary.Remove("x"); 335 Assert.AreEqual(0, dictionary.Count); 336 Assert.Throws<ArgumentNullException>(() => dictionary.Remove(null)); 337 } 338 339 [Test] IDictionary_CopyTo()340 public void IDictionary_CopyTo() 341 { 342 var map = new MapField<string, string> { { "x", "y" } }; 343 IDictionary dictionary = map; 344 var array = new DictionaryEntry[3]; 345 dictionary.CopyTo(array, 1); 346 CollectionAssert.AreEqual(new[] { default, new DictionaryEntry("x", "y"), default }, array); 347 var objectArray = new object[3]; 348 dictionary.CopyTo(objectArray, 1); 349 CollectionAssert.AreEqual(new object[] { null, new DictionaryEntry("x", "y"), null }, objectArray); 350 } 351 352 [Test] IDictionary_IsFixedSize()353 public void IDictionary_IsFixedSize() 354 { 355 var map = new MapField<string, string> { { "x", "y" } }; 356 IDictionary dictionary = map; 357 Assert.IsFalse(dictionary.IsFixedSize); 358 } 359 360 [Test] IDictionary_Keys()361 public void IDictionary_Keys() 362 { 363 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 364 CollectionAssert.AreEqual(new[] { "x" }, dictionary.Keys); 365 } 366 367 [Test] IDictionary_Values()368 public void IDictionary_Values() 369 { 370 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 371 CollectionAssert.AreEqual(new[] { "y" }, dictionary.Values); 372 } 373 374 [Test] IDictionary_IsSynchronized()375 public void IDictionary_IsSynchronized() 376 { 377 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 378 Assert.IsFalse(dictionary.IsSynchronized); 379 } 380 381 [Test] IDictionary_SyncRoot()382 public void IDictionary_SyncRoot() 383 { 384 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 385 Assert.AreSame(dictionary, dictionary.SyncRoot); 386 } 387 388 [Test] IDictionary_Indexer_Get()389 public void IDictionary_Indexer_Get() 390 { 391 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 392 Assert.AreEqual("y", dictionary["x"]); 393 Assert.IsNull(dictionary["a"]); 394 Assert.IsNull(dictionary[5]); 395 Assert.Throws<ArgumentNullException>(() => dictionary[null].GetHashCode()); 396 } 397 398 [Test] IDictionary_Indexer_Set()399 public void IDictionary_Indexer_Set() 400 { 401 var map = new MapField<string, string> { { "x", "y" } }; 402 IDictionary dictionary = map; 403 map["a"] = "b"; 404 Assert.AreEqual("b", map["a"]); 405 map["a"] = "c"; 406 Assert.AreEqual("c", map["a"]); 407 Assert.Throws<InvalidCastException>(() => dictionary[5] = "x"); 408 Assert.Throws<InvalidCastException>(() => dictionary["x"] = 5); 409 Assert.Throws<ArgumentNullException>(() => dictionary[null] = "z"); 410 Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null); 411 } 412 413 [Test] KeysReturnsLiveView()414 public void KeysReturnsLiveView() 415 { 416 var map = new MapField<string, string>(); 417 var keys = map.Keys; 418 CollectionAssert.AreEqual(new string[0], keys); 419 map["foo"] = "bar"; 420 map["x"] = "y"; 421 CollectionAssert.AreEqual(new[] { "foo", "x" }, keys); 422 } 423 424 [Test] ValuesReturnsLiveView()425 public void ValuesReturnsLiveView() 426 { 427 var map = new MapField<string, string>(); 428 var values = map.Values; 429 CollectionAssert.AreEqual(new string[0], values); 430 map["foo"] = "bar"; 431 map["x"] = "y"; 432 CollectionAssert.AreEqual(new[] { "bar", "y" }, values); 433 } 434 435 // Just test keys - we know the implementation is the same for values 436 [Test] ViewsAreReadOnly()437 public void ViewsAreReadOnly() 438 { 439 var map = new MapField<string, string>(); 440 var keys = map.Keys; 441 Assert.IsTrue(keys.IsReadOnly); 442 Assert.Throws<NotSupportedException>(() => keys.Clear()); 443 Assert.Throws<NotSupportedException>(() => keys.Remove("a")); 444 Assert.Throws<NotSupportedException>(() => keys.Add("a")); 445 } 446 447 // Just test keys - we know the implementation is the same for values 448 [Test] ViewCopyTo()449 public void ViewCopyTo() 450 { 451 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 452 var keys = map.Keys; 453 var array = new string[4]; 454 Assert.Throws<ArgumentException>(() => keys.CopyTo(array, 3)); 455 Assert.Throws<ArgumentOutOfRangeException>(() => keys.CopyTo(array, -1)); 456 keys.CopyTo(array, 1); 457 CollectionAssert.AreEqual(new[] { null, "foo", "x", null }, array); 458 } 459 460 // Just test keys - we know the implementation is the same for values 461 [Test] NonGenericViewCopyTo()462 public void NonGenericViewCopyTo() 463 { 464 IDictionary map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 465 ICollection keys = map.Keys; 466 // Note the use of the Array type here rather than string[] 467 Array array = new string[4]; 468 Assert.Throws<ArgumentException>(() => keys.CopyTo(array, 3)); 469 Assert.Throws<ArgumentOutOfRangeException>(() => keys.CopyTo(array, -1)); 470 keys.CopyTo(array, 1); 471 CollectionAssert.AreEqual(new[] { null, "foo", "x", null }, array); 472 } 473 474 [Test] KeysContains()475 public void KeysContains() 476 { 477 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 478 var keys = map.Keys; 479 Assert.IsTrue(keys.Contains("foo")); 480 Assert.IsFalse(keys.Contains("bar")); // It's a value! 481 Assert.IsFalse(keys.Contains("1")); 482 // Keys can't be null, so we should prevent contains check 483 Assert.Throws<ArgumentNullException>(() => keys.Contains(null)); 484 } 485 486 [Test] KeysCopyTo()487 public void KeysCopyTo() 488 { 489 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 490 var keys = map.Keys.ToArray(); // Uses CopyTo internally 491 CollectionAssert.AreEquivalent(new[] { "foo", "x" }, keys); 492 } 493 494 [Test] ValuesContains()495 public void ValuesContains() 496 { 497 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 498 var values = map.Values; 499 Assert.IsTrue(values.Contains("bar")); 500 Assert.IsFalse(values.Contains("foo")); // It's a key! 501 Assert.IsFalse(values.Contains("1")); 502 // Values can be null, so this makes sense 503 Assert.IsFalse(values.Contains(null)); 504 } 505 506 [Test] ValuesCopyTo()507 public void ValuesCopyTo() 508 { 509 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 510 var values = map.Values.ToArray(); // Uses CopyTo internally 511 CollectionAssert.AreEquivalent(new[] { "bar", "y" }, values); 512 } 513 514 [Test] ToString_StringToString()515 public void ToString_StringToString() 516 { 517 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 518 Assert.AreEqual("{ \"foo\": \"bar\", \"x\": \"y\" }", map.ToString()); 519 } 520 521 [Test] ToString_UnsupportedKeyType()522 public void ToString_UnsupportedKeyType() 523 { 524 var map = new MapField<byte, string> { { 10, "foo" } }; 525 Assert.Throws<ArgumentException>(() => map.ToString()); 526 } 527 528 [Test] NaNValuesComparedBitwise()529 public void NaNValuesComparedBitwise() 530 { 531 var map1 = new MapField<string, double> 532 { 533 { "x", SampleNaNs.Regular }, 534 { "y", SampleNaNs.SignallingFlipped } 535 }; 536 537 var map2 = new MapField<string, double> 538 { 539 { "x", SampleNaNs.Regular }, 540 { "y", SampleNaNs.PayloadFlipped } 541 }; 542 543 var map3 = new MapField<string, double> 544 { 545 { "x", SampleNaNs.Regular }, 546 { "y", SampleNaNs.SignallingFlipped } 547 }; 548 549 EqualityTester.AssertInequality(map1, map2); 550 EqualityTester.AssertEquality(map1, map3); 551 Assert.True(map1.Values.Contains(SampleNaNs.SignallingFlipped)); 552 Assert.False(map2.Values.Contains(SampleNaNs.SignallingFlipped)); 553 } 554 555 // This wouldn't usually happen, as protos can't use doubles as map keys, 556 // but let's be consistent. 557 [Test] NaNKeysComparedBitwise()558 public void NaNKeysComparedBitwise() 559 { 560 var map = new MapField<double, string> 561 { 562 { SampleNaNs.Regular, "x" }, 563 { SampleNaNs.SignallingFlipped, "y" } 564 }; 565 Assert.AreEqual("x", map[SampleNaNs.Regular]); 566 Assert.AreEqual("y", map[SampleNaNs.SignallingFlipped]); 567 Assert.False(map.TryGetValue(SampleNaNs.PayloadFlipped, out _)); 568 } 569 570 [Test] AddEntriesFrom_CodedInputStream()571 public void AddEntriesFrom_CodedInputStream() 572 { 573 // map will have string key and string value 574 var keyTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited); 575 var valueTag = WireFormat.MakeTag(2, WireFormat.WireType.LengthDelimited); 576 577 var memoryStream = new MemoryStream(); 578 var output = new CodedOutputStream(memoryStream); 579 output.WriteLength(20); // total of keyTag + key + valueTag + value 580 output.WriteTag(keyTag); 581 output.WriteString("the_key"); 582 output.WriteTag(valueTag); 583 output.WriteString("the_value"); 584 output.Flush(); 585 586 var field = new MapField<string,string>(); 587 var mapCodec = new MapField<string,string>.Codec(FieldCodec.ForString(keyTag, ""), FieldCodec.ForString(valueTag, ""), 10); 588 var input = new CodedInputStream(memoryStream.ToArray()); 589 590 // test the legacy overload of AddEntriesFrom that takes a CodedInputStream 591 field.AddEntriesFrom(input, mapCodec); 592 CollectionAssert.AreEquivalent(new[] { "the_key" }, field.Keys); 593 CollectionAssert.AreEquivalent(new[] { "the_value" }, field.Values); 594 Assert.IsTrue(input.IsAtEnd); 595 } 596 597 [Test] AddEntriesFrom_CodedInputStream_MissingKey()598 public void AddEntriesFrom_CodedInputStream_MissingKey() 599 { 600 // map will have string key and string value 601 var keyTag = WireFormat.MakeTag(1, WireFormat.WireType.LengthDelimited); 602 var valueTag = WireFormat.MakeTag(2, WireFormat.WireType.LengthDelimited); 603 604 var memoryStream = new MemoryStream(); 605 var output = new CodedOutputStream(memoryStream); 606 output.WriteLength(11); // total of valueTag + value 607 output.WriteTag(valueTag); 608 output.WriteString("the_value"); 609 output.Flush(); 610 611 var field = new MapField<string, string>(); 612 var mapCodec = new MapField<string, string>.Codec(FieldCodec.ForString(keyTag, ""), FieldCodec.ForString(valueTag, ""), 10); 613 var input = new CodedInputStream(memoryStream.ToArray()); 614 615 field.AddEntriesFrom(input, mapCodec); 616 CollectionAssert.AreEquivalent(new[] { "" }, field.Keys); 617 CollectionAssert.AreEquivalent(new[] { "the_value" }, field.Values); 618 Assert.IsTrue(input.IsAtEnd); 619 } 620 621 [Test] IDictionaryKeys_Equals_IReadOnlyDictionaryKeys()622 public void IDictionaryKeys_Equals_IReadOnlyDictionaryKeys() 623 { 624 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 625 CollectionAssert.AreEquivalent(((IDictionary<string, string>)map).Keys, ((IReadOnlyDictionary<string, string>)map).Keys); 626 } 627 628 [Test] IDictionaryValues_Equals_IReadOnlyDictionaryValues()629 public void IDictionaryValues_Equals_IReadOnlyDictionaryValues() 630 { 631 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 632 CollectionAssert.AreEquivalent(((IDictionary<string, string>)map).Values, ((IReadOnlyDictionary<string, string>)map).Values); 633 } 634 635 [Test] SortIntKeys_RandomOrder()636 public void SortIntKeys_RandomOrder() 637 { 638 var map = new MapField<int, string>() { { 1, "val" }, { -1, "val"}, { 0, "val" } }; 639 var sortedList = map.GetSortedListCopy(map.ToList()).ToList(); 640 var sortedKeys = sortedList.Select(kvp => kvp.Key); 641 CollectionAssert.AreEqual(new[] { -1, 0, 1 }, sortedKeys); 642 } 643 644 [Test] SortIntKeys_Empty()645 public void SortIntKeys_Empty() 646 { 647 var map = new MapField<int, string> { }; 648 var sortedList = map.GetSortedListCopy(map.ToList()).ToList(); 649 var sortedKeys = sortedList.Select(kvp => kvp.Key); 650 Assert.IsEmpty(sortedKeys); 651 } 652 653 [Test] SortStringKeys_RandomOrder()654 public void SortStringKeys_RandomOrder() 655 { 656 var map = new MapField<string, string> { { "a", "val" }, { "c", "val" }, { "b", "val" } }; 657 var sortedList = map.GetSortedListCopy(map.ToList()).ToList(); 658 var sortedKeys = sortedList.Select(kvp => kvp.Key); 659 CollectionAssert.AreEqual(new[] { "a", "b", "c" }, sortedKeys); 660 } 661 662 [Test] SortStringKeys_EnsureOrdinalSort()663 public void SortStringKeys_EnsureOrdinalSort() 664 { 665 var map = new MapField<string, string> 666 { 667 { "i", "val" } , { "I", "val" }, { "ı", "val" }, { "İ", "val" } 668 }; 669 var sortedList = map.GetSortedListCopy(map.ToList()); 670 var sortedKeys = sortedList.Select(kvp => kvp.Key); 671 // Assert Ordinal sort I, i, ı, İ (Non-ordinal sort returns i, I, İ, ı) 672 // I == 0x49 , i == 0x69 , İ == 0x130 , ı == 0x131 673 CollectionAssert.AreEqual(new[] { "I", "i", "İ", "ı" }, sortedKeys); 674 } 675 676 [Test] SortBoolKeys()677 public void SortBoolKeys() 678 { 679 var map = new MapField<bool, string> 680 { 681 { true, "val" } , { false, "val" } 682 }; 683 var sortedList = map.GetSortedListCopy(map.ToList()); 684 var sortedKeys = sortedList.Select(kvp => kvp.Key); 685 CollectionAssert.AreEqual(new[] { false, true }, sortedKeys); 686 } 687 NewKeyValuePair(TKey key, TValue value)688 private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value) 689 { 690 return new KeyValuePair<TKey, TValue>(key, value); 691 } 692 } 693 } 694