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