using System; using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; using DynamicExpresso.Exceptions; using Microsoft.CSharp.RuntimeBinder; using NUnit.Framework; namespace DynamicExpresso.UnitTest { [TestFixture] public class DynamicTest { [Test] public void Get_Property_of_an_ExpandoObject() { dynamic dyn = new ExpandoObject(); dyn.Foo = "bar"; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo"), Is.EqualTo(dyn.Foo)); } [Test] public void Set_Property_of_an_ExpandoObject() { dynamic dyn = new ExpandoObject(); var interpreter = new Interpreter(InterpreterOptions.Default | InterpreterOptions.LateBindObject) .SetVariable("dyn", (object)dyn); interpreter.Eval("dyn.Foo = 10"); Assert.That(dyn.Foo, Is.EqualTo(10)); } [Test] public void Get_Property_of_a_nested_anonymous() { dynamic dyn = new ExpandoObject(); dyn.Sub = new { Foo = new { Bar = new { Foo2 = "bar" } } }; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Sub.Foo.Bar.Foo2"), Is.EqualTo(dyn.Sub.Foo.Bar.Foo2)); } [Test] public void Set_Property_of_a_nested_ExpandoObject() { dynamic dyn = new ExpandoObject(); dyn.Sub = new ExpandoObject(); dyn.Sub.Foo = "bar"; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); interpreter.Eval("dyn.Sub.Foo = \"foobar\""); Assert.That(dyn.Sub.Foo, Is.EqualTo("foobar")); } [Test] public void Get_Property_of_a_nested_ExpandoObject() { dynamic dyn = new ExpandoObject(); dyn.Sub = new ExpandoObject(); dyn.Sub.Foo = "bar"; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Sub.Foo"), Is.EqualTo(dyn.Sub.Foo)); } //[Test] //public void Set_Property_of_an_ExpandoObject() //{ // dynamic dyn = new ExpandoObject(); // var interpreter = new Interpreter() // .SetVariable("dyn", dyn); // interpreter.Eval("dyn.Foo = 6"); // Assert.That(dyn.Foo, Is.EqualTo(6)); //} [Test] public void Standard_properties_have_precedence_over_dynamic_properties() { var dyn = new TestDynamicClass(); dyn.RealProperty = "bar"; var interpreter = new Interpreter() .SetVariable("dyn", dyn); Assert.That(interpreter.Eval("dyn.RealProperty"), Is.EqualTo(dyn.RealProperty)); } [Test] public void Invoke_Method_of_an_ExpandoObject() { dynamic dyn = new ExpandoObject(); dyn.Foo = new Func(() => "bar"); var interpreter = new Interpreter() .SetVariable("dyn", dyn); Assert.That(interpreter.Eval("dyn.Foo()"), Is.EqualTo(dyn.Foo())); } [Test] public void Invoke_Method_of_a_nested_ExpandoObject() { dynamic dyn = new ExpandoObject(); dyn.Sub = new ExpandoObject(); dyn.Sub.Foo = new Func(() => "bar"); var interpreter = new Interpreter() .SetVariable("dyn", dyn); Assert.That(interpreter.Eval("dyn.Sub.Foo()"), Is.EqualTo(dyn.Sub.Foo())); } [Test] public void Invoke_Method_of_a_nested_ExpandoObject_WithAnonymousType() { dynamic dyn = new ExpandoObject(); dyn.Sub = new ExpandoObject(); dyn.Sub.Foo = new { Func = new Func(() => "bar") }; var interpreter = new Interpreter() .SetVariable("dyn", dyn); Assert.That(interpreter.Eval("dyn.Sub.Foo.Func()"), Is.EqualTo(dyn.Sub.Foo.Func())); } [Test] public void Standard_methods_have_precedence_over_dynamic_methods() { var dyn = new TestDynamicClass(); var interpreter = new Interpreter() .SetVariable("dyn", dyn); Assert.That(interpreter.Eval("dyn.ToString()"), Is.EqualTo(dyn.ToString())); } [Test] public void Case_Insensitive_Dynamic_Members() { dynamic dyn = new ExpandoObject(); dyn.Bar = 10; var interpreter = new Interpreter(); Assert.Throws(() => interpreter.Eval("dyn.BAR", new Parameter("dyn", dyn))); } [Test] public void Get_value_of_a_nested_array() { dynamic dyn = new ExpandoObject(); dyn.Sub = new int[] { 42 }; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Sub[0]"), Is.EqualTo(dyn.Sub[0])); } [Test] public void Get_value_of_a_nested_array_from_anonymous_type() { dynamic dyn = new ExpandoObject(); dyn.Sub = new { Foo = new int[] { 42 }, Bar = new { Sub = new int[] { 43 } } }; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Sub.Foo[0]"), Is.EqualTo(dyn.Sub.Foo[0])); Assert.That(interpreter.Eval("dyn.Sub.Bar.Sub[0]"), Is.EqualTo(dyn.Sub.Bar.Sub[0])); Assert.That(interpreter.Eval("dyn.Sub.Bar.Sub.Length"), Is.EqualTo(dyn.Sub.Bar.Sub.Length)); } [Test] public void Get_value_of_an_array_of_anonymous_type() { dynamic dyn = new ExpandoObject(); var anonType1 = new { Foo = string.Empty }; var anonType2 = new { Foo = "string.Empty" }; var nullAnonType = anonType1; nullAnonType = null; dyn.Sub = new { Arg1 = anonType1, Arg2 = anonType2, Arg3 = nullAnonType, Arr = new[] { anonType1, anonType2, nullAnonType }, ObjArr = new object[] { "Test", anonType1 } }; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Sub.Arg1.Foo"), Is.SameAs(dyn.Sub.Arg1.Foo)); Assert.That(interpreter.Eval("dyn.Sub.Arg2.Foo"), Is.SameAs(dyn.Sub.Arg2.Foo)); Assert.Throws(() => Console.WriteLine(dyn.Sub.Arg3.Foo)); Assert.Throws(() => interpreter.Eval("dyn.Sub.Arg3.Foo")); Assert.That(interpreter.Eval("dyn.Sub.Arr[0].Foo"), Is.SameAs(dyn.Sub.Arr[0].Foo)); Assert.That(interpreter.Eval("dyn.Sub.Arr[1].Foo"), Is.SameAs(dyn.Sub.Arr[1].Foo)); Assert.Throws(() => Console.WriteLine(dyn.Sub.Arr[2].Foo)); Assert.Throws(() => interpreter.Eval("dyn.Sub.Arr[2].Foo")); Assert.That(interpreter.Eval("dyn.Sub.ObjArr[0]"), Is.SameAs(dyn.Sub.ObjArr[0])); Assert.That(interpreter.Eval("dyn.Sub.ObjArr[0].Length"), Is.EqualTo(dyn.Sub.ObjArr[0].Length)); Assert.That(interpreter.Eval("dyn.Sub.ObjArr[1]"), Is.SameAs(dyn.Sub.ObjArr[1])); Assert.That(interpreter.Eval("dyn.Sub.ObjArr[1].Foo"), Is.SameAs(dyn.Sub.ObjArr[1].Foo)); } [Test] public void Set_value_of_a_dynamic_object() { dynamic dyn = new ExpandoObject(); dyn.Foo = new int[5]; dyn.Bar = new Dictionary(); var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); interpreter.Eval("dyn.Foo[2] = 5"); interpreter.Eval("dyn.Bar[\"foo\"] = 50"); Assert.That(dyn.Foo[2], Is.EqualTo(5)); Assert.That(dyn.Bar["foo"], Is.EqualTo(50)); } [Test] public void Get_value_of_a_nested_array_error() { dynamic dyn = new ExpandoObject(); dyn.Sub = new int[] { 42 }; dyn.Bar = 123; var interpreter = new Interpreter().SetVariable("dyn", (object)dyn); Assert.Throws(() => interpreter.Eval("dyn.Bar[0]")); // use index for a property that is not an array Assert.Throws(() => interpreter.Eval("dyn.Sub[\"hello\"]")); // use as an index an invalid type (e.g. string) Assert.Throws(() => interpreter.Eval("dyn.Sub[0"));// pass some invalid syntax Assert.Throws(() => interpreter.Eval("dyn.Sub 0]")); // pass some invalid syntax Assert.Throws(() => interpreter.Eval("dyn.Sub[[0]]")); // pass some invalid syntax Assert.Throws(() => interpreter.Eval("dyn.Sub[1]")); // get an out of bound element } [Test] public void Test_With_Standard_Object() { var myInstance = DateTime.Now; var methodInfo = myInstance.GetType().GetMethod("ToUniversalTime"); var methodCallExpression = Expression.Call(Expression.Constant(myInstance), methodInfo); var expression = Expression.Lambda(methodCallExpression); Assert.That(expression.Compile().DynamicInvoke(), Is.EqualTo(myInstance.ToUniversalTime())); } [Test] public void Test_With_Dynamic_Object() { dynamic myInstance = new ExpandoObject(); myInstance.MyMethod = new Func(() => "hello world"); var binder = Binder.InvokeMember( CSharpBinderFlags.None, "MyMethod", null, this.GetType(), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) }); var methodCallExpression = Expression.Dynamic(binder, typeof(object), Expression.Constant(myInstance)); var expression = Expression.Lambda(methodCallExpression); Assert.That(expression.Compile().DynamicInvoke(), Is.EqualTo(myInstance.MyMethod())); } [Test] public void Test_With_Dynamic_Object_By_Index_Access() { DynamicIndexAccess globals = new DynamicIndexAccess(); Interpreter interpreter = new Interpreter() .SetVariable("Values", new DynamicIndexAccess()); Assert.That(interpreter.Eval("Values[\"Hello\"]"), Is.EqualTo(globals.Values["Hello"])); } [Test] public void Comparison_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo == 500"), Is.EqualTo(dyn.Foo == 500)); Assert.That(interpreter.Eval("500 == dyn.Foo"), Is.EqualTo(500 == dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo != 200"), Is.EqualTo(dyn.Foo != 200)); Assert.That(interpreter.Eval("200 != dyn.Foo"), Is.EqualTo(200 != dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo > 200"), Is.EqualTo(dyn.Foo > 200)); Assert.That(interpreter.Eval("600 > dyn.Foo"), Is.EqualTo(600 > dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo < 600"), Is.EqualTo(dyn.Foo < 600)); Assert.That(interpreter.Eval("200 < dyn.Foo"), Is.EqualTo(200 < dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo >= 500"), Is.EqualTo(dyn.Foo >= 500)); Assert.That(interpreter.Eval("500 >= dyn.Foo"), Is.EqualTo(500 >= dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo <= 500"), Is.EqualTo(dyn.Foo <= 500)); Assert.That(interpreter.Eval("500 <= dyn.Foo"), Is.EqualTo(500 <= dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo + 500"), Is.EqualTo(dyn.Foo + 500)); Assert.That(interpreter.Eval("500 + dyn.Foo"), Is.EqualTo(500 + dyn.Foo)); } [Test] public void Conditional_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; dyn.Bar = 100; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Bar>0?100:200"), Is.EqualTo(dyn.Bar > 0 ? 100 : 200)); } [Test] public void AndAlso_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; dyn.Bar = 100; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo == 500 && dyn.Bar == 100"), Is.EqualTo(dyn.Foo == 500 && dyn.Bar == 100)); Assert.That(interpreter.Eval("dyn.Foo == 500 && dyn.Bar != 100"), Is.EqualTo(dyn.Foo == 500 && dyn.Bar != 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 && dyn.Bar == 100"), Is.EqualTo(dyn.Foo != 500 && dyn.Bar == 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 && dyn.Bar != 100"), Is.EqualTo(dyn.Foo != 500 && dyn.Bar != 100)); //check non dynamic right side Assert.That(interpreter.Eval("dyn.Foo == 500 && 100 == 100"), Is.EqualTo(dyn.Foo == 500 && 100 == 100)); Assert.That(interpreter.Eval("dyn.Foo == 500 && 100 != 100"), Is.EqualTo(dyn.Foo == 500 && 100 != 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 && 100 == 100"), Is.EqualTo(dyn.Foo != 500 && 100 == 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 && 100 != 100"), Is.EqualTo(dyn.Foo != 500 && 100 != 100)); //check non dynamic left side Assert.That(interpreter.Eval("100 == 100 && dyn.Foo == 500"), Is.EqualTo(100 == 100 && dyn.Foo == 500)); Assert.That(interpreter.Eval("100 != 100 && dyn.Foo == 500"), Is.EqualTo(100 != 100 && dyn.Foo == 500)); Assert.That(interpreter.Eval("100 == 100 && dyn.Foo != 500"), Is.EqualTo(100 == 100 && dyn.Foo != 500)); Assert.That(interpreter.Eval("100 != 100 && dyn.Foo != 500"), Is.EqualTo(100 != 100 && dyn.Foo != 500)); } [Test] public void OrElse_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; dyn.Bar = 100; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo == 500 || dyn.Bar == 100"), Is.EqualTo(dyn.Foo == 500 || dyn.Bar == 100)); Assert.That(interpreter.Eval("dyn.Foo == 500 || dyn.Bar != 100"), Is.EqualTo(dyn.Foo == 500 || dyn.Bar != 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 || dyn.Bar == 100"), Is.EqualTo(dyn.Foo != 500 || dyn.Bar == 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 || dyn.Bar != 100"), Is.EqualTo(dyn.Foo != 500 || dyn.Bar != 100)); //check non dynamic right side Assert.That(interpreter.Eval("dyn.Foo == 500 || 100 == 100"), Is.EqualTo(dyn.Foo == 500 || 100 == 100)); Assert.That(interpreter.Eval("dyn.Foo == 500 || 100 != 100"), Is.EqualTo(dyn.Foo == 500 || 100 != 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 || 100 == 100"), Is.EqualTo(dyn.Foo != 500 || 100 == 100)); Assert.That(interpreter.Eval("dyn.Foo != 500 || 100 != 100"), Is.EqualTo(dyn.Foo != 500 || 100 != 100)); //check non dynamic left side Assert.That(interpreter.Eval("100 == 100 || dyn.Foo == 500"), Is.EqualTo(100 == 100 || dyn.Foo == 500)); Assert.That(interpreter.Eval("100 != 100 || dyn.Foo == 500"), Is.EqualTo(100 != 100 || dyn.Foo == 500)); Assert.That(interpreter.Eval("100 == 100 || dyn.Foo != 500"), Is.EqualTo(100 == 100 || dyn.Foo != 500)); Assert.That(interpreter.Eval("100 != 100 || dyn.Foo != 500"), Is.EqualTo(100 != 100 || dyn.Foo != 500)); } [Test] public void Math_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo + 500"), Is.EqualTo(dyn.Foo + 500)); Assert.That(interpreter.Eval("500 + dyn.Foo"), Is.EqualTo(500 + dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo - 500"), Is.EqualTo(dyn.Foo - 500)); Assert.That(interpreter.Eval("500 - dyn.Foo"), Is.EqualTo(500 - dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo / 500"), Is.EqualTo(dyn.Foo / 500)); Assert.That(interpreter.Eval("500 / dyn.Foo"), Is.EqualTo(500 / dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo * 500"), Is.EqualTo(dyn.Foo * 500)); Assert.That(interpreter.Eval("500 * dyn.Foo"), Is.EqualTo(500 * dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo % 500"), Is.EqualTo(dyn.Foo % 500)); Assert.That(interpreter.Eval("500 % dyn.Foo"), Is.EqualTo(500 % dyn.Foo)); } public class ClassWithObjectProperties { public object Foo => 100; public object Bar() => 100; } [Test] public void ObjectLateBinding() { var classWithObjectProperties = new ClassWithObjectProperties(); Assert.Throws(() => { var noLateBindingInterpreter = new Interpreter(); var noLateBindingDel = noLateBindingInterpreter.ParseAsDelegate>("d.Foo+d.Bar()", new[] { "d" }); var noLateBindingResult = noLateBindingDel.Invoke(classWithObjectProperties); }); var lateBindingInterpreter = new Interpreter(InterpreterOptions.Default | InterpreterOptions.LateBindObject); var lateBindingInterpreterDel = lateBindingInterpreter.ParseAsDelegate>("d.Foo+d.Bar()", new[] { "d" }); var lateBindingResult = lateBindingInterpreterDel.Invoke(classWithObjectProperties); Assert.That(lateBindingResult, Is.EqualTo((dynamic)classWithObjectProperties.Foo + (dynamic)classWithObjectProperties.Bar())); lateBindingInterpreter.SetVariable("d", classWithObjectProperties); var evalResult = lateBindingInterpreter.Eval("d.Foo+d.Bar()"); Assert.That(evalResult.GetType(), Is.EqualTo(typeof(int))); Assert.That(evalResult, Is.EqualTo((dynamic)classWithObjectProperties.Foo + (dynamic)classWithObjectProperties.Bar())); } [Test] public void Bitwise_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = 500; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("dyn.Foo & 500"), Is.EqualTo(dyn.Foo & 500)); Assert.That(interpreter.Eval("500 & dyn.Foo"), Is.EqualTo(500 & dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo | 500"), Is.EqualTo(dyn.Foo | 500)); Assert.That(interpreter.Eval("500 | dyn.Foo"), Is.EqualTo(500 | dyn.Foo)); Assert.That(interpreter.Eval("dyn.Foo ^ 500"), Is.EqualTo(dyn.Foo ^ 500)); Assert.That(interpreter.Eval("500 ^ dyn.Foo"), Is.EqualTo(500 ^ dyn.Foo)); } [Test] public void Unary_with_dynamic_properties() { dynamic dyn = new ExpandoObject(); dyn.Foo = true; dyn.Bar = 500; var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("!dyn.Foo"), Is.EqualTo(!dyn.Foo)); Assert.That(interpreter.Eval("-dyn.Bar"), Is.EqualTo(-dyn.Bar)); } [Test] public void Static_method_call_with_dynamic_arg() { dynamic dyn = new ExpandoObject(); dyn.Foo = "test"; var myClass = new MyClass(); var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn); Assert.That(interpreter.Eval("string.IsNullOrEmpty(dyn.Foo)"), Is.EqualTo(string.IsNullOrEmpty(dyn.Foo))); } [Test] public void Method_call_with_dynamic_arg() { dynamic dyn = new ExpandoObject(); dyn.Foo = "test"; var myClass = new MyClass(); var interpreter = new Interpreter() .SetVariable("dyn", (object)dyn) .SetVariable("myClass", myClass); Assert.That(interpreter.Eval("myClass.MyMethod(dyn.Foo)"), Is.EqualTo(myClass.MyMethod(dyn.Foo))); } public class MyClass { public string MyMethod(string input) { return input; } } public class TestDynamicClass : DynamicObject { public string RealProperty { get; set; } public override DynamicMetaObject GetMetaObject(Expression parameter) { throw new Exception("This should not be called"); } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { throw new Exception("This should not be called"); } public override bool TryGetMember(GetMemberBinder binder, out object result) { throw new Exception("This should not be called"); } } public class DynamicIndexAccess : DynamicObject { public dynamic Values { get { return _values; } } private readonly IReadOnlyDictionary _values; public DynamicIndexAccess() { var values = new Dictionary(); values.Add("Hello", "Hello World!"); _values = values; } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { return _values.TryGetValue((string)indexes[0], out result); } public override bool TryGetMember(GetMemberBinder binder, out object result) { return _values.TryGetValue(binder.Name, out result); } } } }