1013 lines
30 KiB
C#
1013 lines
30 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Linq.Expressions;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|
{
|
|
public class CachedExpressionCompilerTest
|
|
{
|
|
[Fact]
|
|
public void Process_IdentityExpression()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => m);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Same(model, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesIdentityExpression()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => m);
|
|
var expression2 = GetTestModelExpression(m => m);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var differentModel = new DifferentModel();
|
|
var expression = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Same(differentModel, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup_ReturningNull()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => (DifferentModel)null);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var differentModel = new DifferentModel();
|
|
var expression = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(null);
|
|
Assert.Same(differentModel, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup_UsingCachedValue()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var differentModel = new DifferentModel();
|
|
var expression1 = GetTestModelExpression(m => differentModel);
|
|
var expression2 = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act - 1
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
|
|
// Assert - 1
|
|
var result1 = func1(null);
|
|
Assert.Same(differentModel, result1);
|
|
|
|
// Act - 2
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert - 2
|
|
var result2 = func1(null);
|
|
Assert.Same(differentModel, result2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup_WhenCapturedLocalChanges()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var differentModel = new DifferentModel();
|
|
var expression = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert - 1
|
|
var result1 = func(null);
|
|
Assert.Same(differentModel, result1);
|
|
|
|
// Act - 2
|
|
differentModel = new DifferentModel();
|
|
|
|
// Assert - 2
|
|
var result2 = func(null);
|
|
Assert.NotSame(differentModel, result1);
|
|
Assert.Same(differentModel, result2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstLookup_WithPrimitiveConstant()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => 10);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(10, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_StaticFieldAccess()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => TestModel.StaticField);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal("StaticValue", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesStaticFieldAccess()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => TestModel.StaticField);
|
|
var expression2 = GetTestModelExpression(m => TestModel.StaticField);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_StaticPropertyAccess()
|
|
{
|
|
// Arrange
|
|
var expected = "TestValue";
|
|
TestModel.StaticProperty = expected;
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => TestModel.StaticProperty);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesStaticPropertyAccess()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => TestModel.StaticProperty);
|
|
var expression2 = GetTestModelExpression(m => TestModel.StaticProperty);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_StaticPropertyAccess_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var expected = "TestValue";
|
|
TestModel.StaticProperty = expected;
|
|
var expression = GetTestModelExpression(m => TestModel.StaticProperty);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(null);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstFieldLookup()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => DifferentModel.Constant);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(10, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ConstFieldLookup_WthNullModel()
|
|
{
|
|
// Arrange
|
|
var expression = GetTestModelExpression(m => DifferentModel.Constant);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(null);
|
|
Assert.Equal(10, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel { Name = "Test" };
|
|
var expression = GetTestModelExpression(m => m.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal("Test", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesSimpleMemberAccess()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => m.Name);
|
|
var expression2 = GetTestModelExpression(m => m.Name);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_ToPrimitive()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel { Age = 12 };
|
|
var expression = GetTestModelExpression(m => m.Age);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(12, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var model = (TestModel)null;
|
|
var expression = GetTestModelExpression(m => m.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_ToPrimitive_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var model = (TestModel)null;
|
|
var expression = GetTestModelExpression(m => m.Age);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnTypeWithBadEqualityComparer()
|
|
{
|
|
// Arrange
|
|
var model = new BadEqualityModel { Id = 7 };
|
|
var expression = GetExpression<BadEqualityModel, int>(m => m.Id);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(7, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnTypeWithBadEqualityComparer_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var model = (BadEqualityModel)null;
|
|
var expression = GetExpression<BadEqualityModel, int>(m => m.Id);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnValueTypeWithBadEqualityComparer()
|
|
{
|
|
// Arrange
|
|
var model = new BadEqualityValueTypeModel { Id = 7 };
|
|
var expression = GetExpression<BadEqualityValueTypeModel, int>(m => m.Id);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(7, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnTypeWithBadEqualityComparer_WithDefaultValue()
|
|
{
|
|
// Arrange
|
|
var model = (BadEqualityValueTypeModel)default;
|
|
var expression = GetExpression<BadEqualityValueTypeModel, int>(m => m.Id);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(model.Id, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnValueType()
|
|
{
|
|
// Arrange
|
|
var model = new DateTime(2000, 1, 1);
|
|
var expression = GetExpression<DateTime, int>(m => m.Year);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(2000, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnValueType_WithDefaultValue()
|
|
{
|
|
// Arrange
|
|
var model = default(DateTime);
|
|
var expression = GetExpression<DateTime, int>(m => m.Year);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(1, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnNullableValueType()
|
|
{
|
|
// Arrange
|
|
var model = new DateTime(2000, 1, 1);
|
|
var nullableModel = (DateTime?)model;
|
|
var expression = GetExpression<DateTime?, DateTime>(m => m.Value);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(nullableModel);
|
|
Assert.Equal(model, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_SimpleMemberAccess_OnNullableValueType_WithNullValue()
|
|
{
|
|
// Arrange
|
|
var nullableModel = (DateTime?)null;
|
|
var expression = GetExpression<DateTime?, DateTime>(m => m.Value);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(nullableModel);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_ToValueType()
|
|
{
|
|
// Arrange
|
|
var dateTime = new DateTime(2000, 1, 1);
|
|
var model = new TestModel { Date = dateTime };
|
|
var expression = GetTestModelExpression(m => m.Date.Year);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(2000, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_ToValueType_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var model = (TestModel)null;
|
|
var expression = GetTestModelExpression(m => m.Date.Year);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_ToReferenceType()
|
|
{
|
|
// Arrange
|
|
var expected = "Test1";
|
|
var model = new TestModel { DifferentModel = new DifferentModel { Name = expected } };
|
|
var expression = GetTestModelExpression(m => m.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesChainedMemberAccess()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => m.DifferentModel.Name);
|
|
var expression2 = GetTestModelExpression(m => m.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CachesChainedMemberAccess_ToValueType()
|
|
{
|
|
// Arrange
|
|
var expression1 = GetTestModelExpression(m => m.Date.Year);
|
|
var expression2 = GetTestModelExpression(m => m.Date.Year);
|
|
|
|
// Act
|
|
var func1 = CachedExpressionCompiler.Process(expression1);
|
|
var func2 = CachedExpressionCompiler.Process(expression2);
|
|
|
|
// Assert
|
|
Assert.NotNull(func1);
|
|
Assert.Same(func1, func2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_LongChain_WithReferenceType()
|
|
{
|
|
// Arrange
|
|
var expected = "TestVal";
|
|
var model = new Chain0Model
|
|
{
|
|
Chain1 = new Chain1Model
|
|
{
|
|
ValueTypeModel = new ValueType1
|
|
{
|
|
TestModel = new TestModel { DifferentModel = new DifferentModel { Name = expected } }
|
|
}
|
|
}
|
|
};
|
|
|
|
var expression = GetExpression<Chain0Model, string>(m => m.Chain1.ValueTypeModel.TestModel.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_LongChain_WithNullIntermediary()
|
|
{
|
|
// Arrange
|
|
var model = new Chain0Model
|
|
{
|
|
Chain1 = new Chain1Model
|
|
{
|
|
ValueTypeModel = new ValueType1 { TestModel = null },
|
|
}
|
|
};
|
|
|
|
var expression = GetExpression<Chain0Model, string>(m => m.Chain1.ValueTypeModel.TestModel.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_LongChain_WithNullValueTypeAccessor()
|
|
{
|
|
// Arrange
|
|
// Chain2 is a value type
|
|
var model = new Chain0Model
|
|
{
|
|
Chain1 = null
|
|
};
|
|
|
|
var expression = GetExpression<Chain0Model, string>(m => m.Chain1.ValueTypeModel.TestModel.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_LongChain_WithNullableValueType()
|
|
{
|
|
// Arrange
|
|
var expected = "TestVal";
|
|
var model = new Chain0Model
|
|
{
|
|
Chain1 = new Chain1Model
|
|
{
|
|
NullableValueTypeModel = new ValueType1
|
|
{
|
|
TestModel = new TestModel { DifferentModel = new DifferentModel { Name = expected } }
|
|
}
|
|
}
|
|
};
|
|
|
|
var expression = GetExpression<Chain0Model, string>(m => m.Chain1.NullableValueTypeModel.Value.TestModel.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_LongChain_WithNullValuedNullableValueType()
|
|
{
|
|
// Arrange
|
|
var model = new Chain0Model
|
|
{
|
|
Chain1 = new Chain1Model
|
|
{
|
|
NullableValueTypeModel = null
|
|
}
|
|
};
|
|
|
|
var expression = GetExpression<Chain0Model, string>(m => m.Chain1.NullableValueTypeModel.Value.TestModel.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_ToReferenceType_WithNullIntermediary()
|
|
{
|
|
// Arrange
|
|
var model = new TestModel { DifferentModel = null };
|
|
var expression = GetTestModelExpression(m => m.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_ToReferenceType_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var model = (TestModel)null;
|
|
var expression = GetTestModelExpression(m => m.DifferentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_OfValueTypes_ReturningReferenceTypeMember()
|
|
{
|
|
// Arrange
|
|
var expected = "TestName";
|
|
var model = new ValueType1
|
|
{
|
|
ValueType2 = new ValueType2 { Name = expected },
|
|
};
|
|
var expression = GetExpression<ValueType1, string>(m => m.ValueType2.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_OfValueTypes_ReturningValueType()
|
|
{
|
|
// Arrange
|
|
var expected = new DateTime(2001, 1, 1);
|
|
var model = new ValueType1
|
|
{
|
|
ValueType2 = new ValueType2 { Date = expected },
|
|
};
|
|
var expression = GetExpression<ValueType1, DateTime>(m => m.ValueType2.Date);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_OfValueTypes_IncludingNullableType()
|
|
{
|
|
// Arrange
|
|
var expected = "TestName";
|
|
var model = new ValueType1
|
|
{
|
|
NullableValueType2 = new ValueType2 { Name = expected },
|
|
};
|
|
var expression = GetExpression<ValueType1, string>(m => m.NullableValueType2.Value.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_OfValueTypes_WithNullValuedNullable()
|
|
{
|
|
// Arrange
|
|
var model = new ValueType1 { NullableValueType2 = null };
|
|
var expression = GetExpression<ValueType1, string>(m => m.NullableValueType2.Value.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ChainedMemberAccess_OfValueTypes_WithNullValuedNullable_ReturningValueType()
|
|
{
|
|
// Arrange
|
|
var model = new ValueType1 { NullableValueType2 = null };
|
|
var expression = GetExpression<ValueType1, DateTime>(m => m.NullableValueType2.Value.Date);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_MemberAccessOnCapturedVariable_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var differentModel = new DifferentModel { Name = "Test" };
|
|
var expression = GetTestModelExpression(m => differentModel.Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.Null(func);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CapturedVariable()
|
|
{
|
|
// Arrange
|
|
var differentModel = new DifferentModel();
|
|
var model = new TestModel();
|
|
var expression = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Same(differentModel, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_CapturedVariable_WithNullModel()
|
|
{
|
|
// Arrange
|
|
var differentModel = new DifferentModel();
|
|
var model = (TestModel)null;
|
|
var expression = GetTestModelExpression(m => differentModel);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.NotNull(func);
|
|
var result = func(model);
|
|
Assert.Same(differentModel, result);
|
|
}
|
|
|
|
|
|
[Fact]
|
|
public void Process_MemberAccess_OnCapturedVariable_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var differentModel = "Hello world";
|
|
var expression = GetTestModelExpression(m => differentModel.Length);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.Null(func);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ComplexChainedMemberAccess_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var expected = "SomeName";
|
|
var model = new TestModel { DifferentModels = new[] { new DifferentModel { Name = expected } } };
|
|
var expression = GetTestModelExpression(m => m.DifferentModels[0].Name);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.Null(func);
|
|
}
|
|
|
|
[Fact]
|
|
public void Process_ArrayMemberAccess_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
var expression = GetTestModelExpression(m => m.Sizes[1]);
|
|
|
|
// Act
|
|
var func = CachedExpressionCompiler.Process(expression);
|
|
|
|
// Assert
|
|
Assert.Null(func);
|
|
}
|
|
|
|
private static Expression<Func<TModel, TResult>> GetExpression<TModel, TResult>(Expression<Func<TModel, TResult>> expression)
|
|
=> expression;
|
|
|
|
private static Expression<Func<TestModel, TResult>> GetTestModelExpression<TResult>(Expression<Func<TestModel, TResult>> expression)
|
|
=> GetExpression(expression);
|
|
|
|
public class TestModel
|
|
{
|
|
public static readonly string StaticField = "StaticValue";
|
|
|
|
public static string StaticProperty { get; set; }
|
|
|
|
public int Age { get; set; }
|
|
|
|
public string Name { get; set; }
|
|
|
|
public DateTime Date { get; set; }
|
|
|
|
public DifferentModel DifferentModel { get; set; }
|
|
|
|
public int[] Sizes { get; set; }
|
|
|
|
public DifferentModel[] DifferentModels { get; set; }
|
|
}
|
|
|
|
public class DifferentModel
|
|
{
|
|
public const int Constant = 10;
|
|
|
|
public string Name { get; set; }
|
|
}
|
|
|
|
public class Chain0Model
|
|
{
|
|
public Chain1Model Chain1 { get; set; }
|
|
}
|
|
|
|
public class Chain1Model
|
|
{
|
|
public ValueType1 ValueTypeModel { get; set; }
|
|
|
|
public ValueType1? NullableValueTypeModel { get; set; }
|
|
}
|
|
|
|
public struct ValueType1
|
|
{
|
|
public TestModel TestModel { get; set; }
|
|
|
|
public ValueType2 ValueType2 { get; set; }
|
|
|
|
public ValueType2? NullableValueType2 { get; set; }
|
|
}
|
|
|
|
public struct ValueType2
|
|
{
|
|
public string Name { get; set; }
|
|
|
|
public DateTime Date { get; set; }
|
|
}
|
|
|
|
public class BadEqualityModel
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return this == obj;
|
|
}
|
|
|
|
public static bool operator ==(BadEqualityModel a, object b)
|
|
{
|
|
if (a is null || b is null)
|
|
{
|
|
throw new TimeZoneNotFoundException();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool operator !=(BadEqualityModel a, object b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
public override int GetHashCode() => 0;
|
|
}
|
|
|
|
public struct BadEqualityValueTypeModel
|
|
{
|
|
public int Id { get; set; }
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return this == obj;
|
|
}
|
|
|
|
public static bool operator ==(BadEqualityValueTypeModel a, object b)
|
|
{
|
|
if (b is null)
|
|
{
|
|
throw new TimeZoneNotFoundException();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool operator !=(BadEqualityValueTypeModel a, object b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
public override int GetHashCode() => 0;
|
|
}
|
|
}
|
|
}
|