Custom dictionary type for attributes

This commit is contained in:
Ryan Nowak 2015-09-28 22:14:32 -07:00
parent 4e08eda58d
commit 6185b16795
6 changed files with 1198 additions and 17 deletions

View File

@ -842,6 +842,22 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
return string.Format(CultureInfo.CurrentCulture, GetString("TempData_CannotSerializeToSession"), p0, p1);
}
/// <summary>
/// The collection already contains an entry with key '{0}'.
/// </summary>
internal static string Dictionary_DuplicateKey
{
get { return GetString("Dictionary_DuplicateKey"); }
}
/// <summary>
/// The collection already contains an entry with key '{0}'.
/// </summary>
internal static string FormatDictionary_DuplicateKey(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Dictionary_DuplicateKey"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -17,6 +17,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
[DebuggerDisplay("{DebuggerToString()}")]
public class TagBuilder : IHtmlContent
{
private AttributeDictionary _attributes;
public TagBuilder(string tagName)
{
if (string.IsNullOrEmpty(tagName))
@ -25,15 +27,25 @@ namespace Microsoft.AspNet.Mvc.Rendering
}
TagName = tagName;
Attributes = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
InnerHtml = new BufferedHtmlContent();
}
/// <summary>
/// Gets the set of attributes that will be written to the tag.
/// </summary>
public IDictionary<string, string> Attributes { get; }
public AttributeDictionary Attributes
{
get
{
// Perf: Avoid allocating `_attributes` if possible
if (_attributes == null)
{
_attributes = new AttributeDictionary();
}
return _attributes;
}
}
public IHtmlContentBuilder InnerHtml { get; }
@ -151,20 +163,24 @@ namespace Microsoft.AspNet.Mvc.Rendering
private void AppendAttributes(TextWriter writer, IHtmlEncoder encoder)
{
foreach (var attribute in Attributes)
// Perf: Avoid allocating enumerator for `_attributes` if possible
if (_attributes != null && _attributes.Count > 0)
{
var key = attribute.Key;
if (string.Equals(key, "id", StringComparison.OrdinalIgnoreCase) &&
string.IsNullOrEmpty(attribute.Value))
foreach (var attribute in Attributes)
{
continue;
}
var key = attribute.Key;
if (string.Equals(key, "id", StringComparison.OrdinalIgnoreCase) &&
string.IsNullOrEmpty(attribute.Value))
{
continue;
}
writer.Write(" ");
writer.Write(key);
writer.Write("=\"");
encoder.HtmlEncode(attribute.Value, writer);
writer.Write("\"");
writer.Write(" ");
writer.Write(key);
writer.Write("=\"");
encoder.HtmlEncode(attribute.Value, writer);
writer.Write("\"");
}
}
}
@ -193,7 +209,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting)
{
if (attributes != null)
// Perf: Avoid allocating enumerator for `attributes` if possible
if (attributes != null && attributes.Count > 0)
{
foreach (var entry in attributes)
{

View File

@ -274,4 +274,7 @@
<data name="TempData_CannotSerializeToSession" xml:space="preserve">
<value>The '{0}' cannot serialize an object of type '{1}' to session state.</value>
</data>
<data name="Dictionary_DuplicateKey" xml:space="preserve">
<value>The collection already contains an entry with key '{0}'.</value>
</data>
</root>

View File

@ -0,0 +1,681 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
/// <summary>
/// A dictionary for HTML attributes.
/// </summary>
public class AttributeDictionary : IDictionary<string, string>, IReadOnlyDictionary<string, string>
{
private List<KeyValuePair<string, string>> _items;
/// <inheritdoc />
public string this[string key]
{
get
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var index = Find(key);
if (index < 0)
{
throw new KeyNotFoundException();
}
else
{
return Get(index).Value;
}
}
set
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var item = new KeyValuePair<string, string>(key, value);
var index = Find(key);
if (index < 0)
{
Insert(~index, item);
}
else
{
Set(index, item);
}
}
}
/// <inheritdoc />
public int Count => _items == null ? 0 : _items.Count;
/// <inheritdoc />
public bool IsReadOnly
{
get
{
return false;
}
}
/// <inheritdoc />
public ICollection<string> Keys
{
get
{
return new KeyCollection(this);
}
}
/// <inheritdoc />
public ICollection<string> Values
{
get
{
return new ValueCollection(this);
}
}
/// <inheritdoc />
IEnumerable<string> IReadOnlyDictionary<string, string>.Keys
{
get
{
return new KeyCollection(this);
}
}
/// <inheritdoc />
IEnumerable<string> IReadOnlyDictionary<string, string>.Values
{
get
{
return new ValueCollection(this);
}
}
private KeyValuePair<string, string> Get(int index)
{
if (index >= Count)
{
throw new ArgumentOutOfRangeException();
}
return _items[index];
}
private void Set(int index, KeyValuePair<string, string> value)
{
if (index > Count)
{
throw new ArgumentOutOfRangeException();
}
if (_items == null)
{
_items = new List<KeyValuePair<string, string>>();
}
_items[index] = value;
}
private void Insert(int index, KeyValuePair<string, string> value)
{
if (index > Count)
{
throw new ArgumentOutOfRangeException();
}
if (_items == null)
{
_items = new List<KeyValuePair<string, string>>();
}
_items.Insert(index, value);
}
private void Remove(int index)
{
if (index >= Count)
{
throw new ArgumentOutOfRangeException();
}
_items.RemoveAt(index);
}
// This API is a lot like List<T>.BinarySearch https://msdn.microsoft.com/en-us/library/3f90y839(v=vs.110).aspx
// If an item is not found, we return the compliment of where it belongs. Then we don't need to search again
// to do something with it.
private int Find(string key)
{
if (Count == 0)
{
return ~0;
}
var start = 0;
var end = Count - 1;
while (start <= end)
{
var pivot = start + (end - start >> 1);
var compare = StringComparer.OrdinalIgnoreCase.Compare(Get(pivot).Key, key);
if (compare == 0)
{
return pivot;
}
if (compare < 0)
{
start = pivot + 1;
}
else
{
end = pivot - 1;
}
}
return ~start;
}
/// <inheritdoc />
public void Clear()
{
if (_items != null)
{
_items.Clear();
}
}
/// <inheritdoc />
public void Add(KeyValuePair<string, string> item)
{
if (item.Key == null)
{
throw new ArgumentException(
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(KeyValuePair<string, string>.Key),
nameof(KeyValuePair<string, string>)),
nameof(item));
}
var index = Find(item.Key);
if (index < 0)
{
Insert(~index, item);
}
else
{
throw new InvalidOperationException(Resources.FormatDictionary_DuplicateKey(item.Key));
}
}
/// <inheritdoc />
public void Add(string key, string value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
Add(new KeyValuePair<string, string>(key, value));
}
/// <inheritdoc />
public bool Contains(KeyValuePair<string, string> item)
{
if (item.Key == null)
{
throw new ArgumentException(
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(KeyValuePair<string, string>.Key),
nameof(KeyValuePair<string, string>)),
nameof(item));
}
var index = Find(item.Key);
if (index < 0)
{
return false;
}
else
{
return string.Equals(item.Value, Get(index).Value, StringComparison.OrdinalIgnoreCase);
}
}
/// <inheritdoc />
public bool ContainsKey(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (Count == 0)
{
return false;
}
return Find(key) >= 0;
}
/// <inheritdoc />
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex >= array.Length)
{
throw new IndexOutOfRangeException();
}
for (var i = 0; i < Count; i++)
{
array[arrayIndex + i] = Get(i);
}
}
/// <inheritdoc />
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
/// <inheritdoc />
public bool Remove(KeyValuePair<string, string> item)
{
if (item.Key == null)
{
throw new ArgumentException(
Resources.FormatPropertyOfTypeCannotBeNull(
nameof(KeyValuePair<string, string>.Key),
nameof(KeyValuePair<string, string>)),
nameof(item));
}
var index = Find(item.Key);
if (index < 0)
{
return false;
}
else if (string.Equals(item.Value, Get(index).Value, StringComparison.OrdinalIgnoreCase))
{
Remove(index);
return true;
}
else
{
return false;
}
}
/// <inheritdoc />
public bool Remove(string key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var index = Find(key);
if (index < 0)
{
return false;
}
else
{
Remove(index);
return true;
}
}
/// <inheritdoc />
public bool TryGetValue(string key, out string value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
var index = Find(key);
if (index < 0)
{
value = null;
return false;
}
else
{
value = Get(index).Value;
return true;
}
}
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <inheritdoc />
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// An enumerator for <see cref="AttributeDictionary"/>.
/// </summary>
public struct Enumerator : IEnumerator<KeyValuePair<string, string>>
{
private readonly AttributeDictionary _attributes;
private int _index;
/// <summary>
/// Creates a new <see cref="Enumerator"/>.
/// </summary>
/// <param name="attributes">The <see cref="AttributeDictionary"/>.</param>
public Enumerator(AttributeDictionary attributes)
{
_attributes = attributes;
_index = -1;
}
/// <inheritdoc />
public KeyValuePair<string, string> Current
{
get
{
return _attributes.Get(_index);
}
}
/// <inheritdoc />
object IEnumerator.Current
{
get
{
return Current;
}
}
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public bool MoveNext()
{
_index++;
return _index < _attributes.Count;
}
/// <inheritdoc />
public void Reset()
{
_index = -1;
}
}
private class KeyCollection : ICollection<string>
{
private readonly AttributeDictionary _attributes;
public KeyCollection(AttributeDictionary attributes)
{
_attributes = attributes;
}
public int Count => _attributes.Count;
public bool IsReadOnly => true;
public void Add(string item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(string item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
for (var i = 0; i < _attributes.Count; i++)
{
if (string.Equals(item, _attributes.Get(i).Key, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public void CopyTo(string[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex >= array.Length)
{
throw new IndexOutOfRangeException();
}
for (var i = 0; i < _attributes.Count; i++)
{
array[arrayIndex + i] = _attributes.Get(i).Key;
}
}
public Enumerator GetEnumerator()
{
return new Enumerator(this._attributes);
}
public bool Remove(string item)
{
throw new NotSupportedException();
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public struct Enumerator : IEnumerator<string>
{
private readonly AttributeDictionary _attributes;
private int _index;
public Enumerator(AttributeDictionary attributes)
{
_attributes = attributes;
_index = -1;
}
public string Current
{
get
{
return _attributes.Get(_index).Key;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public void Dispose()
{
}
public bool MoveNext()
{
_index++;
return _index < _attributes.Count;
}
public void Reset()
{
_index = -1;
}
}
}
private class ValueCollection : ICollection<string>
{
private readonly AttributeDictionary _attributes;
public ValueCollection(AttributeDictionary attributes)
{
_attributes = attributes;
}
public int Count => _attributes.Count;
public bool IsReadOnly => true;
public void Add(string item)
{
throw new NotSupportedException();
}
public void Clear()
{
throw new NotSupportedException();
}
public bool Contains(string item)
{
for (var i = 0; i < _attributes.Count; i++)
{
if (string.Equals(item, _attributes.Get(i).Value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
public void CopyTo(string[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException(nameof(array));
}
if (arrayIndex < 0 || arrayIndex >= array.Length)
{
throw new IndexOutOfRangeException();
}
for (var i = 0; i < _attributes.Count; i++)
{
array[arrayIndex + i] = _attributes.Get(i).Value;
}
}
public Enumerator GetEnumerator()
{
return new Enumerator(this._attributes);
}
public bool Remove(string item)
{
throw new NotSupportedException();
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public struct Enumerator : IEnumerator<string>
{
private readonly AttributeDictionary _attributes;
private int _index;
public Enumerator(AttributeDictionary attributes)
{
_attributes = attributes;
_index = -1;
}
public string Current
{
get
{
return _attributes.Get(_index).Key;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public void Dispose()
{
}
public bool MoveNext()
{
_index++;
return _index < _attributes.Count;
}
public void Reset()
{
_index = -1;
}
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc.Core.Rendering
[Theory]
[InlineData(false, "Hello", "World")]
[InlineData(true, "Hello", "something else")]
[InlineData(true, "hello", "something else")]
public void MergeAttribute_IgnoresCase(bool replaceExisting, string expectedKey, string expectedValue)
{
// Arrange
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Mvc.Core.Rendering
// Assert
var attribute = Assert.Single(tagBuilder.Attributes);
Assert.Equal(new KeyValuePair<string, string>("ClaSs", "success btn"), attribute);
Assert.Equal(new KeyValuePair<string, string>("class", "success btn"), attribute);
}
[Fact]

View File

@ -0,0 +1,464 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Mvc.ViewFeatures
{
public class AttributeDictionaryTest
{
[Fact]
public void AttributeDictionary_AddItems()
{
// Arrange
var attributes = new AttributeDictionary();
// Act
attributes.Add("zero", "0");
attributes.Add("one", "1");
attributes.Add("two", "2");
// Assert
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_AddItems_AsKeyValuePairs()
{
// Arrange
var attributes = new AttributeDictionary();
// Act
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Assert
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_Add_DuplicateKey()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add("zero", "0");
attributes.Add("one", "1");
attributes.Add("two", "2");
// Act & Assert
Assert.Throws<InvalidOperationException>(() => attributes.Add("one", "15"));
}
[Fact]
public void AttributeDictionary_Clear()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
attributes.Clear();
// Assert
Assert.Equal(0, attributes.Count);
Assert.Empty(attributes);
}
[Fact]
public void AttributeDictionary_Contains_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Contains(new KeyValuePair<string, string>("zero", "0"));
// Assert
Assert.True(result);
}
[Fact]
public void AttributeDictionary_Contains_Failure()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Contains(new KeyValuePair<string, string>("zero", "nada"));
// Assert
Assert.False(result);
}
[Fact]
public void AttributeDictionary_ContainsKey_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.ContainsKey("one");
// Assert
Assert.True(result);
}
[Fact]
public void AttributeDictionary_ContainsKey_Failure()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.ContainsKey("one!");
// Assert
Assert.False(result);
}
[Fact]
public void AttributeDictionary_CopyTo()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
var array = new KeyValuePair<string, string>[attributes.Count + 1];
// Act
attributes.CopyTo(array, 1);
// Assert
Assert.Collection(
array,
kvp => Assert.Equal(default(KeyValuePair<string, string>), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_IsReadOnly()
{
// Arrange
var attributes = new AttributeDictionary();
// Act
var result = attributes.IsReadOnly;
// Assert
Assert.False(result);
}
[Fact]
public void AttributeDictionary_Keys()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var keys = attributes.Keys;
// Assert
Assert.Equal(3, keys.Count);
Assert.Collection(
keys,
key => Assert.Equal("one", key),
key => Assert.Equal("two", key),
key => Assert.Equal("zero", key));
}
[Fact]
public void AttributeDictionary_Remove_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Remove("one");
// Assert
Assert.True(result);
Assert.Equal(2, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_Remove_Failure()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Remove("nada");
// Assert
Assert.False(result);
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_Remove_KeyValuePair_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Remove(new KeyValuePair<string, string>("one", "1"));
// Assert
Assert.True(result);
Assert.Equal(2, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_Remove_KeyValuePair_Failure()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var result = attributes.Remove(new KeyValuePair<string, string>("one", "0"));
// Assert
Assert.False(result);
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_TryGetValue_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
string value;
// Act
var result = attributes.TryGetValue("two", out value);
// Assert
Assert.True(result);
Assert.Equal("2", value);
}
[Fact]
public void AttributeDictionary_TryGetValue_Failure()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
string value;
// Act
var result = attributes.TryGetValue("nada", out value);
// Assert
Assert.False(result);
Assert.Null(value);
}
[Fact]
public void AttributeDictionary_Values()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var values = attributes.Values;
// Assert
Assert.Equal(3, values.Count);
Assert.Collection(
values,
key => Assert.Equal("1", key),
key => Assert.Equal("2", key),
key => Assert.Equal("0", key));
}
[Fact]
public void AttributeDictionary_Indexer_Success()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
var value = attributes["two"];
// Assert
Assert.Equal("2", value);
}
[Fact]
public void AttributeDictionary_Indexer_Fails()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act & Assert
Assert.Throws<KeyNotFoundException>(() => attributes["nada"]);
}
[Fact]
public void AttributeDictionary_Indexer_SetValue()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
attributes["one"] = "1!";
// Assert
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1!"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_Indexer_Insert()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
attributes["exciting!"] = "1!";
// Assert
Assert.Equal(4, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("exciting!", "1!"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("one", "1"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
[Fact]
public void AttributeDictionary_CaseInsensitive()
{
// Arrange
var attributes = new AttributeDictionary();
attributes.Add(new KeyValuePair<string, string>("zero", "0"));
attributes.Add(new KeyValuePair<string, string>("one", "1"));
attributes.Add(new KeyValuePair<string, string>("two", "2"));
// Act
attributes["oNe"] = "1!";
// Assert
Assert.Equal(3, attributes.Count);
Assert.Collection(
attributes,
kvp => Assert.Equal(new KeyValuePair<string, string>("oNe", "1!"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("two", "2"), kvp),
kvp => Assert.Equal(new KeyValuePair<string, string>("zero", "0"), kvp));
}
}
}