/Common/Tests/Utilities/SharedProject/SharedProjectTest.cs
C# | 276 lines | 148 code | 28 blank | 100 comment | 11 complexity | 05c2471c64a137e45fafba5eaccfef5c MD5 | raw file
- // Visual Studio Shared Project
- // Copyright(c) Microsoft Corporation
- // All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the License); you may not use
- // this file except in compliance with the License. You may obtain a copy of the
- // License at http://www.apache.org/licenses/LICENSE-2.0
- //
- // THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
- // OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
- // IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
- // MERCHANTABLITY OR NON-INFRINGEMENT.
- //
- // See the Apache Version 2.0 License for specific language governing
- // permissions and limitations under the License.
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.Composition.Hosting;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using Microsoft.Build.Construction;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using Microsoft.VisualStudioTools;
- namespace TestUtilities.SharedProject {
- /// <summary>
- /// Base class for all test cases which generate projects at runtime in a language
- /// agnostic way. This class will initialize the MEF catalog and get the various
- /// project kind(s) to be tested. The kinds wil be available via the ProjectKinds
- /// property.
- ///
- /// It also provides a number of convenience methods for creating project definitions.
- /// This helps to make the project definition more readable and similar to typical
- /// MSBuild structure.
- /// </summary>
- [TestClass]
- public class SharedProjectTest {
- public static CompositionContainer Container;
- public static IEnumerable<ProjectType> ProjectTypes { get; set; }
- static SharedProjectTest() {
- var runningLoc = Path.GetDirectoryName(typeof(SharedProjectTest).Assembly.Location);
- // we want to pick up all of the MEF exports which are available, but they don't
- // depend upon us. So if we're just running some tests in the IDE when the deployment
- // happens it won't have the DLLS with the MEF exports. So we copy them here.
- #if USE_PYTHON_TESTDATA
- TestUtilities.Python.PythonTestData.Deploy(includeTestData: false);
- #else
- TestData.Deploy(null, includeTestData: false);
- #endif
- AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
- try {
- // load all of the available DLLs that depend upon TestUtilities into our catalog
- List<AssemblyCatalog> catalogs = new List<AssemblyCatalog>();
- foreach (var file in Directory.GetFiles(runningLoc, "*.dll")) {
- TryAddAssembly(catalogs, file);
- }
- // Compose everything
- var catalog = new AggregateCatalog(catalogs.ToArray());
- var container = Container = new CompositionContainer(catalog);
- var compBatch = new CompositionBatch();
- container.Compose(compBatch);
- // Initialize our ProjectTypes information from the catalog.
- // First, get a mapping from extension type to all available IProjectProcessor's for
- // that extension
- var processorsMap = container
- .GetExports<IProjectProcessor, IProjectProcessorMetadata>()
- .GroupBy(x => x.Metadata.ProjectExtension)
- .ToDictionary(
- x => x.Key,
- x => x.Select(lazy => lazy.Value).ToArray(),
- StringComparer.OrdinalIgnoreCase
- );
- // Then create the ProjectTypes
- ProjectTypes = container
- .GetExports<ProjectTypeDefinition, IProjectTypeDefinitionMetadata>()
- .Select(lazyVal => {
- var md = lazyVal.Metadata;
- IProjectProcessor[] processors;
- processorsMap.TryGetValue(md.ProjectExtension, out processors);
- return new ProjectType(
- md.CodeExtension,
- md.ProjectExtension,
- Guid.Parse(md.ProjectTypeGuid),
- md.SampleCode,
- processors
- );
- });
- } catch (ReflectionTypeLoadException ex) {
- foreach (var e in ex.LoaderExceptions) {
- Trace.TraceError(e.ToString());
- }
- throw;
- } finally {
- AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
- }
- // something's broken if we don't have any languages to test against, so fail the test.
- Assert.IsTrue(ProjectTypes.Count() > 0, "no project types were registered and no tests will run");
- }
- private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
- Assembly asm = null;
- var name = new AssemblyName(args.Name);
- var path = Path.Combine(VisualStudioPath.PrivateAssemblies, name.Name + ".dll");
- if (File.Exists(path)) {
- asm = Assembly.LoadFile(path);
- } else {
- path = Path.Combine(VisualStudioPath.PublicAssemblies, name.Name + ".dll");
- if (File.Exists(path)) {
- asm = Assembly.LoadFile(path);
- }
- }
- if (asm != null && asm.FullName != name.FullName) {
- asm = null;
- }
- return asm;
- }
- private static void TryAddAssembly(List<AssemblyCatalog> catalogs, string file) {
- Assembly asm;
- try {
- asm = Assembly.Load(Path.GetFileNameWithoutExtension(file));
- } catch {
- return;
- }
- // Include any test assemblies which reference this assembly, they might
- // have defined a project kind.
- foreach (var reference in asm.GetReferencedAssemblies()) {
- if (reference.FullName == typeof(SharedProjectTest).Assembly.GetName().FullName) {
- Console.WriteLine("Including {0}", file);
- catalogs.Add(new AssemblyCatalog(asm));
- break;
- }
- }
- }
- /// <summary>
- /// Helper function to create a ProjectProperty object to simply syntax in
- /// project definitions.
- /// </summary>
- public static ProjectProperty Property(string name, string value) {
- return new ProjectProperty(name, value);
- }
- /// <summary>
- /// Helper function to create a StartupFileProjectProperty object to simply syntax in
- /// project definitions.
- /// </summary>
- public static StartupFileProjectProperty StartupFile(string filename) {
- return new StartupFileProjectProperty(filename);
- }
-
- /// <summary>
- /// Helper function to create a group of properties when creating project definitions.
- /// These aren't strictly necessary and just serve to add structure to the code
- /// and make it similar to an MSBuild project file.
- /// </summary>
- public static ProjectContentGroup PropertyGroup(params ProjectProperty[] properties) {
- return new ProjectContentGroup(properties);
- }
- /// <summary>
- /// Helper function to create a CompileItem object to simply syntax in
- /// defining project definitions.
- /// </summary>
- public static CompileItem Compile(string name, string content = null, bool isExcluded = false, bool isMissing = false) {
- return new CompileItem(name, content, isExcluded, isMissing);
- }
- /// <summary>
- /// Helper function to create a CompileItem object to simply syntax in
- /// defining project definitions.
- /// </summary>
- public static ContentItem Content(string name, string content, bool isExcluded = false) {
- return new ContentItem(name, content, isExcluded);
- }
- /// <summary>
- /// Helper function to create a SymbolicLinkItem object to simply syntax in
- /// defining project definitions.
- /// </summary>
- public static SymbolicLinkItem SymbolicLink(string name, string referencePath, bool isExcluded = false, bool isMissing = false) {
- return new SymbolicLinkItem(name, referencePath, isExcluded, isMissing);
- }
- /// <summary>
- /// Helper function to create a FolderItem object to simply syntax in
- /// defining project definitions.
- /// </summary>
- public static FolderItem Folder(string name, bool isExcluded = false, bool isMissing = false) {
- return new FolderItem(name, isExcluded, isMissing);
- }
- /// <summary>
- /// Helper function to create a CustomItem object which is an MSBuild item with
- /// the specified item type.
- /// </summary>
- public static CustomItem CustomItem(string itemType, string name, string content = null, bool isExcluded = false, bool isMissing = false, IEnumerable<KeyValuePair<string, string>> metadata = null) {
- return new CustomItem(itemType, name, content, isExcluded, isMissing, metadata);
- }
- /// <summary>
- /// Helper function to create a group of items when creating project definitions.
- /// These aren't strictly necessary and just serve to add structure to the code
- /// and make it similar to an MSBuild project file.
- /// </summary>
- public static ProjectContentGroup ItemGroup(params ProjectContentGenerator[] properties) {
- return new ProjectContentGroup(properties);
- }
- /// <summary>
- /// Returns a new SolutionFolder object which can be used to create
- /// a solution folder in the generated project.
- /// </summary>
- public static SolutionFolder SolutionFolder(string name) {
- return new SolutionFolder(name);
- }
- /// <summary>
- /// Returns a new TargetDefinition which represents a specified Target
- /// inside of the project file. The various stages of the target can be
- /// created using the members of the static Tasks class.
- /// </summary>
- public static TargetDefinition Target(string name, params Action<ProjectTargetElement>[] creators) {
- return new TargetDefinition(name, creators);
- }
- /// <summary>
- /// Returns a new TargetDefinition which represents a specified Target
- /// inside of the project file. The various stages of the target can be
- /// created using the members of the static Tasks class.
- /// </summary>
- public static TargetDefinition Target(string name, string dependsOnTargets, params Action<ProjectTargetElement>[] creators) {
- return new TargetDefinition(name, creators) { DependsOnTargets = dependsOnTargets };
- }
- /// <summary>
- /// Returns a new ImportDefinition for the specified project.
- /// </summary>
- /// <param name="project"></param>
- /// <returns></returns>
- public static ImportDefinition Import(string project) {
- return new ImportDefinition(project);
- }
- /// <summary>
- /// Provides tasks for creating target definitions in generated projects.
- /// </summary>
- public static class Tasks {
- /// <summary>
- /// Creates a task which outputs a message during the build.
- /// </summary>
- public static Action<ProjectTargetElement> Message(string message, string importance = null) {
- return target => {
- var messageTask = target.AddTask("Message");
- messageTask.SetParameter("Text", message);
- if (importance != null) {
- messageTask.SetParameter("Importance", importance);
- }
- };
- }
- }
- }
- }