1 // ***********************************************************************
2 // Copyright (c) 2014 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
29 #if !SILVERLIGHT && !NETCF
31 using System.Globalization;
33 using NUnit.Framework.Interfaces;
38 /// TeamCityEventListener class handles ITestListener events
39 /// by issuing TeamCity service messages on the Console.
41 public class TeamCityEventListener : ITestListener
43 readonly TextWriter _outWriter;
46 /// Default constructor using Console.Out
49 /// This constructor must be called before Console.Out is
50 /// redirected in order to work correctly under TeamCity.
52 public TeamCityEventListener() : this(Console.Out) { }
55 /// Construct a TeamCityEventListener specifying a TextWriter. Used for testing.
57 /// <param name="outWriter">The TextWriter to receive normal messages.</param>
58 public TeamCityEventListener(TextWriter outWriter)
60 _outWriter = outWriter;
64 /// Called when a test has just started
66 /// <param name="test">The test that is starting</param>
67 public void TestStarted(ITest test)
70 TC_TestSuiteStarted(test.Name);
72 TC_TestStarted(test.Name);
76 /// Called when a test has finished
78 /// <param name="result">The result of the test</param>
79 public void TestFinished(ITestResult result)
81 string testName = result.Test.Name;
83 if (result.Test.IsSuite)
84 TC_TestSuiteFinished(testName);
86 switch (result.ResultState.Status)
88 case TestStatus.Passed:
89 TC_TestFinished(testName, result.Duration);
91 case TestStatus.Inconclusive:
92 TC_TestIgnored(testName, "Inconclusive");
94 case TestStatus.Skipped:
95 TC_TestIgnored(testName, result.Message);
97 case TestStatus.Failed:
98 TC_TestFailed(testName, result.Message, result.StackTrace);
99 TC_TestFinished(testName, result.Duration);
105 /// Called when a test produces output for immediate display
107 /// <param name="output">A TestOutput object containing the text to display</param>
108 public void TestOutput(TestOutput output) { }
110 #region Helper Methods
112 private void TC_TestSuiteStarted(string name)
114 _outWriter.WriteLine("##teamcity[testSuiteStarted name='{0}']", Escape(name));
117 private void TC_TestSuiteFinished(string name)
119 _outWriter.WriteLine("##teamcity[testSuiteFinished name='{0}']", Escape(name));
122 private void TC_TestStarted(string name)
124 _outWriter.WriteLine("##teamcity[testStarted name='{0}' captureStandardOutput='true']", Escape(name));
127 private void TC_TestFinished(string name, double duration)
129 // TeamCity expects the duration to be in milliseconds
130 int milliseconds = (int)(duration * 1000d);
131 _outWriter.WriteLine("##teamcity[testFinished name='{0}' duration='{1}']", Escape(name), milliseconds);
134 private void TC_TestIgnored(string name, string reason)
136 _outWriter.WriteLine("##teamcity[testIgnored name='{0}' message='{1}']", Escape(name), Escape(reason));
139 private void TC_TestFailed(string name, string message, string details)
141 _outWriter.WriteLine("##teamcity[testFailed name='{0}' message='{1}' details='{2}']", Escape(name), Escape(message), Escape(details));
144 private static string Escape(string input)
147 ? input.Replace("|", "||")
151 .Replace(char.ConvertFromUtf32(int.Parse("0086", NumberStyles.HexNumber)), "|x")
152 .Replace(char.ConvertFromUtf32(int.Parse("2028", NumberStyles.HexNumber)), "|l")
153 .Replace(char.ConvertFromUtf32(int.Parse("2029", NumberStyles.HexNumber)), "|p")