• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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