PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/IronPython_Main/Runtime/Tests/SiteTest/SiteTestScenarios.Tests.Dynamic.cs

#
C# | 1750 lines | 1223 code | 315 blank | 212 comment | 33 complexity | 3b67d3bda5b9a4bb5bd510a67cdb4813 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. using Microsoft.Scripting.Utils;
  20. #endif
  21. using System;
  22. using System.Collections.Generic;
  23. using System.ComponentModel;
  24. using System.Dynamic;
  25. using System.Reflection;
  26. using System.Runtime.CompilerServices;
  27. using System.Runtime.Serialization;
  28. using System.Runtime.Serialization.Formatters.Binary;
  29. using System.Threading;
  30. using SiteTest.Actions;
  31. using System.IO;
  32. namespace SiteTest {
  33. partial class SiteTestScenarios {
  34. [Test("Remoted IDO")]
  35. private void Scenario_Remoted() {
  36. #region CallSite for each action
  37. var setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("__code__"));
  38. var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__code__"));
  39. #endregion
  40. // run locally
  41. object mbro = new MBRODynamicObject();
  42. Assert.AreEqual("MBRO_GetMember", getMember.Target(getMember, mbro));
  43. // run remotely
  44. var domain = AppDomain.CreateDomain("remote");
  45. mbro = domain.CreateInstanceAndUnwrap(typeof(MBRODynamicObject).Assembly.FullName, typeof(MBRODynamicObject).FullName);
  46. getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__code__"));
  47. Assert.AreEqual("123", getMember.Target(getMember, mbro));
  48. // run in current domain
  49. domain = AppDomain.CurrentDomain;
  50. mbro = domain.CreateInstanceAndUnwrap(typeof(MBRODynamicObject).Assembly.FullName, typeof(MBRODynamicObject).FullName);
  51. getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__code__"));
  52. Assert.AreEqual("MBRO_GetMember", getMember.Target(getMember, mbro));
  53. ClearRuleCache(getMember);
  54. // run remotely after clearing cache.
  55. domain = AppDomain.CreateDomain("remote");
  56. mbro = domain.CreateInstanceAndUnwrap(typeof(MBRODynamicObject).Assembly.FullName, typeof(MBRODynamicObject).FullName);
  57. getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__code__"));
  58. Assert.AreEqual("123", getMember.Target(getMember, mbro));
  59. // run locally again
  60. mbro = new MBRODynamicObject();
  61. Assert.AreEqual("MBRO_GetMember", getMember.Target(getMember, mbro));
  62. }
  63. #if CLR45
  64. [Test("Remoted IDO2")]
  65. private void Scenario_Remoted2() {
  66. #region CallSite for each action
  67. var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("Name"));
  68. #endregion
  69. object sdo = new SerializableDO();
  70. MemoryStream ms = new MemoryStream();
  71. BinaryFormatter formatter = new BinaryFormatter();
  72. formatter.Serialize(ms, sdo);
  73. ms.Seek(0, SeekOrigin.Begin);
  74. object sdo2 = formatter.Deserialize(ms);
  75. Assert.AreEqual("SerializableDO", getMember.Target(getMember, sdo2));
  76. }
  77. #endif
  78. // Support for remoted COM is NYI
  79. //[Test(TestState.COM, "Remoted COM")]
  80. //private void Scenario_RemotedCom() {
  81. // #region CallSite for each action
  82. // var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("pLong"));
  83. // #endregion
  84. // // run locally
  85. // MBRODynamicObject mbro = new MBRODynamicObject();
  86. // Assert.AreEqual(0, getMember.Target(getMember, mbro.GetComObj()));
  87. // // run remotely
  88. // var domain = AppDomain.CreateDomain("remote");
  89. // mbro = (MBRODynamicObject)domain.CreateInstanceAndUnwrap(typeof(MBRODynamicObject).Assembly.FullName, typeof(MBRODynamicObject).FullName);
  90. // getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("pLong"));
  91. // Assert.AreEqual("777", getMember.Target(getMember, mbro.GetComObj()));
  92. // // need to clear after TestGetMemberBinder.
  93. // ClearRuleCache(getMember);
  94. // // run in current domain
  95. // domain = AppDomain.CurrentDomain;
  96. // mbro = (MBRODynamicObject)domain.CreateInstanceAndUnwrap(typeof(MBRODynamicObject).Assembly.FullName, typeof(MBRODynamicObject).FullName);
  97. // getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("pLong"));
  98. // Assert.AreEqual(0, getMember.Target(getMember, mbro.GetComObj()));
  99. //}
  100. #region Add new Dynamic (IDO Helper) tests here.
  101. /* Call
  102. * Convert
  103. * Create
  104. * DeleteIndex
  105. * DeleteMember
  106. * GetIndex
  107. * GetMember
  108. * Invoke
  109. * Operation
  110. * SetIndex
  111. * SetMember
  112. */
  113. [Test("Simple cases for a basic derivation Dynamic")]
  114. private void Scenario_DynamicIDO_Simple() {
  115. //Get a simple Dynamic IDO
  116. TestDynamicObject dyn = new TestDynamicObject();
  117. #region CallSite for each MetaAction on this IDO
  118. var call = CallSite<Func<CallSite, object, object>>.Create(new TestInvokeMemberBinder("member"));
  119. var convert = CallSite<Func<CallSite, object, string>>.Create(new TestConvertBinder(typeof(String), true));
  120. var create = CallSite<Func<CallSite, object, object>>.Create(new TestCreateBinder());
  121. var deleteIndex = CallSite<Action<CallSite, object>>.Create(new TestDeleteIndexBinder());
  122. var deleteMember = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member"));
  123. var getIndex = CallSite<Func<CallSite, object, object>>.Create(new TestGetIndexBinder());
  124. var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("member"));
  125. var invoke = CallSite<Func<CallSite, object, object>>.Create(new TestInvokeBinder());
  126. var binaryOperation = CallSite<Func<CallSite, object, object, object>>.Create(new TestBinaryOperationBinder(ExpressionType.Add));
  127. var unaryOperation = CallSite<Func<CallSite, object, object>>.Create(new TestUnaryOperationBinder(ExpressionType.Increment));
  128. var setIndex = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetIndexBinder());
  129. var setIndex2 = CallSite<Func<CallSite, object, object, object, object>>.Create(new TestSetIndexBinder());
  130. var setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member"));
  131. #endregion
  132. /* Invoke each CallSite. We're using a basic derivation of Dynamic which applies no binding
  133. * logic of its own. The default binding of Dynamic is to fallback to the supplied CallSiteBinder
  134. * every time.
  135. *
  136. * We're also using fairly basic derivations of the basic StandardActions for our CallSiteBinders
  137. * that return a Rule that throws a special BindingException when invoked. This is enough to
  138. * let us know that the basic binding mechanism worked from end-to-end.
  139. */
  140. AssertExceptionThrown<BindingException>(() => call.Target(call, dyn));
  141. AssertExceptionThrown<BindingException>(() => convert.Target(convert, dyn));
  142. AssertExceptionThrown<BindingException>(() => create.Target(create, dyn));
  143. AssertExceptionThrown<BindingException>(() => deleteIndex.Target(deleteIndex, dyn));
  144. AssertExceptionThrown<BindingException>(() => deleteMember.Target(deleteMember, dyn));
  145. AssertExceptionThrown<BindingException>(() => getIndex.Target(getIndex, dyn));
  146. AssertExceptionThrown<BindingException>(() => getMember.Target(getMember, dyn));
  147. AssertExceptionThrown<BindingException>(() => invoke.Target(invoke, dyn));
  148. AssertExceptionThrown<BindingException>(() => binaryOperation.Target(binaryOperation, dyn, 5));
  149. AssertExceptionThrown<BindingException>(() => unaryOperation.Target(unaryOperation, dyn));
  150. AssertExceptionThrown<ArgumentException>(() => setIndex.Target(setIndex, dyn, 3));
  151. AssertExceptionThrown<BindingException>(() => setIndex2.Target(setIndex2, dyn, 3, 10));
  152. AssertExceptionThrown<BindingException>(() => setMember.Target(setMember, dyn, 5));
  153. }
  154. [Test("Simple cases for a derivation DynamicObject that fails each operation")]
  155. private void Scenario_FailingDynamicObject() {
  156. //Get a simple Dynamic IDO
  157. TestDynamicObject3 dyn = new TestDynamicObject3();
  158. #region CallSite for each MetaAction on this IDO
  159. var call = CallSite<Func<CallSite, object, object>>.Create(new TestInvokeMemberBinder("member"));
  160. var convert = CallSite<Func<CallSite, object, string>>.Create(new TestConvertBinder(typeof(String), true));
  161. var create = CallSite<Func<CallSite, object, object>>.Create(new TestCreateBinder());
  162. var deleteIndex = CallSite<Action<CallSite, object>>.Create(new TestDeleteIndexBinder());
  163. var deleteMember = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member"));
  164. var getIndex = CallSite<Func<CallSite, object, object>>.Create(new TestGetIndexBinder());
  165. var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("member"));
  166. var invoke = CallSite<Func<CallSite, object, object>>.Create(new TestInvokeBinder());
  167. var binaryOperation = CallSite<Func<CallSite, object, object, object>>.Create(new TestBinaryOperationBinder(ExpressionType.Add));
  168. var unaryOperation = CallSite<Func<CallSite, object, object>>.Create(new TestUnaryOperationBinder(ExpressionType.Increment));
  169. var setIndex = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetIndexBinder());
  170. var setIndex2 = CallSite<Func<CallSite, object, object, object, object>>.Create(new TestSetIndexBinder());
  171. var setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member"));
  172. #endregion
  173. AssertExceptionThrown<BindingException>(() => call.Target(call, dyn));
  174. AssertExceptionThrown<BindingException>(() => convert.Target(convert, dyn));
  175. AssertExceptionThrown<BindingException>(() => create.Target(create, dyn));
  176. AssertExceptionThrown<BindingException>(() => deleteIndex.Target(deleteIndex, dyn));
  177. AssertExceptionThrown<BindingException>(() => deleteMember.Target(deleteMember, dyn));
  178. AssertExceptionThrown<BindingException>(() => getIndex.Target(getIndex, dyn));
  179. AssertExceptionThrown<BindingException>(() => getMember.Target(getMember, dyn));
  180. AssertExceptionThrown<BindingException>(() => invoke.Target(invoke, dyn));
  181. AssertExceptionThrown<BindingException>(() => binaryOperation.Target(binaryOperation, dyn, 5));
  182. AssertExceptionThrown<BindingException>(() => unaryOperation.Target(unaryOperation, dyn));
  183. AssertExceptionThrown<ArgumentException>(() => setIndex.Target(setIndex, dyn, 3));
  184. AssertExceptionThrown<BindingException>(() => setIndex2.Target(setIndex2, dyn, 3, 10));
  185. AssertExceptionThrown<BindingException>(() => setMember.Target(setMember, dyn, 5));
  186. }
  187. [Test("Simple cases for a basic Expando IDO")]
  188. private void Scenario_ExpandoObject() {
  189. //ExpandoObject is a real IDO implementing several actions
  190. var exp = new ExpandoObject();
  191. #region CallSite for each action
  192. var invokeMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestInvokeMemberBinder("member"));
  193. var deleteIndex = CallSite<Action<CallSite, object>>.Create(new TestDeleteIndexBinder());
  194. var deleteMember = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member"));
  195. var deleteMemberUCase = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("MEMBER"));
  196. var setIndex = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetIndexBinder());
  197. var setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member"));
  198. var setMemberUCase = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("MEMBER"));
  199. var setIndex2 = CallSite<Func<CallSite, object, object, object, object>>.Create(new TestSetIndexBinder());
  200. var convert = CallSite<Func<CallSite, object, string>>.Create(new TestConvertBinder(typeof(String), true));
  201. var create = CallSite<Func<CallSite, object, object>>.Create(new TestCreateBinder());
  202. var getIndex = CallSite<Func<CallSite, object, object>>.Create(new TestGetIndexBinder());
  203. var getMember = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("member"));
  204. var getMemberUCase = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("MEMBER"));
  205. var getMember2 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__code__")); //GetMember2 gets a member in the language binder.
  206. var invoke = CallSite<Func<CallSite, object, object>>.Create(new TestInvokeBinder());
  207. var unaryOperationNeg = CallSite<Func<CallSite, object, object>>.Create(new TestUnaryOperationBinder(ExpressionType.Increment));
  208. var binaryOperationNeg = CallSite<Func<CallSite, object, object, object>>.Create(new TestBinaryOperationBinder(ExpressionType.Add));
  209. #endregion
  210. //Those actions implemented by ExpandoObject should succeed (SetMember, GetMember, etc).
  211. //The others should fall back to the CallSiteBinder and trigger a BindingException.
  212. Assert.AreEqual(getMember.Target(getMember2, exp), "123");
  213. //deleting a non-existing member results in falling back to the language's binder
  214. AssertExceptionThrown<BindingException>(() => deleteMember.Target(deleteMember, exp));
  215. //Actions implemented by ExpandoObject (GetMember, SetMember, DeleteMember)
  216. AssertExceptionThrown<BindingException>(() => getMember.Target(getMember, exp));
  217. AssertExceptionThrown<BindingException>(() => getMemberUCase.Target(getMemberUCase, exp));
  218. AssertExceptionThrown<BindingException>(() => deleteMember.Target(deleteMember, exp));
  219. setMember.Target(setMember, exp, 52);
  220. Assert.AreEqual(getMember.Target(getMember, exp), 52);
  221. AssertExceptionThrown<BindingException>(() => getMemberUCase.Target(getMemberUCase, exp));
  222. setMemberUCase.Target(setMemberUCase, exp, 9);
  223. Assert.AreEqual(getMember.Target(getMember, exp), 52);
  224. Assert.AreEqual(getMemberUCase.Target(getMemberUCase, exp), 9);
  225. deleteMember.Target(deleteMember, exp);
  226. AssertExceptionThrown<BindingException>(() => getMember.Target(getMember, exp));
  227. Assert.AreEqual(getMemberUCase.Target(getMemberUCase, exp), 9);
  228. deleteMemberUCase.Target(deleteMemberUCase, exp);
  229. AssertExceptionThrown<BindingException>(() => getMember.Target(getMember, exp));
  230. AssertExceptionThrown<BindingException>(() => getMemberUCase.Target(getMemberUCase, exp));
  231. //assign a delegate value to the member
  232. setMember.Target(setMember, exp, (Func<int, int>)(x => x + 1));
  233. //invokeMember should work since the member value is invokable now.
  234. Assert.AreEqual(invokeMember.Target(invokeMember, exp, 100), 101);
  235. setMember.Target(setMember, exp, 52);
  236. //Actions not implemented by ExpandoObject (which default to our basic MetaActions, which return rules throwing BindingErrors)
  237. //the member value is not invokable so invokeMember will result in exception
  238. AssertExceptionThrown<BindingException>(() => invokeMember.Target(invokeMember, exp, 100));
  239. AssertExceptionThrown<BindingException>(() => convert.Target(convert, exp));
  240. AssertExceptionThrown<BindingException>(() => create.Target(create, exp));
  241. AssertExceptionThrown<BindingException>(() => deleteIndex.Target(deleteIndex, exp));
  242. AssertExceptionThrown<BindingException>(() => getIndex.Target(getIndex, exp));
  243. AssertExceptionThrown<BindingException>(() => invoke.Target(invoke, exp));
  244. AssertExceptionThrown<BindingException>(() => binaryOperationNeg.Target(binaryOperationNeg, exp, 7));
  245. AssertExceptionThrown<BindingException>(() => unaryOperationNeg.Target(unaryOperationNeg, exp));
  246. AssertExceptionThrown<ArgumentException>(() => setIndex.Target(setIndex, exp, 3));
  247. AssertExceptionThrown<BindingException>(() => setIndex2.Target(setIndex2, exp, 3, 10));
  248. }
  249. [Test("Basic tests for INotifyPropertyChanged on ExpandoObject")]
  250. private void Scenario_ExpandoObject_NotifyPropertyChanged() {
  251. var expando = new ExpandoObject();
  252. var inpc = expando as INotifyPropertyChanged;
  253. var dict = expando as IDictionary<string, object>;
  254. string changed = "";
  255. PropertyChangedEventHandler handler = (s, e) => {
  256. Assert.AreEqual(expando, s);
  257. changed += e.PropertyName;
  258. };
  259. inpc.PropertyChanged += handler;
  260. Assert.AreEqual(changed, "");
  261. SetMember(expando, "foo", 123);
  262. Assert.AreEqual(changed, "foo"); changed = "";
  263. SetMemberIgnoreCase(expando, "FOO", 456);
  264. Assert.AreEqual(changed, "foo"); changed = "";
  265. DeleteMember(expando, "foo");
  266. Assert.AreEqual(changed, "foo"); changed = "";
  267. AssertExceptionThrown<BindingException>(() => DeleteMember(expando, "foo"));
  268. Assert.AreEqual(changed, "");
  269. SetMember(expando, "foo", 123);
  270. Assert.AreEqual(changed, "foo"); changed = "";
  271. SetMember(expando, "bar", 456);
  272. Assert.AreEqual(changed, "bar"); changed = "";
  273. SetMember(expando, "zed", 456);
  274. Assert.AreEqual(changed, "zed"); changed = "";
  275. SetMember(expando, "red", 789);
  276. Assert.AreEqual(changed, "red"); changed = "";
  277. DeleteMemberIgnoreCase(expando, "ZeD");
  278. Assert.AreEqual(changed, "zed"); changed = "";
  279. dict.Clear();
  280. Assert.AreEqual(changed, "foobarred"); changed = "";
  281. dict.Add("baz", "555");
  282. Assert.AreEqual(changed, "baz"); changed = "";
  283. dict["baz"] = "555";
  284. // We detect duplicates, so we don't fire it.
  285. // It would be okay to fire here, though.
  286. Assert.AreEqual(changed, "");
  287. dict["baz"] = "abc";
  288. Assert.AreEqual(changed, "baz"); changed = "";
  289. dict.Remove(new KeyValuePair<string, object>("baz", "zzz"));
  290. Assert.AreEqual(changed, "");
  291. dict.Remove(new KeyValuePair<string, object>("baz", "abc"));
  292. Assert.AreEqual(changed, "baz"); changed = "";
  293. dict["baz"] = "abc";
  294. dict.Remove("baz");
  295. Assert.AreEqual(changed, "bazbaz"); changed = "";
  296. // Add and remove handlers
  297. inpc.PropertyChanged += handler;
  298. dict["quux"] = 1;
  299. Assert.AreEqual(changed, "quuxquux"); changed = "";
  300. inpc.PropertyChanged -= handler;
  301. dict["quux"] = 2;
  302. Assert.AreEqual(changed, "quux"); changed = "";
  303. inpc.PropertyChanged -= handler;
  304. dict["quux"] = 3;
  305. Assert.AreEqual(changed, "");
  306. }
  307. private static void SetMember(ExpandoObject expando, string name, object value) {
  308. var site = CallSite<Action<CallSite, ExpandoObject, object>>.Create(new TestSetMemberBinder(name));
  309. site.Target(site, expando, value);
  310. }
  311. private static void SetMemberIgnoreCase(ExpandoObject expando, string name, object value) {
  312. var site = CallSite<Action<CallSite, ExpandoObject, object>>.Create(new TestSetMemberBinder(name, true));
  313. site.Target(site, expando, value);
  314. }
  315. private static void DeleteMember(ExpandoObject expando, string name) {
  316. var site = CallSite<Action<CallSite, ExpandoObject>>.Create(new TestDeleteMemberBinder(name));
  317. site.Target(site, expando);
  318. }
  319. private static void DeleteMemberIgnoreCase(ExpandoObject expando, string name) {
  320. var site = CallSite<Action<CallSite, ExpandoObject>>.Create(new TestDeleteMemberBinder(name, true));
  321. site.Target(site, expando);
  322. }
  323. private static object GetMember(ExpandoObject expando, string name) {
  324. var site = CallSite<Func<CallSite, ExpandoObject, object>>.Create(new TestGetMemberBinder(name));
  325. return site.Target(site, expando);
  326. }
  327. [Test("Test GetDynamicMemberNames for a basic Expando IDO")]
  328. private void Scenario_ExpandoObjectGetDynamicMemberNames() {
  329. var exp = new ExpandoObject();
  330. var setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member1"));
  331. setMember.Target(setMember, exp, 10);
  332. var mo = ((IDynamicMetaObjectProvider)exp).GetMetaObject(Expression.Parameter(typeof(object), "arg0"));
  333. List<string> memberNames = new List<string>(mo.GetDynamicMemberNames());
  334. Assert.AreEqual(1, memberNames.Count);
  335. Assert.AreEqual("member1", memberNames[0]);
  336. setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member2"));
  337. setMember.Target(setMember, exp, 20);
  338. setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member3"));
  339. setMember.Target(setMember, exp, 30);
  340. memberNames = new List<string>(mo.GetDynamicMemberNames());
  341. Assert.AreEqual(3, memberNames.Count);
  342. var deleteMember = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member1"));
  343. deleteMember.Target(deleteMember, exp);
  344. memberNames = new List<string>(mo.GetDynamicMemberNames());
  345. Assert.AreEqual(2, memberNames.Count);
  346. //Add the deleted member back. Since ExpandoObject didn't really deleted the member from the class
  347. //(only the value got deleted), the added member should be at the original index.
  348. setMember = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member1"));
  349. setMember.Target(setMember, exp, 100);
  350. memberNames = new List<string>(mo.GetDynamicMemberNames());
  351. Assert.AreEqual(3, memberNames.Count);
  352. }
  353. #region Testing case-insensitive behavior of ExpandoObject
  354. [Test("Test ExpandoObject when used with case-insensitive get binders-1")]
  355. private void Senario_ExpandoObjectCaseInsensitiveGet1() {
  356. var exp = new ExpandoObject();
  357. ((IDictionary<string, object>)exp).Add("foo", 1);
  358. ((IDictionary<string, object>)exp).Add("FOO", 2);
  359. var getMemberIgnoreCase = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("FoO", true));
  360. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  361. ((IDictionary<string, object>)exp).Remove("foo");
  362. Assert.AreEqual(getMemberIgnoreCase.Target(getMemberIgnoreCase, exp), 2);
  363. ((IDictionary<string, object>)exp).Remove("FOO");
  364. AssertExceptionThrown<BindingException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  365. }
  366. [Test("Test ExpandoObject when used with case-insensitive set binders-1")]
  367. private void Senario_ExpandoObjectCaseInsensitiveSet1() {
  368. var exp = new ExpandoObject();
  369. ((IDictionary<string, object>)exp).Add("foo", 1);
  370. ((IDictionary<string, object>)exp).Add("FOO", 2);
  371. var setMemberIgnoreCase = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("FoO", true));
  372. AssertExceptionThrown<AmbiguousMatchException>(() => setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 100));
  373. ((IDictionary<string, object>)exp).Remove("foo");
  374. setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 100);
  375. Assert.AreEqual(((IDictionary<string, object>)exp)["FOO"], 100);
  376. ((IDictionary<string, object>)exp).Remove("FOO");
  377. setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 101);
  378. Assert.AreEqual(((IDictionary<string, object>)exp)["FoO"], 101);
  379. }
  380. [Test("Test ExpandoObject when used with case-insensitive delete binders-1")]
  381. private void Senario_ExpandoObjectCaseInsensitiveDelete1() {
  382. var exp = new ExpandoObject();
  383. ((IDictionary<string, object>)exp).Add("foo", 1);
  384. ((IDictionary<string, object>)exp).Add("FOO", 2);
  385. var deleteMemberIgnoreCase = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("FoO", true));
  386. AssertExceptionThrown<AmbiguousMatchException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  387. ((IDictionary<string, object>)exp).Remove("foo");
  388. Assert.IsTrue(((IDictionary<string, object>)exp).ContainsKey("FOO"));
  389. deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp);
  390. Assert.IsFalse(((IDictionary<string, object>)exp).ContainsKey("FOO"));
  391. AssertExceptionThrown<BindingException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  392. }
  393. [Test("Test ExpandoObject when used with case-insensitive get binders-2")]
  394. private void Senario_ExpandoObjectCaseInsensitiveGet2() {
  395. var exp = new ExpandoObject();
  396. ((IDictionary<string, object>)exp).Add("foo", 1);
  397. ((IDictionary<string, object>)exp).Add("FOO", 2);
  398. ((IDictionary<string, object>)exp).Remove("foo");
  399. ((IDictionary<string, object>)exp).Remove("FOO");
  400. var getMemberIgnoreCase = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("FoO", true));
  401. AssertExceptionThrown<BindingException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  402. ((IDictionary<string, object>)exp)["foo"] = 1;
  403. Assert.AreEqual(getMemberIgnoreCase.Target(getMemberIgnoreCase, exp), 1);
  404. ((IDictionary<string, object>)exp)["FOO"] = 2;
  405. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  406. }
  407. [Test("Test ExpandoObject when used with case-insensitive set binders-2")]
  408. private void Senario_ExpandoObjectCaseInsensitiveSet2() {
  409. var exp = new ExpandoObject();
  410. ((IDictionary<string, object>)exp).Add("foo", 1);
  411. ((IDictionary<string, object>)exp).Add("FOO", 2);
  412. ((IDictionary<string, object>)exp).Remove("foo");
  413. ((IDictionary<string, object>)exp).Remove("FOO");
  414. var setMemberIgnoreCase = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("FoO", true));
  415. Assert.IsFalse(((IDictionary<string, object>)exp).ContainsKey("foO"));
  416. setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 101);
  417. Assert.AreEqual(((IDictionary<string, object>)exp)["FoO"], 101);
  418. ((IDictionary<string, object>)exp)["foo"] = 1;
  419. AssertExceptionThrown<AmbiguousMatchException>(() => setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 100));
  420. }
  421. [Test("Test ExpandoObject when used with case-insensitive delete binders-2")]
  422. private void Senario_ExpandoObjectCaseInsensitiveDelete2() {
  423. var exp = new ExpandoObject();
  424. ((IDictionary<string, object>)exp).Add("foo", 1);
  425. ((IDictionary<string, object>)exp).Add("FOO", 2);
  426. ((IDictionary<string, object>)exp).Remove("foo");
  427. ((IDictionary<string, object>)exp).Remove("FOO");
  428. var deleteMemberIgnoreCase = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("FoO", true));
  429. AssertExceptionThrown<BindingException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  430. ((IDictionary<string, object>)exp)["foo"] = 1;
  431. Assert.IsTrue(((IDictionary<string, object>)exp).ContainsKey("foo"));
  432. deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp);
  433. Assert.IsFalse(((IDictionary<string, object>)exp).ContainsKey("foo"));
  434. ((IDictionary<string, object>)exp)["foo"] = 1;
  435. ((IDictionary<string, object>)exp)["FOO"] = 2;
  436. AssertExceptionThrown<AmbiguousMatchException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  437. }
  438. [Test("Test ExpandoObject when used with case-insensitive get binders-3")]
  439. private void Senario_ExpandoObjectCaseInsensitiveGet3() {
  440. var exp = new ExpandoObject();
  441. ((IDictionary<string, object>)exp).Add("foo", 1);
  442. ((IDictionary<string, object>)exp).Add("FOO", 2);
  443. ((IDictionary<string, object>)exp).Remove("FOO");
  444. var getMemberIgnoreCase = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("FoO", true));
  445. Assert.AreEqual(getMemberIgnoreCase.Target(getMemberIgnoreCase, exp), 1);
  446. ((IDictionary<string, object>)exp).Remove("foo");
  447. AssertExceptionThrown<BindingException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  448. ((IDictionary<string, object>)exp)["foo"] = 1;
  449. ((IDictionary<string, object>)exp)["FOO"] = 2;
  450. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  451. }
  452. [Test("Test ExpandoObject when used with case-insensitive set binders-3")]
  453. private void Senario_ExpandoObjectCaseInsensitiveSet3() {
  454. var exp = new ExpandoObject();
  455. ((IDictionary<string, object>)exp).Add("foo", 1);
  456. ((IDictionary<string, object>)exp).Add("FOO", 2);
  457. ((IDictionary<string, object>)exp).Remove("foo");
  458. var setMemberIgnoreCase = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("FoO", true));
  459. Assert.AreEqual(((IDictionary<string, object>)exp)["FOO"], 2);
  460. setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 101);
  461. Assert.AreEqual(((IDictionary<string, object>)exp)["FOO"], 101);
  462. ((IDictionary<string, object>)exp)["foo"] = 1;
  463. AssertExceptionThrown<AmbiguousMatchException>(() => setMemberIgnoreCase.Target(setMemberIgnoreCase, exp, 100));
  464. }
  465. [Test("Test ExpandoObject when used with case-insensitive delete binders-3")]
  466. private void Senario_ExpandoObjectCaseInsensitiveDelete3() {
  467. var exp = new ExpandoObject();
  468. ((IDictionary<string, object>)exp).Add("foo", 1);
  469. ((IDictionary<string, object>)exp).Add("FOO", 2);
  470. ((IDictionary<string, object>)exp).Remove("FOO");
  471. var deleteMemberIgnoreCase = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("FoO", true));
  472. Assert.IsTrue(((IDictionary<string, object>)exp).ContainsKey("foo"));
  473. deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp);
  474. Assert.IsFalse(((IDictionary<string, object>)exp).ContainsKey("foo"));
  475. AssertExceptionThrown<BindingException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  476. ((IDictionary<string, object>)exp)["foo"] = 1;
  477. ((IDictionary<string, object>)exp)["FOO"] = 2;
  478. AssertExceptionThrown<AmbiguousMatchException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  479. }
  480. [Test("Test ExpandoObject when used with binders that mix case sensitivity")]
  481. private void Scenario_ExpandoObjectMixCaseSensitivity() {
  482. var exp = new ExpandoObject();
  483. var setMember1 = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("Member"));
  484. var setMember2 = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member"));
  485. var setMember3 = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("MEMBER"));
  486. var setMemberIgnoreCase1 = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("member", true));
  487. var setMemberIgnoreCase2 = CallSite<Func<CallSite, object, object, object>>.Create(new TestSetMemberBinder("MEMBER", true));
  488. var getMember1 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("Member"));
  489. var getMember2 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("member"));
  490. var getMemberIgnoreCase1 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("member", true));
  491. var getMemberIgnoreCase2 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("Member", true));
  492. var getMemberIgnoreCase3 = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("MEMBER", true));
  493. var deleteMember = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member"));
  494. var deleteMemberIgnoreCase = CallSite<Action<CallSite, object>>.Create(new TestDeleteMemberBinder("member", true));
  495. //Verify that ignore case get can fall back correctly when no match
  496. var getMemberIgnoreCase = CallSite<Func<CallSite, object, object>>.Create(new TestGetMemberBinder("__cOdE__", true));
  497. Assert.AreEqual("123", getMemberIgnoreCase.Target(getMemberIgnoreCase, exp));
  498. AssertExceptionThrown<BindingException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  499. //exp.member = 1 (ignore case)
  500. setMemberIgnoreCase1.Target(setMemberIgnoreCase1, exp, 100);
  501. //If case insensitive, get the match if there is only one.
  502. //get exp.Member = 100, found the only match
  503. Assert.AreEqual(getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp), 100);
  504. Assert.AreEqual(getMemberIgnoreCase2.Target(getMemberIgnoreCase2, exp), 100);
  505. deleteMember.Target(deleteMember, exp);
  506. AssertExceptionThrown<BindingException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  507. ((IDictionary<string, object>)exp).Add("member", 1001);
  508. Assert.AreEqual(getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp), 1001);
  509. ((IDictionary<string, object>)exp)["member"] = 1;
  510. //exp.Member = 1 (case insensitive) overwrites the matching member
  511. setMemberIgnoreCase2.Target(setMemberIgnoreCase2, exp, 1);
  512. Assert.AreEqual(getMember2.Target(getMember2, exp), 1);
  513. Assert.AreEqual(getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp), 1);
  514. Assert.AreEqual(getMemberIgnoreCase2.Target(getMemberIgnoreCase2, exp), 1);
  515. //exp.Member = 2 (case sensitive)
  516. setMember1.Target(setMember1, exp, 2);
  517. //get exp.member (ignore case) results in AmbigousMatchException
  518. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  519. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase2.Target(getMemberIgnoreCase2, exp));
  520. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase3.Target(getMemberIgnoreCase3, exp));
  521. //Delete exp.member (case sensitive)
  522. deleteMember.Target(deleteMember, exp);
  523. Assert.AreEqual(getMember1.Target(getMember1, exp), 2);
  524. AssertExceptionThrown<BindingException>(() => getMember2.Target(getMember2, exp));
  525. Assert.AreEqual(getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp), 2);
  526. Assert.AreEqual(getMemberIgnoreCase2.Target(getMemberIgnoreCase1, exp), 2);
  527. //exp.member = 1 (case sensitive)
  528. setMember2.Target(setMember2, exp, 1);
  529. //exp.MEMBER = 3 (ignore case) results in AmbiguousMatchException
  530. AssertExceptionThrown<AmbiguousMatchException>(() => setMemberIgnoreCase2.Target(setMemberIgnoreCase2, exp, 3));
  531. //set exp.MEMBER = 3, case-sensitive
  532. setMember3.Target(setMember3, exp, 3);
  533. //get exp.MEMBER (ignore case) AmbiguousMatchException
  534. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase3.Target(getMemberIgnoreCase3, exp));
  535. //Get exp.Member case sensitively
  536. Assert.AreEqual(getMember1.Target(getMember1, exp), 2);
  537. //Get exp.member case sensitively
  538. Assert.AreEqual(getMember2.Target(getMember2, exp), 1);
  539. //Delete exp.member (case sensitive)
  540. deleteMember.Target(deleteMember, exp);
  541. //Get exp.member (case sensitive) results in BindingException
  542. AssertExceptionThrown<BindingException>(() => getMember2.Target(getMember2, exp));
  543. //Get exp.Member case sensitively
  544. Assert.AreEqual(getMember1.Target(getMember1, exp), 2);
  545. //Get exp.member (case insensitive) results in AmbiguousMatchException
  546. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  547. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase2.Target(getMemberIgnoreCase2, exp));
  548. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase3.Target(getMemberIgnoreCase3, exp));
  549. //add the deleted expt.member back (case sensitive)
  550. setMember2.Target(setMember2, exp, 1);
  551. //get exp.member (case insensitive) results in AmbiguousMatchException
  552. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  553. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase2.Target(getMemberIgnoreCase2, exp));
  554. //apply a case insensitive delete binder results in AmbigousMatchException
  555. AssertExceptionThrown<AmbiguousMatchException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  556. //Delete exp.member (case sensitive)
  557. deleteMember.Target(deleteMember, exp);
  558. //delete exp.member case insensitively results in AmbiguousMatchException
  559. AssertExceptionThrown<AmbiguousMatchException>(() => deleteMemberIgnoreCase.Target(deleteMemberIgnoreCase, exp));
  560. //Get exp.member case sensitively results in exception
  561. AssertExceptionThrown<BindingException>(() => getMember2.Target(getMember2, exp));
  562. //Set exp.member case insensitively results in AmbiguousMatchException
  563. AssertExceptionThrown<AmbiguousMatchException>(() => setMemberIgnoreCase2.Target(setMemberIgnoreCase2, exp, 100));
  564. //Get exp.member case insensitively results in AmbiguousMatchException
  565. AssertExceptionThrown<AmbiguousMatchException>(() => getMemberIgnoreCase1.Target(getMemberIgnoreCase1, exp));
  566. }
  567. #endregion
  568. [Test("Test the IDictionary<string, object> members of ExpandoObject")]
  569. private void Scenario_ExpandoObjectIDictionaryMembers() {
  570. var exp = (IDictionary<string, object>)(new ExpandoObject());
  571. Assert.IsFalse(exp.IsReadOnly);
  572. KeyValuePair<string, object> kv1 = new KeyValuePair<string, object>("a", 1);
  573. KeyValuePair<string, object> kv2 = new KeyValuePair<string, object>("b", 2);
  574. KeyValuePair<string, object> kv3 = new KeyValuePair<string, object>("c", 3);
  575. KeyValuePair<string, object> kv4 = new KeyValuePair<string, object>("c", 4);
  576. KeyValuePair<string, object> kv5 = new KeyValuePair<string, object>("d", 5);
  577. exp.Add("a", 1);
  578. exp.Add("b", 2);
  579. exp.Add(kv3);
  580. Assert.IsTrue(exp.Contains(kv1));
  581. Assert.IsTrue(exp.Contains(kv2));
  582. Assert.IsTrue(exp.Contains(kv3));
  583. Assert.IsFalse(exp.Contains(kv5));
  584. Assert.IsTrue(exp.ContainsKey("a"));
  585. Assert.IsTrue(exp.ContainsKey("b"));
  586. Assert.IsTrue(exp.ContainsKey("c"));
  587. Assert.IsTrue(exp.Remove(kv3));
  588. Assert.IsFalse(exp.ContainsKey("c"));
  589. Assert.IsFalse(exp.Remove(kv3)); //Try to remove non-existant
  590. exp.Add(kv3);
  591. Assert.IsFalse(exp.Remove(kv4)); //Remove KVP with same key, but different value
  592. Assert.AreEqual(3, exp.Count); //Nothing should be removed
  593. //adding the same key causes exception
  594. AssertExceptionThrown<ArgumentException>(() => exp.Add("a", 100));
  595. Assert.AreEqual(3, exp.Count);
  596. List<string> keys = new List<string>(exp.Keys);
  597. Assert.AreEqual(keys[0], "a");
  598. List<object> values = new List<object>(exp.Values);
  599. Assert.AreEqual(values[2], 3);
  600. // Various valid/invalid cases with KeyCollection
  601. AssertExceptionThrown<NotSupportedException>(() => exp.Keys.Add("x"));
  602. AssertExceptionThrown<NotSupportedException>(() => exp.Keys.Clear());
  603. Assert.IsTrue(exp.Keys.Contains("a"));
  604. Assert.IsFalse(exp.Keys.Contains("blah"));
  605. Assert.IsTrue(exp.Keys.IsReadOnly);
  606. AssertExceptionThrown<NotSupportedException>(() => exp.Keys.Remove("HI"));
  607. Assert.IsTrue(exp.Keys.Contains("b"));
  608. Assert.IsFalse(exp.Keys.Contains("foo"));
  609. // Various valid/invalid cases with ValueCollection
  610. AssertExceptionThrown<NotSupportedException>(() => exp.Values.Add(2));
  611. AssertExceptionThrown<NotSupportedException>(() => exp.Values.Clear());
  612. Assert.IsTrue(exp.Values.Contains(2));
  613. Assert.IsFalse(exp.Values.Contains(-2));
  614. Assert.IsFalse(exp.Values.Contains(-2));
  615. Assert.IsTrue(exp.Values.IsReadOnly);
  616. AssertExceptionThrown<NotSupportedException>(() => exp.Values.Remove(1));
  617. Assert.IsFalse(exp.Values.Contains("foo"));
  618. //Iterate the Keys or Values collection and the expando changes will
  619. //cause InvalidOperationException
  620. AssertExceptionThrown<InvalidOperationException>(() => IterateAndModifyKeyCollection(exp));
  621. AssertExceptionThrown<InvalidOperationException>(() => IterateAndModifyValueCollection(exp));
  622. // Additional iterator cases
  623. IterateAndModifyKeyCollection2(exp);
  624. IterateAndModifyValueCollection2(exp);
  625. IterateAndModifyKeyCollection3(exp);
  626. IterateAndModifyValueCollection3(exp);
  627. IterateAndModifyKeyCollection4(exp);
  628. IterateAndModifyValueCollection4(exp);
  629. object value;
  630. Assert.IsTrue(exp.TryGetValue("b", out value));
  631. Assert.AreEqual(2, value);
  632. Assert.IsFalse(exp.TryGetValue("x", out value));
  633. KeyValuePair<string, object>[] arr = new KeyValuePair<string, object>[10];
  634. exp.CopyTo(arr, 0);
  635. Assert.AreEqual(arr[2].Value, 3);
  636. AssertExceptionThrown<ArgumentOutOfRangeException>(() => exp.CopyTo(arr, 8));
  637. foreach (var kv in exp) {
  638. Assert.IsTrue(kv.Value != null);
  639. }
  640. // Use non-generic collection
  641. var exp2 = (System.Collections.IEnumerable)(exp);
  642. int cnt = 0;
  643. foreach (var kv in exp2) {
  644. cnt++;
  645. }
  646. Assert.AreEqual(exp.Count, cnt);
  647. //iterate and modify the expando will cause exception
  648. AssertExceptionThrown<InvalidOperationException>(() => IterateAndModifyExpando(exp));
  649. Assert.IsTrue(exp.Remove("a"));
  650. Assert.IsFalse(exp.Remove("a")); //the 2nd time will fail.
  651. //Assert.AreEqual(2, exp.Count);
  652. //add ["a", 100]
  653. exp.Add("a", 100);
  654. Assert.AreEqual(100, exp["a"]);
  655. exp["a"] = 1;
  656. Assert.AreEqual(1, exp["a"]);
  657. AssertExceptionThrown<KeyNotFoundException>(() => value = exp["e"]);
  658. exp["e"] = 5;
  659. Assert.AreEqual(exp["e"], 5);
  660. exp["e"] = 6;
  661. Assert.AreEqual(exp["e"], 6);
  662. Assert.AreEqual(exp.Remove(new KeyValuePair<string, object>("e", 5)), false);
  663. Assert.AreEqual(exp.Remove(new KeyValuePair<string, object>("e", 6)), true);
  664. AssertExceptionThrown<KeyNotFoundException>(() => value = exp["e"]);
  665. exp.Clear();
  666. Assert.AreEqual(0, exp.Count);
  667. Assert.AreEqual(0, exp.Values.Count);
  668. Assert.AreEqual(0, exp.Keys.Count);
  669. // Some additional cases around null
  670. var expnull = (IDictionary<string, object>)(new ExpandoObject());
  671. expnull.Add("a", null);
  672. AssertExceptionThrown<ArgumentNullException>(() => expnull.Add(null, "SDF"));
  673. KeyValuePair<string, object> kvnull = new KeyValuePair<string, object>();
  674. AssertExceptionThrown<ArgumentNullException>(() => expnull.Add(kvnull));
  675. Assert.IsFalse(expnull.Contains(kvnull));
  676. ExpandoObject expando = new ExpandoObject();
  677. exp = (IDictionary<string, object>)expando;
  678. // thread safety, multiple threads adding to the same object.
  679. // All values should be added
  680. Thread t1 = new Thread(() => ExpandoThreadAdder(expando, "Thread1_"));
  681. Thread t2 = new Thread(() => ExpandoThreadAdder(expando, "Thread2_"));
  682. Thread t3 = new Thread(() => ExpandoThreadAdder(expando, "Thread3_"));
  683. Thread t4 = new Thread(() => ExpandoThreadAdder(expando, "Thread4_"));
  684. t1.Start();
  685. t2.Start();
  686. t3.Start();
  687. t4.Start();
  688. t1.Join();
  689. t2.Join();
  690. t3.Join();
  691. t4.Join();
  692. // all values should be set
  693. for (int i = 0; i < 4; i++) {
  694. for (int j = 0; j < 1000; j++) {
  695. Assert.AreEqual(exp["Thread" + (i + 1) + "_" + j.ToString("0000")], j);
  696. }
  697. }
  698. t1 = new Thread(() => ExpandoThreadAdderRemover(expando, "Thread1_"));
  699. t2 = new Thread(() => ExpandoThreadAdderRemover(expando, "Thread2_"));
  700. t3 = new Thread(() => ExpandoThreadAdderRemover(expando, "Thread3_"));
  701. t4 = new Thread(() => ExpandoThreadAdderRemover(expando, "Thread4_"));
  702. t1.Start();
  703. t2.Start();
  704. t3.Start();
  705. t4.Start();
  706. t1.Join();
  707. t2.Join();
  708. t3.Join();
  709. t4.Join();
  710. // all values should have been set and removed
  711. for (int i = 0; i < 4; i++) {
  712. for (int j = 0; j < 1000; j++) {
  713. Assert.AreEqual(exp.ContainsKey("Thread" + (i + 1) + "_" + j.ToString("0000")), false);
  714. }
  715. }
  716. }
  717. [Test("Test the Contains member of ExpandoObject containing a null value")]
  718. private void Scenario_ExpandoObjectWithNullValue() {
  719. var exp = (IDictionary<string, object>)(new ExpandoObject());
  720. exp.Add("a", null);
  721. Assert.IsTrue(exp.Values.

Large files files are truncated, but you can click here to view the full file