MapFieldTest.cs 21 KB

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