// 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 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>)(capacity => new StringBuilder(capacity)); // Act List 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 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 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 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 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 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, CONST:int, PARAM(0):string ] Expression expr = (Expression>)(x => 42); // Act List capturedConstants; ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants); // Assert Assert.Equal(new object[] { 42 }, capturedConstants.ToArray()); AssertChainEquals(fingerprint, new LambdaExpressionFingerprint(ExpressionType.Lambda, typeof(Func)), 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 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 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, 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>)((x, y) => y + y + x + y); // Act List capturedConstants; ExpressionFingerprintChain fingerprint = FingerprintingExpressionVisitor.GetFingerprintChain(expr, out capturedConstants); // Assert Assert.Empty(capturedConstants); AssertChainEquals(fingerprint, new LambdaExpressionFingerprint(ExpressionType.Lambda, typeof(Func)), 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 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 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); } } }