Port aspnet/Routing/pull/858

This commit is contained in:
Ryan Nowak 2018-10-17 14:09:12 -07:00
parent 39c25357c6
commit d17d9155c7
2 changed files with 272 additions and 1 deletions

View File

@ -388,7 +388,9 @@ namespace Microsoft.AspNetCore.Routing
return false;
}
EnsureCapacity(Count);
// Ensure property storage is converted to array storage as we'll be
// applying the lookup and removal on the array
EnsureCapacity(_count);
var index = FindIndex(key);
if (index >= 0)
@ -404,6 +406,47 @@ namespace Microsoft.AspNetCore.Routing
return false;
}
/// <summary>
/// Attempts to remove and return the value that has the specified key from the <see cref="RouteValueDictionary"/>.
/// </summary>
/// <param name="key">The key of the element to remove and return.</param>
/// <param name="value">When this method returns, contains the object removed from the <see cref="RouteValueDictionary"/>, or <c>null</c> if key does not exist.</param>
/// <returns>
/// <c>true</c> if the object was removed successfully; otherwise, <c>false</c>.
/// </returns>
public bool Remove(string key, out object value)
{
if (key == null)
{
ThrowArgumentNullExceptionForKey();
}
if (_count == 0)
{
value = default;
return false;
}
// Ensure property storage is converted to array storage as we'll be
// applying the lookup and removal on the array
EnsureCapacity(_count);
var index = FindIndex(key);
if (index >= 0)
{
_count--;
var array = _arrayStorage;
value = array[index].Value;
Array.Copy(array, index + 1, array, index, _count - index);
array[_count] = default;
return true;
}
value = default;
return false;
}
/// <summary>
/// Attempts to the add the provided <paramref name="key"/> and <paramref name="value"/> to the dictionary.
/// </summary>

View File

@ -1393,6 +1393,234 @@ namespace Microsoft.AspNetCore.Routing.Tests
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_EmptyStorage()
{
// Arrange
var dict = new RouteValueDictionary();
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.False(result);
Assert.Null(removedValue);
}
[Fact]
public void Remove_KeyAndOutValue_EmptyStringIsAllowed()
{
// Arrange
var dict = new RouteValueDictionary();
// Act
var result = dict.Remove("", out var removedValue);
// Assert
Assert.False(result);
Assert.Null(removedValue);
}
[Fact]
public void Remove_KeyAndOutValue_PropertyStorage_Empty()
{
// Arrange
var dict = new RouteValueDictionary(new { });
// Act
var result = dict.Remove("other", out var removedValue);
// Assert
Assert.False(result);
Assert.Null(removedValue);
Assert.Empty(dict);
Assert.NotNull(dict._propertyStorage);
}
[Fact]
public void Remove_KeyAndOutValue_PropertyStorage_False()
{
// Arrange
var dict = new RouteValueDictionary(new { key = "value" });
// Act
var result = dict.Remove("other", out var removedValue);
// Assert
Assert.False(result);
Assert.Null(removedValue);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_PropertyStorage_True()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary(new { key = value });
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Empty(dict);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_PropertyStorage_True_CaseInsensitive()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary(new { key = value });
// Act
var result = dict.Remove("kEy", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Empty(dict);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_False()
{
// Arrange
var dict = new RouteValueDictionary()
{
{ "key", "value" },
};
// Act
var result = dict.Remove("other", out var removedValue);
// Assert
Assert.False(result);
Assert.Null(removedValue);
Assert.Collection(dict, kvp => { Assert.Equal("key", kvp.Key); Assert.Equal("value", kvp.Value); });
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_True()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary()
{
{ "key", value }
};
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Empty(dict);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_True_CaseInsensitive()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary()
{
{ "key", value }
};
// Act
var result = dict.Remove("kEy", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Empty(dict);
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_KeyExists_First()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary()
{
{ "key", value },
{ "other", 5 },
{ "dotnet", "rocks" }
};
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Equal(2, dict.Count);
Assert.False(dict.ContainsKey("key"));
Assert.True(dict.ContainsKey("other"));
Assert.True(dict.ContainsKey("dotnet"));
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_KeyExists_Middle()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary()
{
{ "other", 5 },
{ "key", value },
{ "dotnet", "rocks" }
};
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Equal(2, dict.Count);
Assert.False(dict.ContainsKey("key"));
Assert.True(dict.ContainsKey("other"));
Assert.True(dict.ContainsKey("dotnet"));
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void Remove_KeyAndOutValue_ListStorage_KeyExists_Last()
{
// Arrange
object value = "value";
var dict = new RouteValueDictionary()
{
{ "other", 5 },
{ "dotnet", "rocks" },
{ "key", value }
};
// Act
var result = dict.Remove("key", out var removedValue);
// Assert
Assert.True(result);
Assert.Same(value, removedValue);
Assert.Equal(2, dict.Count);
Assert.False(dict.ContainsKey("key"));
Assert.True(dict.ContainsKey("other"));
Assert.True(dict.ContainsKey("dotnet"));
Assert.IsType<KeyValuePair<string, object>[]>(dict._arrayStorage);
}
[Fact]
public void TryAdd_EmptyStringIsAllowed()
{