Initial implementation of separate teamcity extension solution
This commit is contained in:
Родитель
b6730d9be8
Коммит
9eae5fb62b
|
@ -0,0 +1,179 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
.vs/
|
||||
tools/Cake
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||
!packages/*/build/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# 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
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# 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
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
|
||||
#LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
|
||||
# =========================
|
||||
# NUnit Specific
|
||||
# =========================
|
||||
|
||||
.~
|
||||
*.userprefs
|
||||
*.StyleCop
|
||||
*.sdf
|
||||
GeneratedAssemblyInfo.cs
|
||||
StyleCop.Cache
|
||||
local.settings.include
|
||||
InternalTrace.txt
|
||||
TestResult.xml
|
||||
testCaseCollection.xml
|
||||
deploy
|
||||
lib
|
||||
test-results
|
||||
package
|
||||
images
|
||||
MockAssemblyResult.xml
|
||||
PortabilityAnalysis*.html
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="NUnit AppVeyor CI" value="https://ci.appveyor.com/nuget/nunit" />
|
||||
<add key="NUNit MyGet Feed" value="https://www.myget.org/F/nunit/api/v2" />
|
||||
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
|
||||
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("teamcity-event-listener")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("teamcity-event-listener")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("a867079b-a172-4dae-9549-63b331092a23")]
|
||||
|
||||
// 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:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.1.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.1.0")]
|
|
@ -0,0 +1,53 @@
|
|||
// ***********************************************************************
|
||||
// Copyright (c) 2016 Charlie Poole
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ***********************************************************************
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
sealed class ExtensionAttribute : Attribute { }
|
||||
}
|
||||
|
||||
namespace NUnit.Engine.Listeners
|
||||
{
|
||||
/// <summary>
|
||||
/// SafeAttributeAccess provides an extension method for accessing XML attributes.
|
||||
/// </summary>
|
||||
public static class SafeAttributeAccess
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value of the given attribute.
|
||||
/// </summary>
|
||||
/// <param name="result">The result.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns></returns>
|
||||
public static string GetAttribute(this XmlNode result, string name)
|
||||
{
|
||||
XmlAttribute attr = result.Attributes[name];
|
||||
|
||||
return attr == null ? null : attr.Value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,427 @@
|
|||
// ***********************************************************************
|
||||
// Copyright (c) 2015 Charlie Poole
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ***********************************************************************
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using NUnit.Engine.Extensibility;
|
||||
|
||||
namespace NUnit.Engine.Listeners
|
||||
{
|
||||
// Note: Setting mimimum engine version in this case is
|
||||
// purely documentary since engines prior to 3.4 do not
|
||||
// check the EngineVersion property and will try to
|
||||
// load this extension anyway.
|
||||
[Extension(Enabled = false, EngineVersion = "3.4")]
|
||||
public class TeamCityEventListener : ITestEventListener
|
||||
{
|
||||
private readonly TextWriter _outWriter;
|
||||
private readonly Dictionary<string, string> _refs = new Dictionary<string, string>();
|
||||
private int _blockCounter;
|
||||
private string _rootFlowId;
|
||||
|
||||
public TeamCityEventListener() : this(Console.Out) { }
|
||||
|
||||
public TeamCityEventListener(TextWriter outWriter)
|
||||
{
|
||||
if (outWriter == null)
|
||||
{
|
||||
throw new ArgumentNullException("outWriter");
|
||||
}
|
||||
|
||||
_outWriter = outWriter;
|
||||
}
|
||||
|
||||
#region ITestEventListener Implementation
|
||||
|
||||
public void OnTestEvent(string report)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(report);
|
||||
|
||||
var testEvent = doc.FirstChild;
|
||||
RegisterMessage(testEvent);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void RegisterMessage(XmlNode testEvent)
|
||||
{
|
||||
if (testEvent == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
var messageName = testEvent.Name;
|
||||
if (string.IsNullOrEmpty(messageName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
messageName = messageName.ToLowerInvariant();
|
||||
if (messageName == "start-run")
|
||||
{
|
||||
_refs.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var fullName = testEvent.GetAttribute("fullname");
|
||||
if (string.IsNullOrEmpty(fullName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var id = testEvent.GetAttribute("id");
|
||||
var parentId = testEvent.GetAttribute("parentId");
|
||||
string flowId;
|
||||
if (parentId != null)
|
||||
{
|
||||
// NUnit 3 case
|
||||
string rootId;
|
||||
flowId = TryFindRootId(parentId, out rootId) ? rootId : id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// NUnit 2 case
|
||||
flowId = _rootFlowId;
|
||||
}
|
||||
|
||||
string testFlowId;
|
||||
if (id != flowId && parentId != null)
|
||||
{
|
||||
testFlowId = id;
|
||||
}
|
||||
else
|
||||
{
|
||||
testFlowId = flowId;
|
||||
if (testFlowId == null)
|
||||
{
|
||||
testFlowId = id;
|
||||
}
|
||||
}
|
||||
|
||||
switch (messageName.ToLowerInvariant())
|
||||
{
|
||||
case "start-suite":
|
||||
_refs[id] = parentId;
|
||||
StartSuiteCase(id, parentId, flowId, fullName);
|
||||
break;
|
||||
|
||||
case "test-suite":
|
||||
_refs.Remove(id);
|
||||
TestSuiteCase(id, parentId, flowId, fullName);
|
||||
break;
|
||||
|
||||
case "start-test":
|
||||
_refs[id] = parentId;
|
||||
CaseStartTest(id, flowId, parentId, testFlowId, fullName);
|
||||
break;
|
||||
|
||||
case "test-case":
|
||||
try
|
||||
{
|
||||
if (!_refs.Remove(id))
|
||||
{
|
||||
// When test without starting
|
||||
CaseStartTest(id, flowId, parentId, testFlowId, fullName);
|
||||
}
|
||||
|
||||
var result = testEvent.GetAttribute("result");
|
||||
if (string.IsNullOrEmpty(result))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (result.ToLowerInvariant())
|
||||
{
|
||||
case "passed":
|
||||
OnTestFinished(testFlowId, testEvent, fullName);
|
||||
break;
|
||||
|
||||
case "inconclusive":
|
||||
OnTestInconclusive(testFlowId, testEvent, fullName);
|
||||
break;
|
||||
|
||||
case "skipped":
|
||||
OnTestSkipped(testFlowId, testEvent, fullName);
|
||||
break;
|
||||
|
||||
case "failed":
|
||||
OnTestFailed(testFlowId, testEvent, fullName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (id != flowId && parentId != null)
|
||||
{
|
||||
OnFlowFinished(id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CaseStartTest(string id, string flowId, string parentId, string testFlowId, string fullName)
|
||||
{
|
||||
if (id != flowId && parentId != null)
|
||||
{
|
||||
OnFlowStarted(id, flowId);
|
||||
}
|
||||
|
||||
OnTestStart(testFlowId, fullName);
|
||||
}
|
||||
|
||||
private void TestSuiteCase(string id, string parentId, string flowId, string fullName)
|
||||
{
|
||||
// NUnit 3 case
|
||||
if (parentId == string.Empty)
|
||||
{
|
||||
OnRootSuiteFinish(flowId, fullName);
|
||||
}
|
||||
|
||||
// NUnit 2 case
|
||||
if (parentId == null)
|
||||
{
|
||||
if (--_blockCounter == 0)
|
||||
{
|
||||
_rootFlowId = null;
|
||||
OnRootSuiteFinish(id, fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void StartSuiteCase(string id, string parentId, string flowId, string fullName)
|
||||
{
|
||||
// NUnit 3 case
|
||||
if (parentId == string.Empty)
|
||||
{
|
||||
OnRootSuiteStart(flowId, fullName);
|
||||
}
|
||||
|
||||
// NUnit 2 case
|
||||
if (parentId == null)
|
||||
{
|
||||
if (_blockCounter++ == 0)
|
||||
{
|
||||
_rootFlowId = id;
|
||||
OnRootSuiteStart(id, fullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryFindParentId(string id, out string parentId)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
return _refs.TryGetValue(id, out parentId) && !string.IsNullOrEmpty(parentId);
|
||||
}
|
||||
|
||||
private bool TryFindRootId(string id, out string rootId)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
while (TryFindParentId(id, out rootId) && id != rootId)
|
||||
{
|
||||
id = rootId;
|
||||
}
|
||||
|
||||
rootId = id;
|
||||
return !string.IsNullOrEmpty(id);
|
||||
}
|
||||
|
||||
private void TrySendOutput(string flowId, XmlNode message, string fullName)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
var output = message.SelectSingleNode("output");
|
||||
if (output == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var outputStr = output.InnerText;
|
||||
if (string.IsNullOrEmpty(outputStr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLine("##teamcity[testStdOut name='{0}' out='{1}' flowId='{2}']", fullName, outputStr, flowId);
|
||||
}
|
||||
|
||||
private void OnRootSuiteStart(string flowId, string assemblyName)
|
||||
{
|
||||
assemblyName = Path.GetFileName(assemblyName);
|
||||
WriteLine("##teamcity[testSuiteStarted name='{0}' flowId='{1}']", assemblyName, flowId);
|
||||
}
|
||||
|
||||
private void OnRootSuiteFinish(string flowId, string assemblyName)
|
||||
{
|
||||
assemblyName = Path.GetFileName(assemblyName);
|
||||
WriteLine("##teamcity[testSuiteFinished name='{0}' flowId='{1}']", assemblyName, flowId);
|
||||
}
|
||||
|
||||
private void OnFlowStarted(string flowId, string parentFlowId)
|
||||
{
|
||||
WriteLine("##teamcity[flowStarted flowId='{0}' parent='{1}']", flowId, parentFlowId);
|
||||
}
|
||||
|
||||
private void OnFlowFinished(string flowId)
|
||||
{
|
||||
WriteLine("##teamcity[flowFinished flowId='{0}']", flowId);
|
||||
}
|
||||
|
||||
private void OnTestStart(string flowId, string fullName)
|
||||
{
|
||||
WriteLine("##teamcity[testStarted name='{0}' captureStandardOutput='false' flowId='{1}']", fullName, flowId);
|
||||
}
|
||||
|
||||
private void OnTestFinished(string flowId, XmlNode message, string fullName)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
var durationStr = message.GetAttribute("duration");
|
||||
double durationDecimal;
|
||||
int durationMilliseconds = 0;
|
||||
if (durationStr != null && double.TryParse(durationStr, NumberStyles.Any, CultureInfo.InvariantCulture, out durationDecimal))
|
||||
{
|
||||
durationMilliseconds = (int)(durationDecimal * 1000d);
|
||||
}
|
||||
|
||||
TrySendOutput(flowId, message, fullName);
|
||||
WriteLine(
|
||||
"##teamcity[testFinished name='{0}' duration='{1}' flowId='{2}']",
|
||||
fullName,
|
||||
durationMilliseconds.ToString(),
|
||||
flowId);
|
||||
}
|
||||
|
||||
private void OnTestFailed(string flowId, XmlNode message, string fullName)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
var errorMmessage = message.SelectSingleNode("failure/message");
|
||||
var stackTrace = message.SelectSingleNode("failure/stack-trace");
|
||||
WriteLine(
|
||||
"##teamcity[testFailed name='{0}' message='{1}' details='{2}' flowId='{3}']",
|
||||
fullName,
|
||||
errorMmessage == null ? string.Empty : errorMmessage.InnerText,
|
||||
stackTrace == null ? string.Empty : stackTrace.InnerText,
|
||||
flowId);
|
||||
|
||||
OnTestFinished(flowId, message, fullName);
|
||||
}
|
||||
|
||||
private void OnTestSkipped(string flowId, XmlNode message, string fullName)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
TrySendOutput(flowId, message, fullName);
|
||||
var reason = message.SelectSingleNode("reason/message");
|
||||
WriteLine(
|
||||
"##teamcity[testIgnored name='{0}' message='{1}' flowId='{2}']",
|
||||
fullName,
|
||||
reason == null ? string.Empty : reason.InnerText,
|
||||
flowId);
|
||||
}
|
||||
|
||||
private void OnTestInconclusive(string flowId, XmlNode message, string fullName)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
throw new ArgumentNullException("message");
|
||||
}
|
||||
|
||||
TrySendOutput(flowId, message, fullName);
|
||||
WriteLine(
|
||||
"##teamcity[testIgnored name='{0}' message='{1}' flowId='{2}']",
|
||||
fullName,
|
||||
"Inconclusive",
|
||||
flowId);
|
||||
}
|
||||
|
||||
private void WriteLine(string format, params string[] arg)
|
||||
{
|
||||
if (format == null)
|
||||
{
|
||||
throw new ArgumentNullException("format");
|
||||
}
|
||||
|
||||
if (arg == null)
|
||||
{
|
||||
throw new ArgumentNullException("arg");
|
||||
}
|
||||
|
||||
var argObjects = new object[arg.Length];
|
||||
for (var i = 0; i < arg.Length; i++)
|
||||
{
|
||||
var str = arg[i];
|
||||
if (str != null)
|
||||
{
|
||||
str = Escape(str);
|
||||
}
|
||||
|
||||
argObjects[i] = str;
|
||||
}
|
||||
|
||||
var message = string.Format(format, argObjects);
|
||||
_outWriter.WriteLine(message);
|
||||
}
|
||||
|
||||
private static string Escape(string input)
|
||||
{
|
||||
return input != null
|
||||
? input.Replace("|", "||")
|
||||
.Replace("'", "|'")
|
||||
.Replace("\n", "|n")
|
||||
.Replace("\r", "|r")
|
||||
.Replace(char.ConvertFromUtf32(int.Parse("0086", NumberStyles.HexNumber)), "|x")
|
||||
.Replace(char.ConvertFromUtf32(int.Parse("2028", NumberStyles.HexNumber)), "|l")
|
||||
.Replace(char.ConvertFromUtf32(int.Parse("2029", NumberStyles.HexNumber)), "|p")
|
||||
.Replace("[", "|[")
|
||||
.Replace("]", "|]")
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit.Engine.Api" version="3.5.0-CI-3064-master" targetFramework="net20" />
|
||||
</packages>
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A867079B-A172-4DAE-9549-63B331092A23}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>teamcity_event_listener</RootNamespace>
|
||||
<AssemblyName>teamcity-event-listener</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\..\..\..\bin\Debug\addins\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\..\..\bin\Release\addins\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.engine.api, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NUnit.Engine.Api.3.5.0-CI-3064-master\lib\nunit.engine.api.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TeamCityEventListener.cs" />
|
||||
<Compile Include="SafeAttributeAccess.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("tests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("709aff60-b15e-43d5-830a-f22e4701d8b8")]
|
||||
|
||||
// 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:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,479 @@
|
|||
// ***********************************************************************
|
||||
// Copyright (c) 2015 Charlie Poole
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
// ***********************************************************************
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Engine.Extensibility;
|
||||
|
||||
namespace NUnit.Engine.Listeners
|
||||
{
|
||||
using System;
|
||||
|
||||
[TestFixture]
|
||||
|
||||
public class TeamCityEventListenerTests
|
||||
{
|
||||
private StringBuilder _output;
|
||||
private StringWriter _outputWriter;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_output = new StringBuilder();
|
||||
_outputWriter = new StringWriter(_output);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_outputWriter.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckExtensionAttribute()
|
||||
{
|
||||
Assert.That(typeof(TeamCityEventListener),
|
||||
Has.Attribute<ExtensionAttribute>()
|
||||
.With.Property("EngineVersion").EqualTo("3.4")
|
||||
.And.Property("Enabled").EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenParallelizedTestsFromNUnit3()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", "", "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-2", "1-1", "Assembly1.Namespace1"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-3", "1-2", "Assembly1.Namespace1.1"));
|
||||
|
||||
// Assembly 2
|
||||
publisher.RegisterMessage(CreateStartSuite("1-6", "", "ddd" + Path.DirectorySeparatorChar + "Assembly2"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-7", "1-6", "Assembly2.Namespace2"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-4", "1-3", "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-4", "1-3", "Assembly1.Namespace1.1.Test1", "0.1", "Text output"));
|
||||
|
||||
// Test Assembly2.Namespace2.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-8", "1-7", "Assembly2.Namespace2.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-8", "1-8", "Assembly2.Namespace2.1.Test1", "0.1", "Text output"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test2
|
||||
publisher.RegisterMessage(CreateStartTest("1-5", "1-3", "Assembly1.Namespace1.1.Test2"));
|
||||
publisher.RegisterMessage(CreateTestCaseFailed("1-5", "1-3", "Assembly1.Namespace1.1.Test2", "0.2", "Error output", "Stack trace"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-7", "1-6", "Assembly2.Namespace2"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-6", "", "Assembly2"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-3", "1-2", "Assembly1.Namespace1.1"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-2", "1-1", "Assembly1.Namespace1"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", "", "Assembly1"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testSuiteStarted name='Assembly2' flowId='1-6']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[flowStarted flowId='1-4' parent='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-4']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-4']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-4']" + Environment.NewLine
|
||||
+ "##teamcity[flowFinished flowId='1-4']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[flowStarted flowId='1-8' parent='1-6']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly2.Namespace2.1.Test1' captureStandardOutput='false' flowId='1-8']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly2.Namespace2.1.Test1' out='Text output' flowId='1-8']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly2.Namespace2.1.Test1' duration='100' flowId='1-8']" + Environment.NewLine
|
||||
+ "##teamcity[flowFinished flowId='1-8']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[flowStarted flowId='1-5' parent='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test2' captureStandardOutput='false' flowId='1-5']" + Environment.NewLine
|
||||
+ "##teamcity[testFailed name='Assembly1.Namespace1.1.Test2' message='Error output' details='Stack trace' flowId='1-5']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test2' duration='200' flowId='1-5']" + Environment.NewLine
|
||||
+ "##teamcity[flowFinished flowId='1-5']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly2' flowId='1-6']" + Environment.NewLine
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenHas1SuiteFromNUnit3()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", "", "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-2", "1-1", "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-2", "1-1", "Assembly1.Namespace1.1.Test1", "0.1", "Text output"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", "", "Assembly1"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[flowStarted flowId='1-2' parent='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[flowFinished flowId='1-2']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenHasNoSuiteFromNUnit3()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-1", "", "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-1", "", "Assembly1.Namespace1.1.Test1", "0.1", "Text output"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenOutputIsEmpty()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-1", "", "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-1", "", "Assembly1.Namespace1.1.Test1", "0.1", ""));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenHasNoOutput()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-1", "", "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-1", "", "Assembly1.Namespace1.1.Test1", "10", null));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='10000' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenTestsFromNUnit2()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", null, "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-2", null, "Assembly1.Namespace1"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-3", null, "Assembly1.Namespace1.1"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-4", null, "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-4", null, "Assembly1.Namespace1.1.Test1", "0.1", "Text output"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test2
|
||||
publisher.RegisterMessage(CreateStartTest("1-5", null, "Assembly1.Namespace1.1.Test2"));
|
||||
publisher.RegisterMessage(CreateTestCaseFailed("1-5", null, "Assembly1.Namespace1.1.Test2", "0.2", "Error output", "Stack trace"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-3", null, "Assembly1.Namespace1.1"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-2", null, "Assembly1.Namespace1"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", null, "Assembly1"));
|
||||
|
||||
// Assembly 2
|
||||
publisher.RegisterMessage(CreateStartSuite("1-6", null, "ddd//Assembly2"));
|
||||
publisher.RegisterMessage(CreateStartSuite("1-7", null, "Assembly2.Namespace2"));
|
||||
|
||||
// Test Assembly2.Namespace2.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-8", null, "Assembly2.Namespace2.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-8", null, "Assembly2.Namespace2.1.Test1", "0.3", "Text output"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-7", null, "Assembly2.Namespace2"));
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-6", null, "Assembly2"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test2' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFailed name='Assembly1.Namespace1.1.Test2' message='Error output' details='Stack trace' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test2' duration='200' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteStarted name='Assembly2' flowId='1-6']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly2.Namespace2.1.Test1' captureStandardOutput='false' flowId='1-6']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly2.Namespace2.1.Test1' out='Text output' flowId='1-6']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly2.Namespace2.1.Test1' duration='300' flowId='1-6']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly2' flowId='1-6']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenHas1SuiteFromNUnit2()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", null, "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-2", null, "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-2", null, "Assembly1.Namespace1.1.Test1", "1.3", "Text output"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", null, "Assembly1"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='1300' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWithValidFlowIdWhenHasNoSuiteFromNUnit2()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateStartTest("1-1", null, "Assembly1.Namespace1.1.Test1"));
|
||||
publisher.RegisterMessage(CreateTestCaseSuccessful("1-1", null, "Assembly1.Namespace1.1.Test1", "0.1", "Text output"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStdOut name='Assembly1.Namespace1.1.Test1' out='Text output' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWhenOneTimeSetUpFailedFromNUnit3()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", "", "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateTestCaseFailed("1-2", "1-1", "Assembly1.Namespace1.1.Test1", "0.1", "Error output", "Stack trace"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", "", "Assembly1"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[flowStarted flowId='1-2' parent='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[testFailed name='Assembly1.Namespace1.1.Test1' message='Error output' details='Stack trace' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-2']" + Environment.NewLine
|
||||
+ "##teamcity[flowFinished flowId='1-2']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldSendMessagesWhenOneTimeSetUpFailedFromNUnit2()
|
||||
{
|
||||
// Given
|
||||
var publisher = CreateInstance();
|
||||
|
||||
// When
|
||||
publisher.RegisterMessage(CreateStartRun(1));
|
||||
|
||||
// Assembly 1
|
||||
publisher.RegisterMessage(CreateStartSuite("1-1", null, "aaa" + Path.DirectorySeparatorChar + "Assembly1"));
|
||||
|
||||
// Test Assembly1.Namespace1.1.Test1
|
||||
publisher.RegisterMessage(CreateTestCaseFailed("1-2", null, "Assembly1.Namespace1.1.Test1", "0.1", "Error output", "Stack trace"));
|
||||
|
||||
publisher.RegisterMessage(CreateFinishSuite("1-1", null, "Assembly1"));
|
||||
|
||||
publisher.RegisterMessage(CreateTestRun());
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(
|
||||
"##teamcity[testSuiteStarted name='Assembly1' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testStarted name='Assembly1.Namespace1.1.Test1' captureStandardOutput='false' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFailed name='Assembly1.Namespace1.1.Test1' message='Error output' details='Stack trace' flowId='1-1']" + Environment.NewLine
|
||||
+ "##teamcity[testFinished name='Assembly1.Namespace1.1.Test1' duration='100' flowId='1-1']" + Environment.NewLine
|
||||
|
||||
+ "##teamcity[testSuiteFinished name='Assembly1' flowId='1-1']" + Environment.NewLine,
|
||||
_output.ToString());
|
||||
}
|
||||
|
||||
private static XmlNode CreateMessage(string text)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml(text);
|
||||
return doc.FirstChild;
|
||||
}
|
||||
|
||||
private static XmlNode CreateStartRun(int count)
|
||||
{
|
||||
return CreateMessage(string.Format("<start-run count='{0}'/>", count));
|
||||
}
|
||||
|
||||
private static XmlNode CreateTestRun()
|
||||
{
|
||||
return CreateMessage("<test-run></test-run>");
|
||||
}
|
||||
|
||||
private static XmlNode CreateStartSuite(string id, string parentId, string name)
|
||||
{
|
||||
return CreateMessage(string.Format("<start-suite id=\"{0}\" {1} name=\"{2}\" fullname=\"{2}\"/>", id, GetNamedAttr("parentId", parentId), name));
|
||||
}
|
||||
|
||||
private static XmlNode CreateFinishSuite(string id, string parentId, string name)
|
||||
{
|
||||
return CreateMessage(string.Format("<test-suite id=\"{0}\" {1} name=\"{2}\" fullname=\"{2}\" runstate=\"Runnable\" testcasecount=\"3\" result=\"Failed\" duration=\"0.251125\" total=\"3\" passed=\"0\" failed=\"0\" inconclusive=\"0\" skipped=\"0\" asserts=\"0\"><failure><message><![CDATA[One or more child tests had errors]]></message></failure></test-suite>", id, GetNamedAttr("parentId", parentId), name));
|
||||
}
|
||||
|
||||
private static XmlNode CreateStartTest(string id, string parentId, string name)
|
||||
{
|
||||
return CreateMessage(string.Format("<start-test id=\"{0}\" {1} name=\"{2}\" fullname=\"{2}\"/>", id, GetNamedAttr("parentId", parentId), name));
|
||||
}
|
||||
|
||||
private static XmlNode CreateTestCaseSuccessful(string id, string parentId, string name, string duration, string output)
|
||||
{
|
||||
return CreateMessage(string.Format("<test-case id=\"{0}\" {1} name=\"{2}\" fullname=\"{2}\" runstate=\"Runnable\" result=\"Passed\" duration=\"{3}\" asserts=\"0\">{4}</test-case>", id, GetNamedAttr("parentId", parentId), name, duration, GetNamedElement("output", output)));
|
||||
}
|
||||
|
||||
private static XmlNode CreateTestCaseFailed(string id, string parentId, string name, string duration, string message, string stackTrace)
|
||||
{
|
||||
return CreateMessage(string.Format("<test-case id=\"{0}\" {1} name=\"{2}\" fullname=\"{2}\" runstate=\"Runnable\" result=\"Failed\" duration=\"{3}\" asserts=\"0\"><properties><property name=\"_CATEGORIES\" value=\"F\" /></properties><failure><message><![CDATA[{4}]]></message><stack-trace><![CDATA[{5}]]></stack-trace></failure></test-case>", id, GetNamedAttr("parentId", parentId), name, duration, message, stackTrace));
|
||||
}
|
||||
|
||||
private TeamCityEventListener CreateInstance()
|
||||
{
|
||||
return new TeamCityEventListener(_outputWriter);
|
||||
}
|
||||
|
||||
private static string GetNamedAttr(string attrName, string attrValue)
|
||||
{
|
||||
if (attrValue == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Format("{0}=\"{1}\"", attrName, attrValue);
|
||||
}
|
||||
|
||||
private static string GetNamedElement(string elementName, string elementValue)
|
||||
{
|
||||
if (elementValue == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return string.Format("<{0}>{1}</{0}>", elementName, elementValue);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="3.4.1" targetFramework="net20" />
|
||||
<package id="NUnit.Engine.Api" version="3.5.0-CI-3064-master" targetFramework="net20" />
|
||||
</packages>
|
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{709AFF60-B15E-43D5-830A-F22E4701D8B8}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>tests</RootNamespace>
|
||||
<AssemblyName>tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.engine.api, Version=3.0.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NUnit.Engine.Api.3.5.0-CI-3064-master\lib\nunit.engine.api.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.4.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NUnit.3.4.1\lib\net20\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NUnit.System.Linq, Version=0.2.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NUnit.3.4.1\lib\net20\NUnit.System.Linq.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TeamCityEventListenerTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\extension\teamcity-event-listener.csproj">
|
||||
<Project>{a867079b-a172-4dae-9549-63b331092a23}</Project>
|
||||
<Name>teamcity-event-listener</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "teamcity-event-listener", "src\extension\teamcity-event-listener.csproj", "{A867079B-A172-4DAE-9549-63B331092A23}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "teamcity-event-listener.tests", "src\tests\teamcity-event-listener.tests.csproj", "{709AFF60-B15E-43D5-830A-F22E4701D8B8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A867079B-A172-4DAE-9549-63B331092A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A867079B-A172-4DAE-9549-63B331092A23}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A867079B-A172-4DAE-9549-63B331092A23}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A867079B-A172-4DAE-9549-63B331092A23}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{709AFF60-B15E-43D5-830A-F22E4701D8B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{709AFF60-B15E-43D5-830A-F22E4701D8B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{709AFF60-B15E-43D5-830A-F22E4701D8B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{709AFF60-B15E-43D5-830A-F22E4701D8B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
Загрузка…
Ссылка в новой задаче