PartCreatorTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. // -----------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // -----------------------------------------------------------------------
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel.Composition;
  7. using System.ComponentModel.Composition.AttributedModel;
  8. using System.ComponentModel.Composition.Primitives;
  9. using System.ComponentModel.Composition.Factories;
  10. using System.ComponentModel.Composition.Hosting;
  11. using System.ComponentModel.Composition.UnitTesting;
  12. using System.Linq;
  13. using System.Reflection;
  14. using System.UnitTesting;
  15. using Microsoft.VisualStudio.TestTools.UnitTesting;
  16. using System.Diagnostics;
  17. using System.ComponentModel.Composition.ReflectionModel;
  18. #if SILVERLIGHT
  19. namespace Tests.Integration
  20. {
  21. [TestClass]
  22. public class PartCreatorTests
  23. {
  24. public interface IId
  25. {
  26. int Id { get; }
  27. }
  28. public interface IIdTypeMetadata
  29. {
  30. string IdType { get; }
  31. string ExportTypeIdentity { get; }
  32. }
  33. [Export(typeof(IId))]
  34. [ExportMetadata("IdType", "PostiveIncrement")]
  35. public class UniqueExport : IId, IDisposable
  36. {
  37. private static int lastId = 0;
  38. public UniqueExport()
  39. {
  40. Id = lastId++;
  41. }
  42. public int Id { get; private set; }
  43. public void Dispose()
  44. {
  45. Id = -1;
  46. }
  47. }
  48. [Export]
  49. [CLSCompliant(false)]
  50. public class PartCreatorImporter
  51. {
  52. [ImportingConstructor]
  53. public PartCreatorImporter(
  54. PartCreator<IId> idCreatorTCtor,
  55. PartCreator<IId, IIdTypeMetadata> idCreatorTMCtor)
  56. {
  57. this._idCreatorTCtor = idCreatorTCtor;
  58. this._idCreatorTMCtor = idCreatorTMCtor;
  59. }
  60. private PartCreator<IId> _idCreatorTCtor;
  61. private PartCreator<IId, IIdTypeMetadata> _idCreatorTMCtor;
  62. [Import(typeof(IId))]
  63. public PartCreator<IId> _idCreatorTField = null; // public so these can work on SL
  64. [Import]
  65. public PartCreator<IId, IIdTypeMetadata> _idCreatorTMField = null; // public so these can work on SL
  66. [Import]
  67. public PartCreator<IId> IdCreatorTProperty { get; set; }
  68. [Import(typeof(IId))]
  69. public PartCreator<IId, IIdTypeMetadata> IdCreatorTMProperty { get; set; }
  70. [ImportMany]
  71. public PartCreator<IId>[] IdCreatorsTProperty { get; set; }
  72. [ImportMany]
  73. public PartCreator<IId, IIdTypeMetadata>[] IdCreatorsTMProperty { get; set; }
  74. public void AssertValid()
  75. {
  76. var ids = new int[]
  77. {
  78. VerifyPartCreator(this._idCreatorTCtor),
  79. VerifyPartCreator(this._idCreatorTMCtor),
  80. VerifyPartCreator(this._idCreatorTField),
  81. VerifyPartCreator(this._idCreatorTMField),
  82. VerifyPartCreator(this.IdCreatorTProperty),
  83. VerifyPartCreator(this.IdCreatorTMProperty),
  84. VerifyPartCreator(this.IdCreatorsTProperty[0]),
  85. VerifyPartCreator(this.IdCreatorsTMProperty[0])
  86. };
  87. Assert.AreEqual(1, this.IdCreatorsTProperty.Length, "Should only be one PartCreator");
  88. Assert.AreEqual(1, this.IdCreatorsTMProperty.Length, "Should only be one PartCreator");
  89. CollectionAssert.AllItemsAreUnique(ids, "There should be no duplicate ids");
  90. }
  91. private int VerifyPartCreator(PartCreator<IId> creator)
  92. {
  93. var val1 = creator.CreatePart();
  94. var val2 = creator.CreatePart();
  95. Assert.AreNotEqual(val1.ExportedValue, val2.ExportedValue, "Values should not be the same");
  96. Assert.AreNotEqual(val1.ExportedValue.Id, val2.ExportedValue.Id, "Value Ids should not be the same");
  97. Assert.IsTrue(val1.ExportedValue.Id >= 0, "Id should be positive");
  98. val1.Dispose();
  99. Assert.IsTrue(val1.ExportedValue.Id < 0, "Disposal of the value should set the id to negative");
  100. return creator.CreatePart().ExportedValue.Id;
  101. }
  102. private int VerifyPartCreator(PartCreator<IId, IIdTypeMetadata> creator)
  103. {
  104. var val = VerifyPartCreator((PartCreator<IId>)creator);
  105. Assert.AreEqual("PostiveIncrement", creator.Metadata.IdType, "IdType should be PositiveIncrement");
  106. Assert.AreEqual(AttributedModelServices.GetTypeIdentity(typeof(ComposablePartDefinition)), creator.Metadata.ExportTypeIdentity);
  107. return val;
  108. }
  109. }
  110. [TestMethod]
  111. public void PartCreatorStandardImports_ShouldWorkProperly()
  112. {
  113. var container = ContainerFactory.CreateWithAttributedCatalog(typeof(UniqueExport), typeof(PartCreatorImporter));
  114. var partCreatorImporter = container.GetExportedValue<PartCreatorImporter>();
  115. partCreatorImporter.AssertValid();
  116. }
  117. [Export]
  118. public class Foo : IDisposable
  119. {
  120. public bool IsDisposed { get; private set; }
  121. public void Dispose()
  122. {
  123. this.IsDisposed = true;
  124. }
  125. }
  126. [Export]
  127. public class SimplePartCreatorImporter
  128. {
  129. [Import]
  130. public PartCreator<Foo> FooFactory { get; set; }
  131. }
  132. [TestMethod]
  133. public void PartCreatorOfT_RecompositionSingle_ShouldBlockChanges()
  134. {
  135. var aggCat = new AggregateCatalog();
  136. var typeCat = new TypeCatalog(typeof(Foo));
  137. aggCat.Catalogs.Add(new TypeCatalog(typeof(SimplePartCreatorImporter)));
  138. aggCat.Catalogs.Add(typeCat);
  139. var container = new CompositionContainer(aggCat);
  140. var fooFactory = container.GetExportedValue<SimplePartCreatorImporter>();
  141. ExceptionAssert.Throws<ChangeRejectedException>(() =>
  142. aggCat.Catalogs.Remove(typeCat));
  143. ExceptionAssert.Throws<ChangeRejectedException>(() =>
  144. aggCat.Catalogs.Add(new TypeCatalog(typeof(Foo))));
  145. }
  146. [Export]
  147. public class ManyPartCreatorImporter
  148. {
  149. [ImportMany(AllowRecomposition = true)]
  150. public PartCreator<Foo>[] FooFactories { get; set; }
  151. }
  152. [TestMethod]
  153. public void FactoryOfT_RecompositionImportMany_ShouldSucceed()
  154. {
  155. var aggCat = new AggregateCatalog();
  156. var typeCat = new TypeCatalog(typeof(Foo));
  157. aggCat.Catalogs.Add(new TypeCatalog(typeof(ManyPartCreatorImporter)));
  158. aggCat.Catalogs.Add(typeCat);
  159. var container = new CompositionContainer(aggCat);
  160. var fooFactories = container.GetExportedValue<ManyPartCreatorImporter>();
  161. Assert.AreEqual(1, fooFactories.FooFactories.Length);
  162. aggCat.Catalogs.Add(new TypeCatalog(typeof(Foo)));
  163. Assert.AreEqual(2, fooFactories.FooFactories.Length);
  164. }
  165. public class PartCreatorExplicitCP
  166. {
  167. [Import(RequiredCreationPolicy = CreationPolicy.Any)]
  168. public PartCreator<Foo> FooCreatorAny { get; set; }
  169. [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
  170. public PartCreator<Foo> FooCreatorNonShared { get; set; }
  171. [Import(RequiredCreationPolicy = CreationPolicy.Shared)]
  172. public PartCreator<Foo> FooCreatorShared { get; set; }
  173. [ImportMany(RequiredCreationPolicy = CreationPolicy.Any)]
  174. public PartCreator<Foo>[] FooCreatorManyAny { get; set; }
  175. [ImportMany(RequiredCreationPolicy = CreationPolicy.NonShared)]
  176. public PartCreator<Foo>[] FooCreatorManyNonShared { get; set; }
  177. [ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)]
  178. public PartCreator<Foo>[] FooCreatorManyShared { get; set; }
  179. }
  180. [TestMethod]
  181. public void PartCreator_ExplicitCreationPolicy_CPShouldBeIgnored()
  182. {
  183. var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Foo));
  184. var part = new PartCreatorExplicitCP();
  185. container.SatisfyImportsOnce(part);
  186. // specifying the required creation policy explicit on the import
  187. // of a PartCreator will be ignored because the PartCreator requires
  188. // the part it wraps to be either Any or NonShared to work properly.
  189. Assert.IsNotNull(part.FooCreatorAny);
  190. Assert.IsNotNull(part.FooCreatorNonShared);
  191. Assert.IsNotNull(part.FooCreatorShared);
  192. Assert.AreEqual(1, part.FooCreatorManyAny.Length);
  193. Assert.AreEqual(1, part.FooCreatorManyNonShared.Length);
  194. Assert.AreEqual(1, part.FooCreatorManyShared.Length);
  195. }
  196. public class PartCreatorImportRequiredMetadata
  197. {
  198. [ImportMany]
  199. public PartCreator<Foo>[] FooCreator { get; set; }
  200. [ImportMany]
  201. public PartCreator<Foo, IIdTypeMetadata>[] FooCreatorWithMetadata { get; set; }
  202. }
  203. [TestMethod]
  204. public void PartCreator_ImportRequiredMetadata_MissingMetadataShouldCauseImportToBeExcluded()
  205. {
  206. var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Foo));
  207. var part = new PartCreatorImportRequiredMetadata();
  208. container.SatisfyImportsOnce(part);
  209. Assert.AreEqual(1, part.FooCreator.Length, "Should contain the one Foo");
  210. Assert.AreEqual(0, part.FooCreatorWithMetadata.Length, "Should NOT contain Foo because it is missing the required Id metadata property");
  211. }
  212. [Export(typeof(Foo))]
  213. [PartCreationPolicy(CreationPolicy.Shared)]
  214. public class SharedFoo : Foo
  215. {
  216. }
  217. [TestMethod]
  218. public void PartCreator_ImportShouldNotImportSharedPart()
  219. {
  220. var container = ContainerFactory.CreateWithAttributedCatalog(typeof(SharedFoo));
  221. var foo = container.GetExportedValue<Foo>();
  222. Assert.IsNotNull(foo, "Ensure that a Foo actually exists in the container");
  223. var part = new PartCreatorImportRequiredMetadata();
  224. container.SatisfyImportsOnce(part);
  225. Assert.AreEqual(0, part.FooCreator.Length, "Should not contain the SharedFoo because the PartCreator should only wrap Any/NonShared parts");
  226. }
  227. [TestMethod]
  228. public void PartCreator_QueryContainerDirectly_ShouldWork()
  229. {
  230. var container = ContainerFactory.CreateWithAttributedCatalog(typeof(Foo));
  231. var importDef = ReflectionModelServices.CreateImportDefinition(
  232. new LazyMemberInfo(MemberTypes.Field, () => new MemberInfo[] { typeof(PartCreatorTests) }), // Give it a bogus member
  233. AttributedModelServices.GetContractName(typeof(Foo)),
  234. AttributedModelServices.GetTypeIdentity(typeof(Foo)),
  235. Enumerable.Empty<KeyValuePair<string, Type>>(),
  236. ImportCardinality.ZeroOrMore,
  237. true,
  238. CreationPolicy.Any,
  239. true, // isPartCreator
  240. null);
  241. var exports = container.GetExports(importDef);
  242. var partCreator = exports.Single();
  243. // Manually walk the steps of using a raw part creator which is modeled as a PartDefinition with
  244. // a single ExportDefinition.
  245. var partDef = (ComposablePartDefinition)partCreator.Value;
  246. var part = partDef.CreatePart();
  247. var foo = (Foo)part.GetExportedValue(partDef.ExportDefinitions.Single());
  248. Assert.IsNotNull(foo);
  249. var foo1 = (Foo)part.GetExportedValue(partDef.ExportDefinitions.Single());
  250. Assert.AreEqual(foo, foo1, "Retrieving the exported value from the same part should return the same value");
  251. // creating a new part should result in getting a new exported value
  252. var part2 = partDef.CreatePart();
  253. var foo2 = (Foo)part2.GetExportedValue(partDef.ExportDefinitions.Single());
  254. Assert.AreNotEqual(foo, foo2, "New part should equate to a new exported value");
  255. // Disposing of part should cause foo to be disposed
  256. ((IDisposable)part).Dispose();
  257. Assert.IsTrue(foo.IsDisposed);
  258. }
  259. [Export]
  260. public class PartImporter<PartType>
  261. {
  262. [Import]
  263. public PartCreator<PartType> Creator { get; set; }
  264. }
  265. [Export]
  266. public class SimpleExport
  267. {
  268. }
  269. [TestMethod]
  270. public void PartCreator_SimpleRejectionRecurrection_ShouldWork()
  271. {
  272. var importTypeCat = new TypeCatalog(typeof(PartImporter<SimpleExport>));
  273. var aggCatalog = new AggregateCatalog(importTypeCat);
  274. var container = ContainerFactory.Create(aggCatalog);
  275. var exports = container.GetExports<PartImporter<SimpleExport>>();
  276. Assert.AreEqual(0, exports.Count());
  277. aggCatalog.Catalogs.Add(new TypeCatalog(typeof(SimpleExport)));
  278. exports = container.GetExports<PartImporter<SimpleExport>>();
  279. Assert.AreEqual(1, exports.Count());
  280. }
  281. }
  282. }
  283. #endif