304 lines
14 KiB
C#
Raw Normal View History

// 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);
}
}
}