using System; using System.Collections; using System.Linq; using DynamicExpresso.Exceptions; using NUnit.Framework; namespace DynamicExpresso.UnitTest { [TestFixture] public class ConstructorTest { [Test] public void New_Of_Base_Type() { var target = new Interpreter(); Assert.That(target.Eval("new DateTime(2015, 1, 24)"), Is.EqualTo(new DateTime(2015, 1, 24))); Assert.That(target.Eval("new string('a', 10)"), Is.EqualTo(new string('a', 10))); } [Test] public void New_Of_Custom_Type() { var target = new Interpreter(); target.Reference(typeof(Uri)); Assert.That(target.Eval("new Uri(\"http://www.google.com\")"), Is.EqualTo(new Uri("http://www.google.com"))); } [Test] public void New_And_Member_Access() { var target = new Interpreter(); Assert.That(target.Eval("new DateTime(2015, 1, 24).Month"), Is.EqualTo(new DateTime(2015, 1, 24).Month)); Assert.That(target.Eval("new DateTime( 2015, 1, 24).Month + 34"), Is.EqualTo(new DateTime(2015, 1, 24).Month + 34)); } [Test] public void Constructor_invocation_without_new_is_not_supported() { var target = new Interpreter(); Assert.Throws(() => target.Parse("DateTime(2010, 5, 23)")); } [Test] public void Unknown_New_Type_Is_Not_Supported() { var target = new Interpreter(); Assert.Throws(() => target.Parse("new unkkeyword()")); } [Test] public void Empty_object_initializer() { var target = new Interpreter(); target.Reference(typeof(MyClass)); Assert.That(target.Eval("new MyClass() {}"), Is.EqualTo(new MyClass() { })); Assert.That(target.Eval("new MyClass(\"test\") {}"), Is.EqualTo(new MyClass("test") { })); Assert.That(target.Eval("new MyClass{}"), Is.EqualTo(new MyClass { })); } [Test] public void Object_initializer() { var target = new Interpreter(); target.Reference(typeof(MyClass)); // each member initializer can end with a comma, even if there's nothing afterwards Assert.That(target.Eval("new MyClass { StrProp = \"test\", }"), Is.EqualTo(new MyClass { StrProp = "test", })); Assert.That(target.Eval("new MyClass { StrProp = \"test\" }"), Is.EqualTo(new MyClass { StrProp = "test" })); Assert.That(target.Eval("new MyClass(\"test\") { IntField = 5, }"), Is.EqualTo(new MyClass("test") { IntField = 5, })); Assert.That(target.Eval("new MyClass(\"test\") { IntField = 5 }"), Is.EqualTo(new MyClass("test") { IntField = 5 })); Assert.That(target.Eval("new MyClass() { StrProp = \"test\", IntField = 5, }"), Is.EqualTo(new MyClass() { StrProp = "test", IntField = 5, })); Assert.That(target.Eval("new MyClass() { StrProp = \"test\", IntField = 5 }"), Is.EqualTo(new MyClass() { StrProp = "test", IntField = 5 })); } [Test] public void Constructor_invocation_generics() { var target = new Interpreter(); target.Reference(typeof(Tuple<>)); target.Reference(typeof(Tuple<,>)); target.Reference(typeof(Tuple<,,>)); Assert.That(target.Eval("new Tuple(1).Item1"), Is.EqualTo(1)); Assert.That(target.Eval("new Tuple(5, \"My str item\").Item2"), Is.EqualTo("My str item")); Assert.That(target.Eval("new Tuple(1, 2, 3).Item3"), Is.EqualTo(3)); } [Test] public void Constructor_invocation_named_generics_with_arity() { var target = new Interpreter(); target.Reference(typeof(Tuple<>), "Toto`1"); target.Reference(typeof(Tuple<,>), "Toto`2"); target.Reference(typeof(Tuple<,,>), "Toto`3"); Assert.That(target.Eval("new Toto(1).Item1"), Is.EqualTo(1)); Assert.That(target.Eval("new Toto(5, \"My str item\").Item2"), Is.EqualTo("My str item")); Assert.That(target.Eval("new Toto(1, 2, 3).Item3"), Is.EqualTo(3)); } [Test] public void Constructor_invocation_named_generics() { var target = new Interpreter(); target.Reference(typeof(Tuple<,>), "Tuple"); Assert.That(target.Eval("new Tuple(5, \"My str item\").Item2"), Is.EqualTo("My str item")); target.Reference(typeof(Tuple<>), "Tuple1"); target.Reference(typeof(Tuple<,,>), "Tuple3"); Assert.That(target.Eval("new Tuple1(1).Item1"), Is.EqualTo(1)); Assert.That(target.Eval("new Tuple3(1, 2, 3).Item3"), Is.EqualTo(3)); } [Test] public void Object_initializer_syntax_error() { var target = new Interpreter(); target.Reference(typeof(MyClass)); Assert.Throws(() => target.Parse("new MyClass() { StrProp }")); Assert.Throws(() => target.Parse("new MyClass() { StrProp = }")); Assert.Throws(() => target.Parse("new MyClass() { StrProp = 5 }")); // type mismatch Assert.Throws(() => target.Parse("new MyClass() { ReadOnlyProp = 5 }")); // read only prop Assert.Throws(() => target.Parse("new MyClass() { UnkProp = 5 }")); // unknown property Assert.Throws(() => target.Parse("new MyClass() { StrProp ")); // no close bracket Assert.Throws(() => target.Parse("new MyClass() StrProp }")); // no open bracket Assert.Throws(() => target.Parse("new MyClass() {{IntField = 5}}")); // multiple bracket Assert.Throws(() => target.Parse("new MyClass() {5}")); // collection initializer not supported } [Test] public void Ctor_params_array() { var target = new Interpreter(); target.Reference(typeof(MyClass)); Assert.That(target.Eval("new MyClass(6, 5, 4, 3).MyArr"), Is.EqualTo(new MyClass(6, 5, 4, 3).MyArr)); } [Test] public void Array_constructor() { var target = new Interpreter(); var arr = target.Eval("new int[] { 1, 2 }"); Assert.That(arr.Length, Is.EqualTo(2)); Assert.That(arr[0], Is.EqualTo(1)); Assert.That(arr[1], Is.EqualTo(2)); } [Test] public void Array_constructor_type_mismatch() { // Exception: an expression of type 'System.Char' cannot be used to initialize an array of type 'System.Int32' var target = new Interpreter(); Assert.Throws(() => target.Eval("new int[] { 1, 'a' }")); } [Test] public void Jagged_array_constructor() { var target = new Interpreter(); var arr = target.Eval("new int[][] { new int[] { 1, 2, }, new int[] { 3, 4, }, }"); Assert.That(arr.Length, Is.EqualTo(2)); Assert.That(arr[0][0], Is.EqualTo(1)); Assert.That(arr[0][1], Is.EqualTo(2)); Assert.That(arr[1][0], Is.EqualTo(3)); Assert.That(arr[1][1], Is.EqualTo(4)); } [Test] public void Array_multi_dimension_constructor() { // creating a multidimensional array is not supported var target = new Interpreter(); Assert.Throws(() => target.Parse("new int[,] { { 1 }, { 2 } }")); } [Test] public void Ctor_NewDictionaryWithItems() { var target = new Interpreter(); target.Reference(typeof(System.Collections.Generic.Dictionary<,>)); var l = target.Eval>("new Dictionary(){{1, \"1\"}, {2, \"2\"}, {3, \"3\"}, {4, \"4\"}, {5, \"5\"}}"); Assert.That(l.Count, Is.EqualTo(5)); for (int i = 0; i < l.Count; ++i) { Assert.That(l[i + 1], Is.EqualTo(i + 1 + "")); } } [Test] public void Ctor_NewMyClassWithItems() { var target = new Interpreter(); target.Reference(typeof(MyClassAdder)); Assert.That(target.Eval("new MyClassAdder(){{ 1, 2, 3, 4, 5},{\"6\" },7 }.Add(true)"), Is.EqualTo(new MyClassAdder() { { 1, 2, 3, 4, 5 }, { "6" }, 7 })); } [Test] public void Ctor_NewMyClass_ExpectedValues() { var target = new Interpreter(); target.Reference(typeof(MyClassAdder)); target.Reference(typeof(MyClass)); var strProp = new Parameter("StrProp", typeof(string).MakeByRefType(), "0"); var intProp = new Parameter("IntField", typeof(int).MakeByRefType(), int.MaxValue); var args = new object[] { strProp.Value, intProp.Value }; Assert.That( target.Parse("new MyClassAdder(){{ 1, 2, 3, 4, 5},{StrProp = \"6\" },7}", strProp, intProp).Invoke(args), Is.EqualTo(new MyClassAdder() { { 1, 2, 3, 4, 5 }, "6", 7 })); Assert.That( target.Eval("new MyClassAdder(){{ 1, 2, 3, 4, 5},string.Empty, 7}"), Is.EqualTo(new MyClassAdder() { { 1, 2, 3, 4, 5 }, string.Empty, 7 })); var IntField = int.MaxValue; Assert.That( target.Parse("new MyClassAdder(){ { IntField = 5 }, { 1, 2, 3, 4, 5},{StrProp = \"6\" }, IntField}", strProp, intProp).Invoke(args), Is.EqualTo(new MyClassAdder() { { IntField = 5 }, { 1, 2, 3, 4, IntField }, "6" })); } [Test] public void Ctor_NewMyClass_CanStillUseMemberSyntax() { var target = new Interpreter(); target.Reference(typeof(MyClassAdder)); target.Reference(typeof(MyClass)); Assert.That( target.Eval("new MyClassAdder() {StrProp = string.Empty, MyArr = new int[] {1, 2, 3, 4, 5}, IntField = int.MinValue }"), Is.EqualTo(new MyClassAdder() { StrProp = string.Empty, MyArr = new[] { 1, 2, 3, 4, 5 }, IntField = int.MinValue })); } [Test] public void Ctor_InvalidInitializerMemberDeclarator() { var target = new Interpreter(); target.Reference(typeof(MyClassAdder)); target.Reference(typeof(MyClass)); Assert.Throws(() => target.Eval("new MyClassAdder(){{ 1, 2, 3, 4, 5},{StrProp = \"6\" },7 }")); //Start with collection, then do member init Assert.Throws(() => target.Eval("new MyClassAdder(){{ 1, 2, 3, 4, 5},StrProp = \"6\" ,7 }")); //Member init first, then attempt a collection init. Assert.Throws(() => target.Eval("new MyClassAdder(){StrProp = \"6\" ,{ 1, 2, 3, 4, 5},7 }")); } [Test] public void Ctor_CannotUseCollectionInitDoesNotImplementIEnumerable() { var target = new Interpreter(); target.Reference(typeof(MyClassAdder)); target.Reference(typeof(MyClass)); Assert.Throws(() => target.Eval("new MyClass(){ 1, 2, 3, 4, 5}")); } [Test] public void Ctor_NewListWithItems() { Ctor_NewListGeneric("\"1\"", "\"2\"", "\"3\"", "{string.Empty}", "string.Empty", "int.MaxValue.ToString()", "{int.MinValue.ToString()}"); Ctor_NewListGeneric("1", "2", "3", "int.MinValue", "int.MaxValue", "{int.MinValue}", "{int.MaxValue}"); Ctor_NewListGeneric("string.Empty", "int.MinValue"); } [Test] public void Ctor_NewListCantFindAddMethod() { var target = new Interpreter(); target.Reference(typeof(System.Collections.Generic.List<>)); try { target.Eval>("new List(){string.Empty}"); } catch (ParseException ex) { if (ex.Message.Contains("The best overloaded Add ")) { Assert.That(ex.Message, Does.Contain("Add")); } else { throw; } } Assert.Throws(() => target.Eval>("new List(){int.MaxValue}")); } public void Ctor_NewListGeneric(params string[] items) { var target = new Interpreter(); target.Reference(typeof(System.Collections.Generic.List<>)); target.Reference(typeof(TObject)); //Create a random list of values to test. var actual = new System.Collections.Generic.List(); foreach (var v in items) { actual.Add(target.Eval(v.Trim('}', '{'))); } for (var min = 0; min < actual.Count; ++min) { for (var count = Math.Min(min, 1); count <= actual.Count - min; ++count) { var evalText = $"new List<{typeof(TObject).Name}>(){{{string.Join(",", items.Skip(min).Take(count))}}}"; System.Collections.Generic.List eval = null; Assert.DoesNotThrow(() => eval = target.Eval>(evalText), evalText); Assert.That(eval.Count, Is.EqualTo(count)); for (var i = 0; i < count; ++i) { Assert.That(eval[i], Is.EqualTo(actual[i + min])); } } } } [Test] public void Ctor_NewListWithString() { var target = new Interpreter(); target.Reference(typeof(System.Collections.Generic.List<>)); var list = target.Eval>("new List(){string.Empty}"); Assert.That(list.Count, Is.EqualTo(1)); for (int i = 0; i < list.Count; ++i) { Assert.That(list[i], Is.SameAs(string.Empty)); } list = target.Eval>("new List(){StrProp = string.Empty}", new Parameter("StrProp", "0")); Assert.That(list[0], Is.SameAs(string.Empty)); list = target.Eval>("new List(){{StrProp = string.Empty}}", new Parameter("StrProp", "0")); Assert.That(list[0], Is.SameAs(string.Empty)); list = target.Eval>("new List(){StrValue()}", new Parameter("StrValue", new Func(() => "Func"))); Assert.That(list[0], Is.EqualTo("Func")); } private class MyClass { public int IntField; public string StrProp { get; set; } public int ReadOnlyProp { get; } public int[] MyArr { get; set; } public MyClass() { } public MyClass(string strValue) { StrProp = strValue; } public MyClass(params int[] intValues) { MyArr = intValues; } public override bool Equals(object obj) { return Equals(obj as MyClass); } public bool Equals(MyClass p) { if (p is null) return false; if (ReferenceEquals(this, p)) return true; return IntField == p.IntField && StrProp == p.StrProp && ReadOnlyProp == p.ReadOnlyProp; } // remove compilation warning public override int GetHashCode() { return 0; } } private class MyClassAdder : MyClass, System.Collections.IEnumerable { public MyClassAdder Add(string s) { StrProp = s; return this; } public MyClassAdder Add(int intValue) { IntField = intValue; return this; } public MyClassAdder Add(params int[] intValues) { MyArr = intValues; return this; } public MyClassAdder Add(bool returnMe) { if (returnMe) { return this; } return null; } IEnumerator IEnumerable.GetEnumerator() { yield break; } } } }