544b53ec23d4ded27dcee8a2c32666d2ca5fcfff
[mono.git] / mcs / class / System.ComponentModel.Composition / Tests / ComponentModelUnitTest / System / ComponentModel / Composition / Hosting / AggregateCatalogTest.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Collections.Generic;\r
6 using System.ComponentModel.Composition.Factories;\r
7 using System.ComponentModel.Composition.Hosting;\r
8 using System.ComponentModel.Composition.Primitives;\r
9 using System.IO;\r
10 using System.Linq;\r
11 using System.Linq.Expressions;\r
12 using System.Threading;\r
13 using System.UnitTesting;\r
14 using Microsoft.VisualStudio.TestTools.UnitTesting;\r
15 \r
16 namespace System.ComponentModel.Composition.Hosting\r
17 {\r
18     [TestClass]\r
19     public class AggregateCatalogTest\r
20     {\r
21         [TestMethod]\r
22         public void Constructor1_ShouldNotThrow()\r
23         {\r
24             new AggregateCatalog();\r
25         }\r
26 \r
27 \r
28         [TestMethod]\r
29         public void Constructor1_ShouldSetCatalogsPropertyToEmpty()\r
30         {\r
31             var catalog = new AggregateCatalog();\r
32 \r
33             EnumerableAssert.IsEmpty(catalog.Catalogs);\r
34         }\r
35 \r
36         [TestMethod]\r
37         public void Constructor1_ShouldSetPartsPropertyToEmpty()\r
38         {\r
39             var catalog = new AggregateCatalog();\r
40 \r
41             EnumerableAssert.IsEmpty(catalog.Parts);\r
42         }\r
43 \r
44         [TestMethod]\r
45         public void Constructor3_NullAsCatalogsArgument_ShouldSetCatalogsPropertyToEmpty()\r
46         {\r
47             var catalog = new AggregateCatalog((IEnumerable<ComposablePartCatalog>)null);\r
48 \r
49             EnumerableAssert.IsEmpty(catalog.Catalogs);\r
50         }\r
51 \r
52         [TestMethod]\r
53         public void Constructor3_EmptyIEnumerableAsCatalogsArgument_ShouldSetCatalogsPropertyToEmpty()\r
54         {\r
55             var catalog = new AggregateCatalog(Enumerable.Empty<ComposablePartCatalog>());\r
56 \r
57             EnumerableAssert.IsEmpty(catalog.Catalogs);\r
58         }\r
59 \r
60         [TestMethod]\r
61         public void Constructor3_NullAsCatalogsArgument_ShouldSetPartsPropertyToEmpty()\r
62         {\r
63             var catalog = new AggregateCatalog((IEnumerable<ComposablePartCatalog>)null);\r
64 \r
65             EnumerableAssert.IsEmpty(catalog.Parts);\r
66         }\r
67 \r
68         [TestMethod]\r
69         public void Constructor3_EmptyIEnumerableAsCatalogsArgument_ShouldSetPartsPropertyToEmpty()\r
70         {\r
71             var catalog = new AggregateCatalog(Enumerable.Empty<ComposablePartCatalog>());\r
72 \r
73             EnumerableAssert.IsEmpty(catalog.Parts);\r
74         }\r
75 \r
76         [TestMethod]\r
77         public void Constructor3_ArrayWithNullAsCatalogsArgument_ShouldThrowArgument()\r
78         {\r
79             var catalogs = new ComposablePartCatalog[] { null };\r
80 \r
81             ExceptionAssert.ThrowsArgument<ArgumentException>("catalogs", () =>\r
82             {\r
83                 new AggregateCatalog(catalogs);\r
84             });\r
85         }\r
86 \r
87         [TestMethod]\r
88         public void Catalogs_WhenCatalogDisposed_ShouldThrowObjectDisposed()\r
89         {\r
90             var catalog = CreateAggregateCatalog();\r
91             catalog.Dispose();\r
92 \r
93             ExceptionAssert.ThrowsDisposed(catalog, () =>\r
94             {\r
95                 var catalogs = catalog.Catalogs;\r
96             });\r
97         }\r
98 \r
99         [TestMethod]\r
100         public void Parts_WhenCatalogDisposed_ShouldThrowObjectDisposed()\r
101         {\r
102             var catalog = CreateAggregateCatalog();\r
103             catalog.Dispose();\r
104 \r
105             ExceptionAssert.ThrowsDisposed(catalog, () =>\r
106             {\r
107                 var parts = catalog.Parts;\r
108             });\r
109         }\r
110 \r
111         [TestMethod]\r
112         public void GetExports_WhenCatalogDisposed_ShouldThrowObjectDisposed()\r
113         {\r
114             var catalog = CreateAggregateCatalog();\r
115             catalog.Dispose();\r
116             var definition = ImportDefinitionFactory.Create();\r
117 \r
118             ExceptionAssert.ThrowsDisposed(catalog, () =>\r
119             {\r
120                 catalog.GetExports(definition);\r
121             });\r
122         }\r
123 \r
124         [TestMethod]\r
125         public void GetExports_NullAsConstraintArgument_ShouldThrowArgumentNull()\r
126         {\r
127             var catalog = CreateAggregateCatalog();\r
128 \r
129             ExceptionAssert.ThrowsArgument<ArgumentNullException>("definition", () =>\r
130             {\r
131                 catalog.GetExports((ImportDefinition)null);\r
132             });\r
133         }\r
134 \r
135         [TestMethod]\r
136         public void Dispose_ShouldNotThrow()\r
137         {\r
138             using (var catalog = CreateAggregateCatalog())\r
139             {\r
140             }\r
141         }\r
142 \r
143         [TestMethod]\r
144         public void Dispose_CanBeCalledMultipleTimes()\r
145         {\r
146             var catalog = CreateAggregateCatalog();\r
147             catalog.Dispose();\r
148             catalog.Dispose();\r
149             catalog.Dispose();\r
150         }\r
151 \r
152 \r
153         [TestMethod]\r
154         public void MutableCatalogNotifications()\r
155         {\r
156             int step = 0;\r
157             int changedStep = 0;\r
158             var catalog = new AggregateCatalog();\r
159 \r
160             var typePartCatalog = new TypeCatalog(typeof(SharedPartStuff));\r
161             var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff));\r
162             var typePartCatalog2 = new TypeCatalog(typeof(SharedPartStuff));\r
163             var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff));\r
164             var typePartCatalog4 = new TypeCatalog(typeof(SharedPartStuff));\r
165             var typePartCatalog5 = new TypeCatalog(typeof(SharedPartStuff));\r
166 \r
167             // Smoke test on inner collection\r
168             catalog.Catalogs.Add(typePartCatalog);\r
169             catalog.Catalogs.Remove(typePartCatalog);\r
170             catalog.Catalogs.Clear();\r
171             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add/Remove/Clear -- PartsAsCollection.Count is now 0");\r
172 \r
173             // Add notifications\r
174             catalog.Changed += delegate(object source, ComposablePartCatalogChangeEventArgs args)\r
175             {\r
176                 // Local code\r
177                 ++step; ++step;\r
178                 changedStep = step;\r
179             };\r
180 \r
181             //Add something then verify counters\r
182             catalog.Catalogs.Add(typePartCatalog);\r
183             Assert.IsTrue(catalog.Catalogs.Count == 1, "Add -- Catalogs.Count is now 1");\r
184             Assert.IsTrue(changedStep == 2, "Add -- Changed must be fired after");\r
185 \r
186             // Reset counters\r
187             step = changedStep = 0;\r
188 \r
189             // Remove something then verify counters\r
190             catalog.Catalogs.Remove(typePartCatalog);\r
191             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add -- Catalogs.Count is now 0");\r
192             Assert.IsTrue(changedStep == 2, "Remove -- Changed must be fired after");\r
193 \r
194 \r
195             //Now Add it back\r
196             catalog.Catalogs.Add(typePartCatalog);\r
197             Assert.IsTrue(catalog.Catalogs.Count == 1, "Add -- Catalogs.Count is now 1");\r
198 \r
199             step = changedStep = 0;\r
200             // Now clear the collection and verify counters\r
201             catalog.Catalogs.Clear();\r
202             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add -- Catalogs.Count is now 0");\r
203             Assert.IsTrue(changedStep == 2, "Remove -- Changed must be fired after");\r
204 \r
205             // Now remove a non existent item and verify counters\r
206             step = changedStep = 0;\r
207             bool removed = catalog.Catalogs.Remove(typePartCatalog);\r
208             Assert.IsTrue(removed == false, "Remove -- correctly returned false");\r
209             Assert.IsTrue(changedStep == 0, "Remove -- Changed should not fire if nothing changed");\r
210 \r
211             // Add a bunch\r
212             step = changedStep = 0;\r
213             catalog.Catalogs.Add(typePartCatalog);\r
214             Assert.IsTrue(catalog.Catalogs.Count == 1, "Add -- Catalogs.Count is now 1");\r
215             Assert.IsTrue(changedStep == 2, "Add -- Changed must be fired after");\r
216 \r
217             catalog.Catalogs.Add(typePartCatalog1);\r
218             Assert.IsTrue(catalog.Catalogs.Count == 2, "Add -- Catalogs.Count is now 1");\r
219             Assert.IsTrue(changedStep == 4, "Add -- Changing must be fired after");\r
220 \r
221             catalog.Catalogs.Add(typePartCatalog2);\r
222             catalog.Catalogs.Add(typePartCatalog3);\r
223             catalog.Catalogs.Add(typePartCatalog4);\r
224             catalog.Catalogs.Add(typePartCatalog5);\r
225             Assert.IsTrue(catalog.Catalogs.Count == 6, "Add -- Catalogs.Count is now 1");\r
226             Assert.IsTrue(changedStep == 12, "Add -- Changing must be fired after");\r
227 \r
228             removed = catalog.Catalogs.Remove(typePartCatalog3);\r
229             Assert.IsTrue(catalog.Catalogs.Count == 5, "Add -- Catalogs.Count is now 5");\r
230             Assert.IsTrue(removed == true, "Remove should have succeeded");\r
231             Assert.IsTrue(changedStep == 14, "Remove -- Changed must be fired after");\r
232             removed = catalog.Catalogs.Remove(typePartCatalog2);\r
233             removed = catalog.Catalogs.Remove(typePartCatalog1);\r
234             removed = catalog.Catalogs.Remove(typePartCatalog4);\r
235             removed = catalog.Catalogs.Remove(typePartCatalog);\r
236             removed = catalog.Catalogs.Remove(typePartCatalog5);\r
237             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add -- Catalogs.Count is now 0");\r
238             Assert.IsTrue(removed == true, "Remove should have succeeded");\r
239             Assert.IsTrue(changedStep == 24, "Remove -- Changing must be fired after");\r
240 \r
241             // Add and then clear a lot\r
242             step = changedStep = 0;\r
243             catalog.Catalogs.Add(typePartCatalog);\r
244             catalog.Catalogs.Add(typePartCatalog1);\r
245             catalog.Catalogs.Add(typePartCatalog2);\r
246             catalog.Catalogs.Add(typePartCatalog3);\r
247             catalog.Catalogs.Add(typePartCatalog4);\r
248             catalog.Catalogs.Add(typePartCatalog5);\r
249             Assert.IsTrue(catalog.Catalogs.Count == 6, "Add -- Catalogs.Count should be 6");\r
250             Assert.IsTrue(changedStep == 12, "Add -- Changing must be fired after");\r
251 \r
252             catalog.Catalogs.Clear();\r
253             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add -- Catalogs.Count should be 0");\r
254 \r
255             step = changedStep = 0;\r
256             int step2 = 100;\r
257             int changedStep2 = 0;\r
258 \r
259             catalog.Changed += delegate(object source, ComposablePartCatalogChangeEventArgs args)\r
260             {\r
261                 // Local code\r
262                 --step2; --step2;\r
263                 changedStep2 = step2;\r
264             };\r
265 \r
266             catalog.Catalogs.Add(typePartCatalog);\r
267             Assert.IsTrue(catalog.Catalogs.Count == 1, "Add -- Catalogs.Count is now 1");\r
268             Assert.IsTrue(changedStep == 2, "Add handler 1 -- Changed must be fired after");\r
269             Assert.IsTrue(changedStep2 == 98, "Add handler 2 -- Changed must be fired after");\r
270 \r
271             catalog.Catalogs.Add(typePartCatalog1);\r
272             Assert.IsTrue(catalog.Catalogs.Count == 2, "Add -- Catalogs.Count is now 1");\r
273             Assert.IsTrue(changedStep == 4, "Add handler 1 -- Changed must be fired after");\r
274             Assert.IsTrue(changedStep2 == 96, "Add handler 2 -- Changed must be firedafter");\r
275 \r
276             catalog.Catalogs.Remove(typePartCatalog);\r
277             Assert.IsTrue(catalog.Catalogs.Count == 1, "Add -- PartsAsCollection.Count is now 1");\r
278             Assert.IsTrue(changedStep == 6, "Add handler 1 -- Changed must be fired and fired after");\r
279             Assert.IsTrue(changedStep2 == 94, "Add handler 2 -- Changed must be fired and fired after");\r
280 \r
281             catalog.Catalogs.Clear();\r
282             Assert.IsTrue(catalog.Catalogs.Count == 0, "Add -- PartsAsCollection.Count is now 0");\r
283             Assert.IsTrue(changedStep == 8, "Add handler 1 -- Changed must be fired after");\r
284             Assert.IsTrue(changedStep2 == 92, "Add handler 2 -- Changed must be fired after");\r
285 \r
286         }\r
287 \r
288         [TestMethod]\r
289         public void DisposeAggregatingCatalog()\r
290         {\r
291             int changedNotification = 0;\r
292 \r
293             var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff));\r
294             var typePartCatalog2 = new TypeCatalog(typeof(SharedPartStuff));\r
295             var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff));\r
296 \r
297             var assemblyPartCatalog1 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
298             var assemblyPartCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
299             var assemblyPartCatalog3 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
300 \r
301 #if !SILVERLIGHT\r
302             var dirPartCatalog1 = new DirectoryCatalog(FileIO.GetRootTemporaryDirectory());\r
303             var dirPartCatalog2 = new DirectoryCatalog(FileIO.GetRootTemporaryDirectory());\r
304             var dirPartCatalog3 = new DirectoryCatalog(FileIO.GetRootTemporaryDirectory());\r
305 #endif\r
306             using (var catalog = new AggregateCatalog())\r
307             {\r
308                 catalog.Catalogs.Add(typePartCatalog1);\r
309                 catalog.Catalogs.Add(typePartCatalog2);\r
310                 catalog.Catalogs.Add(typePartCatalog3);\r
311 \r
312                 catalog.Catalogs.Add(assemblyPartCatalog1);\r
313                 catalog.Catalogs.Add(assemblyPartCatalog2);\r
314                 catalog.Catalogs.Add(assemblyPartCatalog3);\r
315 \r
316 #if !SILVERLIGHT\r
317                 catalog.Catalogs.Add(dirPartCatalog1);\r
318                 catalog.Catalogs.Add(dirPartCatalog2);\r
319                 catalog.Catalogs.Add(dirPartCatalog3);\r
320 #endif\r
321 \r
322                 // Add notifications\r
323                 catalog.Changed += delegate(object source, ComposablePartCatalogChangeEventArgs args)\r
324                 {\r
325                     // Local code\r
326                     ++changedNotification;\r
327                 };\r
328 \r
329             }\r
330 \r
331             Assert.IsTrue(changedNotification == 0, "No changed notifications");\r
332 \r
333             //Ensure that the other catalogs are \r
334             ExceptionAssert.ThrowsDisposed(typePartCatalog1, () =>\r
335             {\r
336                 var iEnum = typePartCatalog1.Parts.GetEnumerator();\r
337             });\r
338 \r
339             ExceptionAssert.ThrowsDisposed(typePartCatalog2, () =>\r
340             {\r
341                 var iEnum = typePartCatalog2.Parts.GetEnumerator();\r
342             });\r
343 \r
344             ExceptionAssert.ThrowsDisposed(typePartCatalog3, () =>\r
345             {\r
346                 var iEnum = typePartCatalog3.Parts.GetEnumerator();\r
347             });\r
348 \r
349             //Ensure that the other catalogs are \r
350             ExceptionAssert.ThrowsDisposed(assemblyPartCatalog1, () =>\r
351             {\r
352                 var iEnum = assemblyPartCatalog1.Parts.GetEnumerator();\r
353             });\r
354 \r
355             ExceptionAssert.ThrowsDisposed(assemblyPartCatalog2, () =>\r
356             {\r
357                 var iEnum = assemblyPartCatalog2.Parts.GetEnumerator();\r
358             });\r
359 \r
360             ExceptionAssert.ThrowsDisposed(assemblyPartCatalog3, () =>\r
361             {\r
362                 var iEnum = assemblyPartCatalog3.Parts.GetEnumerator();\r
363             });\r
364 \r
365 #if !SILVERLIGHT\r
366             //Ensure that the other catalogs are \r
367             ExceptionAssert.ThrowsDisposed(dirPartCatalog1, () =>\r
368             {\r
369                 var iEnum = dirPartCatalog1.Parts.GetEnumerator();\r
370             });\r
371 \r
372             ExceptionAssert.ThrowsDisposed(dirPartCatalog2, () =>\r
373             {\r
374                 var iEnum = dirPartCatalog2.Parts.GetEnumerator();\r
375             });\r
376 \r
377             ExceptionAssert.ThrowsDisposed(dirPartCatalog3, () =>\r
378             {\r
379                 var iEnum = dirPartCatalog3.Parts.GetEnumerator();\r
380             });\r
381 #endif\r
382         }\r
383 \r
384         [TestMethod]\r
385         [Ignore]\r
386         [WorkItem(514749)]\r
387         public void MutableMultithreadedEnumerations()\r
388         {\r
389             var catalog = new AggregateCatalog();\r
390 \r
391             ThreadStart func = delegate()\r
392             {\r
393                 var typePart = new TypeCatalog(typeof(SharedPartStuff));\r
394                 var typePart1 = new TypeCatalog(typeof(SharedPartStuff));\r
395                 var typePart2 = new TypeCatalog(typeof(SharedPartStuff));\r
396                 var typePart3 = new TypeCatalog(typeof(SharedPartStuff));\r
397                 var typePart4 = new TypeCatalog(typeof(SharedPartStuff));\r
398                 var typePart5 = new TypeCatalog(typeof(SharedPartStuff));\r
399 \r
400                 for (int i = 0; i < 100; i++)\r
401                 {\r
402                     catalog.Catalogs.Add(typePart);\r
403                     catalog.Catalogs.Add(typePart1);\r
404                     catalog.Catalogs.Add(typePart2);\r
405                     catalog.Catalogs.Add(typePart3);\r
406                     catalog.Catalogs.Add(typePart4);\r
407                     catalog.Catalogs.Add(typePart5);\r
408 \r
409                     Assert.IsTrue(catalog.Catalogs.Count >= 6, "Catalogs Collection must be at least 6 big");\r
410 \r
411                     for (int k = 0; k < 5; k++)\r
412                     {\r
413                         int j;\r
414                         // Ensure that iterating the returned queryable is okay even though there are many threads mutationg it\r
415                         // We are really just looking to ensure that ollection changed exceptions are not thrown\r
416                         j = 0;\r
417                         var iq = catalog.Parts.GetEnumerator();\r
418                         while (iq.MoveNext())\r
419                         {\r
420                             ++j;\r
421                         }\r
422 \r
423                         Assert.IsTrue(j >= 6, "Catalogs Collection must be at least 6 big");\r
424 \r
425                         // Ensure that iterating the returned enumerator is okay even though there are many threads mutationg it\r
426                         // We are really just looking to ensure that collection changed exceptions are not thrown\r
427                         j = 0;\r
428                         var ie = catalog.Catalogs.GetEnumerator();\r
429                         while (ie.MoveNext())\r
430                         {\r
431                             ++j;\r
432                         }\r
433                         Assert.IsTrue(j >= 6, "Catalogs Collection must be at least 6 big");\r
434                     }\r
435 \r
436 \r
437                     catalog.Catalogs.Remove(typePart);\r
438                     catalog.Catalogs.Remove(typePart1);\r
439                     catalog.Catalogs.Remove(typePart2);\r
440                     catalog.Catalogs.Remove(typePart3);\r
441                     catalog.Catalogs.Remove(typePart4);\r
442                     catalog.Catalogs.Remove(typePart5);\r
443                 }\r
444             };\r
445 \r
446             Thread[] threads = new Thread[100];\r
447             for (int i = 0; i < threads.Length; i++)\r
448             {\r
449                 threads[i] = new Thread(func);\r
450             }\r
451 \r
452             for (int i = 0; i < threads.Length; i++)\r
453             {\r
454                 threads[i].Start();\r
455             }\r
456             for (int i = 0; i < threads.Length; i++)\r
457             {\r
458                 threads[i].Join();\r
459             }\r
460 \r
461             Assert.IsTrue(catalog.Catalogs.Count == 0, "Collection must be empty");\r
462         }\r
463 \r
464 \r
465         public void CreateMainAndOtherChildren(\r
466             out AggregateCatalog[] mainChildren,\r
467             out AggregateCatalog[] otherChildren,\r
468             out TypeCatalog[] componentCatalogs)\r
469         {\r
470             componentCatalogs = new TypeCatalog[] \r
471             {\r
472                 new TypeCatalog(typeof(SharedPartStuff)),\r
473                 new TypeCatalog(typeof(SharedPartStuff)),\r
474                 new TypeCatalog(typeof(SharedPartStuff))\r
475             };\r
476 \r
477             // Create our child catalogs\r
478             mainChildren = new AggregateCatalog[5];\r
479             for (int i = 0; i < mainChildren.Length; i++)\r
480             {\r
481                 mainChildren[i] = new AggregateCatalog(componentCatalogs);\r
482             }\r
483 \r
484             otherChildren = new AggregateCatalog[5];\r
485             for (int i = 0; i < otherChildren.Length; i++)\r
486             {\r
487                 otherChildren[i] = new AggregateCatalog(componentCatalogs);\r
488             }\r
489         }\r
490 \r
491         [TestMethod]\r
492         public void AggregatingCatalogAddAndRemoveChildren()\r
493         {\r
494             int changedCount = 0;\r
495             int typesChanged = 0;\r
496             EventHandler<ComposablePartCatalogChangeEventArgs> onChanged = delegate(object sender, ComposablePartCatalogChangeEventArgs e)\r
497             {\r
498                 ++changedCount;\r
499                 typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count();\r
500             };\r
501 \r
502             // Create our child catalogs\r
503             AggregateCatalog[] mainChildren;\r
504             AggregateCatalog[] otherChildren;\r
505             TypeCatalog[] componentCatalogs;\r
506 \r
507             CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs);\r
508 \r
509             var parent = new AggregateCatalog(mainChildren);\r
510             parent.Changed += onChanged;\r
511 \r
512             for (int i = 0; i < otherChildren.Length; i++)\r
513             {\r
514                 parent.Catalogs.Add(otherChildren[i]);\r
515             }\r
516 \r
517             Assert.AreEqual(otherChildren.Length, changedCount);\r
518             Assert.AreEqual(otherChildren.Length * 3, typesChanged);\r
519 \r
520             changedCount = 0;\r
521             typesChanged = 0;\r
522 \r
523             parent.Catalogs.Remove(otherChildren[0]);\r
524             parent.Catalogs.Remove(otherChildren[1]);\r
525 \r
526             Assert.AreEqual(2, changedCount);\r
527             Assert.AreEqual(2 * 3, typesChanged);\r
528 \r
529             changedCount = 0;\r
530             typesChanged = 0;\r
531 \r
532             parent.Catalogs.Add(otherChildren[0]);\r
533             parent.Catalogs.Add(otherChildren[1]);\r
534 \r
535             Assert.AreEqual(2, changedCount);\r
536             Assert.AreEqual(2 * 3, typesChanged);\r
537 \r
538             changedCount = 0;\r
539             typesChanged = 0;\r
540 \r
541             parent.Catalogs.Clear();\r
542             Assert.AreEqual(1, changedCount);\r
543             Assert.AreEqual((mainChildren.Length + otherChildren.Length) * 3, typesChanged);\r
544 \r
545             changedCount = 0;\r
546             typesChanged = 0;\r
547 \r
548             // These have already been removed and so I should be able remove components from them without recieving notifications\r
549             otherChildren[0].Catalogs.Remove(componentCatalogs[0]);\r
550             otherChildren[1].Catalogs.Remove(componentCatalogs[1]);\r
551             Assert.AreEqual(0, changedCount);\r
552             Assert.AreEqual(0, typesChanged);\r
553 \r
554             // These have already been Cleared and so I should be able remove components from them without recieving notifications\r
555             otherChildren[3].Catalogs.Remove(componentCatalogs[0]);\r
556             otherChildren[4].Catalogs.Remove(componentCatalogs[1]);\r
557             Assert.AreEqual(0, changedCount);\r
558         }\r
559 \r
560         [TestMethod]\r
561         public void AggregatingCatalogAddAndRemoveNestedChildren()\r
562         {\r
563             int changedCount = 0;\r
564             int typesChanged = 0;\r
565 \r
566             EventHandler<ComposablePartCatalogChangeEventArgs> onChanged = delegate(object sender, ComposablePartCatalogChangeEventArgs e)\r
567             {\r
568                 ++changedCount;\r
569                 typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count();\r
570             };\r
571 \r
572             // Create our child catalogs\r
573             AggregateCatalog[] mainChildren;\r
574             AggregateCatalog[] otherChildren;\r
575             TypeCatalog[] componentCatalogs;\r
576             CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs);\r
577 \r
578 \r
579             var parent = new AggregateCatalog(mainChildren);\r
580             parent.Changed += onChanged;\r
581 \r
582             for (int i = 0; i < otherChildren.Length; i++)\r
583             {\r
584                 parent.Catalogs.Add(otherChildren[i]);\r
585             }\r
586 \r
587             Assert.AreEqual(otherChildren.Length, changedCount);\r
588             Assert.AreEqual(otherChildren.Length * 3, typesChanged);\r
589 \r
590             changedCount = 0;\r
591             typesChanged = 0;\r
592 \r
593             otherChildren[0].Catalogs.Remove(componentCatalogs[0]);\r
594             otherChildren[1].Catalogs.Remove(componentCatalogs[1]);\r
595 \r
596             Assert.AreEqual(2, changedCount);\r
597             Assert.AreEqual(2, typesChanged);\r
598 \r
599             changedCount = 0;\r
600             typesChanged = 0;\r
601 \r
602             otherChildren[0].Catalogs.Add(componentCatalogs[0]);\r
603             otherChildren[1].Catalogs.Add(componentCatalogs[1]);\r
604 \r
605             Assert.AreEqual(2, changedCount);\r
606             Assert.AreEqual(2, typesChanged);\r
607 \r
608             changedCount = 0;\r
609             typesChanged = 0;\r
610             otherChildren[1].Catalogs.Clear();\r
611             Assert.AreEqual(1, changedCount);\r
612             Assert.AreEqual(componentCatalogs.Length, typesChanged);\r
613         }\r
614 \r
615         [TestMethod]\r
616         public void AggregatingDisposedAndNotifications()\r
617         {\r
618             int changedCount = 0;\r
619             int typesChanged = 0;\r
620 \r
621             EventHandler<ComposablePartCatalogChangeEventArgs> onChanged = delegate(object sender, ComposablePartCatalogChangeEventArgs e)\r
622             {\r
623                 ++changedCount;\r
624                 typesChanged += e.AddedDefinitions.Concat(e.RemovedDefinitions).Count();\r
625             };\r
626 \r
627             // Create our child catalogs\r
628             AggregateCatalog[] mainChildren;\r
629             AggregateCatalog[] otherChildren;\r
630             TypeCatalog[] componentCatalogs;\r
631             CreateMainAndOtherChildren(out mainChildren, out otherChildren, out componentCatalogs);\r
632 \r
633 \r
634             var parent = new AggregateCatalog(mainChildren);\r
635             parent.Changed += onChanged;\r
636 \r
637             for (int i = 0; i < otherChildren.Length; i++)\r
638             {\r
639                 parent.Catalogs.Add(otherChildren[i]);\r
640             }\r
641 \r
642             Assert.AreEqual(otherChildren.Length, changedCount);\r
643             Assert.AreEqual(otherChildren.Length * 3, typesChanged);\r
644 \r
645             changedCount = 0;\r
646             typesChanged = 0;\r
647 \r
648             parent.Dispose();\r
649 \r
650             Assert.AreEqual(0, changedCount);\r
651             Assert.AreEqual(0, typesChanged);\r
652 \r
653             //Ensure that the children are also disposed\r
654             ExceptionAssert.ThrowsDisposed(otherChildren[0], () =>\r
655             {\r
656                 otherChildren[0].Catalogs.Remove(componentCatalogs[0]);\r
657             });\r
658 \r
659             //Ensure that the children are also disposed\r
660             ExceptionAssert.ThrowsDisposed(otherChildren[4], () =>\r
661             {\r
662                 otherChildren[4].Catalogs.Remove(componentCatalogs[0]);\r
663             });\r
664 \r
665             Assert.AreEqual(0, changedCount);\r
666             Assert.AreEqual(0, typesChanged);\r
667         }\r
668 \r
669         [TestMethod]\r
670         public void AggregatingCatalogParmsConstructorAggregateAggregateCatalogs()\r
671         {\r
672             var aggCatalog1 = new AggregateCatalog();\r
673             var aggCatalog2 = new AggregateCatalog();\r
674             var aggCatalog3 = new AggregateCatalog();\r
675 \r
676             // Construct with one catalog parameter\r
677             var catalog = new AggregateCatalog(aggCatalog1);\r
678             Assert.IsTrue(catalog.Catalogs.Count == 1);\r
679 \r
680             // Construct with two catalog parameters\r
681             catalog = new AggregateCatalog(aggCatalog1, aggCatalog2);\r
682             Assert.IsTrue(catalog.Catalogs.Count == 2);\r
683 \r
684             // Construct with three catalog parameters\r
685             catalog = new AggregateCatalog(aggCatalog1, aggCatalog2, aggCatalog3);\r
686             Assert.IsTrue(catalog.Catalogs.Count == 3);\r
687         }\r
688 \r
689 \r
690         [TestMethod]\r
691         public void AggregatingCatalogParmsConstructorAggregateAssemblyCatalogs()\r
692         {\r
693             var assemblyCatalog1 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
694             var assemblyCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
695             var assemblyCatalog3 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
696 \r
697             // Construct with one catalog parameter\r
698             var catalog = new AggregateCatalog(assemblyCatalog1);\r
699             Assert.IsTrue(catalog.Catalogs.Count == 1);\r
700 \r
701             // Construct with two catalog parameters\r
702             catalog = new AggregateCatalog(assemblyCatalog1, assemblyCatalog2);\r
703             Assert.IsTrue(catalog.Catalogs.Count == 2);\r
704 \r
705             // Construct with three catalog parameters\r
706             catalog = new AggregateCatalog(assemblyCatalog1, assemblyCatalog2, assemblyCatalog3);\r
707             Assert.IsTrue(catalog.Catalogs.Count == 3);\r
708         }\r
709 \r
710         [TestMethod]\r
711         public void AggregatingCatalogParmsConstructorMixedCatalogs()\r
712         {\r
713             var typePartCatalog1 = new TypeCatalog(typeof(SharedPartStuff));\r
714             var assemblyCatalog2 = new AssemblyCatalog(typeof(SharedPartStuff).Assembly);\r
715             var typePartCatalog3 = new TypeCatalog(typeof(SharedPartStuff));\r
716 \r
717             // Construct with three catalog parameters\r
718             var catalog = new AggregateCatalog(typePartCatalog1, assemblyCatalog2, typePartCatalog3);\r
719             Assert.IsTrue(catalog.Catalogs.Count == 3);\r
720         }\r
721 \r
722         [TestMethod]\r
723         public void AggregatingCatalogRaisesChangesForCatalogsPassedToConstructor()\r
724         {\r
725             var subCatalog = CreateAggregateCatalog();\r
726             var testCatalog = new AggregateCatalog(subCatalog);\r
727 \r
728             bool changedCalled = false;\r
729             testCatalog.Changed += delegate\r
730             {\r
731                 changedCalled = true;\r
732             };\r
733 \r
734             subCatalog.Catalogs.Add(new TypeCatalog(typeof(SharedPartStuff)));\r
735 \r
736             Assert.IsTrue(changedCalled);\r
737         }\r
738 \r
739         private AggregateCatalog CreateAggregateCatalog()\r
740         {\r
741             return new AggregateCatalog();\r
742         }\r
743 \r
744         [TestMethod]\r
745         public void CatalogEvents_AggregateAddRemove()\r
746         {\r
747             var catalog = new AggregateCatalog();\r
748             AggregateTests(catalog, catalog);\r
749         }\r
750 \r
751         [TestMethod]\r
752         public void CatalogEvents_DeepAggregateAddRemove()\r
753         {\r
754             var deepCatalog = new AggregateCatalog();\r
755             var midCatalog = new AggregateCatalog(new ComposablePartCatalog[] { deepCatalog });\r
756             var topCatalog = new AggregateCatalog(new ComposablePartCatalog[] { midCatalog });\r
757             AggregateTests(topCatalog, deepCatalog);\r
758         }\r
759 \r
760         private void AggregateTests(AggregateCatalog watchedCatalog, AggregateCatalog modifiedCatalog)\r
761         {\r
762             var fooCatalog = new TypeCatalog(new Type[] { typeof(FooExporter) });\r
763             var barCatalog = new TypeCatalog(new Type[] { typeof(BarExporter) });\r
764             var bothCatalog = new TypeCatalog(new Type[] { typeof(FooExporter), typeof(BarExporter) });\r
765 \r
766             var catalogListener = new CatalogListener(watchedCatalog, modifiedCatalog);\r
767 \r
768             catalogListener.VerifyAdd(fooCatalog, typeof(FooExporter));\r
769             catalogListener.VerifyAdd(barCatalog, typeof(BarExporter));\r
770             catalogListener.VerifyRemove(fooCatalog, typeof(FooExporter));\r
771             catalogListener.VerifyRemove(barCatalog, typeof(BarExporter));\r
772 \r
773             catalogListener.VerifyAdd(bothCatalog, typeof(FooExporter), typeof(BarExporter));\r
774             catalogListener.VerifyClear(typeof(FooExporter), typeof(BarExporter));\r
775 \r
776             catalogListener.VerifyAdd(bothCatalog, typeof(FooExporter), typeof(BarExporter));\r
777             catalogListener.VerifyRemove(bothCatalog, typeof(FooExporter), typeof(BarExporter));\r
778         }\r
779 \r
780 \r
781         public interface IFoo { }\r
782         public interface IBar { }\r
783 \r
784         [Export(typeof(IFoo))]\r
785         public class FooExporter : IFoo\r
786         {\r
787         }\r
788 \r
789         [Export(typeof(IBar))]\r
790         public class BarExporter : IBar\r
791         {\r
792         }\r
793 \r
794         public class CatalogListener\r
795         {\r
796             private AggregateCatalog _watchedCatalog;\r
797             private AggregateCatalog _modifiedCatalog;\r
798             private string[] _expectedAdds;\r
799             private string[] _expectedRemoves;\r
800             private int _changedEventCount;\r
801             private int _changingEventCount;\r
802 \r
803             public CatalogListener(AggregateCatalog watchCatalog, AggregateCatalog modifiedCatalog)\r
804             {\r
805                 watchCatalog.Changing += OnChanging;\r
806                 watchCatalog.Changed += OnChanged;\r
807                 this._watchedCatalog = watchCatalog;\r
808                 this._modifiedCatalog = modifiedCatalog;\r
809             }\r
810 \r
811             public void VerifyAdd(ComposablePartCatalog catalogToAdd, params Type[] expectedTypesAdded)\r
812             {\r
813                 this._expectedAdds = GetDisplayNames(expectedTypesAdded);\r
814 \r
815                 this._modifiedCatalog.Catalogs.Add(catalogToAdd);\r
816 \r
817                 Assert.IsTrue(this._changingEventCount == 1, "Changing event should have been called");\r
818                 Assert.IsTrue(this._changedEventCount == 1, "Changed event should have been called");\r
819 \r
820                 ResetState();\r
821             }\r
822 \r
823             public void VerifyRemove(ComposablePartCatalog catalogToRemove, params Type[] expectedTypesRemoved)\r
824             {\r
825                 this._expectedAdds = null;\r
826                 this._expectedRemoves = GetDisplayNames(expectedTypesRemoved);\r
827 \r
828                 this._modifiedCatalog.Catalogs.Remove(catalogToRemove);\r
829 \r
830                 Assert.IsTrue(this._changingEventCount == 1, "Changing event should have been called");\r
831                 Assert.IsTrue(this._changedEventCount == 1, "Changed event should have been called");\r
832 \r
833                 ResetState();\r
834             }\r
835 \r
836             public void VerifyClear(params Type[] expectedTypesRemoved)\r
837             {\r
838                 this._expectedAdds = null;\r
839                 this._expectedRemoves = GetDisplayNames(expectedTypesRemoved);\r
840 \r
841                 this._modifiedCatalog.Catalogs.Clear();\r
842 \r
843                 Assert.IsTrue(this._changingEventCount == 1, "Changing event should have been called");\r
844                 Assert.IsTrue(this._changedEventCount == 1, "Changed event should have been called");\r
845 \r
846                 ResetState();\r
847             }\r
848 \r
849             public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs args)\r
850             {\r
851                 Assert.IsTrue(this._expectedAdds != null || this._expectedRemoves != null);\r
852 \r
853                 if (this._expectedAdds == null)\r
854                 {\r
855                     EnumerableAssert.IsEmpty(args.AddedDefinitions);\r
856                 }\r
857                 else\r
858                 {\r
859                     EnumerableAssert.AreSequenceEqual(this._expectedAdds, GetDisplayNames(args.AddedDefinitions));\r
860                 }\r
861 \r
862                 if (this._expectedRemoves == null)\r
863                 {\r
864                     EnumerableAssert.IsEmpty(args.RemovedDefinitions);\r
865                 }\r
866                 else\r
867                 {\r
868                     EnumerableAssert.AreSequenceEqual(this._expectedRemoves, GetDisplayNames(args.RemovedDefinitions));\r
869                 }\r
870 \r
871                 Assert.IsFalse(ContainsChanges(), "The catalog should NOT contain the changes yet");\r
872 \r
873                 this._changingEventCount++;\r
874             }\r
875 \r
876             public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs args)\r
877             {\r
878                 Assert.IsTrue(this._expectedAdds != null || this._expectedRemoves != null);\r
879 \r
880                 if (this._expectedAdds == null)\r
881                 {\r
882                     EnumerableAssert.IsEmpty(args.AddedDefinitions);\r
883                 }\r
884                 else\r
885                 {\r
886                     EnumerableAssert.AreSequenceEqual(this._expectedAdds, GetDisplayNames(args.AddedDefinitions));\r
887                 }\r
888 \r
889                 if (this._expectedRemoves == null)\r
890                 {\r
891                     EnumerableAssert.IsEmpty(args.RemovedDefinitions);\r
892                 }\r
893                 else\r
894                 {\r
895                     EnumerableAssert.AreSequenceEqual(this._expectedRemoves, GetDisplayNames(args.RemovedDefinitions));\r
896                 }\r
897 \r
898                 Assert.IsNull(args.AtomicComposition);\r
899                 Assert.IsTrue(ContainsChanges(), "The catalog should contain the changes");\r
900 \r
901                 this._changedEventCount++;\r
902             }\r
903 \r
904             private bool ContainsChanges()\r
905             {\r
906                 var allParts = GetDisplayNames(this._watchedCatalog.Parts);\r
907 \r
908                 if (this._expectedAdds != null)\r
909                 {\r
910                     foreach (var add in this._expectedAdds)\r
911                     {\r
912                         if (!allParts.Contains(add))\r
913                         {\r
914                             return false;\r
915                         }\r
916                     }\r
917                 }\r
918 \r
919                 if (this._expectedRemoves != null)\r
920                 {\r
921                     foreach (var remove in this._expectedRemoves)\r
922                     {\r
923                         if (allParts.Contains(remove))\r
924                         {\r
925                             return false;\r
926                         }\r
927                     }\r
928                 }\r
929 \r
930                 return true;\r
931             }\r
932 \r
933             private void ResetState()\r
934             {\r
935                 this._expectedAdds = null;\r
936                 this._expectedRemoves = null;\r
937                 this._changedEventCount = 0;\r
938                 this._changingEventCount = 0;\r
939             }\r
940 \r
941             private static string[] GetDisplayNames(IEnumerable<ComposablePartDefinition> definitions)\r
942             {\r
943                 return definitions.OfType<ICompositionElement>().Select(p => p.DisplayName).ToArray();\r
944             }\r
945 \r
946             private static string[] GetDisplayNames(IEnumerable<Type> types)\r
947             {\r
948                 return GetDisplayNames(types.Select(t => AttributedModelServices.CreatePartDefinition(t, null)));\r
949             }\r
950         }\r
951 \r
952         [Export]\r
953         [PartCreationPolicy(CreationPolicy.Shared)]\r
954         public class SharedPartStuff\r
955         {\r
956             Guid id = Guid.NewGuid();\r
957 \r
958             public override string ToString()\r
959             {\r
960                 return id.ToString();\r
961             }\r
962         }\r
963     }\r
964 }\r