using NFluent; using System.Collections.Generic; using System.Linq.Dynamic.Core.CustomTypeProviders; using System.Linq.Dynamic.Core.Exceptions; using System.Linq.Expressions; using System.Reflection; using Xunit; using User = System.Linq.Dynamic.Core.Tests.Helpers.Models.User; namespace System.Linq.Dynamic.Core.Tests { public class DynamicExpressionParserTests { private class MyClass { public int Foo() { return 42; } } private class ComplexParseLambda1Result { public int? Age; public int TotalIncome; } [DynamicLinqType] public class ComplexParseLambda3Result { public int? Age { get; set; } public int TotalIncome { get; set; } } private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider { private HashSet _customTypes; public virtual HashSet GetCustomTypes() { if (_customTypes != null) return _customTypes; _customTypes = new HashSet( FindTypesMarkedWithDynamicLinqTypeAttribute(new[] {this.GetType().GetTypeInfo().Assembly})); return _customTypes; } } [Fact] public void ParseLambda_ToList() { // Arrange var testList = User.GenerateSampleModels(51); var qry = testList.AsQueryable(); // Act string query = "OrderBy(gg => gg.Income).ToList()"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); Delegate del = expression.Compile(); IEnumerable result = del.DynamicInvoke(qry) as IEnumerable; var expected = qry.OrderBy(gg => gg.Income).ToList(); // Assert Check.That(result).IsNotNull(); Check.That(result).HasSize(expected.Count); Check.That(result.ToArray()[0]).Equals(expected[0]); } [Fact] public void ParseLambda_Complex_1() { // Arrange var testList = User.GenerateSampleModels(51); var qry = testList.AsQueryable(); var externals = new Dictionary { { "Users", qry } }; // Act string query = "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(null, query, externals); Delegate del = expression.Compile(); IEnumerable result = del.DynamicInvoke() as IEnumerable; var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }).Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast().ToArray(); // Assert Check.That(result).IsNotNull(); Check.That(result).HasSize(expected.Length); Check.That(result.ToArray()[0]).Equals(expected[0]); } [Fact] public void ParseLambda_Complex_2() { // Arrange var testList = User.GenerateSampleModels(51); var qry = testList.AsQueryable(); // Act string query = "GroupBy(x => new { x.Profile.Age }, it).OrderBy(gg => gg.Key.Age).Select(j => new (j.Key.Age, j.Sum(k => k.Income) As TotalIncome))"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(qry.GetType(), null, query); Delegate del = expression.Compile(); IEnumerable result = del.DynamicInvoke(qry) as IEnumerable; var expected = qry.GroupBy(x => new { x.Profile.Age }, x => x).OrderBy(gg => gg.Key.Age).Select(j => new { j.Key.Age, TotalIncome = j.Sum(k => k.Income) }).Select(c => new ComplexParseLambda1Result { Age = c.Age, TotalIncome = c.TotalIncome }).Cast().ToArray(); // Assert Check.That(result).IsNotNull(); Check.That(result).HasSize(expected.Length); Check.That(result.ToArray()[0]).Equals(expected[0]); } [Fact] public void ParseLambda_Complex_3() { GlobalConfig.CustomTypeProvider = new TestCustomTypeProvider(); // Arrange var testList = User.GenerateSampleModels(51); var qry = testList.AsQueryable(); var externals = new Dictionary { { "Users", qry } }; // Act string query = "Users.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new System.Linq.Dynamic.Core.Tests.DynamicExpressionParserTests+ComplexParseLambda3Result{j.Key.Age, j.Sum(k => k.Income) As TotalIncome})"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(null, query, externals); Delegate del = expression.Compile(); IEnumerable result = del.DynamicInvoke() as IEnumerable; var expected = qry.GroupBy(x => new { x.Profile.Age }).OrderBy(gg => gg.Key.Age).Select(j => new ComplexParseLambda3Result { Age = j.Key.Age, TotalIncome = j.Sum(k => k.Income) }).Cast().ToArray(); // Assert Check.That(result).IsNotNull(); Check.That(result).HasSize(expected.Length); Check.That(result.ToArray()[0]).Equals(expected[0]); GlobalConfig.CustomTypeProvider = null; } [Fact] public void ParseLambda_Select_1() { // Arrange var testList = User.GenerateSampleModels(51); var qry = testList.AsQueryable(); var externals = new Dictionary { {"Users", qry} }; // Act string query = "Users.Select(j => new User(j.Income As Income))"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(null, query, externals); Delegate del = expression.Compile(); object result = del.DynamicInvoke(); // Assert Assert.NotNull(result); } [Fact] public void ParseLambda_Select_2() { // Arrange var testList = User.GenerateSampleModels(5); var qry = testList.AsQueryable(); var externals = new Dictionary { {"Users", qry} }; // Act string query = "Users.Select(j => j)"; LambdaExpression expression = DynamicExpressionParser.ParseLambda(null, query, externals); Delegate del = expression.Compile(); object result = del.DynamicInvoke(); // Assert Assert.NotNull(result); } // https://github.com/StefH/System.Linq.Dynamic.Core/issues/58 [Fact] public void ParseLambda_4_Issue58() { var expressionParams = new[] { Expression.Parameter(typeof (MyClass), "myObj") }; var myClassInstance = new MyClass(); var invokersMerge = new List { myClassInstance }; LambdaExpression expression = DynamicExpressionParser.ParseLambda(false, expressionParams, null, "myObj.Foo()"); Delegate del = expression.Compile(); object result = del.DynamicInvoke(invokersMerge.ToArray()); Check.That(result).Equals(42); } [Fact] public void ParseLambda_DuplicateParameterNames_ThrowsException() { // Arrange var parameters = new[] { Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "x") }; // Act and Assert Check.ThatCode(() => DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42")) .Throws() .WithMessage("The identifier 'x' was defined more than once"); } [Fact] public void ParseLambda_EmptyParameterList() { // Arrange var pEmpty = new ParameterExpression[] { }; // Act var @delegate = DynamicExpressionParser.ParseLambda(pEmpty, null, "1+2").Compile(); int? result = @delegate.DynamicInvoke() as int?; // Assert Check.That(result).Equals(3); } [Fact] public void ParseLambda_ParameterName() { // Arrange var parameters = new[] { Expression.Parameter(typeof(int), "x") }; // Assert var expressionX = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "x == 42"); var expressionIT = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); // Assert Assert.Equal(typeof(bool), expressionX.Body.Type); Assert.Equal(typeof(bool), expressionIT.Body.Type); } [Fact] public void ParseLambda_ParameterName_Empty() { // Arrange var parameters = new[] { Expression.Parameter(typeof(int), "") }; // Assert var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); // Assert Assert.Equal(typeof(bool), expression.Body.Type); } [Fact] public void ParseLambda_ParameterName_Null() { // Arrange var parameters = new[] { Expression.Parameter(typeof(int), null) }; // Assert var expression = DynamicExpressionParser.ParseLambda(parameters, typeof(bool), "it == 42"); // Assert Assert.Equal(typeof(bool), expression.Body.Type); } [Fact] public void ParseLambda_ParameterExpressionMethodCall_ReturnsIntExpression() { var expression = DynamicExpressionParser.ParseLambda(true, new[] { Expression.Parameter(typeof(int), "x") }, typeof(int), "x + 1"); Assert.Equal(typeof(int), expression.Body.Type); } [Fact] public void ParseLambda_RealNumbers() { var parameters = new ParameterExpression[0]; var result1 = DynamicExpressionParser.ParseLambda(parameters, typeof(double), "0.10"); var result2 = DynamicExpressionParser.ParseLambda(parameters, typeof(double), "0.10d"); var result3 = DynamicExpressionParser.ParseLambda(parameters, typeof(float), "0.10f"); var result4 = DynamicExpressionParser.ParseLambda(parameters, typeof(decimal), "0.10m"); // Assert Assert.Equal(0.10d, result1.Compile().DynamicInvoke()); Assert.Equal(0.10d, result2.Compile().DynamicInvoke()); Assert.Equal(0.10f, result3.Compile().DynamicInvoke()); Assert.Equal(0.10m, result4.Compile().DynamicInvoke()); } [Fact] public void ParseLambda_StringLiteral_ReturnsBooleanLambdaExpression() { var expression = DynamicExpressionParser.ParseLambda(new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), "Property1 == \"test\""); Assert.Equal(typeof(Boolean), expression.Body.Type); } [Fact] public void ParseLambda_StringLiteralEmpty_ReturnsBooleanLambdaExpression() { var expression = DynamicExpressionParser.ParseLambda(new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), "Property1 == \"\""); Assert.Equal(typeof(Boolean), expression.Body.Type); } [Fact] public void ParseLambda_StringLiteralEmbeddedQuote_ReturnsBooleanLambdaExpression() { string expectedRightValue = "\"test \\\"string\""; var expression = DynamicExpressionParser.ParseLambda( new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), string.Format("Property1 == {0}", expectedRightValue)); string rightValue = ((BinaryExpression)expression.Body).Right.ToString(); Assert.Equal(typeof(Boolean), expression.Body.Type); Assert.Equal(expectedRightValue, rightValue); } [Fact] public void ParseLambda_StringLiteralStartEmbeddedQuote_ReturnsBooleanLambdaExpression() { string expectedRightValue = "\"\\\"test\""; var expression = DynamicExpressionParser.ParseLambda( new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), string.Format("Property1 == {0}", expectedRightValue)); string rightValue = ((BinaryExpression)expression.Body).Right.ToString(); Assert.Equal(typeof(Boolean), expression.Body.Type); Assert.Equal(expectedRightValue, rightValue); } [Fact] public void ParseLambda_StringLiteral_MissingClosingQuote() { string expectedRightValue = "\"test\\\""; Assert.Throws(() => DynamicExpressionParser.ParseLambda( new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), string.Format("Property1 == {0}", expectedRightValue))); } [Fact] public void ParseLambda_StringLiteralEscapedBackslash_ReturnsBooleanLambdaExpression() { string expectedRightValue = "\"test\\string\""; var expression = DynamicExpressionParser.ParseLambda( new[] { Expression.Parameter(typeof(string), "Property1") }, typeof(Boolean), string.Format("Property1 == {0}", expectedRightValue)); string rightValue = ((BinaryExpression)expression.Body).Right.ToString(); Assert.Equal(typeof(Boolean), expression.Body.Type); Assert.Equal(expectedRightValue, rightValue); } [Fact] public void ParseLambda_TupleToStringMethodCall_ReturnsStringLambdaExpression() { var expression = DynamicExpressionParser.ParseLambda( typeof(Tuple), typeof(string), "it.ToString()"); Assert.Equal(typeof(string), expression.ReturnType); } [Fact] public void ParseLambda_IllegalMethodCall_ThrowsException() { Check.ThatCode(() => { DynamicExpressionParser.ParseLambda(typeof(IO.FileStream), null, "it.Close()"); }) .Throws().WithMessage("Methods on type 'Stream' are not accessible"); } } }