From b33dcd2afa36b18b59217018c7704559f67c5b62 Mon Sep 17 00:00:00 2001 From: Dale Ragan Date: Tue, 1 Jun 2010 22:53:40 -0400 Subject: [PATCH] Finished hooking up the custom tool to call into the SpecFlow Generator. When a feature file is created or saved, it creates the feature code file. The MonoDevelop add-in is complete. The only difference between it and the Visual Studio one is VB support. --- .../MonoDevelop.TechTalk.SpecFlow.csproj | 1 + .../MonoDevelopProjectReader.cs | 69 ++++++++++++++++++ .../SingleFeatureFileGenerator.cs | 72 +++++++++++++++++-- 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs diff --git a/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj index bde702836..973aab1cc 100644 --- a/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj +++ b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj @@ -69,6 +69,7 @@ + diff --git a/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs b/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs new file mode 100644 index 000000000..90831f6bf --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using MonoDevelop.Projects; + +using TechTalk.SpecFlow.Generator.Configuration; + +namespace MonoDevelop.TechTalk.SpecFlow +{ + internal static class MonoDevelopProjectReader + { + public static SpecFlowProject CreateSpecFlowProjectFrom(Project project) + { + var specFlowProject = new SpecFlowProject(); + specFlowProject.ProjectFolder = project.BaseDirectory; + specFlowProject.ProjectName = project.Name; + + string defaultNamespace = "Namespace"; + if (project is DotNetProject) + { + defaultNamespace = ((DotNetProject)project).GetDefaultNamespace(project.Name); + } + + // No way to get AssemblyName right now, therefore we'll just use DefaultNamespace + specFlowProject.AssemblyName = defaultNamespace; + specFlowProject.DefaultNamespace = defaultNamespace; + + foreach (SolutionItemConfiguration configuration in project.Configurations) + { + MonoDevelop.Core.LoggingService.LogInfo(configuration.Name); + } + + // TODO: Find out if we really need to add all the feature files everytime we generate + foreach (ProjectFile projectFile in project.Files.Where(IsFeatureOrAppConfigFile)) + { + string extension = Path.GetExtension(projectFile.Name); + + if (extension.Equals(".feature", StringComparison.InvariantCultureIgnoreCase)) + { + string fileName = projectFile.FilePath.ToRelative(project.BaseDirectory); + var featureFile = new SpecFlowFeatureFile(fileName); + var customToolNamespace = projectFile.CustomToolNamespace; + + if (!String.IsNullOrEmpty(customToolNamespace)) + featureFile.CustomNamespace = customToolNamespace; + + specFlowProject.FeatureFiles.Add(featureFile); + } + + if (extension.Equals(".config", StringComparison.InvariantCultureIgnoreCase)) + { + string configContent = File.ReadAllText(projectFile.FilePath); + GeneratorConfigurationReader.UpdateConfigFromFileContent(specFlowProject.GeneratorConfiguration, configContent); + } + } + + return specFlowProject; + } + + private static bool IsFeatureOrAppConfigFile(ProjectFile projectFile) + { + string extension = Path.GetExtension(projectFile.Name); + return extension.Equals(".feature", StringComparison.InvariantCultureIgnoreCase) + || projectFile.Name.Equals("app.config", StringComparison.InvariantCultureIgnoreCase); + } + } +} diff --git a/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs b/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs index f880566d1..d4fe99119 100644 --- a/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs +++ b/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs @@ -1,12 +1,18 @@ using System; +using System.CodeDom.Compiler; using System.IO; using System.Text; using System.Threading; +using Microsoft.CSharp; using MonoDevelop.Core; using MonoDevelop.Ide.CustomTools; using MonoDevelop.Projects; +using TechTalk.SpecFlow.Generator; +using TechTalk.SpecFlow.Generator.Configuration; +using TechTalk.SpecFlow.Parser; + namespace MonoDevelop.TechTalk.SpecFlow { public class SingleFeatureFileGenerator : ISingleFileCustomTool @@ -14,12 +20,70 @@ public class SingleFeatureFileGenerator : ISingleFileCustomTool public IAsyncOperation Generate(IProgressMonitor monitor, ProjectFile file, SingleFileCustomToolResult result) { return new ThreadAsyncOperation(() => { - FilePath outputFile = file.FilePath.ChangeExtension(".feature.cs"); - string content = @"// {0} was generated successfully"; - File.WriteAllText(outputFile, String.Format(content, outputFile.FileName), Encoding.UTF8); - result.GeneratedFilePath = outputFile; + + FilePath codeFilePath = file.FilePath.ChangeExtension(".feature.cs"); + + try + { + codeFilePath = GenerateFeatureCodeFileFor(file); + } + catch (Exception ex) + { + HandleException(ex, file, result); + } + + result.GeneratedFilePath = codeFilePath; + }, result); } + + private FilePath GenerateFeatureCodeFileFor(ProjectFile featureFile) + { + // TODO: We only support C# for now, later we'll add support to grab the provider based on the project + CodeDomProvider codeProvider = new CSharpCodeProvider(); + FilePath outputFile = featureFile.FilePath.ChangeExtension(".feature." + codeProvider.FileExtension); + SpecFlowProject specFlowProject = MonoDevelopProjectReader.CreateSpecFlowProjectFrom(featureFile.Project); + var specFlowGenerator = new SpecFlowGenerator(specFlowProject); + + using (var writer = new StringWriter(new StringBuilder())) + using (var reader = new StringReader(File.ReadAllText(featureFile.FilePath))) + { + SpecFlowFeatureFile specFlowFeatureFile = specFlowProject.GetOrCreateFeatureFile(featureFile.FilePath); + specFlowGenerator.GenerateTestFile(specFlowFeatureFile, codeProvider, reader, writer); + File.WriteAllText(outputFile, writer.ToString()); + } + + return outputFile; + } + + private void HandleException(Exception ex, ProjectFile file, SingleFileCustomToolResult result) + { + if (ex is SpecFlowParserException) + { + SpecFlowParserException sfpex = (SpecFlowParserException) ex; + + if (sfpex.ErrorDetails == null || sfpex.ErrorDetails.Count == 0) + { + result.UnhandledException = ex; + } + else + { + var compilerErrors = new CompilerErrorCollection(); + + foreach (var errorDetail in sfpex.ErrorDetails) + { + var compilerError = new CompilerError(file.Name, errorDetail.ForcedLine, errorDetail.ForcedColumn, "0", errorDetail.Message); + compilerErrors.Add(compilerError); + } + + result.Errors.AddRange(compilerErrors); + } + } + else + { + result.UnhandledException = ex; + } + } } internal class ThreadAsyncOperation : IAsyncOperation