PageRenderTime 73ms CodeModel.GetById 57ms RepoModel.GetById 0ms app.codeStats 0ms

/Common/Tests/Utilities/SharedProject/SharedProjectTest.cs

https://gitlab.com/SplatoonModdingHub/PTVS
C# | 276 lines | 148 code | 28 blank | 100 comment | 11 complexity | 05c2471c64a137e45fafba5eaccfef5c MD5 | raw file
  1. // Visual Studio Shared Project
  2. // Copyright(c) Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the License); you may not use
  6. // this file except in compliance with the License. You may obtain a copy of the
  7. // License at http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
  10. // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
  11. // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  12. // MERCHANTABLITY OR NON-INFRINGEMENT.
  13. //
  14. // See the Apache Version 2.0 License for specific language governing
  15. // permissions and limitations under the License.
  16. using System;
  17. using System.Collections.Generic;
  18. using System.ComponentModel.Composition.Hosting;
  19. using System.Diagnostics;
  20. using System.IO;
  21. using System.Linq;
  22. using System.Reflection;
  23. using Microsoft.Build.Construction;
  24. using Microsoft.VisualStudio.TestTools.UnitTesting;
  25. using Microsoft.VisualStudioTools;
  26. namespace TestUtilities.SharedProject {
  27. /// <summary>
  28. /// Base class for all test cases which generate projects at runtime in a language
  29. /// agnostic way. This class will initialize the MEF catalog and get the various
  30. /// project kind(s) to be tested. The kinds wil be available via the ProjectKinds
  31. /// property.
  32. ///
  33. /// It also provides a number of convenience methods for creating project definitions.
  34. /// This helps to make the project definition more readable and similar to typical
  35. /// MSBuild structure.
  36. /// </summary>
  37. [TestClass]
  38. public class SharedProjectTest {
  39. public static CompositionContainer Container;
  40. public static IEnumerable<ProjectType> ProjectTypes { get; set; }
  41. static SharedProjectTest() {
  42. var runningLoc = Path.GetDirectoryName(typeof(SharedProjectTest).Assembly.Location);
  43. // we want to pick up all of the MEF exports which are available, but they don't
  44. // depend upon us. So if we're just running some tests in the IDE when the deployment
  45. // happens it won't have the DLLS with the MEF exports. So we copy them here.
  46. #if USE_PYTHON_TESTDATA
  47. TestUtilities.Python.PythonTestData.Deploy(includeTestData: false);
  48. #else
  49. TestData.Deploy(null, includeTestData: false);
  50. #endif
  51. AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
  52. try {
  53. // load all of the available DLLs that depend upon TestUtilities into our catalog
  54. List<AssemblyCatalog> catalogs = new List<AssemblyCatalog>();
  55. foreach (var file in Directory.GetFiles(runningLoc, "*.dll")) {
  56. TryAddAssembly(catalogs, file);
  57. }
  58. // Compose everything
  59. var catalog = new AggregateCatalog(catalogs.ToArray());
  60. var container = Container = new CompositionContainer(catalog);
  61. var compBatch = new CompositionBatch();
  62. container.Compose(compBatch);
  63. // Initialize our ProjectTypes information from the catalog.
  64. // First, get a mapping from extension type to all available IProjectProcessor's for
  65. // that extension
  66. var processorsMap = container
  67. .GetExports<IProjectProcessor, IProjectProcessorMetadata>()
  68. .GroupBy(x => x.Metadata.ProjectExtension)
  69. .ToDictionary(
  70. x => x.Key,
  71. x => x.Select(lazy => lazy.Value).ToArray(),
  72. StringComparer.OrdinalIgnoreCase
  73. );
  74. // Then create the ProjectTypes
  75. ProjectTypes = container
  76. .GetExports<ProjectTypeDefinition, IProjectTypeDefinitionMetadata>()
  77. .Select(lazyVal => {
  78. var md = lazyVal.Metadata;
  79. IProjectProcessor[] processors;
  80. processorsMap.TryGetValue(md.ProjectExtension, out processors);
  81. return new ProjectType(
  82. md.CodeExtension,
  83. md.ProjectExtension,
  84. Guid.Parse(md.ProjectTypeGuid),
  85. md.SampleCode,
  86. processors
  87. );
  88. });
  89. } catch (ReflectionTypeLoadException ex) {
  90. foreach (var e in ex.LoaderExceptions) {
  91. Trace.TraceError(e.ToString());
  92. }
  93. throw;
  94. } finally {
  95. AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
  96. }
  97. // something's broken if we don't have any languages to test against, so fail the test.
  98. Assert.IsTrue(ProjectTypes.Count() > 0, "no project types were registered and no tests will run");
  99. }
  100. private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
  101. Assembly asm = null;
  102. var name = new AssemblyName(args.Name);
  103. var path = Path.Combine(VisualStudioPath.PrivateAssemblies, name.Name + ".dll");
  104. if (File.Exists(path)) {
  105. asm = Assembly.LoadFile(path);
  106. } else {
  107. path = Path.Combine(VisualStudioPath.PublicAssemblies, name.Name + ".dll");
  108. if (File.Exists(path)) {
  109. asm = Assembly.LoadFile(path);
  110. }
  111. }
  112. if (asm != null && asm.FullName != name.FullName) {
  113. asm = null;
  114. }
  115. return asm;
  116. }
  117. private static void TryAddAssembly(List<AssemblyCatalog> catalogs, string file) {
  118. Assembly asm;
  119. try {
  120. asm = Assembly.Load(Path.GetFileNameWithoutExtension(file));
  121. } catch {
  122. return;
  123. }
  124. // Include any test assemblies which reference this assembly, they might
  125. // have defined a project kind.
  126. foreach (var reference in asm.GetReferencedAssemblies()) {
  127. if (reference.FullName == typeof(SharedProjectTest).Assembly.GetName().FullName) {
  128. Console.WriteLine("Including {0}", file);
  129. catalogs.Add(new AssemblyCatalog(asm));
  130. break;
  131. }
  132. }
  133. }
  134. /// <summary>
  135. /// Helper function to create a ProjectProperty object to simply syntax in
  136. /// project definitions.
  137. /// </summary>
  138. public static ProjectProperty Property(string name, string value) {
  139. return new ProjectProperty(name, value);
  140. }
  141. /// <summary>
  142. /// Helper function to create a StartupFileProjectProperty object to simply syntax in
  143. /// project definitions.
  144. /// </summary>
  145. public static StartupFileProjectProperty StartupFile(string filename) {
  146. return new StartupFileProjectProperty(filename);
  147. }
  148. /// <summary>
  149. /// Helper function to create a group of properties when creating project definitions.
  150. /// These aren't strictly necessary and just serve to add structure to the code
  151. /// and make it similar to an MSBuild project file.
  152. /// </summary>
  153. public static ProjectContentGroup PropertyGroup(params ProjectProperty[] properties) {
  154. return new ProjectContentGroup(properties);
  155. }
  156. /// <summary>
  157. /// Helper function to create a CompileItem object to simply syntax in
  158. /// defining project definitions.
  159. /// </summary>
  160. public static CompileItem Compile(string name, string content = null, bool isExcluded = false, bool isMissing = false) {
  161. return new CompileItem(name, content, isExcluded, isMissing);
  162. }
  163. /// <summary>
  164. /// Helper function to create a CompileItem object to simply syntax in
  165. /// defining project definitions.
  166. /// </summary>
  167. public static ContentItem Content(string name, string content, bool isExcluded = false) {
  168. return new ContentItem(name, content, isExcluded);
  169. }
  170. /// <summary>
  171. /// Helper function to create a SymbolicLinkItem object to simply syntax in
  172. /// defining project definitions.
  173. /// </summary>
  174. public static SymbolicLinkItem SymbolicLink(string name, string referencePath, bool isExcluded = false, bool isMissing = false) {
  175. return new SymbolicLinkItem(name, referencePath, isExcluded, isMissing);
  176. }
  177. /// <summary>
  178. /// Helper function to create a FolderItem object to simply syntax in
  179. /// defining project definitions.
  180. /// </summary>
  181. public static FolderItem Folder(string name, bool isExcluded = false, bool isMissing = false) {
  182. return new FolderItem(name, isExcluded, isMissing);
  183. }
  184. /// <summary>
  185. /// Helper function to create a CustomItem object which is an MSBuild item with
  186. /// the specified item type.
  187. /// </summary>
  188. public static CustomItem CustomItem(string itemType, string name, string content = null, bool isExcluded = false, bool isMissing = false, IEnumerable<KeyValuePair<string, string>> metadata = null) {
  189. return new CustomItem(itemType, name, content, isExcluded, isMissing, metadata);
  190. }
  191. /// <summary>
  192. /// Helper function to create a group of items when creating project definitions.
  193. /// These aren't strictly necessary and just serve to add structure to the code
  194. /// and make it similar to an MSBuild project file.
  195. /// </summary>
  196. public static ProjectContentGroup ItemGroup(params ProjectContentGenerator[] properties) {
  197. return new ProjectContentGroup(properties);
  198. }
  199. /// <summary>
  200. /// Returns a new SolutionFolder object which can be used to create
  201. /// a solution folder in the generated project.
  202. /// </summary>
  203. public static SolutionFolder SolutionFolder(string name) {
  204. return new SolutionFolder(name);
  205. }
  206. /// <summary>
  207. /// Returns a new TargetDefinition which represents a specified Target
  208. /// inside of the project file. The various stages of the target can be
  209. /// created using the members of the static Tasks class.
  210. /// </summary>
  211. public static TargetDefinition Target(string name, params Action<ProjectTargetElement>[] creators) {
  212. return new TargetDefinition(name, creators);
  213. }
  214. /// <summary>
  215. /// Returns a new TargetDefinition which represents a specified Target
  216. /// inside of the project file. The various stages of the target can be
  217. /// created using the members of the static Tasks class.
  218. /// </summary>
  219. public static TargetDefinition Target(string name, string dependsOnTargets, params Action<ProjectTargetElement>[] creators) {
  220. return new TargetDefinition(name, creators) { DependsOnTargets = dependsOnTargets };
  221. }
  222. /// <summary>
  223. /// Returns a new ImportDefinition for the specified project.
  224. /// </summary>
  225. /// <param name="project"></param>
  226. /// <returns></returns>
  227. public static ImportDefinition Import(string project) {
  228. return new ImportDefinition(project);
  229. }
  230. /// <summary>
  231. /// Provides tasks for creating target definitions in generated projects.
  232. /// </summary>
  233. public static class Tasks {
  234. /// <summary>
  235. /// Creates a task which outputs a message during the build.
  236. /// </summary>
  237. public static Action<ProjectTargetElement> Message(string message, string importance = null) {
  238. return target => {
  239. var messageTask = target.AddTask("Message");
  240. messageTask.SetParameter("Text", message);
  241. if (importance != null) {
  242. messageTask.SetParameter("Importance", importance);
  243. }
  244. };
  245. }
  246. }
  247. }
  248. }