diff --git a/Generator.cs b/Generators/CS/MSTestGenerator.cs
similarity index 62%
rename from Generator.cs
rename to Generators/CS/MSTestGenerator.cs
index a2500cc..81d0084 100644
--- a/Generator.cs
+++ b/Generators/CS/MSTestGenerator.cs
@@ -6,28 +6,14 @@
using System.Text;
using Tenrec.Components;
-namespace Tenrec
+namespace Tenrec.Generators.CS
{
- ///
- /// Produces code files automatically, generating test code based on Grasshopper files.
- ///
- ///
- /// This must be called once to produce the test files automatically, or every time you add or remove a or add or remove a file from the test folder.
- ///
- public class Generator
+ ///
+ public class MSTestGenerator : IGenerator
{
- ///
- /// Generate a code file containing all the -based tests present in Grasshopper files.
- ///
- /// The folder containing the Grasshopper files.
- /// The folder where to save the source code file.
- /// The name of the resulting code file.
- /// Language of the code file. Currently only supports "cs"(C#).
- /// Testing framework. Currently only supports MSTest.
- /// A log of the process.
- public static string CreateAutoTestSourceFile(string[] ghTestFolders,
- string outputFolder, string outputName = "TenrecGeneratedTests",
- string language = "cs", string testFramework = "mstest")
+ ///
+ public string CreateAutoTestSourceFile
+ (string[] ghTestFolders, string outputFolder, string outputName)
{
var log = new StringBuilder();
var sb = new StringBuilder();
@@ -35,14 +21,10 @@ public static string CreateAutoTestSourceFile(string[] ghTestFolders,
var fileName = string.Empty;
try
{
- if (ghTestFolders == null && ghTestFolders.Length == 0)
+ if (ghTestFolders == null || ghTestFolders.Length == 0)
throw new ArgumentNullException(nameof(outputFolder));
if (string.IsNullOrEmpty(outputFolder))
throw new ArgumentNullException(nameof(outputFolder));
- if (!language.Equals("cs"))
- throw new NotImplementedException(nameof(language));
- if (!testFramework.Equals("mstest"))
- throw new NotImplementedException(nameof(testFramework));
sb.AppendLine("using Microsoft.VisualStudio.TestTools.UnitTesting;");
sb.AppendLine();
@@ -55,7 +37,7 @@ public static string CreateAutoTestSourceFile(string[] ghTestFolders,
{
foreach (var file in files)
{
- if (OpenDocument(file, out GH_Document doc))
+ if (Utils.IOHelper.OpenDocument(file, out GH_Document doc))
{
var groups = new List();
foreach (var obj in doc.Objects)
@@ -65,10 +47,10 @@ public static string CreateAutoTestSourceFile(string[] ghTestFolders,
groups.Add(obj);
}
}
- if (groups != null && groups.Any())
+ if (groups.Any())
{
sb.AppendLine(" [TestClass]");
- sb.AppendLine($" public class AutoTest_{CodeableNickname(doc.DisplayName)}");
+ sb.AppendLine($" public class AutoTest_{Utils.StringHelper.CodeableNickname(doc.DisplayName)}");
sb.AppendLine(" {");
sb.AppendLine($" public string FilePath => @\"{doc.FilePath}\";");
sb.AppendLine(" private TestContext testContextInstance;");
@@ -76,7 +58,7 @@ public static string CreateAutoTestSourceFile(string[] ghTestFolders,
foreach (var group in groups)
{
sb.AppendLine(" [TestMethod]");
- sb.AppendLine($" public void {CodeableNickname(group.NickName)}()");
+ sb.AppendLine($" public void {Utils.StringHelper.CodeableNickname(group.NickName)}()");
sb.AppendLine(" {");
sb.AppendLine($" Tenrec.Runner.Initialize(TestContext);");
sb.AppendLine($" Tenrec.Runner.RunTenrecGroup(FilePath, new System.Guid(\"{group.InstanceGuid}\"), TestContext);");
@@ -111,23 +93,5 @@ public static string CreateAutoTestSourceFile(string[] ghTestFolders,
return log.ToString();
}
-
- private static string CodeableNickname(string nickname)
- {
- return nickname.Replace(" ", "_");
- }
-
- private static bool OpenDocument(string filePath, out GH_Document doc)
- {
- var io = new GH_DocumentIO();
- if (!io.Open(filePath))
- {
- doc = null;
- throw new Exception($"Failed to open file: {filePath}");
- }
- doc = io.Document;
- doc.Enabled = true;
- return true;
- }
}
}
diff --git a/Generators/CS/XUnitGenerator.cs b/Generators/CS/XUnitGenerator.cs
new file mode 100644
index 0000000..80e3a3e
--- /dev/null
+++ b/Generators/CS/XUnitGenerator.cs
@@ -0,0 +1,112 @@
+using Grasshopper.Kernel;
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Tenrec.Components;
+using Tenrec.Utils;
+
+namespace Tenrec.Generators.CS
+{
+ ///
+ public class XUnitGenerator : IGenerator
+ {
+ ///
+ public string CreateAutoTestSourceFile
+ (string[] ghTestFolders, string outputFolder, string outputName)
+ {
+ var log = new StringBuilder();
+ var sb = new StringBuilder();
+ var exits = false;
+ var fileName = string.Empty;
+ try
+ {
+ if (ghTestFolders == null || ghTestFolders.Length == 0)
+ throw new ArgumentNullException(nameof(outputFolder));
+ if (string.IsNullOrEmpty(outputFolder))
+ throw new ArgumentNullException(nameof(outputFolder));
+
+ sb.AppendLine("using Xunit;");
+ sb.AppendLine("using Xunit.Abstractions;");
+ sb.AppendLine();
+ sb.AppendLine("namespace TenrecGeneratedTests");
+ sb.AppendLine("{");
+ foreach (var folder in ghTestFolders)
+ {
+ var files = Directory.EnumerateFiles(folder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".gh") || s.EndsWith(".ghx"));
+ if (!files.Any())
+ {
+ log.Append($"{folder} does not contain any readable format (.gh|.ghx)");
+ continue;
+ }
+ foreach (var file in files)
+ {
+ if (!IOHelper.OpenDocument(file, out GH_Document doc))
+ {
+ log.Append($"Failed to open {file}.");
+ continue;
+ }
+ var groups = doc.Objects.Where(o => o.ComponentGuid == Group_UnitTest.ID).ToList();
+ if (groups.Count == 0)
+ continue;
+
+ #region Crearte_Fixture_Class
+ sb.AppendLine(StringHelper.IndexedString($"public class AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}_Fixture : GHFileFixture", 1));
+ sb.AppendLine(StringHelper.IndexedString("{", 1));
+ sb.AppendLine(StringHelper.IndexedString($"public AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}_Fixture()", 2));
+ sb.AppendLine(StringHelper.IndexedString($" : base(@\"{file}\")", 3));
+ sb.AppendLine(StringHelper.IndexedString("{", 2));
+ sb.AppendLine(StringHelper.IndexedString("}", 2));
+ sb.AppendLine(StringHelper.IndexedString("}", 1));
+ #endregion
+
+ sb.AppendLine(StringHelper.IndexedString($"public class AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}" +
+ $" : IClassFixture", 1));
+ sb.AppendLine(StringHelper.IndexedString("{", 1));
+
+ sb.AppendLine(StringHelper.IndexedString($"private readonly AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}_Fixture fixture;", 2));
+ sb.AppendLine(StringHelper.IndexedString("private readonly ITestOutputHelper context;", 2));
+
+ #region Test_Class_Constructor
+ sb.AppendLine(StringHelper.IndexedString($"public AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}" +
+ $" (AutoTest_{StringHelper.CodeableNickname(doc.DisplayName)}_Fixture fixture, " +
+ $"ITestOutputHelper context)", 2));
+ sb.AppendLine(StringHelper.IndexedString("{", 2));
+ sb.AppendLine(StringHelper.IndexedString("this.fixture = fixture;", 3));
+ sb.AppendLine(StringHelper.IndexedString("this.context = context;", 3));
+ sb.AppendLine(StringHelper.IndexedString("}", 2));
+ #endregion
+
+ foreach (var group in groups)
+ {
+ sb.AppendLine(StringHelper.IndexedString("[Fact]", 2));
+ sb.AppendLine(StringHelper.IndexedString($"public void {StringHelper.CodeableNickname(group.NickName)}()", 2));
+ sb.AppendLine(StringHelper.IndexedString("{", 2));
+ sb.AppendLine(StringHelper.IndexedString($"fixture.RunGroup(fixture.Doc, new System.Guid(\"{group.InstanceGuid}\"), context);", 3));
+ sb.AppendLine(StringHelper.IndexedString("}", 2));
+ }
+ sb.AppendLine(StringHelper.IndexedString("}", 1));
+ sb.AppendLine();
+ doc.Dispose();
+ }
+ }
+ sb.AppendLine("}");
+
+ fileName = Path.Combine(outputFolder, outputName + ".cs");
+ exits = File.Exists(fileName);
+ File.WriteAllText(fileName, sb.ToString());
+ }
+ catch (Exception e)
+ {
+ log.AppendLine($"EXCEPTION: {e}.");
+ }
+
+ if (exits)
+ log.AppendLine($"File successfully overwritten.");
+ else
+ log.AppendLine($"File successfully created.");
+
+ return log.ToString();
+ }
+ }
+}
diff --git a/Generators/IGenerator.cs b/Generators/IGenerator.cs
new file mode 100644
index 0000000..819d14c
--- /dev/null
+++ b/Generators/IGenerator.cs
@@ -0,0 +1,21 @@
+namespace Tenrec.Generators
+{
+ ///
+ /// Produces code files automatically, generating test code based on Grasshopper files.
+ ///
+ ///
+ /// This must be called once to produce the test files automatically, or every time you add or remove a or add or remove a file from the test folder.
+ ///
+ public interface IGenerator
+ {
+ ///
+ /// Generate a code file containing all the -based tests present in Grasshopper files.
+ ///
+ /// The folder containing the Grasshopper files.
+ /// The folder where to save the source code file.
+ /// The name of the resulting code file.
+ /// A log of the process.
+ string CreateAutoTestSourceFile
+ (string[] ghTestFolders, string outputFolder, string outputName);
+ }
+}
diff --git a/Tenrec.csproj b/Tenrec.csproj
index 3817348..9d8bc47 100644
--- a/Tenrec.csproj
+++ b/Tenrec.csproj
@@ -71,6 +71,9 @@
+
+
+
True
@@ -79,7 +82,6 @@
-
@@ -90,6 +92,8 @@
UnitTestsSourceCodeGeneratorForm.cs
+
+
diff --git a/Tenrec.sln b/Tenrec.sln
index 46c3631..dee81bf 100644
--- a/Tenrec.sln
+++ b/Tenrec.sln
@@ -5,8 +5,6 @@ VisualStudioVersion = 16.0.31229.75
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tenrec", "Tenrec.csproj", "{C0E5FFFB-45D6-41A1-9E5A-342E01327EB3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleTestProjectTenrec", "..\SampleTestProjectTenrec\SampleTestProjectTenrec.csproj", "{C8CF27BE-2BBE-4AD2-A62A-BC55DC3A3C31}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -17,10 +15,6 @@ Global
{C0E5FFFB-45D6-41A1-9E5A-342E01327EB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0E5FFFB-45D6-41A1-9E5A-342E01327EB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0E5FFFB-45D6-41A1-9E5A-342E01327EB3}.Release|Any CPU.Build.0 = Release|Any CPU
- {C8CF27BE-2BBE-4AD2-A62A-BC55DC3A3C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C8CF27BE-2BBE-4AD2-A62A-BC55DC3A3C31}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C8CF27BE-2BBE-4AD2-A62A-BC55DC3A3C31}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C8CF27BE-2BBE-4AD2-A62A-BC55DC3A3C31}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/UI/UnitTestsSourceCodeGeneratorForm.Designer.cs b/UI/UnitTestsSourceCodeGeneratorForm.Designer.cs
index dd14374..eae5147 100644
--- a/UI/UnitTestsSourceCodeGeneratorForm.Designer.cs
+++ b/UI/UnitTestsSourceCodeGeneratorForm.Designer.cs
@@ -199,7 +199,7 @@ private void InitializeComponent()
this.comboBoxFramework.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxFramework.FormattingEnabled = true;
this.comboBoxFramework.Items.AddRange(new object[] {
- "MSTest"});
+ "MSTest", "XUnit"});
this.comboBoxFramework.Location = new System.Drawing.Point(3, 16);
this.comboBoxFramework.Name = "comboBoxFramework";
this.comboBoxFramework.Size = new System.Drawing.Size(192, 21);
diff --git a/UI/UnitTestsSourceCodeGeneratorForm.cs b/UI/UnitTestsSourceCodeGeneratorForm.cs
index 8b5dc17..149a07b 100644
--- a/UI/UnitTestsSourceCodeGeneratorForm.cs
+++ b/UI/UnitTestsSourceCodeGeneratorForm.cs
@@ -1,6 +1,8 @@
using System;
using System.Drawing;
using System.Windows.Forms;
+using Tenrec.Generators;
+using Tenrec.Generators.CS;
namespace Tenrec.UI
{
@@ -51,7 +53,7 @@ private bool CanGenerate(out string message)
}
else
{
- foreach(var folder in folders)
+ foreach (var folder in folders)
{
if (!System.IO.Directory.Exists(folder))
{
@@ -59,7 +61,7 @@ private bool CanGenerate(out string message)
return false;
}
}
- }
+ }
var outputFolder = GetOutputFolder();
if (string.IsNullOrEmpty(outputFolder))
{
@@ -101,7 +103,7 @@ private void UpdateState()
private void UnitTestsSourceCodeGeneratorForm_Load(object sender, EventArgs e)
{
var activeDoc = Grasshopper.Instances.ActiveCanvas?.Document;
- if(activeDoc != null && !string.IsNullOrEmpty(activeDoc.FilePath))
+ if (activeDoc != null && !string.IsNullOrEmpty(activeDoc.FilePath))
{
textBoxFiles.Text = System.IO.Path.GetDirectoryName(activeDoc.FilePath);
}
@@ -122,8 +124,16 @@ private void buttonGenerate_Click(object sender, EventArgs e)
var outputFolder = GetOutputFolder();
var outputName = GetOutputName();
var language = GetLanguage();
- var framework = GetFramework();
- textBoxLog.Text = Generator.CreateAutoTestSourceFile(folderFiles, outputFolder, outputName, language, framework);
+ var framework = GetFramework();
+ IGenerator generator = null;
+ if (language == "cs")
+ {
+ if (framework == "mstest")
+ generator = new MSTestGenerator();
+ else if (framework == "xunit")
+ generator = new XUnitGenerator();
+ }
+ textBoxLog.Text = generator.CreateAutoTestSourceFile(folderFiles, outputFolder, outputName);
if (textBoxLog.Text.Contains("successfully"))
textBoxLog.ForeColor = Grasshopper.GUI.GH_GraphicsUtil.BlendColour(Color.Green, Color.Black, 0.5);
else
diff --git a/Utils/IOHelper.cs b/Utils/IOHelper.cs
new file mode 100644
index 0000000..2e87db8
--- /dev/null
+++ b/Utils/IOHelper.cs
@@ -0,0 +1,24 @@
+using Grasshopper.Kernel;
+using System;
+
+namespace Tenrec.Utils
+{
+ ///
+ /// Contains helper classes for input/output operations used in Tenrec project.
+ ///
+ public static class IOHelper
+ {
+ public static bool OpenDocument(string filePath, out GH_Document doc)
+ {
+ var io = new GH_DocumentIO();
+ if (!io.Open(filePath))
+ {
+ doc = null;
+ throw new Exception($"Failed to open file: {filePath}");
+ }
+ doc = io.Document;
+ doc.Enabled = true;
+ return true;
+ }
+ }
+}
diff --git a/Utils/StringHelper.cs b/Utils/StringHelper.cs
new file mode 100644
index 0000000..bbc877c
--- /dev/null
+++ b/Utils/StringHelper.cs
@@ -0,0 +1,28 @@
+namespace Tenrec.Utils
+{
+ ///
+ /// Contains helper classes for string manipulation required in Tenrec project.
+ ///
+ public static class StringHelper
+ {
+ ///
+ /// Replaces all spaces in the string with '_'
+ ///
+ /// source string
+ /// source string without any spaces (all spaces replaced with '_')
+ public static string CodeableNickname(string nickname)
+ {
+ return nickname.Replace(" ", "_");
+ }
+ ///
+ /// Adds identation based on provided index. index can start from 0 (no identation).
+ ///
+ /// source string
+ /// level of identation desired.
+ /// An identated string
+ public static string IndexedString(string str, int index)
+ {
+ return string.Concat(System.Linq.Enumerable.Repeat(" ", index)) + str;
+ }
+ }
+}