Cleaning up PR to match coding guidelines

* Refactoring tests to use facts and adding additional unit tests
This commit is contained in:
Pranav K 2014-10-31 10:46:30 -07:00
Родитель afc70adcb6
Коммит 0a83ae8b99
14 изменённых файлов: 418 добавлений и 313 удалений

Просмотреть файл

@ -7,6 +7,7 @@ using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc.Properties;
@ -86,7 +87,9 @@ namespace System.Web.Mvc.Async
private string CreateUniqueId()
{
return base.UniqueId + DescriptorUtil.CreateUniqueId(TaskMethodInfo);
var builder = new StringBuilder(base.UniqueId);
DescriptorUtil.AppendUniqueId(builder, MethodInfo);
return builder.ToString();
}
[SuppressMessage("Microsoft.Web.FxCop", "MW1201:DoNotCallProblematicMethodsOnTask", Justification = "This is commented in great detail.")]

Просмотреть файл

@ -11,6 +11,7 @@ namespace System.Web.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
private static readonly char[] _splitParameter = new[] { ',' };
private readonly object _typeId = new object();
private string _roles;
@ -144,7 +145,7 @@ namespace System.Web.Mvc
return new string[0];
}
var split = from piece in original.Split(StringSplits.Comma)
var split = from piece in original.Split(_splitParameter)
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;

Просмотреть файл

@ -1,10 +0,0 @@
namespace System.Web.Mvc
{
internal static class StringSplits
{
// note array contents not technically read-only; just... don't edit them!
internal static readonly char[]
Period = new[] { '.' },
Comma = new[] { ',' };
}
}

Просмотреть файл

@ -24,7 +24,7 @@ namespace System.Web.Mvc
}
if (controllerDescriptor.ControllerType.Namespace != null)
{
return controllerDescriptor.ControllerType.Namespace.Split(StringSplits.Period).Last();
return controllerDescriptor.ControllerType.Namespace.Split('.').Last();
}
throw Error.InvalidOperation(MvcResources.AttributeRouting_CouldNotInferAreaNameFromMissingNamespace, controllerDescriptor.ControllerName);

Просмотреть файл

@ -23,9 +23,9 @@ namespace System.Web.Mvc
}
}
internal static void AppendUniqueId(StringBuilder builder, object part)
public static void AppendUniqueId(StringBuilder builder, object part)
{
// We can special-case certain part type
// We can special-case certain part types
MemberInfo memberInfo = part as MemberInfo;
if (memberInfo != null)
{
@ -43,49 +43,23 @@ namespace System.Web.Mvc
AppendPartToUniqueIdBuilder(builder, part);
}
// special-case common constructors to avoid extra array allocation
public static string CreateUniqueId(object part0)
{
var builder = new StringBuilder();
AppendUniqueId(builder, part0);
return builder.ToString();
}
public static string CreateUniqueId(object part0, object part1)
{
var builder = new StringBuilder();
StringBuilder builder = new StringBuilder();
AppendUniqueId(builder, part0);
AppendUniqueId(builder, part1);
return builder.ToString();
}
public static string CreateUniqueId(object part0, object part1, object part2)
{
var builder = new StringBuilder();
StringBuilder builder = new StringBuilder();
AppendUniqueId(builder, part0);
AppendUniqueId(builder, part1);
AppendUniqueId(builder, part2);
return builder.ToString();
}
public static string CreateUniqueId(params object[] parts)
{
// returns a unique string made up of the pieces passed in
StringBuilder builder = new StringBuilder();
for (int i = 0; i < parts.Length; i++)
{
AppendUniqueId(builder, parts[i]);
}
return builder.ToString();
}
public static string CreateUniqueId(IEnumerable<object> parts)
{
// returns a unique string made up of the pieces passed in
StringBuilder builder = new StringBuilder();
foreach (object part in parts)
{
AppendUniqueId(builder, part);
}
return builder.ToString();
}
public static TDescriptor[] LazilyFetchOrCreateDescriptors<TReflection, TDescriptor, TArgument>(
ref TDescriptor[] cacheLocation,

Просмотреть файл

@ -54,7 +54,7 @@ namespace System.Web.Mvc.Html
{
// We don't call ModelMetadata.GetDisplayName here because we want to fall back to the field name rather than the ModelType.
// This is similar to how the LabelHelpers get the text of a label.
string resolvedDisplayName = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split(StringSplits.Period).Last();
string resolvedDisplayName = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
return new MvcHtmlString(HttpUtility.HtmlEncode(resolvedDisplayName));
}

Просмотреть файл

@ -145,7 +145,7 @@ namespace System.Web.Mvc.Html
internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, string htmlFieldName, string labelText = null, IDictionary<string, object> htmlAttributes = null)
{
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split(StringSplits.Period).Last();
string resolvedLabelText = labelText ?? metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
if (String.IsNullOrEmpty(resolvedLabelText))
{
return MvcHtmlString.Empty;

Просмотреть файл

@ -8,6 +8,7 @@ using System.Linq;
using System.Runtime.Caching;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Web.Mvc.Properties;
using System.Web.UI;
@ -18,18 +19,23 @@ namespace System.Web.Mvc
public class OutputCacheAttribute : ActionFilterAttribute, IExceptionFilter
{
private const string CacheKeyPrefix = "_MvcChildActionCache_";
private static readonly char[] _splitParameter = new[] { ';' };
private static ObjectCache _childActionCache;
private static object _childActionFilterFinishCallbackKey = new object();
private readonly Func<string[]> _splitVaryByParamThunk;
private OutputCacheParameters _cacheSettings = new OutputCacheParameters { VaryByParam = "*" };
private Func<ObjectCache> _childActionCacheThunk = () => ChildActionCache;
private bool _locationWasSet;
private bool _noStoreWasSet;
private string[] _tokenizedVaryByParams;
public OutputCacheAttribute()
{
_splitVaryByParamThunk = () => GetTokenizedVaryByParam(VaryByParam);
}
internal OutputCacheAttribute(ObjectCache childActionCache)
: this()
{
_childActionCacheThunk = () => childActionCache;
}
@ -110,7 +116,12 @@ namespace System.Web.Mvc
public string VaryByParam
{
get { return _cacheSettings.VaryByParam ?? String.Empty; }
set { _cacheSettings.VaryByParam = value; }
set
{
// Reset the tokenized values if the property gets modified.
_tokenizedVaryByParams = null;
_cacheSettings.VaryByParam = value;
}
}
private static void ClearChildActionFilterFinishCallback(ControllerContext controllerContext)
@ -145,7 +156,7 @@ namespace System.Web.Mvc
uniqueIdBuilder.Append(filterContext.ActionDescriptor.UniqueId);
// Unique ID from the VaryByCustom settings, if any
uniqueIdBuilder.Append(DescriptorUtil.CreateUniqueId(VaryByCustom));
DescriptorUtil.AppendUniqueId(uniqueIdBuilder, VaryByCustom);
if (!String.IsNullOrEmpty(VaryByCustom))
{
string varyByCustomResult = filterContext.HttpContext.ApplicationInstance.GetVaryByCustomString(HttpContext.Current, VaryByCustom);
@ -153,7 +164,7 @@ namespace System.Web.Mvc
}
// Unique ID from the VaryByParam settings, if any
uniqueIdBuilder.Append(GetUniqueIdFromActionParameters(filterContext, VaryByParam));
BuildUniqueIdFromActionParameters(uniqueIdBuilder, filterContext);
// The key is typically too long to be useful, so we use a cryptographic hash
// as the actual key (better randomization and key distribution, so small vary
@ -164,77 +175,50 @@ namespace System.Web.Mvc
}
}
private static string GetUniqueIdFromActionParameters(ActionExecutingContext filterContext, string varyByParam)
internal void BuildUniqueIdFromActionParameters(
StringBuilder builder,
ActionExecutingContext filterContext)
{
if (string.Equals(varyByParam, "none", StringComparison.OrdinalIgnoreCase))
if (String.Equals(VaryByParam, "none", StringComparison.OrdinalIgnoreCase))
{
return "";
// nothing to do
return;
}
var args = filterContext.ActionParameters;
if (args.Count == 0) return ""; // nothing to do
// Generate a unique ID of normalized key names + key values
var result = new StringBuilder();
if (string.Equals(varyByParam, "*", StringComparison.OrdinalIgnoreCase))
{ // use all available key/value pairs (without caring about order, so sort the keys)
string[] keys = new string[args.Count];
args.Keys.CopyTo(keys, 0);
Array.Sort(keys, StringComparer.OrdinalIgnoreCase);
for(int i = 0; i < keys.Length; i++)
{
var key = keys[i];
DescriptorUtil.AppendUniqueId(result, key.ToUpperInvariant());
DescriptorUtil.AppendUniqueId(result, args[key]);
}
}
else
{ // use only the key/value pairs specified in the varyByParam string; lazily create a sorted
// dictionary to represent the selected keys (normalizes the order at the same time)
SortedList<string, object> keyValues = null;
foreach (var pair in args)
{
if (ContainsToken(varyByParam, pair.Key))
{
if(keyValues == null) keyValues = new SortedList<string, object>(args.Count, StringComparer.OrdinalIgnoreCase);
keyValues[pair.Key] = pair.Value;
}
}
if (keyValues != null) // something to do
{
foreach (var pair in keyValues)
{
DescriptorUtil.AppendUniqueId(result, pair.Key.ToUpperInvariant());
DescriptorUtil.AppendUniqueId(result, pair.Value);
}
}
}
return result.ToString();
}
// check a delimited string i.e. "a;bcd; e" contains the given part, without actually splitting it -
// by searching for the entire token, then checking whether the before/after is EOF, delimiter or white-space
public static bool ContainsToken(string value, string token)
{
if (string.IsNullOrEmpty(token)) return false;
if (string.IsNullOrEmpty(value)) return false;
const char delimiter = ';'; // could be parameterized easily enough
int lastIndex = -1, idx, endIndex = value.Length - token.Length, tokenLength = token.Length;
while ((idx = value.IndexOf(token, lastIndex + 1, StringComparison.OrdinalIgnoreCase)) > lastIndex)
if (String.Equals(VaryByParam, "*", StringComparison.Ordinal))
{
lastIndex = idx;
char c;
if (
(idx == 0 || ((c = value[idx - 1]) == delimiter) || char.IsWhiteSpace(c))
&&
(idx == endIndex || ((c = value[idx + tokenLength]) == delimiter) || char.IsWhiteSpace(c))
)
// use all available key/value pairs. Keys need to be sorted so we end up with a stable identifier.
IEnumerable<KeyValuePair<string, object>> orderedParameters = filterContext
.ActionParameters
.OrderBy(k => k.Key, StringComparer.OrdinalIgnoreCase);
foreach (KeyValuePair<string, object> item in orderedParameters)
{
return true;
DescriptorUtil.AppendUniqueId(builder, item.Key.ToUpperInvariant());
DescriptorUtil.AppendUniqueId(builder, item.Value);
}
return;
}
LazyInitializer.EnsureInitialized(ref _tokenizedVaryByParams,
_splitVaryByParamThunk);
// By default, filterContext.ActionParameter is a case insensitive (StringComparer.OrdinalIgnoreCase)
// dictionary. If the user has replaced it with a dictionary that uses a different comparer, we'll
// wrap it in a Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
// This allows us to lookup values for action parameters when the VaryByParam token key differs in case.
Dictionary<string, object> keyValues = GetCaseInsensitiveActionParametersDictionary(
filterContext.ActionParameters);
for (int i = 0; i < _tokenizedVaryByParams.Length; i++)
{
string key = _tokenizedVaryByParams[i];
DescriptorUtil.AppendUniqueId(builder, key);
object value;
keyValues.TryGetValue(key, out value);
DescriptorUtil.AppendUniqueId(builder, value);
}
return false;
}
public static bool IsChildActionCacheActive(ControllerContext controllerContext)
@ -394,11 +378,37 @@ namespace System.Web.Mvc
case OutputCacheLocation.Downstream:
return true;
default:
default:
return false;
}
}
private static string[] GetTokenizedVaryByParam(string varyByParam)
{
// Vary by specific parameters
IEnumerable<string> splitTokens = from part in varyByParam.Split(_splitParameter)
let trimmed = part.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed.ToUpperInvariant();
return splitTokens.ToArray();
}
private static Dictionary<string, object> GetCaseInsensitiveActionParametersDictionary(
IDictionary<string, object> actionParameters)
{
// The ControllerActionInvoker starts off with a Dictionary<string, object> with
// StringComparer.OrdinalIgnoreCase. Check if we are working with this type to start with.
// If not produce a new dictionary from the existing one.
var dictionary = actionParameters as Dictionary<string, object>;
if (dictionary != null && dictionary.Comparer == StringComparer.OrdinalIgnoreCase)
{
return dictionary;
}
return new Dictionary<string, object>(actionParameters, StringComparer.OrdinalIgnoreCase);
}
[SuppressMessage("ASP.NET.Security", "CA5328:ValidateRequestShouldBeEnabled", Justification = "Instances of this type are not created in response to direct user input.")]
private sealed class OutputCachedPage : Page
{

Просмотреть файл

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Web.Mvc.Properties;
namespace System.Web.Mvc
@ -73,7 +74,9 @@ namespace System.Web.Mvc
private string CreateUniqueId()
{
return base.UniqueId + DescriptorUtil.CreateUniqueId(MethodInfo);
var builder = new StringBuilder(base.UniqueId);
DescriptorUtil.AppendUniqueId(builder, MethodInfo);
return builder.ToString();
}
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)

Просмотреть файл

@ -155,7 +155,6 @@
<Compile Include="ActionMethodSelectorBase.cs" />
<Compile Include="ActionNameSelector.cs" />
<Compile Include="AreaReference.cs" />
<Compile Include="Common\StringSplits.cs" />
<Compile Include="ControllerDescriptorExtensions.cs" />
<Compile Include="Async\IAsyncActionInvokerFactory.cs" />
<Compile Include="CopyOnWriteDictionary.cs" />

Просмотреть файл

@ -78,7 +78,7 @@ namespace System.Web.WebPages.Razor
}
// Get the segments removing any empty entries
IEnumerable<string> segments = virtualPath.Split(ForwardSlash, StringSplitOptions.RemoveEmptyEntries);
IEnumerable<string> segments = virtualPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (!segments.Any())
{
@ -87,7 +87,7 @@ namespace System.Web.WebPages.Razor
return WebDefaultNamespace + "." + String.Join(".", segments);
}
private static readonly char[] ForwardSlash = new[] {'/'};
private static string GetDirectory(string virtualPath)
{
int lastSlash = virtualPath.LastIndexOf('/');

Просмотреть файл

@ -20,10 +20,9 @@ namespace System.Web.WebPages
}
else
{
_urlData = pathInfo.Split(ForwardSlash).ToList();
_urlData = pathInfo.Split(new char[] { '/' }).ToList();
}
}
private static readonly char[] ForwardSlash = new[] { '/' };
public int Count
{

Просмотреть файл

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Text;
using Microsoft.TestCommon;
namespace System.Web.Mvc.Test
@ -7,31 +8,33 @@ namespace System.Web.Mvc.Test
public class DescriptorUtilTest
{
[Fact]
public void CreateUniqueId_FromIUniquelyIdentifiable()
public void AppendUniqueId_FromIUniquelyIdentifiable()
{
// Arrange
CustomUniquelyIdentifiable custom = new CustomUniquelyIdentifiable("hello-world");
StringBuilder builder = new StringBuilder();
// Act
string retVal = DescriptorUtil.CreateUniqueId(custom);
DescriptorUtil.AppendUniqueId(builder, custom);
// Assert
Assert.Equal("[11]hello-world", retVal);
Assert.Equal("[11]hello-world", builder.ToString());
}
[Fact]
public void CreateUniqueId_FromMemberInfo()
public void AppendUniqueId_FromMemberInfo()
{
// Arrange
string moduleVersionId = typeof(DescriptorUtilTest).Module.ModuleVersionId.ToString();
string metadataToken = typeof(DescriptorUtilTest).MetadataToken.ToString();
string expected = String.Format("[{0}]{1}[{2}]{3}", moduleVersionId.Length, moduleVersionId, metadataToken.Length, metadataToken);
StringBuilder builder = new StringBuilder();
// Act
string retVal = DescriptorUtil.CreateUniqueId(typeof(DescriptorUtilTest));
DescriptorUtil.AppendUniqueId(builder, typeof(DescriptorUtilTest));
// Assert
Assert.Equal(expected, retVal);
Assert.Equal(expected, builder.ToString());
}
[Fact]

Просмотреть файл

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.TestUtil;
using System.Web.UI;
using Microsoft.TestCommon;
@ -286,7 +288,8 @@ namespace System.Web.Mvc.Test
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
Assert.Equal(@"VjXrM/nTu6zOLCi+teZcx7qDQRk/Q+G5ZirKHhH7MOA=", result1);
Assert.Equal(@"Wi7TLgf052Ao0ZJX890MgynId6jByOf+xZ1G+5RHJUU=", result2);
}
[Fact]
@ -304,7 +307,8 @@ namespace System.Web.Mvc.Test
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
Assert.Equal("tXBiB5qdEzfISoXUUqXAzPcd7y/9Ik6eFqy8w9i3aDw=", result1);
Assert.Equal("Bb+E/t8aWfczVF2UFVr+FUpuGYjR/SPhzt71q+oAlHk=", result2);
}
[Fact]
@ -323,16 +327,17 @@ namespace System.Web.Mvc.Test
// Assert
Assert.Equal(result1, result2);
Assert.Equal("IA6+zemlkqp8Ye59mwMEMYGo69mUVkAcFLa5z0keL50=", result1);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "bar" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "bar" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "2";
// Act
@ -341,16 +346,17 @@ namespace System.Web.Mvc.Test
// Assert
Assert.Equal(result1, result2);
Assert.Equal("sx9+LHPOjtSGct8z6Cn4ml+2yKODPojZtrLhrhJofKM=", result1);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_SingleSpecified_Different()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "foo" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "2";
// Act
@ -358,17 +364,18 @@ namespace System.Web.Mvc.Test
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
Assert.Equal("tXBiB5qdEzfISoXUUqXAzPcd7y/9Ik6eFqy8w9i3aDw=", result1);
Assert.Equal("Bb+E/t8aWfczVF2UFVr+FUpuGYjR/SPhzt71q+oAlHk=", result2);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_SingleSpecified_Same()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "foo" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
// Act
@ -376,18 +383,19 @@ namespace System.Web.Mvc.Test
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.Equal(result1, result2);
Assert.Equal("tXBiB5qdEzfISoXUUqXAzPcd7y/9Ik6eFqy8w9i3aDw=", result1);
Assert.Equal("tXBiB5qdEzfISoXUUqXAzPcd7y/9Ik6eFqy8w9i3aDw=", result2);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Different()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo;bar;blap" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "foo;bar;blap" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "3";
@ -399,20 +407,239 @@ namespace System.Web.Mvc.Test
Assert.NotEqual(result1, result2);
}
public static TheoryDataSet<ActionExecutingContext, string, string>
GetUniqueIdFromActionParameters_ReturnsValuesThatExistInFilterContextData
{
get
{
TheoryDataSet<ActionExecutingContext, string, string> theoryData =
new TheoryDataSet<ActionExecutingContext, string, string>();
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "foo-value";
context1.ActionParameters["bar"] = "bar-value";
context1.ActionParameters["blap"] = "blap-value";
string expected1 = "[3]FOO[9]foo-value[3]BAR[9]bar-value[4]BLAP[10]blap-value";
theoryData.Add(context1.Object, "foo;bar;blap", expected1);
theoryData.Add(context1.Object, "foo; bar; blap;", expected1);
theoryData.Add(context1.Object, ";foo; bar; blap ", expected1);
theoryData.Add(context1.Object, "foo; BAR;Blap", expected1);
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["test"] = "test-value";
context2.ActionParameters["shouldnot-exist"] = "shouldnot-exist";
context2.ActionParameters["test2"] = "test2-value";
theoryData.Add(context2.Object, "test;test2", "[4]TEST[10]test-value[5]TEST2[11]test2-value");
theoryData.Add(context2.Object, "test;test3", "[4]TEST[10]test-value[5]TEST3[-1]");
MockActionExecutingContext context3 = new MockActionExecutingContext();
theoryData.Add(context3.Object, "test1;test2", "[5]TEST1[-1][5]TEST2[-1]");
return theoryData;
}
}
[Theory]
[PropertyData("GetUniqueIdFromActionParameters_ReturnsValuesThatExistInFilterContextData")]
public static void BuildUniqueIdFromActionParameters_UsesValuesForActionParametersThatExistInVaryByParams(
ActionExecutingContext context, string varyByParam, string expected)
{
// Arrange
StringBuilder builder = new StringBuilder();
OutputCacheAttribute attribute = new OutputCacheAttribute { VaryByParam = varyByParam };
// Act
attribute.BuildUniqueIdFromActionParameters(builder, context);
// Assert
Assert.Equal(expected, builder.ToString());
}
[Theory]
[InlineData("Foo;bar")]
[InlineData("FOO;bAr")]
[InlineData("foo;bar")]
public static void BuildUniqueIdFromActionParameters_LooksUpActionParametersInCaseInsensitiveManner(
string varyByParam)
{
// Arrange
MockActionExecutingContext context = new MockActionExecutingContext();
Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal)
{
{ "foo", "foo-value" },
{ "Bar", "bar-value" }
};
context.ActionParameters = dictionary;
string expected = "[3]FOO[9]foo-value[3]BAR[9]bar-value";
StringBuilder builder = new StringBuilder();
OutputCacheAttribute attribute = new OutputCacheAttribute { VaryByParam = varyByParam };
// Act
attribute.BuildUniqueIdFromActionParameters(builder, context.Object);
// Assert
Assert.Equal(expected, builder.ToString());
}
[Theory]
[InlineData("none")]
[InlineData("NONE")]
[InlineData("None")]
public static void BuildUniqueIdFromActionParameters_NoOpsIfVaryByParamIsNone(string varyByParam)
{
// Arrange
StringBuilder builder = new StringBuilder();
ActionExecutingContext context = new MockActionExecutingContext().Object;
OutputCacheAttribute attribute = new OutputCacheAttribute { VaryByParam = varyByParam };
// Act
attribute.BuildUniqueIdFromActionParameters(builder, context);
// Assert
Assert.Empty(builder.ToString());
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Same()
public static void BuildUniqueIdFromActionParameters_UsesAllActionParametersIfStar()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo;bar;blap" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attribute = new OutputCacheAttribute { VaryByParam = "*" };
StringBuilder builder1 = new StringBuilder();
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["test1"] = "test1-value";
context1.ActionParameters["test2"] = 3;
context1.ActionParameters["test3"] = null;
string expected = "[5]TEST1[11]test1-value[5]TEST2[1]3[5]TEST3[-1]";
// Act - 1
attribute.BuildUniqueIdFromActionParameters(builder1, context1.Object);
// Assert - 1
Assert.Equal(expected, builder1.ToString());
// Arrange - 2
// Verify that the keys are stable sorted when "*" is used.
StringBuilder builder2 = new StringBuilder();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["test3"] = null;
context2.ActionParameters["test2"] = 3;
context2.ActionParameters["test1"] = "test1-value";
// Act - 2
attribute.BuildUniqueIdFromActionParameters(builder2, context2.Object);
// Assert - 2
Assert.Equal(expected, builder2.ToString());
}
[Fact]
public static void BuildUniqueIdFromActionParameters_DoesNotCacheTokenizedStringsWhenStar()
{
// Arrange
OutputCacheAttribute attribute = new OutputCacheAttribute { VaryByParam = "*" };
StringBuilder builder1 = new StringBuilder();
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["test1"] = "test1-value";
context1.ActionParameters["test2"] = "Test2-Value";
context1.ActionParameters["test3"] = null;
string expected1 = "[5]TEST1[11]test1-value[5]TEST2[11]Test2-Value[5]TEST3[-1]";
// Act - 1
attribute.BuildUniqueIdFromActionParameters(builder1, context1.Object);
// Assert - 1
Assert.Equal(expected1, builder1.ToString());
// Arrange - 2
// Verify that the keys are stable sorted when "*" is used.
StringBuilder builder2 = new StringBuilder();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["Foo"] = null;
context2.ActionParameters["bar"] = "bar-value";
string expected2 = "[3]BAR[9]bar-value[3]FOO[-1]";
// Act - 2
attribute.BuildUniqueIdFromActionParameters(builder2, context2.Object);
// Assert - 2
Assert.Equal(expected2, builder2.ToString());
}
public static IEnumerable<string> VaryByParamWithWhitespaceData = new[]
{
"foo;bar;blap",
"foo; bar; blap",
";foo; bar; blap",
" foo; bar; blap",
" foo;bar; blap; ",
" ;foo; bar ; blap; ",
};
public static IEnumerable<object[]> GetChildActionUniqueId_VariesByActionParametersData
{
get
{
return VaryByParamWithWhitespaceData.Select(v => new[] { v });
}
}
[Theory]
[PropertyData("GetChildActionUniqueId_VariesByActionParametersData")]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByGivenParameters(string varbyParam)
{
// Arrange
string expected = "z2Fr6HAipKCkLdVkHMdHgeDBJyYutdqqTW07BMO31fQ=";
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = varbyParam };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context1.ActionParameters["xyz"] = Guid.NewGuid().ToString();
context1.ActionParameters["some-key"] = Guid.NewGuid().ToString();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = Guid.NewGuid().ToString();
context2.ActionParameters["different-key"] = Guid.NewGuid().ToString();
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.Equal(expected, result1);
Assert.Equal(expected, result2);
}
public static IEnumerable<object[]> GetChildActionUniqueId_VariesByActionParameters_WithDifferentValuesData
{
get
{
return VaryByParamWithWhitespaceData.Select(v => new[] { v, "20", "34" });
}
}
[Theory]
[PropertyData("GetChildActionUniqueId_VariesByActionParameters_WithDifferentValuesData")]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_WithDifferentValues(
string varyByParam, string value1, string value2)
{
// Arrange
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = varyByParam };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = value1;
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = value2;
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
@ -420,43 +647,25 @@ namespace System.Web.Mvc.Test
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.Equal(result1, result2);
Assert.Equal("WBXQR5lxerJG33HR3XQ9sJsXuKu6eVPgmMqeWMu8t2s=", result1);
Assert.Equal("CLMC7fAU4959ECaI79HRFglt7SnNRANMJeYderjF4m8=", result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace1_Same()
[Theory]
[PropertyData("GetChildActionUniqueId_VariesByActionParameters_WithDifferentValuesData")]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace_DifferentSecond(
string varyByParam, string value1, string value2)
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo; bar; blap" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = varyByParam };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["bar"] = value1;
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.Equal(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace1_DifferentFirst()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo; bar; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "4";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["bar"] = value2;
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
@ -467,41 +676,23 @@ namespace System.Web.Mvc.Test
// Assert
Assert.NotEqual(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace1_DifferentSecond()
[Theory]
[PropertyData("GetChildActionUniqueId_VariesByActionParameters_WithDifferentValuesData")]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace_DifferentThird(
string varyByParam, string value1, string value2)
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo; bar; blap" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = varyByParam };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["blap"] = value1;
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "4";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace1_DifferentThird()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo; bar; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "4";
context2.ActionParameters["blap"] = value2;
context2.ActionParameters["xyz"] = "abc";
// Act
@ -513,40 +704,16 @@ namespace System.Web.Mvc.Test
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace2_Same()
public void GetChildActionUniqueId_VariesByActionParameters_MatchesActionParametersInCaseInsensitiveManner()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.Equal(result1, result2);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace2_CaseOnly_Same()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["BAR"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["BLAP"] = "3";
@ -559,86 +726,18 @@ namespace System.Web.Mvc.Test
// Assert
Assert.Equal(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace2_DifferentFirst()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "4";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace2_DifferentSecond()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "4";
context2.ActionParameters["blap"] = "3";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
}
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_MultipleSpecified_Whitespace2_DifferentThird()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "foo ; bar ; blap" };
var context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "4";
context2.ActionParameters["xyz"] = "abc";
// Act
string result1 = attr.GetChildActionUniqueId(context1.Object);
string result2 = attr.GetChildActionUniqueId(context2.Object);
// Assert
Assert.NotEqual(result1, result2);
}
[Fact]
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_Wildcard_Same()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "*" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "*" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
@ -656,13 +755,13 @@ namespace System.Web.Mvc.Test
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_Wildcard_CaseOnly_Same()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "*" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "*" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["BLAP"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["FOO"] = "1";
context2.ActionParameters["bar"] = "2";
context2.ActionParameters["blap"] = "3";
@ -680,13 +779,13 @@ namespace System.Web.Mvc.Test
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_Wildcard_Different()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "*" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "*" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
context1.ActionParameters["bar"] = "2";
context1.ActionParameters["blap"] = "3";
context1.ActionParameters["xyz"] = "abc";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "1";
context2.ActionParameters["bar"] = "4";
context2.ActionParameters["blap"] = "3";
@ -704,10 +803,10 @@ namespace System.Web.Mvc.Test
public void GetChildActionUniqueId_VariesByActionParameters_OnlyVariesByTheGivenParameters_None()
{
// Arrange
var attr = new OutputCacheAttribute { VaryByParam = "none" };
var context1 = new MockActionExecutingContext();
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "none" };
MockActionExecutingContext context1 = new MockActionExecutingContext();
context1.ActionParameters["foo"] = "1";
var context2 = new MockActionExecutingContext();
MockActionExecutingContext context2 = new MockActionExecutingContext();
context2.ActionParameters["foo"] = "2";
// Act
@ -718,13 +817,37 @@ namespace System.Web.Mvc.Test
Assert.Equal(result1, result2);
}
[Theory]
[InlineData("bar", "VmmIPvA0bdX40A9EDzcQdDGenn9mW2fLitLhN3Q+q0o=")]
[InlineData("*", "lRpCzLPVjS7Lwbix/vbtbdUWELWQOxiJaVemFCgM0ew=")]
[InlineData("none", "IA6+zemlkqp8Ye59mwMEMYGo69mUVkAcFLa5z0keL50=")]
public void GetChildActionUniqueId_ReturnsDifferentValuesIfVaryByParamValueIsModified(string varyByParam, string expected)
{
// Arrange
MockActionExecutingContext context = new MockActionExecutingContext();
context.ActionParameters["foo"] = "1";
context.ActionParameters["bar"] = "2";
OutputCacheAttribute attr = new OutputCacheAttribute { VaryByParam = "foo" };
// Act - 1
string result1 = attr.GetChildActionUniqueId(context.Object);
// Assert - 1
Assert.Equal("tXBiB5qdEzfISoXUUqXAzPcd7y/9Ik6eFqy8w9i3aDw=", result1);
// Act - 2
attr.VaryByParam = varyByParam;
string result2 = attr.GetChildActionUniqueId(context.Object);
// Assert - 2
Assert.Equal(expected, result2);
}
class MockActionExecutingContext : Mock<ActionExecutingContext>
{
public Dictionary<string, object> ActionParameters = new Dictionary<string, object>();
// StringComparer.OrdinalIgnoreCase matches the behavior of ControllerActionInvoker.
public Dictionary<string, object> ActionParameters = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
public MockActionExecutingContext()
{