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.Generic; 35 using Google.Protobuf.TestProtos; 36 using NUnit.Framework; 37 using System.Collections; 38 using System.Linq; 39 40 namespace Google.Protobuf.Collections 41 { 42 /// <summary> 43 /// Tests for MapField which aren't reliant on the encoded format - 44 /// tests for serialization/deserialization are part of GeneratedMessageTest. 45 /// </summary> 46 public class MapFieldTest 47 { 48 [Test] Clone_ClonesMessages()49 public void Clone_ClonesMessages() 50 { 51 var message = new ForeignMessage { C = 20 }; 52 var map = new MapField<string, ForeignMessage> { { "x", message } }; 53 var clone = map.Clone(); 54 map["x"].C = 30; 55 Assert.AreEqual(20, clone["x"].C); 56 } 57 58 [Test] NullValuesProhibited()59 public void NullValuesProhibited() 60 { 61 TestNullValues<int?>(0); 62 TestNullValues(""); 63 TestNullValues(new TestAllTypes()); 64 } 65 TestNullValues(T nonNullValue)66 private void TestNullValues<T>(T nonNullValue) 67 { 68 var map = new MapField<int, T>(); 69 var nullValue = (T) (object) null; 70 Assert.Throws<ArgumentNullException>(() => map.Add(0, nullValue)); 71 Assert.Throws<ArgumentNullException>(() => map[0] = nullValue); 72 map.Add(1, nonNullValue); 73 map[1] = nonNullValue; 74 } 75 76 [Test] Add_ForbidsNullKeys()77 public void Add_ForbidsNullKeys() 78 { 79 var map = new MapField<string, ForeignMessage>(); 80 Assert.Throws<ArgumentNullException>(() => map.Add(null, new ForeignMessage())); 81 } 82 83 [Test] Indexer_ForbidsNullKeys()84 public void Indexer_ForbidsNullKeys() 85 { 86 var map = new MapField<string, ForeignMessage>(); 87 Assert.Throws<ArgumentNullException>(() => map[null] = new ForeignMessage()); 88 } 89 90 [Test] AddPreservesInsertionOrder()91 public void AddPreservesInsertionOrder() 92 { 93 var map = new MapField<string, string>(); 94 map.Add("a", "v1"); 95 map.Add("b", "v2"); 96 map.Add("c", "v3"); 97 map.Remove("b"); 98 map.Add("d", "v4"); 99 CollectionAssert.AreEqual(new[] { "a", "c", "d" }, map.Keys); 100 CollectionAssert.AreEqual(new[] { "v1", "v3", "v4" }, map.Values); 101 } 102 103 [Test] EqualityIsOrderInsensitive()104 public void EqualityIsOrderInsensitive() 105 { 106 var map1 = new MapField<string, string>(); 107 map1.Add("a", "v1"); 108 map1.Add("b", "v2"); 109 110 var map2 = new MapField<string, string>(); 111 map2.Add("b", "v2"); 112 map2.Add("a", "v1"); 113 114 EqualityTester.AssertEquality(map1, map2); 115 } 116 117 [Test] EqualityIsKeySensitive()118 public void EqualityIsKeySensitive() 119 { 120 var map1 = new MapField<string, string>(); 121 map1.Add("first key", "v1"); 122 map1.Add("second key", "v2"); 123 124 var map2 = new MapField<string, string>(); 125 map2.Add("third key", "v1"); 126 map2.Add("fourth key", "v2"); 127 128 EqualityTester.AssertInequality(map1, map2); 129 } 130 131 [Test] Equality_Simple()132 public void Equality_Simple() 133 { 134 var map = new MapField<string, string>(); 135 EqualityTester.AssertEquality(map, map); 136 EqualityTester.AssertInequality(map, null); 137 Assert.IsFalse(map.Equals(new object())); 138 } 139 140 [Test] EqualityIsValueSensitive()141 public void EqualityIsValueSensitive() 142 { 143 // Note: Without some care, it's a little easier than one might 144 // hope to see hash collisions, but only in some environments... 145 var map1 = new MapField<string, string>(); 146 map1.Add("a", "first value"); 147 map1.Add("b", "second value"); 148 149 var map2 = new MapField<string, string>(); 150 map2.Add("a", "third value"); 151 map2.Add("b", "fourth value"); 152 153 EqualityTester.AssertInequality(map1, map2); 154 } 155 156 [Test] Add_Dictionary()157 public void Add_Dictionary() 158 { 159 var map1 = new MapField<string, string> 160 { 161 { "x", "y" }, 162 { "a", "b" } 163 }; 164 var map2 = new MapField<string, string> 165 { 166 { "before", "" }, 167 map1, 168 { "after", "" } 169 }; 170 var expected = new MapField<string, string> 171 { 172 { "before", "" }, 173 { "x", "y" }, 174 { "a", "b" }, 175 { "after", "" } 176 }; 177 Assert.AreEqual(expected, map2); 178 CollectionAssert.AreEqual(new[] { "before", "x", "a", "after" }, map2.Keys); 179 } 180 181 // General IDictionary<TKey, TValue> behavior tests 182 [Test] Add_KeyAlreadyExists()183 public void Add_KeyAlreadyExists() 184 { 185 var map = new MapField<string, string>(); 186 map.Add("foo", "bar"); 187 Assert.Throws<ArgumentException>(() => map.Add("foo", "baz")); 188 } 189 190 [Test] Add_Pair()191 public void Add_Pair() 192 { 193 var map = new MapField<string, string>(); 194 ICollection<KeyValuePair<string, string>> collection = map; 195 collection.Add(NewKeyValuePair("x", "y")); 196 Assert.AreEqual("y", map["x"]); 197 Assert.Throws<ArgumentException>(() => collection.Add(NewKeyValuePair("x", "z"))); 198 } 199 200 [Test] Contains_Pair()201 public void Contains_Pair() 202 { 203 var map = new MapField<string, string> { { "x", "y" } }; 204 ICollection<KeyValuePair<string, string>> collection = map; 205 Assert.IsTrue(collection.Contains(NewKeyValuePair("x", "y"))); 206 Assert.IsFalse(collection.Contains(NewKeyValuePair("x", "z"))); 207 Assert.IsFalse(collection.Contains(NewKeyValuePair("z", "y"))); 208 } 209 210 [Test] Remove_Key()211 public void Remove_Key() 212 { 213 var map = new MapField<string, string>(); 214 map.Add("foo", "bar"); 215 Assert.AreEqual(1, map.Count); 216 Assert.IsFalse(map.Remove("missing")); 217 Assert.AreEqual(1, map.Count); 218 Assert.IsTrue(map.Remove("foo")); 219 Assert.AreEqual(0, map.Count); 220 Assert.Throws<ArgumentNullException>(() => map.Remove(null)); 221 } 222 223 [Test] Remove_Pair()224 public void Remove_Pair() 225 { 226 var map = new MapField<string, string>(); 227 map.Add("foo", "bar"); 228 ICollection<KeyValuePair<string, string>> collection = map; 229 Assert.AreEqual(1, map.Count); 230 Assert.IsFalse(collection.Remove(NewKeyValuePair("wrong key", "bar"))); 231 Assert.AreEqual(1, map.Count); 232 Assert.IsFalse(collection.Remove(NewKeyValuePair("foo", "wrong value"))); 233 Assert.AreEqual(1, map.Count); 234 Assert.IsTrue(collection.Remove(NewKeyValuePair("foo", "bar"))); 235 Assert.AreEqual(0, map.Count); 236 Assert.Throws<ArgumentException>(() => collection.Remove(new KeyValuePair<string, string>(null, ""))); 237 } 238 239 [Test] CopyTo_Pair()240 public void CopyTo_Pair() 241 { 242 var map = new MapField<string, string>(); 243 map.Add("foo", "bar"); 244 ICollection<KeyValuePair<string, string>> collection = map; 245 KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[3]; 246 collection.CopyTo(array, 1); 247 Assert.AreEqual(NewKeyValuePair("foo", "bar"), array[1]); 248 } 249 250 [Test] Clear()251 public void Clear() 252 { 253 var map = new MapField<string, string> { { "x", "y" } }; 254 Assert.AreEqual(1, map.Count); 255 map.Clear(); 256 Assert.AreEqual(0, map.Count); 257 map.Add("x", "y"); 258 Assert.AreEqual(1, map.Count); 259 } 260 261 [Test] Indexer_Get()262 public void Indexer_Get() 263 { 264 var map = new MapField<string, string> { { "x", "y" } }; 265 Assert.AreEqual("y", map["x"]); 266 Assert.Throws<KeyNotFoundException>(() => { var ignored = map["z"]; }); 267 } 268 269 [Test] Indexer_Set()270 public void Indexer_Set() 271 { 272 var map = new MapField<string, string>(); 273 map["x"] = "y"; 274 Assert.AreEqual("y", map["x"]); 275 map["x"] = "z"; // This won't throw, unlike Add. 276 Assert.AreEqual("z", map["x"]); 277 } 278 279 [Test] GetEnumerator_NonGeneric()280 public void GetEnumerator_NonGeneric() 281 { 282 IEnumerable map = new MapField<string, string> { { "x", "y" } }; 283 CollectionAssert.AreEqual(new[] { new KeyValuePair<string, string>("x", "y") }, 284 map.Cast<object>().ToList()); 285 } 286 287 // Test for the explicitly-implemented non-generic IDictionary interface 288 [Test] IDictionary_GetEnumerator()289 public void IDictionary_GetEnumerator() 290 { 291 IDictionary map = new MapField<string, string> { { "x", "y" } }; 292 var enumerator = map.GetEnumerator(); 293 294 // Commented assertions show an ideal situation - it looks like 295 // the LinkedList enumerator doesn't throw when you ask for the current entry 296 // at an inappropriate time; fixing this would be more work than it's worth. 297 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 298 Assert.IsTrue(enumerator.MoveNext()); 299 Assert.AreEqual("x", enumerator.Key); 300 Assert.AreEqual("y", enumerator.Value); 301 Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Current); 302 Assert.AreEqual(new DictionaryEntry("x", "y"), enumerator.Entry); 303 Assert.IsFalse(enumerator.MoveNext()); 304 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 305 enumerator.Reset(); 306 // Assert.Throws<InvalidOperationException>(() => enumerator.Current.GetHashCode()); 307 Assert.IsTrue(enumerator.MoveNext()); 308 Assert.AreEqual("x", enumerator.Key); // Assume the rest are okay 309 } 310 311 [Test] IDictionary_Add()312 public void IDictionary_Add() 313 { 314 var map = new MapField<string, string> { { "x", "y" } }; 315 IDictionary dictionary = map; 316 dictionary.Add("a", "b"); 317 Assert.AreEqual("b", map["a"]); 318 Assert.Throws<ArgumentException>(() => dictionary.Add("a", "duplicate")); 319 Assert.Throws<InvalidCastException>(() => dictionary.Add(new object(), "key is bad")); 320 Assert.Throws<InvalidCastException>(() => dictionary.Add("value is bad", new object())); 321 } 322 323 [Test] IDictionary_Contains()324 public void IDictionary_Contains() 325 { 326 var map = new MapField<string, string> { { "x", "y" } }; 327 IDictionary dictionary = map; 328 329 Assert.IsFalse(dictionary.Contains("a")); 330 Assert.IsFalse(dictionary.Contains(5)); 331 // Surprising, but IDictionary.Contains is only about keys. 332 Assert.IsFalse(dictionary.Contains(new DictionaryEntry("x", "y"))); 333 Assert.IsTrue(dictionary.Contains("x")); 334 } 335 336 [Test] IDictionary_Remove()337 public void IDictionary_Remove() 338 { 339 var map = new MapField<string, string> { { "x", "y" } }; 340 IDictionary dictionary = map; 341 dictionary.Remove("a"); 342 Assert.AreEqual(1, dictionary.Count); 343 dictionary.Remove(5); 344 Assert.AreEqual(1, dictionary.Count); 345 dictionary.Remove(new DictionaryEntry("x", "y")); 346 Assert.AreEqual(1, dictionary.Count); 347 dictionary.Remove("x"); 348 Assert.AreEqual(0, dictionary.Count); 349 Assert.Throws<ArgumentNullException>(() => dictionary.Remove(null)); 350 } 351 352 [Test] IDictionary_CopyTo()353 public void IDictionary_CopyTo() 354 { 355 var map = new MapField<string, string> { { "x", "y" } }; 356 IDictionary dictionary = map; 357 var array = new DictionaryEntry[3]; 358 dictionary.CopyTo(array, 1); 359 CollectionAssert.AreEqual(new[] { default(DictionaryEntry), new DictionaryEntry("x", "y"), default(DictionaryEntry) }, 360 array); 361 var objectArray = new object[3]; 362 dictionary.CopyTo(objectArray, 1); 363 CollectionAssert.AreEqual(new object[] { null, new DictionaryEntry("x", "y"), null }, 364 objectArray); 365 } 366 367 [Test] IDictionary_IsFixedSize()368 public void IDictionary_IsFixedSize() 369 { 370 var map = new MapField<string, string> { { "x", "y" } }; 371 IDictionary dictionary = map; 372 Assert.IsFalse(dictionary.IsFixedSize); 373 } 374 375 [Test] IDictionary_Keys()376 public void IDictionary_Keys() 377 { 378 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 379 CollectionAssert.AreEqual(new[] { "x" }, dictionary.Keys); 380 } 381 382 [Test] IDictionary_Values()383 public void IDictionary_Values() 384 { 385 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 386 CollectionAssert.AreEqual(new[] { "y" }, dictionary.Values); 387 } 388 389 [Test] IDictionary_IsSynchronized()390 public void IDictionary_IsSynchronized() 391 { 392 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 393 Assert.IsFalse(dictionary.IsSynchronized); 394 } 395 396 [Test] IDictionary_SyncRoot()397 public void IDictionary_SyncRoot() 398 { 399 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 400 Assert.AreSame(dictionary, dictionary.SyncRoot); 401 } 402 403 [Test] IDictionary_Indexer_Get()404 public void IDictionary_Indexer_Get() 405 { 406 IDictionary dictionary = new MapField<string, string> { { "x", "y" } }; 407 Assert.AreEqual("y", dictionary["x"]); 408 Assert.IsNull(dictionary["a"]); 409 Assert.IsNull(dictionary[5]); 410 Assert.Throws<ArgumentNullException>(() => dictionary[null].GetHashCode()); 411 } 412 413 [Test] IDictionary_Indexer_Set()414 public void IDictionary_Indexer_Set() 415 { 416 var map = new MapField<string, string> { { "x", "y" } }; 417 IDictionary dictionary = map; 418 map["a"] = "b"; 419 Assert.AreEqual("b", map["a"]); 420 map["a"] = "c"; 421 Assert.AreEqual("c", map["a"]); 422 Assert.Throws<InvalidCastException>(() => dictionary[5] = "x"); 423 Assert.Throws<InvalidCastException>(() => dictionary["x"] = 5); 424 Assert.Throws<ArgumentNullException>(() => dictionary[null] = "z"); 425 Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null); 426 } 427 428 [Test] KeysReturnsLiveView()429 public void KeysReturnsLiveView() 430 { 431 var map = new MapField<string, string>(); 432 var keys = map.Keys; 433 CollectionAssert.AreEqual(new string[0], keys); 434 map["foo"] = "bar"; 435 map["x"] = "y"; 436 CollectionAssert.AreEqual(new[] { "foo", "x" }, keys); 437 } 438 439 [Test] ValuesReturnsLiveView()440 public void ValuesReturnsLiveView() 441 { 442 var map = new MapField<string, string>(); 443 var values = map.Values; 444 CollectionAssert.AreEqual(new string[0], values); 445 map["foo"] = "bar"; 446 map["x"] = "y"; 447 CollectionAssert.AreEqual(new[] { "bar", "y" }, values); 448 } 449 450 // Just test keys - we know the implementation is the same for values 451 [Test] ViewsAreReadOnly()452 public void ViewsAreReadOnly() 453 { 454 var map = new MapField<string, string>(); 455 var keys = map.Keys; 456 Assert.IsTrue(keys.IsReadOnly); 457 Assert.Throws<NotSupportedException>(() => keys.Clear()); 458 Assert.Throws<NotSupportedException>(() => keys.Remove("a")); 459 Assert.Throws<NotSupportedException>(() => keys.Add("a")); 460 } 461 462 // Just test keys - we know the implementation is the same for values 463 [Test] ViewCopyTo()464 public void ViewCopyTo() 465 { 466 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 467 var keys = map.Keys; 468 var array = new string[4]; 469 Assert.Throws<ArgumentException>(() => keys.CopyTo(array, 3)); 470 Assert.Throws<ArgumentOutOfRangeException>(() => keys.CopyTo(array, -1)); 471 keys.CopyTo(array, 1); 472 CollectionAssert.AreEqual(new[] { null, "foo", "x", null }, array); 473 } 474 475 // Just test keys - we know the implementation is the same for values 476 [Test] NonGenericViewCopyTo()477 public void NonGenericViewCopyTo() 478 { 479 IDictionary map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 480 ICollection keys = map.Keys; 481 // Note the use of the Array type here rather than string[] 482 Array array = new string[4]; 483 Assert.Throws<ArgumentException>(() => keys.CopyTo(array, 3)); 484 Assert.Throws<ArgumentOutOfRangeException>(() => keys.CopyTo(array, -1)); 485 keys.CopyTo(array, 1); 486 CollectionAssert.AreEqual(new[] { null, "foo", "x", null }, array); 487 } 488 489 [Test] KeysContains()490 public void KeysContains() 491 { 492 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 493 var keys = map.Keys; 494 Assert.IsTrue(keys.Contains("foo")); 495 Assert.IsFalse(keys.Contains("bar")); // It's a value! 496 Assert.IsFalse(keys.Contains("1")); 497 // Keys can't be null, so we should prevent contains check 498 Assert.Throws<ArgumentNullException>(() => keys.Contains(null)); 499 } 500 501 [Test] ValuesContains()502 public void ValuesContains() 503 { 504 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 505 var values = map.Values; 506 Assert.IsTrue(values.Contains("bar")); 507 Assert.IsFalse(values.Contains("foo")); // It's a key! 508 Assert.IsFalse(values.Contains("1")); 509 // Values can be null, so this makes sense 510 Assert.IsFalse(values.Contains(null)); 511 } 512 513 [Test] ToString_StringToString()514 public void ToString_StringToString() 515 { 516 var map = new MapField<string, string> { { "foo", "bar" }, { "x", "y" } }; 517 Assert.AreEqual("{ \"foo\": \"bar\", \"x\": \"y\" }", map.ToString()); 518 } 519 520 [Test] ToString_UnsupportedKeyType()521 public void ToString_UnsupportedKeyType() 522 { 523 var map = new MapField<byte, string> { { 10, "foo" } }; 524 Assert.Throws<ArgumentException>(() => map.ToString()); 525 } 526 NewKeyValuePair(TKey key, TValue value)527 private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value) 528 { 529 return new KeyValuePair<TKey, TValue>(key, value); 530 } 531 } 532 } 533