|
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Diagnostics; |
4 | 4 | using System.Dynamic; |
5 | | -using System.Linq.Expressions; |
| 5 | +using System.Globalization; |
| 6 | +using System.Reflection; |
6 | 7 |
|
7 | 8 | using NUnit.Framework; |
8 | 9 |
|
@@ -926,6 +927,278 @@ def SetValue(self): |
926 | 927 | } |
927 | 928 | } |
928 | 929 |
|
| 930 | + public class DynamicFixture : DynamicObject |
| 931 | + { |
| 932 | + private Dictionary<string, object> _properties = new Dictionary<string, object>(); |
| 933 | + |
| 934 | + public override bool TryGetMember(GetMemberBinder binder, out object result) |
| 935 | + { |
| 936 | + return _properties.TryGetValue(binder.Name, out result); |
| 937 | + } |
| 938 | + |
| 939 | + public override bool TrySetMember(SetMemberBinder binder, object value) |
| 940 | + { |
| 941 | + _properties[binder.Name] = value; |
| 942 | + return true; |
| 943 | + } |
| 944 | + |
| 945 | + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) |
| 946 | + { |
| 947 | + try |
| 948 | + { |
| 949 | + result = _properties.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, _properties, args, |
| 950 | + CultureInfo.InvariantCulture); |
| 951 | + return true; |
| 952 | + } |
| 953 | + catch |
| 954 | + { |
| 955 | + result = null; |
| 956 | + return false; |
| 957 | + } |
| 958 | + } |
| 959 | + |
| 960 | + public Dictionary<string, object> Properties { get { return _properties; } } |
| 961 | + |
| 962 | + public string NonDynamicProperty { get; set; } |
| 963 | + } |
| 964 | + |
| 965 | + public class TestPerson : IComparable, IComparable<TestPerson> |
| 966 | + { |
| 967 | + public int Id { get; private set; } |
| 968 | + public string Name { get; private set; } |
| 969 | + |
| 970 | + public TestPerson(int id, string name) |
| 971 | + { |
| 972 | + Id = id; |
| 973 | + Name = name; |
| 974 | + } |
| 975 | + |
| 976 | + public int CompareTo(object obj) |
| 977 | + { |
| 978 | + return CompareTo(obj as TestPerson); |
| 979 | + } |
| 980 | + |
| 981 | + public int CompareTo(TestPerson other) |
| 982 | + { |
| 983 | + if (ReferenceEquals(this, other)) return 0; |
| 984 | + if (other == null) return 1; |
| 985 | + if (Id < other.Id) return -1; |
| 986 | + if (Id > other.Id) return 1; |
| 987 | + return 0; |
| 988 | + } |
| 989 | + |
| 990 | + public override bool Equals(object obj) |
| 991 | + { |
| 992 | + return Equals(obj as TestPerson); |
| 993 | + } |
| 994 | + |
| 995 | + public bool Equals(TestPerson other) |
| 996 | + { |
| 997 | + return CompareTo(other) == 0; |
| 998 | + } |
| 999 | + } |
| 1000 | + |
| 1001 | + private static TestCaseData[] DynamicPropertiesGetterTestCases() => new[] |
| 1002 | + { |
| 1003 | + new TestCaseData(true), |
| 1004 | + new TestCaseData(10), |
| 1005 | + new TestCaseData(10.1), |
| 1006 | + new TestCaseData(10.2m), |
| 1007 | + new TestCaseData("Some string"), |
| 1008 | + new TestCaseData(new DateTime(2023, 6, 22)), |
| 1009 | + new TestCaseData(new List<int> { 1, 2, 3, 4, 5 }), |
| 1010 | + new TestCaseData(new Dictionary<string, int> { { "first", 1 }, { "second", 2 }, { "third", 3 } }), |
| 1011 | + new TestCaseData(new Fixture()), |
| 1012 | + }; |
| 1013 | + |
| 1014 | + [TestCaseSource(nameof(DynamicPropertiesGetterTestCases))] |
| 1015 | + public void TestGetPublicDynamicObjectPropertyWorks(object property) |
| 1016 | + { |
| 1017 | + dynamic model = PyModule.FromString("module", @" |
| 1018 | +from clr import AddReference |
| 1019 | +AddReference(""Python.EmbeddingTest"") |
| 1020 | +AddReference(""System"") |
| 1021 | +
|
| 1022 | +from Python.EmbeddingTest import * |
| 1023 | +
|
| 1024 | +class TestGetPublicDynamicObjectPropertyWorks: |
| 1025 | + def GetValue(self, fixture): |
| 1026 | + return fixture.DynamicProperty |
| 1027 | +").GetAttr("TestGetPublicDynamicObjectPropertyWorks").Invoke(); |
| 1028 | + |
| 1029 | + dynamic fixture = new DynamicFixture(); |
| 1030 | + fixture.DynamicProperty = property; |
| 1031 | + |
| 1032 | + using (Py.GIL()) |
| 1033 | + { |
| 1034 | + Assert.AreEqual(property, (model.GetValue(fixture) as PyObject).AsManagedObject(property.GetType())); |
| 1035 | + } |
| 1036 | + } |
| 1037 | + |
| 1038 | + [Test] |
| 1039 | + public void TestGetNullPublicDynamicObjectPropertyWorks() |
| 1040 | + { |
| 1041 | + dynamic model = PyModule.FromString("module", @" |
| 1042 | +from clr import AddReference |
| 1043 | +AddReference(""Python.EmbeddingTest"") |
| 1044 | +AddReference(""System"") |
| 1045 | +
|
| 1046 | +from Python.EmbeddingTest import * |
| 1047 | +
|
| 1048 | +class TestGetNullPublicDynamicObjectPropertyWorks: |
| 1049 | + def GetValue(self, fixture): |
| 1050 | + return fixture.DynamicProperty |
| 1051 | +
|
| 1052 | + def IsNone(self, fixture): |
| 1053 | + return fixture.DynamicProperty is None |
| 1054 | +").GetAttr("TestGetNullPublicDynamicObjectPropertyWorks").Invoke(); |
| 1055 | + |
| 1056 | + dynamic fixture = new DynamicFixture(); |
| 1057 | + fixture.DynamicProperty = null; |
| 1058 | + |
| 1059 | + using (Py.GIL()) |
| 1060 | + { |
| 1061 | + Assert.IsNull(model.GetValue(fixture)); |
| 1062 | + Assert.IsTrue(model.IsNone(fixture).As<bool>()); |
| 1063 | + } |
| 1064 | + } |
| 1065 | + |
| 1066 | + [Test] |
| 1067 | + public void TestGetNonExistingPublicDynamicObjectPropertyThrows() |
| 1068 | + { |
| 1069 | + dynamic model = PyModule.FromString("module", @" |
| 1070 | +from clr import AddReference |
| 1071 | +AddReference(""Python.EmbeddingTest"") |
| 1072 | +AddReference(""System"") |
| 1073 | +
|
| 1074 | +from Python.EmbeddingTest import * |
| 1075 | +
|
| 1076 | +class TestGetNonExistingPublicDynamicObjectPropertyThrows: |
| 1077 | + def GetValue(self, fixture): |
| 1078 | + try: |
| 1079 | + prop = fixture.AnotherProperty |
| 1080 | + except AttributeError as e: |
| 1081 | + return e |
| 1082 | +
|
| 1083 | + return None |
| 1084 | +").GetAttr("TestGetNonExistingPublicDynamicObjectPropertyThrows").Invoke(); |
| 1085 | + |
| 1086 | + dynamic fixture = new DynamicFixture(); |
| 1087 | + fixture.DynamicProperty = "Dynamic property"; |
| 1088 | + |
| 1089 | + using (Py.GIL()) |
| 1090 | + { |
| 1091 | + var result = model.GetValue(fixture) as PyObject; |
| 1092 | + Assert.IsFalse(result.IsNone()); |
| 1093 | + Assert.AreEqual(result.PyType, Exceptions.AttributeError); |
| 1094 | + Assert.AreEqual("'DynamicFixture' object has no attribute 'AnotherProperty'", |
| 1095 | + result.ToString()); |
| 1096 | + } |
| 1097 | + } |
| 1098 | + |
| 1099 | + private static TestCaseData[] DynamicPropertiesSetterTestCases() => new[] |
| 1100 | + { |
| 1101 | + new TestCaseData("True", null), |
| 1102 | + new TestCaseData("10", null), |
| 1103 | + new TestCaseData("10.1", null), |
| 1104 | + new TestCaseData("'Some string'", null), |
| 1105 | + new TestCaseData("datetime(2023, 6, 22)", null), |
| 1106 | + new TestCaseData("[1, 2, 3, 4, 5]", null), |
| 1107 | + new TestCaseData("System.DateTime(2023, 6, 22)", typeof(DateTime)), |
| 1108 | + new TestCaseData("TestPropertyAccess.TestPerson(123, 'John doe')", typeof(TestPerson)), |
| 1109 | + new TestCaseData("System.Collections.Generic.List[str]()", typeof(List<string>)), |
| 1110 | + }; |
| 1111 | + |
| 1112 | + [TestCaseSource(nameof(DynamicPropertiesSetterTestCases))] |
| 1113 | + public void TestSetPublicDynamicObjectPropertyWorks(string valueCode, Type expectedType) |
| 1114 | + { |
| 1115 | + dynamic model = PyModule.FromString("module", $@" |
| 1116 | +from clr import AddReference |
| 1117 | +AddReference(""Python.EmbeddingTest"") |
| 1118 | +AddReference(""System"") |
| 1119 | +
|
| 1120 | +from datetime import datetime |
| 1121 | +import System |
| 1122 | +from Python.EmbeddingTest import * |
| 1123 | +
|
| 1124 | +value = {valueCode} |
| 1125 | +
|
| 1126 | +class TestGetPublicDynamicObjectPropertyWorks: |
| 1127 | + def SetValue(self, fixture): |
| 1128 | + fixture.DynamicProperty = value |
| 1129 | +
|
| 1130 | + def GetPythonValue(self): |
| 1131 | + return value |
| 1132 | +").GetAttr("TestGetPublicDynamicObjectPropertyWorks").Invoke(); |
| 1133 | + |
| 1134 | + dynamic fixture = new DynamicFixture(); |
| 1135 | + |
| 1136 | + using (Py.GIL()) |
| 1137 | + { |
| 1138 | + model.SetValue(fixture); |
| 1139 | + var expectedAsPyObject = model.GetPythonValue() as PyObject; |
| 1140 | + var expected = expectedType != null ? expectedAsPyObject.AsManagedObject(expectedType) : expectedAsPyObject; |
| 1141 | + |
| 1142 | + Assert.AreEqual(expected, fixture.DynamicProperty); |
| 1143 | + } |
| 1144 | + } |
| 1145 | + |
| 1146 | + [Test] |
| 1147 | + public void TestSetNullPublicDynamicObjectPropertyWorks() |
| 1148 | + { |
| 1149 | + dynamic model = PyModule.FromString("module", $@" |
| 1150 | +from clr import AddReference |
| 1151 | +AddReference(""Python.EmbeddingTest"") |
| 1152 | +AddReference(""System"") |
| 1153 | +
|
| 1154 | +from datetime import datetime |
| 1155 | +import System |
| 1156 | +from Python.EmbeddingTest import * |
| 1157 | +
|
| 1158 | +class TestSetNullPublicDynamicObjectPropertyWorks: |
| 1159 | + def SetValue(self, fixture): |
| 1160 | + fixture.DynamicProperty = None |
| 1161 | +").GetAttr("TestSetNullPublicDynamicObjectPropertyWorks").Invoke(); |
| 1162 | + |
| 1163 | + dynamic fixture = new DynamicFixture(); |
| 1164 | + |
| 1165 | + using (Py.GIL()) |
| 1166 | + { |
| 1167 | + model.SetValue(fixture); |
| 1168 | + |
| 1169 | + Assert.IsTrue(fixture.DynamicProperty.IsNone()); |
| 1170 | + } |
| 1171 | + } |
| 1172 | + |
| 1173 | + [Test] |
| 1174 | + public void TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks() |
| 1175 | + { |
| 1176 | + var expected = "Non Dynamic Property"; |
| 1177 | + dynamic model = PyModule.FromString("module", $@" |
| 1178 | +from clr import AddReference |
| 1179 | +AddReference(""Python.EmbeddingTest"") |
| 1180 | +AddReference(""System"") |
| 1181 | +
|
| 1182 | +from datetime import datetime |
| 1183 | +import System |
| 1184 | +from Python.EmbeddingTest import * |
| 1185 | +
|
| 1186 | +class TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks: |
| 1187 | + def SetValue(self, fixture): |
| 1188 | + fixture.NonDynamicProperty = ""{expected}"" |
| 1189 | +").GetAttr("TestSetPublicNonDynamicObjectPropertyToActualPropertyWorks").Invoke(); |
| 1190 | + |
| 1191 | + var fixture = new DynamicFixture(); |
| 1192 | + |
| 1193 | + using (Py.GIL()) |
| 1194 | + { |
| 1195 | + model.SetValue(fixture); |
| 1196 | + Assert.AreEqual(expected, fixture.NonDynamicProperty); |
| 1197 | + Assert.AreEqual(expected, ((dynamic)fixture).NonDynamicProperty); |
| 1198 | + Assert.IsFalse(fixture.Properties.ContainsKey(nameof(fixture.NonDynamicProperty))); |
| 1199 | + } |
| 1200 | + } |
| 1201 | + |
929 | 1202 | [Explicit] |
930 | 1203 | [TestCase(true, TestName = "CSharpGetPropertyPerformance")] |
931 | 1204 | [TestCase(false, TestName = "PythonGetPropertyPerformance")] |
|
0 commit comments