From a637adf38e97afeace05c2e5d66b5ad38b1a9de6 Mon Sep 17 00:00:00 2001 From: TechTalk Date: Tue, 24 Oct 2017 09:45:45 +0200 Subject: [PATCH 1/3] Initial commit --- .gitignore | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..940794e60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,288 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs diff --git a/README.md b/README.md new file mode 100644 index 000000000..6336c1d34 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# SpecFlow.TestProjectGenerator +Code for generating the Visual Studio projects during the tests of SpecFlow & SpecFlow+Runner From 0724f65ea8a3cd9e80501acdb2f6eb4729133366 Mon Sep 17 00:00:00 2001 From: TechTalk Date: Tue, 24 Oct 2017 09:46:11 +0200 Subject: [PATCH 2/3] Create LICENSE.txt --- LICENSE.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000..665e1cd48 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,31 @@ +SpecFlow Licence (New BSD License) + +Copyright (c) 2009, TechTalk + +Disclaimer: + * The initial codebase of Specflow was written by TechTalk employees. + No 3rd party code was included. + * No code of customer projects was used to create this project. + * TechTalk had the full rights to publish the initial codebase. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the SpecFlow project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL TECHTALK OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 387079c7a32812eeeecafe9d4ddd1bd542fbe066 Mon Sep 17 00:00:00 2001 From: Andreas Willich Date: Tue, 24 Oct 2017 13:20:26 +0200 Subject: [PATCH 3/3] init commit --- SpecFlow.TestProjectGenerator.sln | 25 ++ .../AppConfigDriver.cs | 9 + .../CurrentVersionDriver.cs | 51 +++ SpecFlow.TestProjectGenerator/EnvExpander.cs | 41 +++ SpecFlow.TestProjectGenerator/Folders.cs | 65 ++++ .../Helpers/FileSystemHelper.cs | 203 ++++++++++++ .../Helpers/Utility.cs | 17 + .../InputProjectDriver.cs | 213 ++++++++++++ .../Inputs/BindingClassInput.cs | 17 + .../Inputs/CodeFileInput.cs | 9 + .../Inputs/ContentFileInput.cs | 10 + .../Inputs/FeatureFileInput.cs | 14 + .../Inputs/FileInput.cs | 20 ++ .../Inputs/FileInputWithContent.cs | 13 + .../Inputs/StepBindingInput.cs | 18 ++ .../ProcessHelper.cs | 89 +++++ ...CSharpProgramLanguageInputProjectDriver.cs | 30 ++ .../CSharpProgramLanguageProjectCompiler.cs | 57 ++++ .../IProgramLanguageInputProjectDriver.cs | 11 + .../IProgramLanguageProjectCompiler.cs | 13 + .../ProgramLanguageInputProjectDriver.cs | 17 + .../ProgramLanguageProjectCompiler.cs | 34 ++ .../VBNetProgramLanguageInputProjectDriver.cs | 30 ++ .../VBNetProjectCompiler.cs | 55 ++++ .../ProgrammingLanguage.cs | 10 + .../ProjectCompiler.cs | 303 ++++++++++++++++++ .../ProjectCompilerHelper.cs | 77 +++++ .../SpecFlow.TestProjectGenerator.csproj | 34 ++ .../TestProjectTemplates/App.config | 22 ++ .../TestProjectTemplates/BindingClass.cs | 17 + .../TestProjectTemplates/BindingClass.vb | 17 + .../My Project/Application.Designer.vb | 13 + .../My Project/Application.myapp | 10 + .../My Project/AssemblyInfo.vb | 35 ++ .../My Project/Resources.Designer.vb | 62 ++++ .../My Project/Resources.resx.content | 117 +++++++ .../My Project/Settings.Designer.vb | 73 +++++ .../My Project/Settings.settings | 7 + .../TestProjectTemplates/NuGet.config | 6 + .../TestProjectFile.csproj | 61 ++++ .../TestProjectFile.vbproj | 112 +++++++ .../TestProjectFile_Before20.csproj | 67 ++++ .../TestProjectSolution.sln | 22 ++ .../TestProjectTemplates/local.runsettings | 24 ++ .../TestProjectTemplates/local.testsettings | 29 ++ .../TestProjectTemplates/packages.config | 5 + .../UnitTestProvider.cs | 15 + .../VisualStudioFinder.cs | 46 +++ 48 files changed, 2245 insertions(+) create mode 100644 SpecFlow.TestProjectGenerator.sln create mode 100644 SpecFlow.TestProjectGenerator/AppConfigDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/CurrentVersionDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/EnvExpander.cs create mode 100644 SpecFlow.TestProjectGenerator/Folders.cs create mode 100644 SpecFlow.TestProjectGenerator/Helpers/FileSystemHelper.cs create mode 100644 SpecFlow.TestProjectGenerator/Helpers/Utility.cs create mode 100644 SpecFlow.TestProjectGenerator/InputProjectDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/BindingClassInput.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/CodeFileInput.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/ContentFileInput.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/FeatureFileInput.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/FileInput.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/FileInputWithContent.cs create mode 100644 SpecFlow.TestProjectGenerator/Inputs/StepBindingInput.cs create mode 100644 SpecFlow.TestProjectGenerator/ProcessHelper.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageInputProjectDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageProjectCompiler.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageInputProjectDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageProjectCompiler.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageInputProjectDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageProjectCompiler.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProgramLanguageInputProjectDriver.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProjectCompiler.cs create mode 100644 SpecFlow.TestProjectGenerator/ProgrammingLanguage.cs create mode 100644 SpecFlow.TestProjectGenerator/ProjectCompiler.cs create mode 100644 SpecFlow.TestProjectGenerator/ProjectCompilerHelper.cs create mode 100644 SpecFlow.TestProjectGenerator/SpecFlow.TestProjectGenerator.csproj create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/App.config create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.cs create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.vb create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.Designer.vb create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.myapp create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/AssemblyInfo.vb create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.Designer.vb create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.resx.content create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.Designer.vb create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.settings create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/NuGet.config create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.csproj create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.vbproj create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile_Before20.csproj create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectSolution.sln create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/local.runsettings create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/local.testsettings create mode 100644 SpecFlow.TestProjectGenerator/TestProjectTemplates/packages.config create mode 100644 SpecFlow.TestProjectGenerator/UnitTestProvider.cs create mode 100644 SpecFlow.TestProjectGenerator/VisualStudioFinder.cs diff --git a/SpecFlow.TestProjectGenerator.sln b/SpecFlow.TestProjectGenerator.sln new file mode 100644 index 000000000..660e97f36 --- /dev/null +++ b/SpecFlow.TestProjectGenerator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2005 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpecFlow.TestProjectGenerator", "SpecFlow.TestProjectGenerator\SpecFlow.TestProjectGenerator.csproj", "{84640955-1A9B-4AA5-B06D-4AD7EC75E3C0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {84640955-1A9B-4AA5-B06D-4AD7EC75E3C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84640955-1A9B-4AA5-B06D-4AD7EC75E3C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84640955-1A9B-4AA5-B06D-4AD7EC75E3C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84640955-1A9B-4AA5-B06D-4AD7EC75E3C0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D96E16DE-09FD-41AF-A2B4-5704919244C0} + EndGlobalSection +EndGlobal diff --git a/SpecFlow.TestProjectGenerator/AppConfigDriver.cs b/SpecFlow.TestProjectGenerator/AppConfigDriver.cs new file mode 100644 index 000000000..d2d877620 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/AppConfigDriver.cs @@ -0,0 +1,9 @@ +using System.Configuration; + +namespace SpecFlow.TestProjectGenerator +{ + public class AppConfigDriver + { + public string ProjectName => ConfigurationManager.AppSettings["testProjectFolder"] ?? "TestProject"; + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/CurrentVersionDriver.cs b/SpecFlow.TestProjectGenerator/CurrentVersionDriver.cs new file mode 100644 index 000000000..1599bc6d6 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/CurrentVersionDriver.cs @@ -0,0 +1,51 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace SpecFlow.TestProjectGenerator +{ + public static class CurrentVersionDriver + { + static CurrentVersionDriver() + { + var assembly = Assembly.GetExecutingAssembly(); + var assemblyName = assembly.GetName().Name; + var gitVersionInformationType = assembly.GetType(assemblyName + ".GitVersionInformation"); + var fields = gitVersionInformationType.GetFields(); + Major = (string)gitVersionInformationType?.GetField("Major")?.GetValue(null); + Minor = (string)gitVersionInformationType?.GetField("Minor")?.GetValue(null); + Patch = (string)gitVersionInformationType?.GetField("Patch")?.GetValue(null); + NuGetVersion = (string)gitVersionInformationType?.GetField("NuGetVersion")?.GetValue(null); + + foreach (var field in fields) + { + Debug.WriteLine(string.Format("{0}: {1}", field.Name, field.GetValue(null))); + } + + + var specFlowAssembly = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "TechTalk.SpecFlow").SingleOrDefault(); + if (specFlowAssembly != null) + { + var specFlowVersion = specFlowAssembly.GetName().Version; + + SpecFlowMajor = specFlowVersion.Major; + SpecFlowMinor = specFlowVersion.Minor; + + SpecFlowVersion = $"{specFlowVersion.Major}.{specFlowVersion.Minor}.0"; + SpecFlowVersionDash = $"{specFlowVersion.Major}-{specFlowVersion.Minor}-0"; + } + } + + public static string SpecFlowVersionDash { get; private set; } + + public static string Major { get; private set; } + public static string Minor { get; private set; } + public static string Patch { get; private set; } + public static string NuGetVersion { get; private set; } + + public static string SpecFlowVersion { get; private set; } + public static int SpecFlowMajor { get; set; } + public static int SpecFlowMinor { get; set; } + } +} diff --git a/SpecFlow.TestProjectGenerator/EnvExpander.cs b/SpecFlow.TestProjectGenerator/EnvExpander.cs new file mode 100644 index 000000000..c170ac263 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/EnvExpander.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace SpecFlow.TestProjectGenerator +{ + public static class EnvironmentExtensions + { + private static readonly List _envVariablesWithPaths = new List + { + "TMP", + "TEMP" + }; + + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern int GetLongPathName(string path, StringBuilder longPath, int longPathLength); + + public static string ExpandEnvironmentWithLongFilenames(string text) + { + var result = text; + foreach (var envVariablesWithPath in _envVariablesWithPaths) + { + if (result.Contains(envVariablesWithPath)) + { + var envVariableName = $"%{envVariablesWithPath}%"; + var envVarValue = Environment.ExpandEnvironmentVariables(envVariableName); + + var longPath = new StringBuilder(255); + GetLongPathName(envVarValue, longPath, longPath.Capacity); + + result = result.Replace(envVariableName, longPath.ToString()); + } + } + + result = Environment.ExpandEnvironmentVariables(result); + + return result; + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Folders.cs b/SpecFlow.TestProjectGenerator/Folders.cs new file mode 100644 index 000000000..d925c3c60 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Folders.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Reflection; + +namespace SpecFlow.TestProjectGenerator +{ + public class Folders + { + protected string _nugetFolder; + protected string _packageFolder; + protected string _sourceRoot; + protected string _specFlow; + protected string _vsAdapterFolder; + + protected bool _vsAdapterFolderChanged; + + public string TestFolder => Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath); + + public virtual string SourceRoot + { + get => !string.IsNullOrWhiteSpace(_sourceRoot) ? _sourceRoot : Path.GetFullPath(Path.Combine(TestFolder, "..", "..", "..")); + set => _sourceRoot = value; + } + + public string VSAdapterFolder + { + get + { + return !string.IsNullOrWhiteSpace(_vsAdapterFolder) ? _vsAdapterFolder : VsAdapterFolderProjectBinaries; + } + set + { + _vsAdapterFolder = value; + _vsAdapterFolderChanged = true; + } + } + + public virtual string VsAdapterFolderProjectBinaries => throw new NotImplementedException(); + + public bool VsAdapterFolderChanged => _vsAdapterFolderChanged; + + + public virtual string NuGetFolder + { + get => !string.IsNullOrWhiteSpace(_nugetFolder) ? _nugetFolder : Path.GetFullPath(Path.Combine(SourceRoot, "Installer", "NuGetPackages", "bin", "Debug")); + set => _nugetFolder = value; + } + + public string PackageFolder + { + get => !string.IsNullOrWhiteSpace(_packageFolder) ? _packageFolder : Path.GetFullPath(Path.Combine(SourceRoot, "packages")); + set => _packageFolder = value; + } + + public string GlobalPackages => Path.Combine(Environment.ExpandEnvironmentVariables("%USERPROFILE%"), ".nuget", "packages"); + + public string SpecFlow + { + get => !string.IsNullOrWhiteSpace(_specFlow) ? _specFlow : Path.GetFullPath(TestFolder); + set => _specFlow = value; + } + + public virtual string TestProjectRootFolder => Path.Combine(Path.GetTempPath(), "SpecFlow"); + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Helpers/FileSystemHelper.cs b/SpecFlow.TestProjectGenerator/Helpers/FileSystemHelper.cs new file mode 100644 index 000000000..c626bb499 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Helpers/FileSystemHelper.cs @@ -0,0 +1,203 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace SpecFlow.TestProjectGenerator.Helpers +{ + public static class FileSystemHelper + { + public static void CopyFileToFolder(string filePath, string folderName) + { + File.Copy(filePath, Path.Combine(folderName, Path.GetFileName(filePath))); + } + + public static string GetRelativePath(string path, string basePath) + { + path = Path.GetFullPath(path); + basePath = Path.GetFullPath(basePath); + if (String.Equals(path, basePath, StringComparison.OrdinalIgnoreCase)) + return "."; // the "this folder" + + if (path.StartsWith(basePath + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) + return path.Substring(basePath.Length + 1); + + //handle different drives + string pathRoot = Path.GetPathRoot(path); + if (!String.Equals(pathRoot, Path.GetPathRoot(basePath), StringComparison.OrdinalIgnoreCase)) + return path; + + //handle ".." pathes + string[] pathParts = path.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + string[] basePathParts = basePath.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + int commonFolderCount = 0; + while (commonFolderCount < pathParts.Length && commonFolderCount < basePathParts.Length && + String.Equals(pathParts[commonFolderCount], basePathParts[commonFolderCount], StringComparison.OrdinalIgnoreCase)) + commonFolderCount++; + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < basePathParts.Length - commonFolderCount; i++) + { + result.Append(".."); + result.Append(Path.DirectorySeparatorChar); + } + + if (pathParts.Length - commonFolderCount == 0) + return result.ToString().TrimEnd(Path.DirectorySeparatorChar); + + result.Append(String.Join(Path.DirectorySeparatorChar.ToString(), pathParts, commonFolderCount, pathParts.Length - commonFolderCount)); + return result.ToString(); + } + + // This method accepts two strings the represent two files to + // compare. A return value of true indicates that the contents of the files + // are the same. A return value of any other value indicates that the + // files are not the same. + public static bool FileCompare(string filePath1, string filePath2) + { + int file1byte; + int file2byte; + + // Determine if the same file was referenced two times. + if (String.Equals(filePath1, filePath2, StringComparison.CurrentCultureIgnoreCase)) + { + // Return true to indicate that the files are the same. + return true; + } + + // Open the two files. + using (FileStream fs1 = new FileStream(filePath1, FileMode.Open, FileAccess.Read)) + using (FileStream fs2 = new FileStream(filePath2, FileMode.Open, FileAccess.Read)) + { + // Check the file sizes. If they are not the same, the files + // are not the same. + if (fs1.Length != fs2.Length) + { + // Return false to indicate files are different + return false; + } + + // Read and compare a byte from each file until either a + // non-matching set of bytes is found or until the end of + // file1 is reached. + do + { + // Read one byte from each file. + file1byte = fs1.ReadByte(); + file2byte = fs2.ReadByte(); + } while ((file1byte == file2byte) && (file1byte != -1)); + } + + // Return the success of the comparison. "file1byte" is + // equal to "file2byte" at this point only if the files are + // the same. + return ((file1byte - file2byte) == 0); + } + + public static void CopyDirectory(string sourcePath, string destPath, bool cleanDestination = true) + { + if (cleanDestination) + EnsureEmptyFolder(destPath); + else + { + if (!Directory.Exists(destPath)) + { + Directory.CreateDirectory(destPath); + } + } + + foreach (string file in Directory.GetFiles(sourcePath)) + { + var fileName = Path.GetFileName(file); + Debug.Assert(fileName != null); + string dest = Path.Combine(destPath, fileName); + File.Copy(file, dest, true); + File.SetAttributes(dest, File.GetAttributes(dest) & (~FileAttributes.ReadOnly)); + } + + foreach (string folder in Directory.GetDirectories(sourcePath)) + { + var folderName = Path.GetFileName(folder); + Debug.Assert(folderName != null); + string dest = Path.Combine(destPath, folderName); + CopyDirectory(folder, dest); + } + } + + public static void EnsureEmptyFolder(string folderName) + { + folderName = Path.GetFullPath(folderName); + + if (!Directory.Exists(folderName)) + { + Directory.CreateDirectory(folderName); + return; + } + + DeleteFolderContent(folderName); + } + + public static void EnsureFolderOfFile(string filePath) + { + string directory = Path.GetDirectoryName(filePath); + if (directory != null && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + } + + + private static void Retry(int number, Action action) + { + try + { + action(); + } + catch (UnauthorizedAccessException) + { + var i = number - 1; + + if (i == 0) + throw; + + Retry(i, action); + } + } + + public static void DeleteFolderContent(string folderName) + { + foreach (string file in Directory.GetFiles(folderName)) + { + try + { + Retry(5, () => File.Delete(file)); + } + catch (Exception ex) + { + throw new Exception("Unable to delete file: " + file, ex); + } + } + + foreach (string folder in Directory.GetDirectories(folderName)) + { + try + { + Retry(5, () => Directory.Delete(folder, true)); + } + catch (Exception ex) + { + throw new Exception("Unable to delete folder: " + folder, ex); + } + } + } + + public static void DeleteFolder(string path) + { + if (!Directory.Exists(path)) + return; + + DeleteFolderContent(path); + + Retry(5, () => Directory.Delete(path, true)); + } + } +} diff --git a/SpecFlow.TestProjectGenerator/Helpers/Utility.cs b/SpecFlow.TestProjectGenerator/Helpers/Utility.cs new file mode 100644 index 000000000..3be24aea3 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Helpers/Utility.cs @@ -0,0 +1,17 @@ +using Microsoft.FSharp.Core; + +namespace SpecFlow.TestProjectGenerator.Helpers +{ + static class Utility + { + public static bool IsSome(this FSharpOption option) + { + return FSharpOption.get_IsSome(option); + } + + public static bool IsNone(this FSharpOption option) + { + return FSharpOption.get_IsNone(option); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/InputProjectDriver.cs b/SpecFlow.TestProjectGenerator/InputProjectDriver.cs new file mode 100644 index 000000000..322ae9ff8 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/InputProjectDriver.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.FSharp.Core; +using SpecFlow.TestProjectGenerator.Helpers; +using SpecFlow.TestProjectGenerator.Inputs; +using SpecFlow.TestProjectGenerator.ProgramLanguageDrivers; + +namespace SpecFlow.TestProjectGenerator +{ + public class InputProjectDriver + { + private readonly Folders _folders; + private readonly AppConfigDriver _appConfigDriver; + private IProgramLanguageInputProjectDriver _programLanguageInputProjectDriver; + public Guid ProjectGuid { get; } = Guid.NewGuid(); + private ProgrammingLanguage _programmingLanguage; + private string _solutionFolder; + + public string ProjectFolder => Path.Combine(SolutionFolder, "Project"); + public string ProjectName => $"TestProject_{ProjectGuid:N}"; + public string ProjectFileName => _programLanguageInputProjectDriver.GetProjectFileName(ProjectName); + public string ProjectPath => Path.Combine(ProjectFolder, ProjectFileName); + + public string SolutionFolder => _solutionFolder; + + public string SolutionName => ProjectName; + public string SolutionFileName => $"{SolutionName}.sln"; + public string SolutionPath => Path.Combine(SolutionFolder, SolutionFileName); + public string PackageFolderPath => Path.Combine(SolutionFolder, "packages"); + + public List FeatureFiles { get; private set; } = new List(); + public FeatureFileInput CurrentFeatureFile { get; set; } + + public List ContentFiles { get; private set; } = new List(); + public ContentFileInput CurrentSpecRunConfigFile { get; set; } + + public List BindingClasses { get; private set; } = new List(); + //public BindingClassInput CurrentBindingClass { get; set; } + public BindingClassInput DefaultBindingClass { get; set; } + + public List CodeFileInputs { get; private set; } = new List(); + + public UnitTestProvider UnitTestProvider { get; set; } = UnitTestProvider.SpecRun; + + public ProgrammingLanguage ProgrammingLanguage + { + get { return _programmingLanguage; } + set + { + _programmingLanguage = value; + SwitchProgramLanguage(value); + } + } + + public string NetFrameworkVersion { get; set; } = "v4.5"; + + public string AdditionalSpecFlowPlugins { get; set; } = ""; + + public string AdditionalSpecFlowSettings { get; set; } = ""; + + public List AdditionalReferences { get; private set; } = new List(); + + public string CompiledAssemblyPath => Path.Combine(DeploymentFolder, "TestProject.dll"); + + public string DeploymentFolder => Path.Combine(ProjectFolder, "bin", "Debug"); + public string TestingFrameworkReference { get; set; } + + + public InputProjectDriver(Folders folders, AppConfigDriver appConfigDriver) + { + TestingFrameworkReference = ""; + + _folders = folders; + _appConfigDriver = appConfigDriver; + + SwitchProgramLanguage(ProgrammingLanguage.CSharp); + + SetProjectRootFolder(FSharpOption.None); + + GetAndAddDefaultBindingClass(); + } + + private void GetAndAddDefaultBindingClass() + { + BindingClasses.Clear(); + DefaultBindingClass = _programLanguageInputProjectDriver.GetDefaultBindingClass(); + BindingClasses.Add(DefaultBindingClass); + } + + private void SwitchProgramLanguage(ProgrammingLanguage value) + { + switch (value) + { + case ProgrammingLanguage.CSharp: + _programLanguageInputProjectDriver = new CSharpProgramLanguageInputProjectDriver(); + break; + case ProgrammingLanguage.VB: + _programLanguageInputProjectDriver = new VBNetProgramLanguageInputProjectDriver(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + + _programmingLanguage = value; + + GetAndAddDefaultBindingClass(); + } + + internal string DetermineDirectoryForTestProjects() + { + return Path.Combine(_folders.TestProjectRootFolder, Environment.ExpandEnvironmentVariables(_appConfigDriver.ProjectName)); + } + + private string GetFeatureFileName() + { + return $"Feature{FeatureFiles.Count + 1}.feature"; + } + + public void AddFeatureFile(string featureFileText) + { + var featureFile = new FeatureFileInput(GetFeatureFileName(), featureFileText); + FeatureFiles.Add(featureFile); + CurrentFeatureFile = featureFile; + } + + public ContentFileInput AddContentFile(string fileName, string fileContent) + { + var contentFileInput = new ContentFileInput(fileName, fileContent); + var existingContentFiles = ContentFiles.Where(cf => cf.FileName == fileName).ToList(); + if (existingContentFiles.Any()) + { + existingContentFiles.ForEach(i => ContentFiles.Remove(i)); + } + + ContentFiles.Add(contentFileInput); + return contentFileInput; + } + + public void AddSpecRunConfigFile(string fileName, string fileContent) + { + CurrentSpecRunConfigFile = AddContentFile(fileName, fileContent); + } + + public void AddStepBinding(string scenarioBlock, string regex, string csharpcode = "//nop", string vbnetcode = "'nop") + { + DefaultBindingClass.StepBindings.Add(new StepBindingInput(scenarioBlock, regex, csharpcode, vbnetcode)); + } + + public void AddBindingCode(string bindingCode) + { + DefaultBindingClass.OtherBindings.Add(bindingCode); + } + + public void AddEventBinding(string eventType, string code) + { + AddBindingCode(GetBindingCode(eventType, code)); + } + + private string GetBindingCode(string eventType, string code) + { + return _programLanguageInputProjectDriver.GetBindingCode(eventType, code); + } + + + public void SetTestAdapterPath(string testAdapterPath) + { + _folders.VSAdapterFolder = Path.Combine(SolutionFolder, testAdapterPath); + } + + + public void SetProjectRootFolder(FSharpOption rootFolder) + { + var tempDirectory = DetermineDirectoryForTestProjects(); + + if (rootFolder.IsSome()) + { + tempDirectory = Path.Combine(tempDirectory, rootFolder.Value); + } + + _solutionFolder = Path.Combine(tempDirectory, "Project_" + ProjectGuid.ToString("D")); + } + + public void UseNUnitVersionAsTestingFramework(string nunitVersion) + { + if (nunitVersion == "2.6.4") + { + TestingFrameworkReference = "" + + $"{Path.Combine(PackageFolderPath, $"NUnit.{nunitVersion}", "lib", "nunit.framework.dll")}" + + ""; + } + else + { + TestingFrameworkReference = "" + + $"{Path.Combine(PackageFolderPath, $"NUnit.{nunitVersion}", "lib", "net45", "nunit.framework.dll")}" + + ""; + } + + + + + TestingFrameworkPackage = $""; + } + + public void AddBindingClass(string fileName, string code) + { + CodeFileInputs.Add(new CodeFileInput(fileName, ".", code)); + } + + public string TestingFrameworkPackage { get; set; } + } +} diff --git a/SpecFlow.TestProjectGenerator/Inputs/BindingClassInput.cs b/SpecFlow.TestProjectGenerator/Inputs/BindingClassInput.cs new file mode 100644 index 000000000..7ebe90523 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/BindingClassInput.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class BindingClassInput : FileInput + { + public BindingClassInput(string fileName, string folder = ".") + : base(fileName, folder) + { + StepBindings = new List(); + OtherBindings = new List(); + } + + public List StepBindings { get; } + public List OtherBindings { get; } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/CodeFileInput.cs b/SpecFlow.TestProjectGenerator/Inputs/CodeFileInput.cs new file mode 100644 index 000000000..49895563f --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/CodeFileInput.cs @@ -0,0 +1,9 @@ +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class CodeFileInput : FileInputWithContent + { + public CodeFileInput(string fileName, string folder, string content) : base(fileName, content, folder) + { + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/ContentFileInput.cs b/SpecFlow.TestProjectGenerator/Inputs/ContentFileInput.cs new file mode 100644 index 000000000..7d50d06e0 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/ContentFileInput.cs @@ -0,0 +1,10 @@ +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class ContentFileInput : FileInputWithContent + { + public ContentFileInput(string fileName, string content, string folder = ".") + : base(fileName, content, folder) + { + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/FeatureFileInput.cs b/SpecFlow.TestProjectGenerator/Inputs/FeatureFileInput.cs new file mode 100644 index 000000000..b7fcb132b --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/FeatureFileInput.cs @@ -0,0 +1,14 @@ +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class FeatureFileInput : FileInputWithContent + { + public FeatureFileInput(string fileName, string content, string folder = ".") : base(fileName, UnescapeContent(content), folder) + { + } + + private static string UnescapeContent(string content) + { + return content?.Replace("'''", "\"\"\""); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/FileInput.cs b/SpecFlow.TestProjectGenerator/Inputs/FileInput.cs new file mode 100644 index 000000000..0f6d85b29 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/FileInput.cs @@ -0,0 +1,20 @@ +using System.IO; + +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public abstract class FileInput + { + protected FileInput(string fileName, string folder) + { + FileName = fileName; + Folder = folder; + } + + public string FileName { get; } + public string Folder { get; } + + public string ProjectRelativePath => Folder == "." ? FileName : Path.Combine(Folder, FileName); + + public string Name => Path.GetFileNameWithoutExtension(FileName); + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/FileInputWithContent.cs b/SpecFlow.TestProjectGenerator/Inputs/FileInputWithContent.cs new file mode 100644 index 000000000..7ed1ac706 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/FileInputWithContent.cs @@ -0,0 +1,13 @@ +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class FileInputWithContent : FileInput + { + public FileInputWithContent(string fileName, string content, string folder) : base(fileName, folder) + { + Content = content; + } + + public string Content { get; } + public string SourceFilePath { get; set; } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/Inputs/StepBindingInput.cs b/SpecFlow.TestProjectGenerator/Inputs/StepBindingInput.cs new file mode 100644 index 000000000..552ad23d7 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/Inputs/StepBindingInput.cs @@ -0,0 +1,18 @@ +namespace SpecFlow.TestProjectGenerator.Inputs +{ + public class StepBindingInput + { + public StepBindingInput(string scenarioBlock, string regex, string cSharpCode, string vbNetCode) + { + ScenarioBlock = scenarioBlock; + Regex = regex; + CSharpCode = cSharpCode; + VBNetCode = vbNetCode; + } + + public string ScenarioBlock { get; } + public string Regex { get; } + public string CSharpCode { get; } + public string VBNetCode { get; } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProcessHelper.cs b/SpecFlow.TestProjectGenerator/ProcessHelper.cs new file mode 100644 index 000000000..0e13afb7c --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProcessHelper.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics; +using System.Text; +using System.Threading; + +namespace SpecFlow.TestProjectGenerator +{ + public class ProcessHelper + { + private static TimeSpan _timeout = TimeSpan.FromMinutes(10); + private static int _timeOutInMilliseconds = Convert.ToInt32(_timeout.TotalMilliseconds); + + public string ConsoleOutput { get; private set; } + + public int RunProcess(string executablePath, string argumentsFormat, params object[] arguments) + { + var parameters = string.Format(argumentsFormat, arguments); + + Console.WriteLine($"Starting external program: \"{executablePath}\" {parameters}"); + ProcessStartInfo psi = new ProcessStartInfo(executablePath, parameters); + psi.RedirectStandardOutput = true; + psi.RedirectStandardError = true; + psi.UseShellExecute = false; + psi.CreateNoWindow = false; + + + var process = new Process + { + StartInfo = psi, + EnableRaisingEvents = true, + + }; + + StringBuilder output = new StringBuilder(); + + + using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) + { + using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) + { + process.OutputDataReceived += (sender, e) => + { + if (e.Data == null) + { + outputWaitHandle.Set(); + } + else + { + output.AppendLine(e.Data); + } + }; + process.ErrorDataReceived += (sender, e) => + { + if (e.Data == null) + { + errorWaitHandle.Set(); + } + else + { + output.AppendLine(e.Data); + } + }; + + + process.Start(); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + if (process.WaitForExit(_timeOutInMilliseconds) && + outputWaitHandle.WaitOne(_timeOutInMilliseconds) && + errorWaitHandle.WaitOne(_timeOutInMilliseconds)) + { + ConsoleOutput = output.ToString(); + } + else + { + throw new TimeoutException($"Process {psi.FileName} {psi.Arguments} took longer than {_timeout.TotalMinutes} min to complete"); + } + + } + } + Console.WriteLine(ConsoleOutput); + + + return process.ExitCode; + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageInputProjectDriver.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageInputProjectDriver.cs new file mode 100644 index 000000000..8eef2fefe --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageInputProjectDriver.cs @@ -0,0 +1,30 @@ +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + public class CSharpProgramLanguageInputProjectDriver : ProgramLanguageInputProjectDriver + { + public override string GetBindingCode(string eventType, string code) + { + var staticKeyword = IsStaticEvent(eventType) ? "static" : ""; + return string.Format(@"[{0}] {1} public void {0}() + {{ + Console.WriteLine(""BindingExecuted:{0}""); + {2} + }}", + eventType, + staticKeyword, + code); + } + + public override string GetProjectFileName(string projectName) + { + return $"{projectName}.csproj"; + } + + public override BindingClassInput GetDefaultBindingClass() + { + return new BindingClassInput("DefaultBindings.cs"); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageProjectCompiler.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageProjectCompiler.cs new file mode 100644 index 000000000..cff7d42f1 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/CSharpProgramLanguageProjectCompiler.cs @@ -0,0 +1,57 @@ +using System.Text; +using Microsoft.Build.Evaluation; +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + class CSharpProgramLanguageProjectCompiler : ProgramLanguageProjectCompiler + { + + public CSharpProgramLanguageProjectCompiler(ProjectCompilerHelper projectCompilerHelper) : base(projectCompilerHelper) + { + + } + + public override string FileEnding => ".cs"; + public override string ProjectFileName + { + get + { + if (CurrentVersionDriver.SpecFlowMajor < 2) + { + return "TestProjectFile_Before20.csproj"; + } + return "TestProjectFile.csproj"; + } + } + + public override void AdditionalAdjustments(Project project, InputProjectDriver inputProjectDriver) + { + + } + + protected override string BindingClassFileName => "BindingClass.cs"; + + protected override string GetBindingsCode(BindingClassInput bindingClassInput) + { + StringBuilder result = new StringBuilder(); + + int counter = 0; + + foreach (var stepBindingInput in bindingClassInput.StepBindings) + { + result.AppendFormat(@"[{2}(@""{3}"")]public void sb{0}() {{ + {1} + }}", ++counter, stepBindingInput.CSharpCode, stepBindingInput.ScenarioBlock, stepBindingInput.Regex); + result.AppendLine(); + } + + foreach (var otherBinding in bindingClassInput.OtherBindings) + { + result.AppendLine(otherBinding); + } + + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageInputProjectDriver.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageInputProjectDriver.cs new file mode 100644 index 000000000..7db10b7d4 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageInputProjectDriver.cs @@ -0,0 +1,11 @@ +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + public interface IProgramLanguageInputProjectDriver + { + string GetBindingCode(string eventType, string code); + string GetProjectFileName(string projectName); + BindingClassInput GetDefaultBindingClass(); + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageProjectCompiler.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageProjectCompiler.cs new file mode 100644 index 000000000..88a20815e --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/IProgramLanguageProjectCompiler.cs @@ -0,0 +1,13 @@ +using Microsoft.Build.Evaluation; +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + public interface IProgramLanguageProjectCompiler + { + void AddBindingClass(InputProjectDriver inputProjectDriver, Project project, BindingClassInput bindingClassInput); + string FileEnding { get; } + string ProjectFileName { get; } + void AdditionalAdjustments(Project project, InputProjectDriver inputProjectDriver); + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageInputProjectDriver.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageInputProjectDriver.cs new file mode 100644 index 000000000..985086d54 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageInputProjectDriver.cs @@ -0,0 +1,17 @@ +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + public abstract class ProgramLanguageInputProjectDriver : IProgramLanguageInputProjectDriver + { + protected bool IsStaticEvent(string eventType) + { + return eventType == "BeforeFeature" || eventType == "AfterFeature" || eventType == "BeforeTestRun" || eventType == "AfterTestRun"; + } + + public abstract string GetBindingCode(string eventType, string code); + public abstract string GetProjectFileName(string projectName); + public abstract BindingClassInput GetDefaultBindingClass(); + } + +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageProjectCompiler.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageProjectCompiler.cs new file mode 100644 index 000000000..4e8ead38e --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/ProgramLanguageProjectCompiler.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Microsoft.Build.Evaluation; +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + internal abstract class ProgramLanguageProjectCompiler : IProgramLanguageProjectCompiler + { + protected ProjectCompilerHelper ProjectCompilerHelper; + + protected ProgramLanguageProjectCompiler(ProjectCompilerHelper projectCompilerHelper) + { + ProjectCompilerHelper = projectCompilerHelper; + } + + + public void AddBindingClass(InputProjectDriver inputProjectDriver, Project project, BindingClassInput bindingClassInput) + { + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, BindingClassFileName, bindingClassInput.FileName, new Dictionary + { + { "ClassName", bindingClassInput.Name}, + { "Bindings", GetBindingsCode(bindingClassInput)}, + }); + project.AddItem("Compile", bindingClassInput.ProjectRelativePath); + } + + protected abstract string BindingClassFileName { get; } + + protected abstract string GetBindingsCode(BindingClassInput bindingClassInput); + public abstract string FileEnding { get; } + public abstract string ProjectFileName { get; } + public abstract void AdditionalAdjustments(Project project, InputProjectDriver inputProjectDriver); + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProgramLanguageInputProjectDriver.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProgramLanguageInputProjectDriver.cs new file mode 100644 index 000000000..72923e9d4 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProgramLanguageInputProjectDriver.cs @@ -0,0 +1,30 @@ +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + class VBNetProgramLanguageInputProjectDriver : ProgramLanguageInputProjectDriver + { + public override string GetBindingCode(string eventType, string code) + { + var staticKeyword = IsStaticEvent(eventType) ? "Shared" : ""; + return string.Format(@"<{0}> {1} Public Sub {0}() + {{ + Console.WriteLine(""BindingExecuted:{0}"") + {2} + End Sub", + eventType, + staticKeyword, + code); + } + + public override string GetProjectFileName(string projectName) + { + return $"{projectName}.vbproj"; + } + + public override BindingClassInput GetDefaultBindingClass() + { + return new BindingClassInput("DefaultBindings.vb"); + } + } +} diff --git a/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProjectCompiler.cs b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProjectCompiler.cs new file mode 100644 index 000000000..68b480588 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgramLanguageDrivers/VBNetProjectCompiler.cs @@ -0,0 +1,55 @@ +using System.IO; +using System.Text; +using Microsoft.Build.Evaluation; +using SpecFlow.TestProjectGenerator.Inputs; + +namespace SpecFlow.TestProjectGenerator.ProgramLanguageDrivers +{ + internal class VBNetProjectCompiler : ProgramLanguageProjectCompiler + { + public VBNetProjectCompiler(ProjectCompilerHelper projectCompilerHelper) : base(projectCompilerHelper) + { + } + + protected override string BindingClassFileName => "BindingClass.vb"; + public override string FileEnding => ".vb"; + + public override string ProjectFileName => "TestProjectFile.vbproj"; + + protected override string GetBindingsCode(BindingClassInput bindingClassInput) + { + var result = new StringBuilder(); + + var counter = 0; + + foreach (var stepBindingInput in bindingClassInput.StepBindings) + { + result.AppendFormat(@"<[{2}](""{3}"")> Public Sub sb{0}() + {1} + End Sub", ++counter, stepBindingInput.VBNetCode, stepBindingInput.ScenarioBlock, stepBindingInput.Regex); + result.AppendLine(); + } + + foreach (var otherBinding in bindingClassInput.OtherBindings) + { + result.AppendLine(otherBinding); + } + + return result.ToString(); + } + + public override void AdditionalAdjustments(Project project, InputProjectDriver inputProjectDriver) + { + var myProjectFolder = Path.Combine(inputProjectDriver.ProjectFolder, "My Project"); + Directory.CreateDirectory(myProjectFolder); + + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Application.Designer.vb", @"My Project\Application.Designer.vb"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Application.myapp", @"My Project\Application.myapp"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.AssemblyInfo.vb", @"My Project\AssemblyInfo.vb"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Resources.Designer.vb", @"My Project\Resources.Designer.vb"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Resources.resx.content", @"My Project\Resources.resx"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Settings.Designer.vb", @"My Project\Settings.Designer.vb"); + ProjectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, @"My_Project.Settings.settings", @"My Project\Settings.settings"); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProgrammingLanguage.cs b/SpecFlow.TestProjectGenerator/ProgrammingLanguage.cs new file mode 100644 index 000000000..10dc10a68 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProgrammingLanguage.cs @@ -0,0 +1,10 @@ +namespace SpecFlow.TestProjectGenerator +{ + public enum ProgrammingLanguage + { + Other, + CSharp, + VB, + FSharp + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProjectCompiler.cs b/SpecFlow.TestProjectGenerator/ProjectCompiler.cs new file mode 100644 index 000000000..079dd49e2 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProjectCompiler.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.Build.Evaluation; +using SpecFlow.TestProjectGenerator.Helpers; +using SpecFlow.TestProjectGenerator.Inputs; +using SpecFlow.TestProjectGenerator.ProgramLanguageDrivers; + +namespace SpecFlow.TestProjectGenerator +{ + public class ProjectCompiler + { + protected readonly Folders _folders; + protected readonly ProjectCompilerHelper _projectCompilerHelper; + protected readonly VisualStudioFinder _visualStudioFinder; + protected IProgramLanguageProjectCompiler _programLanguageProjectCompiler; + + public ProjectCompiler(Folders folders, VisualStudioFinder visualStudioFinder, ProjectCompilerHelper projectCompilerHelper) + { + _folders = folders; + _visualStudioFinder = visualStudioFinder; + _projectCompilerHelper = projectCompilerHelper; + } + + public void Compile(InputProjectDriver inputProjectDriver) + { + _programLanguageProjectCompiler = GetProgramLanguageProjectCompiler(inputProjectDriver); + + Console.WriteLine("Compiling project '{0}' in '{1}'", inputProjectDriver.ProjectFileName, inputProjectDriver.ProjectFolder); + + EnsureCompilationFolder(inputProjectDriver.ProjectFolder); + + Project project = CreateProject(inputProjectDriver, inputProjectDriver.ProjectFileName); + + AddAdditionalStuff(inputProjectDriver, project); + + + AddAppConfig(inputProjectDriver, project); + AddMsTestTestSettings(inputProjectDriver, project); + + + AddNugetStuff(inputProjectDriver, project); + + + var references = new List(inputProjectDriver.AdditionalReferences); + if (inputProjectDriver.UnitTestProvider == UnitTestProvider.SpecRunWithNUnit || inputProjectDriver.UnitTestProvider == UnitTestProvider.SpecRunWithNUnit2 || inputProjectDriver.UnitTestProvider == UnitTestProvider.NUnit3 || inputProjectDriver.UnitTestProvider == UnitTestProvider.NUnit2) + { + references.Add(Path.Combine(_folders.TestFolder, "nunit.framework.dll")); + } + + foreach (var referencePath in references) + { + project.AddItem("Reference", Path.GetFileNameWithoutExtension(referencePath), + new Dictionary() + { + {"HintPath", Path.Combine(_folders.TestFolder, referencePath)} + }); + } + + foreach (var bindingClassInput in inputProjectDriver.BindingClasses) + { + AddBindingClass(inputProjectDriver, project, bindingClassInput); + } + + foreach (var featureFileInput in inputProjectDriver.FeatureFiles) + { + string outputPath = Path.Combine(inputProjectDriver.ProjectFolder, featureFileInput.ProjectRelativePath); + WriteOutInputFile(featureFileInput, outputPath); + var generatedFile = featureFileInput.ProjectRelativePath + _programLanguageProjectCompiler.FileEnding; + project.AddItem("None", featureFileInput.ProjectRelativePath, new[] + { + new KeyValuePair("Generator", "SpecFlowSingleFileGenerator"), + new KeyValuePair("LastGenOutput", generatedFile), + }); + project.AddItem("Compile", generatedFile); + } + + foreach (var codeFileInput in inputProjectDriver.CodeFileInputs) + { + string outputPath = Path.Combine(inputProjectDriver.ProjectFolder, codeFileInput.ProjectRelativePath); + WriteOutInputFile(codeFileInput, outputPath); + + project.AddItem("Compile", outputPath); + } + + + foreach (var contentFileInput in inputProjectDriver.ContentFiles) + { + if (contentFileInput.Content != null) + { + _projectCompilerHelper.SaveFileFromTemplate(inputProjectDriver.ProjectFolder, contentFileInput.Content, contentFileInput.ProjectRelativePath); + } + else + { + string outputPath = Path.Combine(inputProjectDriver.ProjectFolder, contentFileInput.ProjectRelativePath); + File.Copy(contentFileInput.SourceFilePath, outputPath, true); + } + + project.AddItem("Content", contentFileInput.ProjectRelativePath, new[] + { + new KeyValuePair("CopyToOutputDirectory", "PreserveNewest"), + }); + } + + _programLanguageProjectCompiler.AdditionalAdjustments(project, inputProjectDriver); + + + if (_folders.VsAdapterFolderChanged) + { + CopyVSTestAdapter(_folders.VsAdapterFolderProjectBinaries, _folders.VSAdapterFolder); + } + + project.Save(); + + RestoreNugetPackage(inputProjectDriver); + + CompileOutProc(project, inputProjectDriver); + + ProjectCollection.GlobalProjectCollection.UnloadAllProjects(); + } + + protected virtual void AddAdditionalStuff(InputProjectDriver inputProjectDriver, Project project) + { + } + + private void CopyVSTestAdapter(string foldersVsAdapterFolderProjectBinaries, string foldersVsAdapterFolder) + { + FileSystemHelper.CopyDirectory(foldersVsAdapterFolderProjectBinaries, foldersVsAdapterFolder, true); + } + + + private void RestoreNugetPackage(InputProjectDriver inputProjectDriver) + { + var processPath = Path.Combine(_folders.GlobalPackages, "NuGet.CommandLine","4.1.0", "tools", "NuGet.exe"); + + if (!File.Exists(processPath)) + { + throw new FileNotFoundException("NuGet.exe could not be found! Is the version number correct?", processPath); + } + + var commandLineArgs = $"restore {inputProjectDriver.SolutionPath}"; + + + var nugetRestore = new ProcessHelper(); + int nugetExitCode = nugetRestore.RunProcess(processPath, commandLineArgs); + + if (nugetExitCode > 0) + { + throw new Exception("NuGet restore failed - rebuild solution to generate latest packages"); + } + } + + + private IProgramLanguageProjectCompiler GetProgramLanguageProjectCompiler(InputProjectDriver inputProjectDriver) + { + switch (inputProjectDriver.ProgrammingLanguage) + { + case ProgrammingLanguage.CSharp: + return new CSharpProgramLanguageProjectCompiler(_projectCompilerHelper); + case ProgrammingLanguage.VB: + return new VBNetProjectCompiler(_projectCompilerHelper); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void AddNugetStuff(InputProjectDriver inputProjectDriver, Project project) + { + _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.SolutionFolder, "NuGet.config", "NuGet.config", new Dictionary() + { + {"FeedPath", _folders.NuGetFolder} + }); + project.AddItem("None", "..\\NuGet.config"); + + _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, "packages.config", "packages.config", new Dictionary() + { + {"NuGetVersion", CurrentVersionDriver.NuGetVersion}, + {"TestingFrameworkPackage", inputProjectDriver.TestingFrameworkPackage} + }); + project.AddItem("None", "packages.config"); + } + + + private static void WriteOutInputFile(FileInputWithContent inputFile, string outputPath) + { + if (inputFile.SourceFilePath != null) + File.Copy(inputFile.SourceFilePath, outputPath, true); + else + File.WriteAllText(outputPath, inputFile.Content, Encoding.UTF8); + } + + private void CompileOutProc(Project project, InputProjectDriver inputProjectDriver) + { + string msBuildPath = _visualStudioFinder.FindMSBuild(); + Console.WriteLine("Invoke MsBuild from {0}", msBuildPath); + ProcessStartInfo psi = new ProcessStartInfo(msBuildPath, $"/nologo /v:m \"{inputProjectDriver.SolutionPath}\"") + { + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + var p = Process.Start(psi); + + var buildOutput = p.StandardOutput.ReadToEnd(); + Console.WriteLine(buildOutput); + + p.WaitForExit(); + + if (p.ExitCode > 0) + { + var firstErrorLine = buildOutput.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(l => l.Contains("): error ")); + throw new Exception($"Build failed: {firstErrorLine}"); + } + } + + private void AddBindingClass(InputProjectDriver inputProjectDriver, Project project, BindingClassInput bindingClassInput) + { + _programLanguageProjectCompiler.AddBindingClass(inputProjectDriver, project, bindingClassInput); + } + + private void AddAppConfig(InputProjectDriver inputProjectDriver, Project project) + { + var replacements = new Dictionary + { + {"UnitTestProvider", GetUnitTestProvider(inputProjectDriver)}, + {"AdditionalSpecFlowPlugins", inputProjectDriver.AdditionalSpecFlowPlugins}, + {"AdditionalSpecFlowSettings", inputProjectDriver.AdditionalSpecFlowSettings}, + }; + _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, "App.config", "App.config", replacements); + project.AddItem("None", "App.config"); + } + + private void AddMsTestTestSettings(InputProjectDriver inputProjectDriver, Project project) + { + _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, "local.testsettings", "local.testsettings", new Dictionary()); + project.AddItem("None", "local.testsettings"); + + var replacements = new Dictionary() {{"VSAdapterPath", _folders.VSAdapterFolder}}; + _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, "local.runsettings", "local.runsettings", replacements); + project.AddItem("None", "local.runsettings"); + } + + + private string GetUnitTestProvider(InputProjectDriver inputProjectDriver) + { + switch (inputProjectDriver.UnitTestProvider) + { + case UnitTestProvider.SpecRunWithNUnit: + return "SpecRun+NUnit"; + case UnitTestProvider.SpecRunWithNUnit2: + return "SpecRun+NUnit.2"; + case UnitTestProvider.SpecRunWithMsTest: + return "SpecRun+MsTest"; + case UnitTestProvider.MSTest: + return "MSTest"; + case UnitTestProvider.NUnit2: + return "NUnit2"; + case UnitTestProvider.NUnit3: + return "NUnit"; + case UnitTestProvider.XUnit: + return "XUnit"; + default: + return "SpecRun"; + } + } + + + private Project CreateProject(InputProjectDriver inputProjectDriver, string outputFileName) + { + // the MsBuild global collection caches the project file, so we need to generate a unique project file name. + Guid projectId = inputProjectDriver.ProjectGuid; + + string solutionFileName = _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.SolutionFolder, "TestProjectSolution.sln", inputProjectDriver.SolutionFileName, + new Dictionary() + { + {"ProjectGuid", projectId.ToString("B").ToUpper()}, + {"ProjectName", Path.GetFileNameWithoutExtension(outputFileName)}, + {"ProjectFileName", inputProjectDriver.ProjectPath} + }); + + string projectFileName = _projectCompilerHelper.SaveFileFromResourceTemplate(inputProjectDriver.ProjectFolder, _programLanguageProjectCompiler.ProjectFileName, outputFileName, new Dictionary + { + {"ProjectGuid", projectId.ToString("B")}, + {"ProjectName", Path.GetFileNameWithoutExtension(outputFileName)}, + {"NetFrameworkVersion", inputProjectDriver.NetFrameworkVersion}, + {"TestingFramework", inputProjectDriver.TestingFrameworkReference} + }); + + + ProjectCollection.GlobalProjectCollection.UnloadAllProjects(); + return new Project(projectFileName); + } + + private void EnsureCompilationFolder(string compilationFolder) + { + FileSystemHelper.EnsureEmptyFolder(compilationFolder); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/ProjectCompilerHelper.cs b/SpecFlow.TestProjectGenerator/ProjectCompilerHelper.cs new file mode 100644 index 000000000..e068ccf22 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/ProjectCompilerHelper.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace SpecFlow.TestProjectGenerator +{ + public class ProjectCompilerHelper + { + protected readonly Folders _folders; + + protected ProjectCompilerHelper(Folders folders) + { + _folders = folders; + } + + public string SaveFileFromResourceTemplate(string compilationFolder, string templateName, string outputFileName, Dictionary replacements = null) + { + return SaveFileFromResourceTemplate(compilationFolder, templateName, outputFileName, Encoding.UTF8, replacements); + } + + public string SaveFileFromResourceTemplate(string compilationFolder, string templateName, string outputFileName, Encoding encoding, Dictionary replacements = null) + { + return SaveFileFromResourceTemplate(typeof(ProjectCompilerHelper), compilationFolder, templateName, outputFileName, encoding, replacements); + } + + public string SaveFileFromResourceTemplate(Type type, string compilationFolder, string templateName, string outputFileName, Encoding encoding, Dictionary replacements = null) + { + var assembly = Assembly.GetAssembly(type); + Debug.Assert(assembly != null); + + string resourceNameToFind = "TestProjectTemplates." + templateName; + var resourceName = assembly.GetManifestResourceNames().Where(i => i.EndsWith(resourceNameToFind)).SingleOrDefault(); + Debug.Assert(resourceName != null); + + var projectTemplateStream = assembly.GetManifestResourceStream(resourceName); + Debug.Assert(projectTemplateStream != null); + + string template = new StreamReader(projectTemplateStream).ReadToEnd(); + + return SaveFileFromTemplate(compilationFolder, template, outputFileName, encoding, replacements); + } + + public string SaveFileFromTemplate(string compilationFolder, string templateContent, string outputFileName, Dictionary replacements = null) + { + return SaveFileFromTemplate(compilationFolder, templateContent, outputFileName, Encoding.UTF8, replacements); + } + + public string SaveFileFromTemplate(string compilationFolder, string templateContent, string outputFileName, Encoding encoding, Dictionary replacements = null) + { + string outputPath = Path.Combine(compilationFolder, outputFileName); + string fileContent = templateContent; + + if (replacements != null) + { + fileContent = replacements.Aggregate(fileContent, (current, replacement) => current.Replace("{" + replacement.Key + "}", replacement.Value)); + } + + fileContent = ReplacePlaceholders(fileContent); + + File.WriteAllText(outputPath, fileContent, encoding); + + return outputPath; + } + + protected virtual string ReplacePlaceholders(string fileContent) + { + return fileContent + .Replace("{SpecFlowRoot}", _folders.SpecFlow) + .Replace("{SupportedFramework}", CurrentVersionDriver.SpecFlowMajor >= 2 ? "NET45" : "NET35") + .Replace("{SpecFlowDev}", Path.Combine(_folders.TestFolder, "SpecFlowDevelopment")); + } + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/SpecFlow.TestProjectGenerator.csproj b/SpecFlow.TestProjectGenerator/SpecFlow.TestProjectGenerator.csproj new file mode 100644 index 000000000..d6272cc5e --- /dev/null +++ b/SpecFlow.TestProjectGenerator/SpecFlow.TestProjectGenerator.csproj @@ -0,0 +1,34 @@ + + + + net46 + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/App.config b/SpecFlow.TestProjectGenerator/TestProjectTemplates/App.config new file mode 100644 index 000000000..b78e648dc --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/App.config @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + {AdditionalSpecFlowPlugins} + + + {AdditionalSpecFlowSettings} + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.cs b/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.cs new file mode 100644 index 000000000..940ecf211 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TechTalk.SpecFlow; +using TechTalk.SpecRun; + +namespace SpecRun.TestProject +{ + [Binding] + public class {ClassName} + { + {Bindings} + } +} diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.vb b/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.vb new file mode 100644 index 000000000..0778a8943 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/BindingClass.vb @@ -0,0 +1,17 @@ +Imports System +Imports System.IO +Imports System.Reflection +Imports System.Collections.Generic +Imports System.Linq +Imports System.Text +Imports TechTalk.SpecFlow +Imports TechTalk.SpecRun + +Namespace SpecRun.TestProject + + + Public Class {ClassName} + {Bindings} + End Class + +End Namespace \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.Designer.vb b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.Designer.vb new file mode 100644 index 000000000..88dd01c78 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.myapp b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.myapp new file mode 100644 index 000000000..758895def --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 1 + true + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/AssemblyInfo.vb b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/AssemblyInfo.vb new file mode 100644 index 000000000..2393a6b65 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.Designer.vb b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.Designer.vb new file mode 100644 index 000000000..d543b9108 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.Designer.vb @@ -0,0 +1,62 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("ClassLibrary1.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set(ByVal value As Global.System.Globalization.CultureInfo) + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.resx.content b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.resx.content new file mode 100644 index 000000000..af7dbebba --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Resources.resx.content @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.Designer.vb b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.Designer.vb new file mode 100644 index 000000000..26b335f83 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.SpecRun.TestProject.My.MySettings + Get + Return Global.SpecRun.TestProject.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.settings b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.settings new file mode 100644 index 000000000..85b890b3c --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/NuGet.config b/SpecFlow.TestProjectGenerator/TestProjectTemplates/NuGet.config new file mode 100644 index 000000000..55a6957fd --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/NuGet.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.csproj b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.csproj new file mode 100644 index 000000000..190e5fc17 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.csproj @@ -0,0 +1,61 @@ + + + + Debug + AnyCPU + + + 2.0 + {ProjectGuid} + Library + Properties + TestProject + TestProject + {NetFrameworkVersion} + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + {TestingFramework} + + + + + {SpecFlowRoot}\lib\{SupportedFramework}\TechTalk.SpecFlow.dll + + + {SpecRunPlugin}\SpecRun.SpecFlowPlugin.dll + + + {SpecRunPlugin}\TechTalk.SpecRun.dll + + + + + + + + + + true + + + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.vbproj b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.vbproj new file mode 100644 index 000000000..e9cd6de0a --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile.vbproj @@ -0,0 +1,112 @@ + + + + + Debug + AnyCPU + {ProjectGuid} + Library + TestProject + TestProject + 512 + Windows + {NetFrameworkVersion} + + + true + full + true + true + bin\Debug\ + ClassLibrary1.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + ClassLibrary1.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + On + + + Binary + + + Off + + + On + + + + + + + + + + + {SpecFlowRoot}\lib\NET45\TechTalk.SpecFlow.dll + + + {SpecRunPlugin}\SpecRun.SpecFlowPlugin.dll + + + {SpecRunPlugin}\TechTalk.SpecRun.dll + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile_Before20.csproj b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile_Before20.csproj new file mode 100644 index 000000000..b56756643 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectFile_Before20.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + + + 2.0 + {ProjectGuid} + Library + Properties + TestProject + TestProject + {NetFrameworkVersion} + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + {TestingFramework} + + + + 3.5 + + + {SpecFlowRoot}\lib\{SupportedFramework}\TechTalk.SpecFlow.dll + + + {SpecRunPlugin}\SpecRun.SpecFlowPlugin.dll + + + {SpecRunPlugin}\TechTalk.SpecRun.dll + + + + + False + + + + + + + + + true + + + diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectSolution.sln b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectSolution.sln new file mode 100644 index 000000000..ad95f5d26 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/TestProjectSolution.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "{ProjectName}", "{ProjectFileName}", "{ProjectGuid}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ProjectGuid}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ProjectGuid}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ProjectGuid}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ProjectGuid}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.runsettings b/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.runsettings new file mode 100644 index 000000000..327c8a794 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.runsettings @@ -0,0 +1,24 @@ + + + + + 1 + + .\TestResults + + + x86 + + + + + + {VSAdapterPath} + + + + + + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.testsettings b/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.testsettings new file mode 100644 index 000000000..6f1757013 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/local.testsettings @@ -0,0 +1,29 @@ + + + These are default test settings for a local test run. + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/TestProjectTemplates/packages.config b/SpecFlow.TestProjectGenerator/TestProjectTemplates/packages.config new file mode 100644 index 000000000..1f0fa66d6 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/TestProjectTemplates/packages.config @@ -0,0 +1,5 @@ + + + + {TestingFrameworkPackage} + \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/UnitTestProvider.cs b/SpecFlow.TestProjectGenerator/UnitTestProvider.cs new file mode 100644 index 000000000..364b252a3 --- /dev/null +++ b/SpecFlow.TestProjectGenerator/UnitTestProvider.cs @@ -0,0 +1,15 @@ +namespace SpecFlow.TestProjectGenerator +{ + public enum UnitTestProvider + { + SpecRun, + SpecRunWithNUnit, + SpecRunWithNUnit2, + SpecRunWithMsTest, + MSTest, + XUnit, + NUnit3, + NUnit2 + + } +} \ No newline at end of file diff --git a/SpecFlow.TestProjectGenerator/VisualStudioFinder.cs b/SpecFlow.TestProjectGenerator/VisualStudioFinder.cs new file mode 100644 index 000000000..c8a614d4d --- /dev/null +++ b/SpecFlow.TestProjectGenerator/VisualStudioFinder.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Linq; + +namespace SpecFlow.TestProjectGenerator +{ + public class VisualStudioFinder + { + private const string VsWhereParameter = @"-latest -products * -requires Microsoft.VisualStudio.PackageGroup.TestTools.Core -property installationPath"; + private readonly Folders _folders; + + public VisualStudioFinder(Folders folders) + { + _folders = folders; + } + + public string Find() + { + string vsWherePath = Path.Combine(_folders.GlobalPackages, "vswhere", "2.2.7", "tools", "vswhere.exe"); + + if (!File.Exists(vsWherePath)) + { + throw new FileNotFoundException("vswhere can not be found! Is the version number correct?", vsWherePath); + } + + var ph = new ProcessHelper(); + ph.RunProcess(vsWherePath, VsWhereParameter); + var output = ph.ConsoleOutput; + + + var lines = output.Split(new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries); + return lines.First(); + } + + public string FindMSBuild() + { + var msbuildExe = Path.Combine(Find(), "MSBuild", "15.0", "Bin", "msbuild.exe"); + return msbuildExe; + } + + public string FindDevEnv() + { + return Path.Combine(Find(), "Common7", "IDE", "devenv.exe"); + } + } +} \ No newline at end of file