PageRenderTime 108ms CodeModel.GetById 29ms app.highlight 68ms RepoModel.GetById 1ms 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

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

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