304 lines
14 KiB
C#
304 lines
14 KiB
C#
|
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|||
|
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Linq.Expressions;
|
|||
|
using System.Reflection;
|
|||
|
using System.Text;
|
|||
|
using Xunit;
|
|||
|
|
|||
|
namespace System.Web.Mvc.ExpressionUtil.Test
|
|||
|
{
|
|||
|
public class FingerprintingExpressionVisitorTest
|
|||
|
{
|
|||
|
private const ExpressionFingerprint _nullFingerprint = null;
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void TypeOverridesAllMethods()
|
|||
|
{
|
|||
|
// Ensures that the FingerprintingExpressionVisitor type overrides all VisitXxx methods so that
|
|||
|
// it can properly set the "I gave up" flag when it encounters an Expression it's not familiar
|
|||
|
// with.
|
|||
|
|
|||
|
var methodsOnExpressionVisitorRequiringOverride = typeof(ExpressionVisitor).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(mi => mi.IsVirtual).Select(mi => mi.GetBaseDefinition()).Where(mi => mi.DeclaringType == typeof(ExpressionVisitor));
|
|||
|
var methodsOnFingerprintingExpressionVisitor = typeof(FingerprintingExpressionVisitor).GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(mi => mi.DeclaringType == typeof(FingerprintingExpressionVisitor));
|
|||
|
|
|||
|
var missingMethods = methodsOnExpressionVisitorRequiringOverride.Except(methodsOnFingerprintingExpressionVisitor.Select(mi => mi.GetBaseDefinition())).ToArray();
|
|||
|
if (missingMethods.Length != 0)
|
|||
|
{
|
|||
|
StringBuilder sb = new StringBuilder("The following methods are declared on ExpressionVisitor and must be overridden on FingerprintingExpressionVisitor:");
|
|||
|
foreach (MethodInfo method in missingMethods)
|
|||
|
{
|
|||
|
sb.AppendLine();
|
|||
|
sb.Append(method);
|
|||
|
}
|
|||
|
Assert.True(false, sb.ToString());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Visit_Null()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ NULL ]
|
|||
|
Expression expr = null;
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint, _nullFingerprint);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void Visit_Unknown()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// if we fingerprinted ctors, would fingerprint as [ NEW(StringBuilder(int)):StringBuilder, PARAM(0):int ]
|
|||
|
// but since we don't fingerprint ctors, should just return null (signaling failure)
|
|||
|
Expression expr = (Expression<Func<int, StringBuilder>>)(capacity => new StringBuilder(capacity));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Null(fingerprint); // Can't fingerprint ctor
|
|||
|
Assert.Null(capturedConstants); // Can't fingerprint ctor
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitBinary()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ OP_GREATERTHAN:bool, CONST:int, CONST:int ]
|
|||
|
Expression expr = Expression.MakeBinary(ExpressionType.GreaterThan, Expression.Constant(42), Expression.Constant(84));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { 42, 84 }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new BinaryExpressionFingerprint(ExpressionType.GreaterThan, typeof(bool), null /* method */),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitConditional()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ CONDITIONAL:int, CONST:bool, CONST:int, CONST:int ]
|
|||
|
Expression expr = Expression.Condition(Expression.Constant(true), Expression.Constant(42), Expression.Constant(84));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { true, 42, 84 }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new ConditionalExpressionFingerprint(ExpressionType.Conditional, typeof(int)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(bool)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitConstant()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ CONST:int ]
|
|||
|
Expression expr = Expression.Constant(42);
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { 42 }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitDefault()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ DEFAULT:int ]
|
|||
|
Expression expr = Expression.Default(typeof(int));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint, new DefaultExpressionFingerprint(ExpressionType.Default, typeof(int)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitIndex()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ INDEX:object, PARAM(0):object[], CONST:int ]
|
|||
|
Expression expr = Expression.MakeIndex(Expression.Parameter(typeof(object[])), null /* indexer */, new Expression[] { Expression.Constant(42) });
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { 42 }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new IndexExpressionFingerprint(ExpressionType.Index, typeof(object), null /* indexer */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(object[]), 0 /* parameterIndex */),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitLambda()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ LAMBDA:Func<string, int>, CONST:int, PARAM(0):string ]
|
|||
|
Expression expr = (Expression<Func<string, int>>)(x => 42);
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { 42 }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new LambdaExpressionFingerprint(ExpressionType.Lambda, typeof(Func<string, int>)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(int)),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(string), 0 /* parameterIndex */));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitMember()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ MEMBER(String.Empty):string, NULL ]
|
|||
|
Expression expr = Expression.Field(null, typeof(string), "Empty");
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new MemberExpressionFingerprint(ExpressionType.MemberAccess, typeof(string), typeof(string).GetField("Empty")),
|
|||
|
_nullFingerprint);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitMethodCall()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ CALL(GC.KeepAlive):void, NULL, PARAM(0):object ]
|
|||
|
Expression expr = Expression.Call(typeof(GC).GetMethod("KeepAlive"), Expression.Parameter(typeof(object)));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new MethodCallExpressionFingerprint(ExpressionType.Call, typeof(void), typeof(GC).GetMethod("KeepAlive")),
|
|||
|
_nullFingerprint,
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(object), 0 /* parameterIndex */));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitParameter()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ LAMBDA:Func<int, int, int>, OP_ADD:int, OP_ADD:int, OP_ADD:int, PARAM(0):int, PARAM(0):int, PARAM(1):int, PARAM(0):int, PARAM(1):int, PARAM(0):int ]
|
|||
|
// (note that the parameters are out of order since 'y' is used first, but this is ok due
|
|||
|
// to preservation of alpha equivalence within the VisitParameter method.)
|
|||
|
Expression expr = (Expression<Func<int, int, int>>)((x, y) => y + y + x + y);
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new LambdaExpressionFingerprint(ExpressionType.Lambda, typeof(Func<int, int, int>)),
|
|||
|
new BinaryExpressionFingerprint(ExpressionType.Add, typeof(int), null /* method */),
|
|||
|
new BinaryExpressionFingerprint(ExpressionType.Add, typeof(int), null /* method */),
|
|||
|
new BinaryExpressionFingerprint(ExpressionType.Add, typeof(int), null /* method */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 0 /* parameterIndex */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 0 /* parameterIndex */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 1 /* parameterIndex */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 0 /* parameterIndex */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 1 /* parameterIndex */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 0 /* parameterIndex */));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitTypeBinary()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ TYPEIS(DateTime):bool, CONST:string ]
|
|||
|
Expression expr = Expression.TypeIs(Expression.Constant("hello"), typeof(DateTime));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Equal(new object[] { "hello" }, capturedConstants.ToArray());
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new TypeBinaryExpressionFingerprint(ExpressionType.TypeIs, typeof(bool), typeof(DateTime)),
|
|||
|
new ConstantExpressionFingerprint(ExpressionType.Constant, typeof(string)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void VisitUnary()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
|
|||
|
// fingerprints as [ OP_NOT:int, PARAM:int ]
|
|||
|
Expression expr = Expression.Not(Expression.Parameter(typeof(int)));
|
|||
|
|
|||
|
// Act
|
|||
|
List<object> capturedConstants;
|
|||
|
ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants);
|
|||
|
|
|||
|
// Assert
|
|||
|
Assert.Empty(capturedConstants);
|
|||
|
AssertChainEquals(fingerprint,
|
|||
|
new UnaryExpressionFingerprint(ExpressionType.Not, typeof(int), null /* method */),
|
|||
|
new ParameterExpressionFingerprint(ExpressionType.Parameter, typeof(int), 0 /* parameterIndex */));
|
|||
|
}
|
|||
|
|
|||
|
internal static void AssertChainEquals(ExpressionFingerprintChain fingerprintChain, params ExpressionFingerprint[] expectedElements)
|
|||
|
{
|
|||
|
ExpressionFingerprintChain newChain = new ExpressionFingerprintChain();
|
|||
|
newChain.Elements.AddRange(expectedElements);
|
|||
|
Assert.Equal(fingerprintChain, newChain);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|