diff --git a/.gitignore b/.gitignore index 6cb5191ba..278e533ba 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ obj/ [Rr]elease*/ _ReSharper*/ [Tt]est[Rr]esult* +*.pidb +*.userprefs *.resharper *.cache *.gpState diff --git a/Documentation/SpecFlowMonoGuide.docx b/Documentation/SpecFlowMonoGuide.docx new file mode 100644 index 000000000..ca3d78f81 Binary files /dev/null and b/Documentation/SpecFlowMonoGuide.docx differ diff --git a/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowEventDefinitionIcon.ico b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowEventDefinitionIcon.ico new file mode 100644 index 000000000..7ab089ad0 Binary files /dev/null and b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowEventDefinitionIcon.ico differ diff --git a/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowFeatureIcon.ico b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowFeatureIcon.ico new file mode 100644 index 000000000..7ab089ad0 Binary files /dev/null and b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowFeatureIcon.ico differ diff --git a/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowStepDefinitionIcon.ico b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowStepDefinitionIcon.ico new file mode 100644 index 000000000..7ab089ad0 Binary files /dev/null and b/IdeIntegration/MonoDevelopIntegration/Gui/SpecFlowStepDefinitionIcon.ico differ diff --git a/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.addin.xml b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.addin.xml new file mode 100644 index 000000000..8321e4a2e --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.addin.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj new file mode 100644 index 000000000..8d81a8bcd --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/MonoDevelop.TechTalk.SpecFlow.csproj @@ -0,0 +1,89 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {199D2315-F5BD-463C-B16A-BE31A845792B} + Library + MonoDevelop.TechTalk.SpecFlow + MonoDevelop.TechTalk.SpecFlow + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + + + none + false + bin\Release + prompt + 4 + + + + + MonoDevelop.TechTalk.SpecFlow.addin.xml + + + SpecFlowFeature.xft.xml + + + SpecFlowFeatureIcon.ico + + + SpecFlowEventDefinitionIcon.ico + + + SpecFlowStepDefinitionIcon.ico + + + SpecFlowStepDefinition.xft.xml + + + SpecFlowEventDefinition.xft.xml + + + + + + + + + + False + ..\..\lib\monodevelop\MonoDevelop.Ide.dll + + + False + ..\..\lib\monodevelop\MonoDevelop.Core.dll + + + + + + + + + + VersionInfo.cs + + + + + {453D8014-B6CD-4E86-80A8-D59F59092334} + TechTalk.SpecFlow.Generator + + + {7CCEF6D6-FC17-422E-9BED-EDD752B6496F} + TechTalk.SpecFlow.Parser + + + diff --git a/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs b/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs new file mode 100644 index 000000000..e2ff4e548 --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/MonoDevelopProjectReader.cs @@ -0,0 +1,64 @@ +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; + + // 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/Properties/AssemblyInfo.cs b/IdeIntegration/MonoDevelopIntegration/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..a9f9a529a --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MonoDevelop.TechTalk.SpecFlow")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MonoDevelop.TechTalk.SpecFlow")] +[assembly: AssemblyCopyright("Copyright © TechTalk, SineSignal, LLC 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0ACD56A4-7A81-11DF-93DF-699DDFD72085")] + diff --git a/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs b/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs new file mode 100644 index 000000000..d4fe99119 --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/SingleFeatureFileGenerator.cs @@ -0,0 +1,154 @@ +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 + { + public IAsyncOperation Generate(IProgressMonitor monitor, ProjectFile file, SingleFileCustomToolResult result) + { + return new ThreadAsyncOperation(() => { + + 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 + { + private Thread Thread { get; set; } + private bool Cancelled { get; set; } + private SingleFileCustomToolResult Result { get; set; } + private Action Task { get; set; } + + public ThreadAsyncOperation(Action task, SingleFileCustomToolResult result) + { + if (result == null) + throw new ArgumentNullException("result"); + + Task = task; + Result = result; + Thread = new Thread(Run); + Thread.Start(); + } + + private void Run() + { + try + { + Task(); + } + catch (ThreadAbortException ex) + { + Result.UnhandledException = ex; + Thread.ResetAbort(); + } + catch (Exception ex) + { + Result.UnhandledException = ex; + } + + if (Completed != null) + Completed(this); + } + + public event OperationHandler Completed; + + public void Cancel() + { + Thread.Abort(); + } + + public void WaitForCompleted() + { + Thread.Join(); + } + + public bool IsCompleted + { + get { return !Thread.IsAlive; } + } + + public bool Success + { + get { return !Cancelled && Result.Success; } + } + + public bool SuccessWithWarnings + { + get { return !Cancelled && Result.SuccessWithWarnings; } + } + } +} diff --git a/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowEventDefinition.xft.xml b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowEventDefinition.xft.xml new file mode 100644 index 000000000..5869c3e7e --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowEventDefinition.xft.xml @@ -0,0 +1,101 @@ + + \ No newline at end of file diff --git a/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowFeature.xft.xml b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowFeature.xft.xml new file mode 100644 index 000000000..ccd7506e0 --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowFeature.xft.xml @@ -0,0 +1,29 @@ + + \ No newline at end of file diff --git a/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowStepDefinition.xft.xml b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowStepDefinition.xft.xml new file mode 100644 index 000000000..eb2598875 --- /dev/null +++ b/IdeIntegration/MonoDevelopIntegration/Templates/SpecFlowStepDefinition.xft.xml @@ -0,0 +1,61 @@ + + \ No newline at end of file diff --git a/Parser/TechTalk.SpecFlow.Parser.csproj b/Parser/TechTalk.SpecFlow.Parser.csproj index 05968a9b7..6335caf29 100644 --- a/Parser/TechTalk.SpecFlow.Parser.csproj +++ b/Parser/TechTalk.SpecFlow.Parser.csproj @@ -1,4 +1,4 @@ - + Debug @@ -33,34 +33,38 @@ 4 - + + + 3.5 + + + 3.5 + + + 3.5 + + + + False ..\lib\gherkin\Gherkin.dll - + False ..\lib\gherkin\IKVM.OpenJDK.Core.dll - + False ..\lib\gherkin\IKVM.OpenJDK.Text.dll - + False ..\lib\gherkin\IKVM.Runtime.dll - - - 3.5 - - - 3.5 - - - 3.5 + + False + ..\lib\gherkin\IKVM.OpenJDK.Security.dll - - @@ -105,6 +109,7 @@ + - \ No newline at end of file + diff --git a/Tests/ParserTests/ParserTests.csproj b/Tests/ParserTests/ParserTests.csproj index 13b494008..d25894cc8 100644 --- a/Tests/ParserTests/ParserTests.csproj +++ b/Tests/ParserTests/ParserTests.csproj @@ -1,4 +1,4 @@ - + Debug @@ -33,10 +33,6 @@ 4 - - False - ..\..\lib\nunit\nunit.framework.dll - 3.5 @@ -49,6 +45,10 @@ + + False + ..\..\lib\nunit\nunit.framework.dll + @@ -134,6 +134,7 @@ +