From 61537d55898c125a84255163c04930fd84f6a257 Mon Sep 17 00:00:00 2001 From: kharnagy Date: Mon, 10 Jun 2013 11:07:14 -0400 Subject: [PATCH] Initial commit --- .gitignore | 4 + NUnit.csproj | 135 ++++++++++++++++++++++++++++++++ NUnit.sln | 20 +++++ NUnitActionEditor.cs | 154 +++++++++++++++++++++++++++++++++++++ NUnitAppAction.cs | 134 ++++++++++++++++++++++++++++++++ Properties/AssemblyInfo.cs | 17 ++++ README.md | 19 +++++ packages.config | 5 ++ 8 files changed, 488 insertions(+) create mode 100644 .gitignore create mode 100644 NUnit.csproj create mode 100644 NUnit.sln create mode 100644 NUnitActionEditor.cs create mode 100644 NUnitAppAction.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 README.md create mode 100644 packages.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a7b981 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +packages/* +bin/* +obj/* +*.suo \ No newline at end of file diff --git a/NUnit.csproj b/NUnit.csproj new file mode 100644 index 0000000..2f25d5b --- /dev/null +++ b/NUnit.csproj @@ -0,0 +1,135 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {34B976CF-D676-42F4-80DF-8515D45E1B62} + Library + Properties + Inedo.BuildMasterExtensions.NUnit + NUnit + + + 3.5 + + + + + + + + + + + true + + + v2.0 + http://localhost/NUnit/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + packages\Inedo.BuildMaster.SDK.3.5\lib\net20\BuildMaster.Web.Controls.dll + False + + + packages\Inedo.BuildMaster.SDK.3.5\lib\net20\BuildMasterCore.dll + False + + + packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + False + + + packages\Inedo.BuildMaster.SDK.3.5\lib\net20\InedoLib.dll + False + + + + + + + + + ASPXCodeBehind + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + + + \ No newline at end of file diff --git a/NUnit.sln b/NUnit.sln new file mode 100644 index 0000000..bef1baf --- /dev/null +++ b/NUnit.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnit", "NUnit.csproj", "{34B976CF-D676-42F4-80DF-8515D45E1B62}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {34B976CF-D676-42F4-80DF-8515D45E1B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34B976CF-D676-42F4-80DF-8515D45E1B62}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34B976CF-D676-42F4-80DF-8515D45E1B62}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34B976CF-D676-42F4-80DF-8515D45E1B62}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NUnitActionEditor.cs b/NUnitActionEditor.cs new file mode 100644 index 0000000..4af7432 --- /dev/null +++ b/NUnitActionEditor.cs @@ -0,0 +1,154 @@ +using System.Web.UI.WebControls; +using Inedo.BuildMaster.Data; +using Inedo.BuildMaster.Extensibility.Actions; +using Inedo.BuildMaster.Web.Controls; +using Inedo.BuildMaster.Web.Controls.Extensions; +using Inedo.Linq; +using Inedo.Web.Controls; + +namespace Inedo.BuildMasterExtensions.NUnit +{ + /// + /// Custom editor for the NUnit action. + /// + internal sealed class NUnitActionEditor : ActionEditorBase + { + private SourceControlFileFolderPicker txtExePath; + private ValidatingTextBox txtTestFile, txtGroupName; + private DropDownList ddlFrameworkVersion; + private ValidatingTextBox txtAdditionalArguments; + + /// + /// Initializes a new instance of the class. + /// + public NUnitActionEditor() + { + this.ValidateBeforeCreate += this.NUnitActionEditor_ValidateBeforeCreate; + } + + /// + /// Gets a value indicating whether a textbox to edit the source directory should be displayed. + /// + /// + /// true if a textbox to edit the source directory should be displayed; otherwise, false. + /// + public override bool DisplaySourceDirectory { get { return true; } } + + /// + /// Handles the ValidateBeforeCreate event of the NUnitActionEditor control. + /// + /// The source of the event. + /// The instance containing the event data. + private void NUnitActionEditor_ValidateBeforeCreate(object sender, ValidationEventArgs e) + { + if (this.DeployableId == 0) + { + e.ValidLevel = ValidationLevels.Error; + e.Message = + "In order to create a unit testing action, you must use an action group " + + "that has a default deployable selected. To do so, return to the Deployment " + + "Plans section and choose \"Create New Action Group,\" and select a default " + + "deployable for the new group."; + return; + } + + e.ValidLevel = ValidationLevels.Valid; + } + + protected override void CreateChildControls() + { + var deployable = StoredProcs + .Applications_GetDeployable(this.DeployableId) + .Execute() + .FirstOrDefault(); + + this.txtExePath = new SourceControlFileFolderPicker + { + Required = true, + DisplayMode = SourceControlBrowser.DisplayModes.FoldersAndFiles, + ServerId = this.ServerId + }; + + this.txtGroupName = new ValidatingTextBox + { + Text = deployable != null ? deployable.Deployable_Name : string.Empty, + Width= 300, + Required = true + }; + + this.txtTestFile = new ValidatingTextBox + { + Required = true, + Width = 300 + }; + + this.ddlFrameworkVersion = new DropDownList(); + this.ddlFrameworkVersion.Items.Add(new ListItem("2.0.50727", "2.0.50727")); + this.ddlFrameworkVersion.Items.Add(new ListItem("4.0.30319", "4.0.30319")); + this.ddlFrameworkVersion.Items.Add(new ListItem("unspecified", "")); + this.ddlFrameworkVersion.SelectedValue = ""; + + this.txtAdditionalArguments = new ValidatingTextBox + { + Required = false, + Width = 300 + }; + + this.Controls.Add( + new FormFieldGroup( + "NUnit Executable Path", + "The path to (and including) nunit-console.exe.", + false, + new StandardFormField("NUnit Exe Path:", this.txtExePath) + ), + new FormFieldGroup( + ".NET Framework Version", + "The version of .NET which will host the unit test runner.", + false, + new StandardFormField(".NET Framework Version:", this.ddlFrameworkVersion) + ), + new FormFieldGroup( + "Test File", + "The path relative to the source directory of the DLL, project file, or NUnit file to test against.", + false, + new StandardFormField("Test File:", this.txtTestFile) + ), + new FormFieldGroup( + "Group Name", + "The Group name allows you to easily identify the unit test.", + false, + new StandardFormField("Group Name:", this.txtGroupName) + ), + new FormFieldGroup( + "Additional Arguments", + "The additional arguments to pass to the NUnit executable.", + true, + new StandardFormField("Additional Arguments:", this.txtAdditionalArguments) + ) + ); + } + + public override void BindToForm(ActionBase extension) + { + var nunitAction = (NUnitAppAction)extension; + + this.txtExePath.Text = nunitAction.ExePath; + this.txtTestFile.Text = nunitAction.TestFile; + this.txtGroupName.Text = nunitAction.GroupName; + this.ddlFrameworkVersion.SelectedValue = nunitAction.FrameworkVersion ?? ""; + this.txtAdditionalArguments.Text = nunitAction.AdditionalArguments; + } + + public override ActionBase CreateFromForm() + { + return new NUnitAppAction + { + ExePath = this.txtExePath.Text, + TestFile = this.txtTestFile.Text, + GroupName = this.txtGroupName.Text, + FrameworkVersion = this.ddlFrameworkVersion.SelectedValue, + AdditionalArguments = this.txtAdditionalArguments.Text + }; + } + } +} diff --git a/NUnitAppAction.cs b/NUnitAppAction.cs new file mode 100644 index 0000000..e212acd --- /dev/null +++ b/NUnitAppAction.cs @@ -0,0 +1,134 @@ +using System; +using System.IO; +using System.Xml; +using Inedo.BuildMaster; +using Inedo.BuildMaster.Extensibility.Actions; +using Inedo.BuildMaster.Extensibility.Actions.Testing; +using Inedo.BuildMaster.Extensibility.Agents; +using Inedo.BuildMaster.Web; + +namespace Inedo.BuildMasterExtensions.NUnit +{ + /// + /// Action that runs NUnit unit tests on a specified project, assembly, or NUnit file. + /// + [ActionProperties( + "Execute NUnit Tests", + "Runs NUnit unit tests on a specified project, assembly, or NUnit file.", + "Testing")] + [CustomEditor(typeof(NUnitActionEditor))] + [RequiresInterface(typeof(IRemoteProcessExecuter))] + [RequiresInterface(typeof(IFileOperationsExecuter))] + public sealed class NUnitAppAction : UnitTestActionBase + { + /// + /// Initializes a new instance of the class. + /// + public NUnitAppAction() + { + } + + /// + /// Gets or sets the test runner exe path + /// + [Persistent] + public string ExePath { get; set; } + + /// + /// Gets or sets the file nunit will test against (could be dll, proj, or config file based on test runner) + /// + [Persistent] + public string TestFile { get; set; } + + /// + /// Gets or sets the .NET Framework version to run against. + /// + [Persistent] + public string FrameworkVersion { get; set; } + + /// + /// Gets or sets the additional arguments. + /// + [Persistent] + public string AdditionalArguments { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + /// + /// This should return a user-friendly string describing what the Action does + /// and the state of its important persistent properties. + /// + public override string ToString() + { + return string.Format("Run NUnit Unit Tests on {0}{1}", this.TestFile, Util.ConcatNE(" with the additional arguments: ", this.AdditionalArguments)); + } + + /// + /// Runs a unit test against the target specified in the action. + /// After the test is run, use the method + /// to save the test results to the database. + /// + protected override void RunTests() + { + var doc = new XmlDocument(); + + using (var agent = Util.Agents.CreateAgentFromId(this.ServerId)) + { + var fileOps = (IFileOperationsExecuter)agent; + var nunitPath = fileOps.GetWorkingDirectory(this.Context.ApplicationId, this.Context.DeployableId, this.ExePath); + + var tmpFileName = fileOps.CombinePath(this.RemoteConfiguration.TempDirectory, Guid.NewGuid().ToString() + ".xml"); + + this.ExecuteCommandLine( + nunitPath, + string.Format("\"{0}\" /xml:\"{1}\" {2}", this.TestFile, tmpFileName, this.AdditionalArguments), + this.RemoteConfiguration.SourceDirectory + ); + + using (var stream = new MemoryStream(fileOps.ReadAllFileBytes(tmpFileName), false)) + { + doc.Load(stream); + } + } + + var testStart = DateTime.Parse(doc.SelectSingleNode("//test-results").Attributes["time"].Value); + + var nodeList = doc.SelectNodes("//test-case"); + + foreach (XmlNode node in nodeList) + { + string testName = node.Attributes["name"].Value; + + // skip tests that weren't actually run + if (string.Equals(node.Attributes["executed"].Value, "false", StringComparison.OrdinalIgnoreCase)) + { + LogInformation(String.Format("NUnit Test: {0} (skipped)", testName)); + continue; + } + + bool nodeResult = node.Attributes["success"].Value.Equals("True", StringComparison.OrdinalIgnoreCase); + + double testLength = double.Parse(node.Attributes["time"].Value); + + this.LogInformation(string.Format("NUnit Test: {0}, Result: {1}, Test Length: {2} secs", + testName, + nodeResult, + testLength)); + + this.RecordResult( + testName, + nodeResult, + node.OuterXml, + testStart, + testStart.AddSeconds(testLength) + ); + + testStart = testStart.AddSeconds(testLength); + } + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2be1627 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,17 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Inedo.BuildMaster.Extensibility; + +[assembly: AssemblyTitle("NUnit")] +[assembly: AssemblyDescription("Contains a unit testing action for NUnit-based tests.")] + +[assembly: ComVisible(false)] +[assembly: AssemblyCompany("Inedo, LLC")] +[assembly: AssemblyProduct("BuildMaster")] +[assembly: AssemblyCopyright("Copyright © 2008 - 2012")] +[assembly: AssemblyVersion("0.0.0.0")] +[assembly: AssemblyFileVersion("0.0")] +[assembly: BuildMasterAssembly] +[assembly: CLSCompliant(false)] +[assembly: RequiredBuildMasterVersion("3.0.0")] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bdc04c --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +bmx-nunit +========= + +BuildMaster NUnit extension + +**License** + +Copyright (c) 2013 Inedo, LLC. All rights reserved. + +http://inedo.com/buildmaster/extensions/NUnit + +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, the above URL, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, the above URL, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Redistributions with modifications must also contain a notice describing the modifications made +* Neither the name of Inedo, BuildMaster, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE REGENTS 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. diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..1f15956 --- /dev/null +++ b/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file