1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-08 03:27:04 +09:00
Satori/src/libraries/System.Collections/tests/Generic/Dictionary/Dictionary.Tests.cs
2022-11-23 11:59:22 -08:00

499 lines
21 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using Xunit;
namespace System.Collections.Tests
{
public partial class Dictionary_IDictionary_NonGeneric_Tests : IDictionary_NonGeneric_Tests
{
protected override IDictionary NonGenericIDictionaryFactory()
{
return new Dictionary<string, string>();
}
protected override ModifyOperation ModifyEnumeratorThrows => PlatformDetection.IsNetFramework ? base.ModifyEnumeratorThrows : ModifyOperation.Add | ModifyOperation.Insert;
protected override ModifyOperation ModifyEnumeratorAllowed => PlatformDetection.IsNetFramework ? base.ModifyEnumeratorAllowed : ModifyOperation.Overwrite | ModifyOperation.Remove | ModifyOperation.Clear;
/// <summary>
/// Creates an object that is dependent on the seed given. The object may be either
/// a value type or a reference type, chosen based on the value of the seed.
/// </summary>
protected override object CreateTKey(int seed)
{
int stringLength = seed % 10 + 5;
Random rand = new Random(seed);
byte[] bytes = new byte[stringLength];
rand.NextBytes(bytes);
return Convert.ToBase64String(bytes);
}
/// <summary>
/// Creates an object that is dependent on the seed given. The object may be either
/// a value type or a reference type, chosen based on the value of the seed.
/// </summary>
protected override object CreateTValue(int seed) => CreateTKey(seed);
protected override Type ICollection_NonGeneric_CopyTo_IndexLargerThanArrayCount_ThrowType => typeof(ArgumentOutOfRangeException);
#region IDictionary tests
[Fact]
public void IDictionary_NonGeneric_ItemSet_NullValueWhenDefaultValueIsNonNull()
{
IDictionary dictionary = new Dictionary<string, int>();
Assert.Throws<ArgumentNullException>(() => dictionary[GetNewKey(dictionary)] = null);
}
[Fact]
public void IDictionary_NonGeneric_ItemSet_KeyOfWrongType()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, string>();
AssertExtensions.Throws<ArgumentException>("key", () => dictionary[23] = CreateTValue(12345));
Assert.Empty(dictionary);
}
}
[Fact]
public void IDictionary_NonGeneric_ItemSet_ValueOfWrongType()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, string>();
object missingKey = GetNewKey(dictionary);
AssertExtensions.Throws<ArgumentException>("value", () => dictionary[missingKey] = 324);
Assert.Empty(dictionary);
}
}
[Fact]
public void IDictionary_NonGeneric_Add_KeyOfWrongType()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, string>();
object missingKey = 23;
AssertExtensions.Throws<ArgumentException>("key", () => dictionary.Add(missingKey, CreateTValue(12345)));
Assert.Empty(dictionary);
}
}
[Fact]
public void IDictionary_NonGeneric_Add_ValueOfWrongType()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, string>();
object missingKey = GetNewKey(dictionary);
AssertExtensions.Throws<ArgumentException>("value", () => dictionary.Add(missingKey, 324));
Assert.Empty(dictionary);
}
}
[Fact]
public void IDictionary_NonGeneric_Add_NullValueWhenDefaultTValueIsNonNull()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, int>();
object missingKey = GetNewKey(dictionary);
Assert.Throws<ArgumentNullException>(() => dictionary.Add(missingKey, null));
Assert.Empty(dictionary);
}
}
[Fact]
public void IDictionary_NonGeneric_Contains_KeyOfWrongType()
{
if (!IsReadOnly)
{
IDictionary dictionary = new Dictionary<string, int>();
Assert.False(dictionary.Contains(1));
}
}
[Fact]
public void Clear_OnEmptyCollection_DoesNotInvalidateEnumerator()
{
if (ModifyEnumeratorAllowed.HasFlag(ModifyOperation.Clear))
{
IDictionary dictionary = new Dictionary<string, string>();
IEnumerator valuesEnum = dictionary.GetEnumerator();
dictionary.Clear();
Assert.Empty(dictionary);
Assert.False(valuesEnum.MoveNext());
}
}
#endregion
#region ICollection tests
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void ICollection_NonGeneric_CopyTo_ArrayOfIncorrectKeyValuePairType(int count)
{
ICollection collection = NonGenericICollectionFactory(count);
KeyValuePair<string, int>[] array = new KeyValuePair<string, int>[count * 3 / 2];
AssertExtensions.Throws<ArgumentException>(null, () => collection.CopyTo(array, 0));
}
[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void ICollection_NonGeneric_CopyTo_ArrayOfCorrectKeyValuePairType(int count)
{
ICollection collection = NonGenericICollectionFactory(count);
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[count];
collection.CopyTo(array, 0);
int i = 0;
foreach (object obj in collection)
Assert.Equal(array[i++], obj);
}
#endregion
}
public class Dictionary_Tests
{
[Fact]
public void CopyConstructorExceptions()
{
AssertExtensions.Throws<ArgumentNullException>("dictionary", () => new Dictionary<int, int>((IDictionary<int, int>)null));
AssertExtensions.Throws<ArgumentNullException>("dictionary", () => new Dictionary<int, int>((IDictionary<int, int>)null, null));
AssertExtensions.Throws<ArgumentNullException>("dictionary", () => new Dictionary<int, int>((IDictionary<int, int>)null, EqualityComparer<int>.Default));
AssertExtensions.Throws<ArgumentOutOfRangeException>("capacity", () => new Dictionary<int, int>(new NegativeCountDictionary<int, int>()));
AssertExtensions.Throws<ArgumentOutOfRangeException>("capacity", () => new Dictionary<int, int>(new NegativeCountDictionary<int, int>(), null));
AssertExtensions.Throws<ArgumentOutOfRangeException>("capacity", () => new Dictionary<int, int>(new NegativeCountDictionary<int, int>(), EqualityComparer<int>.Default));
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(101)]
public void ICollection_NonGeneric_CopyTo_NonContiguousDictionary(int count)
{
ICollection collection = (ICollection)CreateDictionary(count, k => k.ToString());
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[count];
collection.CopyTo(array, 0);
int i = 0;
foreach (object obj in collection)
Assert.Equal(array[i++], obj);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(101)]
public void ICollection_Generic_CopyTo_NonContiguousDictionary(int count)
{
ICollection<KeyValuePair<string, string>> collection = CreateDictionary(count, k => k.ToString());
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[count];
collection.CopyTo(array, 0);
int i = 0;
foreach (KeyValuePair<string, string> obj in collection)
Assert.Equal(array[i++], obj);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(101)]
public void IDictionary_Generic_CopyTo_NonContiguousDictionary(int count)
{
IDictionary<string, string> collection = CreateDictionary(count, k => k.ToString());
KeyValuePair<string, string>[] array = new KeyValuePair<string, string>[count];
collection.CopyTo(array, 0);
int i = 0;
foreach (KeyValuePair<string, string> obj in collection)
Assert.Equal(array[i++], obj);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(101)]
public void CopyTo_NonContiguousDictionary(int count)
{
Dictionary<string, string> collection = (Dictionary<string, string>)CreateDictionary(count, k => k.ToString());
string[] array = new string[count];
collection.Keys.CopyTo(array, 0);
int i = 0;
foreach (KeyValuePair<string, string> obj in collection)
Assert.Equal(array[i++], obj.Key);
collection.Values.CopyTo(array, 0);
i = 0;
foreach (KeyValuePair<string, string> obj in collection)
Assert.Equal(array[i++], obj.Key);
}
[Fact]
public void Remove_NonExistentEntries_DoesNotPreventEnumeration()
{
const string SubKey = "-sub-key";
var dictionary = new Dictionary<string, string>();
dictionary.Add("a", "b");
dictionary.Add("c", "d");
foreach (string key in dictionary.Keys)
{
if (dictionary.Remove(key + SubKey))
break;
}
dictionary.Add("c" + SubKey, "d");
foreach (string key in dictionary.Keys)
{
if (dictionary.Remove(key + SubKey))
break;
}
}
[Fact]
public void TryAdd_ItemAlreadyExists_DoesNotInvalidateEnumerator()
{
var dictionary = new Dictionary<string, string>();
dictionary.Add("a", "b");
IEnumerator valuesEnum = dictionary.GetEnumerator();
Assert.False(dictionary.TryAdd("a", "c"));
Assert.True(valuesEnum.MoveNext());
}
[Theory]
[MemberData(nameof(CopyConstructorInt32Data))]
public void CopyConstructorInt32(int size, Func<int, int> keyValueSelector, Func<IDictionary<int, int>, IDictionary<int, int>> dictionarySelector)
{
TestCopyConstructor(size, keyValueSelector, dictionarySelector);
}
public static IEnumerable<object[]> CopyConstructorInt32Data
{
get { return GetCopyConstructorData(i => i); }
}
[Theory]
[MemberData(nameof(CopyConstructorStringData))]
public void CopyConstructorString(int size, Func<int, string> keyValueSelector, Func<IDictionary<string, string>, IDictionary<string, string>> dictionarySelector)
{
TestCopyConstructor(size, keyValueSelector, dictionarySelector);
}
public static IEnumerable<object[]> CopyConstructorStringData
{
get { return GetCopyConstructorData(i => i.ToString()); }
}
private static void TestCopyConstructor<T>(int size, Func<int, T> keyValueSelector, Func<IDictionary<T, T>, IDictionary<T, T>> dictionarySelector)
{
IDictionary<T, T> expected = CreateDictionary(size, keyValueSelector);
IDictionary<T, T> input = dictionarySelector(CreateDictionary(size, keyValueSelector));
Assert.Equal(expected, new Dictionary<T, T>(input));
}
[Theory]
[MemberData(nameof(CopyConstructorInt32ComparerData))]
public void CopyConstructorInt32Comparer(int size, Func<int, int> keyValueSelector, Func<IDictionary<int, int>, IDictionary<int, int>> dictionarySelector, IEqualityComparer<int> comparer)
{
TestCopyConstructor(size, keyValueSelector, dictionarySelector, comparer);
}
public static IEnumerable<object[]> CopyConstructorInt32ComparerData
{
get
{
var comparers = new IEqualityComparer<int>[]
{
null,
EqualityComparer<int>.Default
};
return GetCopyConstructorData(i => i, comparers);
}
}
[Theory]
[MemberData(nameof(CopyConstructorStringComparerData))]
public void CopyConstructorStringComparer(int size, Func<int, string> keyValueSelector, Func<IDictionary<string, string>, IDictionary<string, string>> dictionarySelector, IEqualityComparer<string> comparer)
{
TestCopyConstructor(size, keyValueSelector, dictionarySelector, comparer);
}
[Fact]
public void CantAcceptDuplicateKeysFromSourceDictionary()
{
Dictionary<string, int> source = new Dictionary<string, int> { { "a", 1 }, { "A", 1 } };
AssertExtensions.Throws<ArgumentException>(null, () => new Dictionary<string, int>(source, StringComparer.OrdinalIgnoreCase));
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization))]
// https://github.com/dotnet/runtime/issues/44681
public void DictionaryOrdinalIgnoreCaseCyrillicKeys()
{
const string Lower = "\u0430\u0431\u0432\u0433\u0434\u0435\u0451\u0436\u0437\u0438\u0439\u043A\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044C\u044B\u044A\u044D\u044E\u044F";
const string Higher = "\u0410\u0411\u0412\u0413\u0414\u0415\u0401\u0416\u0417\u0418\u0419\u041A\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042C\u042B\u042A\u042D\u042E\u042F";
var dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < Lower.Length; i++)
{
dictionary[Lower[i].ToString()] = i;
Assert.Equal(i, dictionary[Higher[i].ToString()]);
}
}
public static IEnumerable<object[]> CopyConstructorStringComparerData
{
get
{
var comparers = new IEqualityComparer<string>[]
{
null,
EqualityComparer<string>.Default,
StringComparer.Ordinal,
StringComparer.OrdinalIgnoreCase
};
return GetCopyConstructorData(i => i.ToString(), comparers);
}
}
private static void TestCopyConstructor<T>(int size, Func<int, T> keyValueSelector, Func<IDictionary<T, T>, IDictionary<T, T>> dictionarySelector, IEqualityComparer<T> comparer)
{
IDictionary<T, T> expected = CreateDictionary(size, keyValueSelector, comparer);
IDictionary<T, T> input = dictionarySelector(CreateDictionary(size, keyValueSelector, comparer));
Assert.Equal(expected, new Dictionary<T, T>(input, comparer));
}
private static IEnumerable<object[]> GetCopyConstructorData<T>(Func<int, T> keyValueSelector, IEqualityComparer<T>[] comparers = null)
{
var dictionarySelectors = new Func<IDictionary<T, T>, IDictionary<T, T>>[]
{
d => d,
d => new DictionarySubclass<T, T>(d),
d => new ReadOnlyDictionary<T, T>(d)
};
var sizes = new int[] { 0, 1, 2, 3 };
foreach (Func<IDictionary<T, T>, IDictionary<T, T>> dictionarySelector in dictionarySelectors)
{
foreach (int size in sizes)
{
if (comparers != null)
{
foreach (IEqualityComparer<T> comparer in comparers)
{
yield return new object[] { size, keyValueSelector, dictionarySelector, comparer };
}
}
else
{
yield return new object[] { size, keyValueSelector, dictionarySelector };
}
}
}
}
private static IDictionary<T, T> CreateDictionary<T>(int size, Func<int, T> keyValueSelector, IEqualityComparer<T> comparer = null)
{
Dictionary<T, T> dict = Enumerable.Range(0, size + 1).ToDictionary(keyValueSelector, keyValueSelector, comparer);
// Remove first item to reduce Count to size and alter the contiguity of the dictionary
dict.Remove(keyValueSelector(0));
return dict;
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsBinaryFormatterSupported))]
public void ComparerSerialization()
{
// Strings switch between randomized and non-randomized comparers,
// however this should never be observable externally.
TestComparerSerialization(EqualityComparer<string>.Default);
// OrdinalCaseSensitiveComparer is internal and (de)serializes as OrdinalComparer
TestComparerSerialization(StringComparer.Ordinal, "System.OrdinalComparer");
// OrdinalIgnoreCaseComparer is internal and (de)serializes as OrdinalComparer
TestComparerSerialization(StringComparer.OrdinalIgnoreCase, "System.OrdinalComparer");
TestComparerSerialization(StringComparer.CurrentCulture);
TestComparerSerialization(StringComparer.CurrentCultureIgnoreCase);
TestComparerSerialization(StringComparer.InvariantCulture);
TestComparerSerialization(StringComparer.InvariantCultureIgnoreCase);
// Check other types while here, IEquatable valuetype, nullable valuetype, and non IEquatable object
TestComparerSerialization(EqualityComparer<int>.Default);
TestComparerSerialization(EqualityComparer<int?>.Default);
TestComparerSerialization(EqualityComparer<object>.Default);
}
private static void TestComparerSerialization<T>(IEqualityComparer<T> equalityComparer, string internalTypeName = null)
{
var bf = new BinaryFormatter();
var s = new MemoryStream();
var dict = new Dictionary<T, T>(equalityComparer);
Assert.Same(equalityComparer, dict.Comparer);
bf.Serialize(s, dict);
s.Position = 0;
dict = (Dictionary<T, T>)bf.Deserialize(s);
if (internalTypeName == null)
{
Assert.IsType(equalityComparer.GetType(), dict.Comparer);
}
else
{
Assert.Equal(internalTypeName, dict.Comparer.GetType().ToString());
}
Assert.True(equalityComparer.Equals(dict.Comparer));
}
private sealed class DictionarySubclass<TKey, TValue> : Dictionary<TKey, TValue>
{
public DictionarySubclass(IDictionary<TKey, TValue> dictionary)
{
foreach (var pair in dictionary)
{
Add(pair.Key, pair.Value);
}
}
}
/// <summary>
/// An incorrectly implemented dictionary that returns -1 from Count.
/// </summary>
private sealed class NegativeCountDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public int Count { get { return -1; } }
public TValue this[TKey key] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
public bool IsReadOnly { get { throw new NotImplementedException(); } }
public ICollection<TKey> Keys { get { throw new NotImplementedException(); } }
public ICollection<TValue> Values { get { throw new NotImplementedException(); } }
public void Add(KeyValuePair<TKey, TValue> item) { throw new NotImplementedException(); }
public void Add(TKey key, TValue value) { throw new NotImplementedException(); }
public void Clear() { throw new NotImplementedException(); }
public bool Contains(KeyValuePair<TKey, TValue> item) { throw new NotImplementedException(); }
public bool ContainsKey(TKey key) { throw new NotImplementedException(); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { throw new NotImplementedException(); }
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { throw new NotImplementedException(); }
public bool Remove(KeyValuePair<TKey, TValue> item) { throw new NotImplementedException(); }
public bool Remove(TKey key) { throw new NotImplementedException(); }
public bool TryGetValue(TKey key, out TValue value) { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
}
}