[mcs] Validate more nameof argument expressions
[mono.git] / mcs / class / System.ComponentModel.Composition / Tests / ComponentModelUnitTest / System / Integration / RejectionTests.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Linq;\r
6 using System.Text;\r
7 using System.Collections.Generic;\r
8 using System.ComponentModel.Composition;\r
9 using System.ComponentModel.Composition.Factories;\r
10 using System.ComponentModel.Composition.Hosting;\r
11 using Microsoft.VisualStudio.TestTools.UnitTesting;\r
12 using System.UnitTesting;\r
13 \r
14 namespace Tests.Integration\r
15 {\r
16     [TestClass]\r
17     public class RejectionTests\r
18     {\r
19         public interface IExtension\r
20         {\r
21             int Id { get; set; }\r
22         }\r
23 \r
24         [Export]\r
25         public class MyImporter\r
26         {\r
27             [ImportMany(AllowRecomposition = true)]\r
28             public IExtension[] Extensions { get; set; }\r
29         }\r
30 \r
31         [Export(typeof(IExtension))]\r
32         public class Extension1 : IExtension\r
33         {\r
34             [Import("IExtension.IdValue")]\r
35             public int Id { get; set; }\r
36         }\r
37 \r
38         [Export(typeof(IExtension))]\r
39         public class Extension2 : IExtension\r
40         {\r
41             [Import("IExtension.IdValue2")]\r
42             public int Id { get; set; }\r
43         }\r
44 \r
45         [TestMethod]\r
46         public void Rejection_ExtensionLightUp_AddedViaBatch()\r
47         {\r
48             var container = ContainerFactory.CreateWithAttributedCatalog(\r
49                 typeof(MyImporter),\r
50                 typeof(Extension1),\r
51                 typeof(Extension2));\r
52 \r
53             var importer = container.GetExportedValue<MyImporter>();\r
54 \r
55             Assert.AreEqual(0, importer.Extensions.Length, "Should have 0 extensions");\r
56 \r
57             container.ComposeExportedValue<int>("IExtension.IdValue", 10);\r
58 \r
59             Assert.AreEqual(1, importer.Extensions.Length, "Should have 1 extension");\r
60             Assert.AreEqual(10, importer.Extensions[0].Id);\r
61 \r
62             container.ComposeExportedValue<int>("IExtension.IdValue2", 20);\r
63 \r
64             Assert.AreEqual(2, importer.Extensions.Length, "Should have 2 extension");\r
65             Assert.AreEqual(10, importer.Extensions[0].Id);\r
66             Assert.AreEqual(20, importer.Extensions[1].Id);\r
67         }\r
68 \r
69         public class ExtensionValues\r
70         {\r
71             [Export("IExtension.IdValue")]\r
72             public int Value = 10;\r
73 \r
74             [Export("IExtension.IdValue2")]\r
75             public int Value2 = 20;\r
76         }\r
77 \r
78         [TestMethod]\r
79         public void Rejection_ExtensionLightUp_AddedViaCatalog()\r
80         {\r
81             var ext1Cat = CatalogFactory.CreateAttributed(typeof(Extension1));\r
82             var ext2Cat = CatalogFactory.CreateAttributed(typeof(Extension2));\r
83             var hostCat = CatalogFactory.CreateAttributed(typeof(MyImporter));\r
84             var valueCat = CatalogFactory.CreateAttributed(typeof(ExtensionValues));\r
85 \r
86             var catalog = new AggregateCatalog();\r
87             catalog.Catalogs.Add(hostCat);\r
88 \r
89             var container = ContainerFactory.Create(catalog);\r
90 \r
91             var importer = container.GetExportedValue<MyImporter>();\r
92 \r
93             Assert.AreEqual(0, importer.Extensions.Length, "Should have 0 extensions");\r
94 \r
95             catalog.Catalogs.Add(ext1Cat);\r
96 \r
97             Assert.AreEqual(0, importer.Extensions.Length, "Should have 0 extensions after ext1 added without dependency");\r
98 \r
99             catalog.Catalogs.Add(ext2Cat);\r
100 \r
101             Assert.AreEqual(0, importer.Extensions.Length, "Should have 0 extensions after ext2 added without dependency");\r
102 \r
103             catalog.Catalogs.Add(valueCat);\r
104 \r
105             Assert.AreEqual(2, importer.Extensions.Length, "Should have 2 extension");\r
106             Assert.AreEqual(10, importer.Extensions[0].Id);\r
107             Assert.AreEqual(20, importer.Extensions[1].Id);\r
108         }\r
109 \r
110         public interface IMissing { }\r
111         public interface ISingle { }\r
112         public interface IMultiple { }\r
113         public interface IConditional { }\r
114         public class SingleImpl : ISingle { }\r
115         public class MultipleImpl : IMultiple { }\r
116 \r
117         public class NoImportPart\r
118         {\r
119             public NoImportPart()\r
120             {\r
121                 SingleExport = new SingleImpl();\r
122                 MultipleExport1 = new MultipleImpl();\r
123                 MultipleExport2 = new MultipleImpl();\r
124             }\r
125 \r
126             [Export]\r
127             public ISingle SingleExport { private set; get; }\r
128 \r
129             [Export]\r
130             public IMultiple MultipleExport1 { private set; get; }\r
131 \r
132             [Export]\r
133             public IMultiple MultipleExport2 { private set; get; }\r
134         }\r
135 \r
136         [Export]\r
137         public class Needy\r
138         {\r
139             public Needy() { }\r
140 \r
141             [Import]\r
142             public ISingle SingleImport { get; set; }\r
143         }\r
144 \r
145         [TestMethod]\r
146         public void Rejection_Resurrection()\r
147         {\r
148             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy));\r
149 \r
150             var exports1 = container.GetExportedValues<Needy>();\r
151 \r
152             Assert.AreEqual(0, exports1.Count(), "Catalog entry should be rejected");\r
153 \r
154             container.ComposeParts(new NoImportPart());\r
155 \r
156             var exports2 = container.GetExportedValues<Needy>();\r
157             Assert.AreEqual(1, exports2.Count(), "Catalog entry should be ressurrected");\r
158         }\r
159 \r
160         [TestMethod]\r
161         public void Rejection_BatchSatisfiesBatch()\r
162         {\r
163             var container = ContainerFactory.Create();\r
164             var needy = new Needy();\r
165             container.ComposeParts(needy, new NoImportPart());\r
166             Assert.IsInstanceOfType(needy.SingleImport, typeof(SingleImpl), "Import not satisifed as expected");\r
167         }\r
168 \r
169         [TestMethod]\r
170         public void Rejection_BatchSatisfiesBatchReversed()\r
171         {\r
172             var container = ContainerFactory.Create();\r
173             var needy = new Needy();\r
174             container.ComposeParts(new NoImportPart(), needy);\r
175             Assert.IsInstanceOfType(needy.SingleImport, typeof(SingleImpl), "Import not satisifed as expected");\r
176         }\r
177 \r
178         [TestMethod]\r
179         public void Rejection_CatalogSatisfiesBatch()\r
180         {\r
181             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(NoImportPart));\r
182             var needy = new Needy();\r
183             container.ComposeParts(needy);\r
184             Assert.IsInstanceOfType(needy.SingleImport, typeof(SingleImpl), "Import not satisifed as expected");\r
185         }\r
186 \r
187         [TestMethod]\r
188         public void Rejection_TransitiveDependenciesSatisfied()\r
189         {\r
190             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(NoImportPart));\r
191             var needy = container.GetExportedValue<Needy>();\r
192             Assert.IsNotNull(needy);\r
193             Assert.IsInstanceOfType(needy.SingleImport, typeof(SingleImpl), "Import not satisifed as expected");\r
194         }\r
195 \r
196         [TestMethod]\r
197         public void Rejection_TransitiveDependenciesUnsatisfied_ShouldThrowCardinalityMismatch()\r
198         {\r
199             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(MissingImportPart));\r
200 \r
201             ExceptionAssert.Throws<ImportCardinalityMismatchException>(() =>\r
202                 container.GetExportedValue<Needy>());\r
203         }\r
204 \r
205         public class MissingImportPart : NoImportPart\r
206         {\r
207             [Import]\r
208             public IMissing MissingImport { set; get; }\r
209         }\r
210 \r
211         [TestMethod]\r
212         public void Rejection_BatchRevert()\r
213         {\r
214             var container = ContainerFactory.Create();\r
215 \r
216             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
217                 container.ComposeParts(new MissingImportPart()));\r
218         }\r
219 \r
220         [TestMethod]\r
221         public void Rejection_DefendPromisesOnceMade()\r
222         {\r
223             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy));\r
224 \r
225             var addBatch = new CompositionBatch();\r
226             var removeBatch = new CompositionBatch();\r
227             var addedPart = addBatch.AddPart(new NoImportPart());\r
228             removeBatch.RemovePart(addedPart);\r
229 \r
230             // Add then remove should be fine as long as exports aren't used yet.\r
231             container.Compose(addBatch);\r
232             container.Compose(removeBatch);\r
233 \r
234             // Add the dependencies\r
235             container.Compose(addBatch);\r
236 \r
237             // Retrieve needy which uses an export from addedPart\r
238             var export = container.GetExportedValue<Needy>();\r
239 \r
240             // Should not be able to remove the addedPart because someone depends on it.\r
241             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
242                 container.Compose(removeBatch));\r
243         }\r
244 \r
245         [TestMethod]\r
246         public void Rejection_DefendPromisesLazily()\r
247         {\r
248             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy));\r
249 \r
250             // Add the missing dependency for Needy\r
251             container.ComposeParts(new NoImportPart());\r
252 \r
253             // This change should succeed since the component "Needy" hasn't been fully composed\r
254             // and one way of satisfying its needs is as good ask another\r
255             var export = container.GetExport<Needy>();\r
256 \r
257             // Cannot add another import because it would break existing promised compositions\r
258             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
259                 container.ComposeParts(new NoImportPart()));\r
260 \r
261             // Instansitate the object\r
262             var needy = export.Value;\r
263 \r
264             // Cannot add another import because it would break existing compositions\r
265             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
266                 container.ComposeParts(new NoImportPart()));\r
267         }\r
268 \r
269 \r
270         [TestMethod]\r
271         public void Rejection_SwitchPromiseFromManualToCatalog()\r
272         {\r
273             // This test shows how the priority list in the AggregateCatalog can actually play with \r
274             // the rejection work. Until the actual object is actually pulled on and satisfied the \r
275             // promise can be moved around even for not-recomposable imports but once the object is \r
276             // pulled on it is fixed from that point on.\r
277 \r
278             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Needy), typeof(NoImportPart));\r
279 \r
280             // Add the missing dependency for Needy\r
281             container.ComposeParts(new NoImportPart());\r
282 \r
283             // This change should succeed since the component "Needy" hasn't been fully composed\r
284             // and one way of satisfying its needs is as good as another\r
285             var export = container.GetExport<Needy>();\r
286 \r
287             // Adding more exports doesn't fail because we push the promise to use the NoImportPart from the catalog\r
288             // using the priorities from the AggregateExportProvider\r
289             container.ComposeParts(new NoImportPart());\r
290 \r
291             // Instansitate the object\r
292             var needy = export.Value;\r
293 \r
294             // Cannot add another import because it would break existing compositions\r
295             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
296                 container.ComposeParts(new NoImportPart()));\r
297         }\r
298 \r
299         public interface ILoopA { }\r
300         public interface ILoopB { }\r
301 \r
302         [Export(typeof(ILoopA))]\r
303         public class LoopA1 : ILoopA\r
304         {\r
305             [Import]\r
306             public ILoopB LoopB { set; get; }\r
307         }\r
308 \r
309         [Export(typeof(ILoopA))]\r
310         public class LoopA2 : ILoopA\r
311         {\r
312             [Import]\r
313             public ILoopB LoopB { set; get; }\r
314         }\r
315 \r
316         [Export(typeof(ILoopB))]\r
317         public class LoopB1 : ILoopB\r
318         {\r
319             [Import]\r
320             public ILoopA LoopA { set; get; }\r
321         }\r
322 \r
323         [Export(typeof(ILoopB))]\r
324         public class LoopB2 : ILoopB\r
325         {\r
326             [Import]\r
327             public ILoopA LoopA { set; get; }\r
328         }\r
329 \r
330         // This is an interesting situation.  There are several possible self-consistent outcomes:\r
331         // - All parts involved in the loop are rejected\r
332         // - A consistent subset are not rejected (exactly one of LoopA1/LoopA2 and one of LoopB1/LoopB2\r
333         //\r
334         // Both have desireable and undesirable characteristics.  The first case is non-discriminatory but\r
335         // rejects more parts than are necessary, the second minimizes rejection but must choose a subset\r
336         // on somewhat arbitary grounds.\r
337         [TestMethod]\r
338         public void Rejection_TheClemensLoop()\r
339         {\r
340             var catalog = new TypeCatalog(new Type[] { typeof(LoopA1), typeof(LoopA2), typeof(LoopB1), typeof(LoopB2) });\r
341             var container = new CompositionContainer(catalog);\r
342             var exportsA = container.GetExportedValues<ILoopA>();\r
343             var exportsB = container.GetExportedValues<ILoopB>();\r
344 \r
345             // These assertions would prove solution one\r
346             Assert.AreEqual(0, exportsA.Count(), "Catalog ILoopA entries should be rejected");\r
347             Assert.AreEqual(0, exportsB.Count(), "Catalog ILoopB entries should be rejected");\r
348 \r
349             // These assertions would prove solution two\r
350             //Assert.AreEqual(1, exportsA.Count, "Only noe ILoopA entry should not be rejected");\r
351             //Assert.AreEqual(1, exportsB.Count, "Only noe ILoopB entry should not be rejected");\r
352         }\r
353 \r
354         public interface IWorkItem\r
355         {\r
356             string Id { get; set; }\r
357         }\r
358 \r
359         [Export]\r
360         public class AllWorkItems\r
361         {\r
362             [ImportMany(AllowRecomposition = true)]\r
363             public Lazy<IWorkItem>[] WorkItems { get; set; }\r
364         }\r
365 \r
366         [Export(typeof(IWorkItem))]\r
367         public class WorkItem : IWorkItem\r
368         {\r
369             [Import("WorkItem.Id", AllowRecomposition = true)]\r
370             public string Id { get; set; }\r
371         }\r
372 \r
373         public class Ids\r
374         {\r
375             [Export("WorkItem.Id")]\r
376             public string Id = "MyId";\r
377 \r
378         }\r
379 \r
380         [TestMethod]\r
381         public void AppliedStateNotCompleteedYet()\r
382         {\r
383             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(AllWorkItems));\r
384 \r
385             container.ComposeExportedValue<string>("WorkItem.Id", "A");\r
386 \r
387             var workItems = container.GetExportedValue<AllWorkItems>();\r
388 \r
389             Assert.AreEqual(0, workItems.WorkItems.Length);\r
390 \r
391             container.ComposeParts(new WorkItem());\r
392 \r
393             Assert.AreEqual(1, workItems.WorkItems.Length);\r
394             Assert.AreEqual("A", workItems.WorkItems[0].Value.Id);\r
395         }\r
396 \r
397         [Export]\r
398         public class ClassWithMissingImport\r
399         {\r
400             [Import]\r
401             private string _importNotFound = null;\r
402         }\r
403 \r
404         [TestMethod]\r
405         public void AppliedStateStored_ShouldRevertStateOnFailure()\r
406         {\r
407             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(AllWorkItems), typeof(WorkItem), typeof(Ids));\r
408 \r
409             var workItems = container.GetExportedValue<AllWorkItems>();\r
410 \r
411             Assert.AreEqual(1, workItems.WorkItems.Length);\r
412 \r
413             var batch = new CompositionBatch();\r
414 \r
415             batch.AddExportedValue("WorkItem.Id", "B");\r
416             batch.AddPart(new ClassWithMissingImport());\r
417 \r
418             ExceptionAssert.Throws<ChangeRejectedException>(() =>\r
419                 container.Compose(batch));\r
420 \r
421             Assert.AreEqual("MyId", workItems.WorkItems[0].Value.Id);\r
422         }\r
423 \r
424         [Export]\r
425         public class OptionalImporter\r
426         {\r
427             [Import(AllowDefault = true)]\r
428             public ClassWithMissingImport Import { get; set; }\r
429         }\r
430 \r
431         [TestMethod]\r
432         public void OptionalImportWithMissingDependency_ShouldRejectAndComposeFine()\r
433         {\r
434             var container = ContainerFactory.CreateWithAttributedCatalog(typeof(OptionalImporter), typeof(ClassWithMissingImport));\r
435 \r
436             var importer = container.GetExportedValue<OptionalImporter>();\r
437 \r
438             Assert.IsNull(importer.Import);\r
439         }\r
440 \r
441         [Export]\r
442         public class PartA\r
443         {\r
444             [Import(AllowDefault = true, AllowRecomposition = true)]\r
445             public PartB ImportB { get; set; }\r
446         }\r
447 \r
448         [Export]\r
449         public class PartB\r
450         {\r
451             [Import]\r
452             public PartC ImportC { get; set; }\r
453         }\r
454 \r
455         [Export]\r
456         public class PartC\r
457         {\r
458             [Import]\r
459             public PartB ImportB { get; set; }\r
460         }\r
461 \r
462         [TestMethod]\r
463         [WorkItem(684510)]\r
464         public void PartAOptionalDependsOnPartB_PartBGetAddedLater()\r
465         {\r
466             var container = new CompositionContainer(new TypeCatalog(typeof(PartC), typeof(PartA)));\r
467             var partA = container.GetExportedValue<PartA>();\r
468 \r
469             Assert.IsNull(partA.ImportB);\r
470 \r
471             var partB = new PartB();\r
472             container.ComposeParts(partB);\r
473 \r
474             Assert.AreEqual(partA.ImportB, partB);\r
475             Assert.IsNotNull(partB.ImportC);\r
476         }\r
477 \r
478         [Export]\r
479         public class PartA2\r
480         {\r
481             [Import(AllowDefault = true, AllowRecomposition = true)]\r
482             public PartB ImportB { get; set; }\r
483 \r
484             [Import(AllowDefault = true, AllowRecomposition = true)]\r
485             public PartC ImportC { get; set; }\r
486         }\r
487 \r
488         [TestMethod]\r
489         [WorkItem(684510)]\r
490         public void PartAOptionalDependsOnPartBAndPartC_PartCGetRecurrected()\r
491         {\r
492             var container = new CompositionContainer(new TypeCatalog(typeof(PartA2), typeof(PartB)));\r
493             var partA = container.GetExportedValue<PartA2>();\r
494 \r
495             Assert.IsNull(partA.ImportB);\r
496             Assert.IsNull(partA.ImportC);\r
497 \r
498             var partC = new PartC();\r
499             container.ComposeParts(partC);\r
500 \r
501             Assert.AreEqual(partA.ImportB, partC.ImportB);\r
502             Assert.AreEqual(partA.ImportC, partC);\r
503         }\r
504     }\r
505 }\r