From 893d2d0af5d24d559214183fe86119d0ccc485c1 Mon Sep 17 00:00:00 2001 From: NikolayPianikov Date: Mon, 10 Dec 2018 17:36:14 +0300 Subject: [PATCH] #25 Support test metadata --- .../Write/Specials/TeamCityTestWriterTest.cs | 48 +++++++++++++++++ .../Write/Specials/TeamCityWriterBaseTest.cs | 4 +- TeamCity.ServiceMessages.sln.DotSettings | 2 + .../Write/Special/ITeamCityArtifactsWriter.cs | 4 +- .../Write/Special/ITeamCityBlockWriter.cs | 2 +- .../Special/ITeamCityBuildStatusWriter.cs | 6 +-- .../ITeamCityCompilationBlockWriter.cs | 2 +- .../Write/Special/ITeamCityMessageWriter.cs | 2 +- .../Write/Special/ITeamCityTestWriter.cs | 37 ++++++++++++- .../Special/Impl/TeamCityWriterFacade.cs | 1 - .../Impl/Updater/FlowMessageUpdater.cs | 14 ++--- .../Special/Impl/Updater/TimestampUpdater.cs | 5 +- .../Special/Impl/Writer/TeamCityTestWriter.cs | 54 +++++++++++++++++-- 13 files changed, 151 insertions(+), 30 deletions(-) diff --git a/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityTestWriterTest.cs b/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityTestWriterTest.cs index bae5093..3e8254a 100644 --- a/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityTestWriterTest.cs +++ b/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityTestWriterTest.cs @@ -75,5 +75,53 @@ public void TestStdOut() { DoTest(x => x.WriteStdOutput("outp4uz"), "##teamcity[testStdOut name='BadaBumBigBadaBum' out='outp4uz' tc:tags='tc:parseServiceMessagesInside']"); } + + [Test] + public void TestWriteTextValue() + { + DoTest(x => x.WriteValue("strVal", "myVal"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' value='strVal' name='myVal']"); + } + + [Test] + [TestCase(1.0d, "1")] + [TestCase(0.0d, "0")] + [TestCase(-1.0d, "-1")] + [TestCase(1.33d, "1.33")] + [TestCase(-1.33d, "-1.33")] + [TestCase(0.33d, "0.33")] + public void TestWriteNumber(double value, string expectedValueInMessage) + { + DoTest(x => x.WriteValue(value, "myVal"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='number' value='" + expectedValueInMessage + "' name='myVal']"); + } + + [Test] + public void TestWriteLink() + { + DoTest(x => x.WriteLink("http://abc.com", "abc"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='link' value='http://abc.com' name='abc']"); + } + + [Test] + public void TestWriteFile() + { + DoTest(x => x.WriteFile("abc.txt", "abc"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='artifact' value='abc.txt' name='abc']"); + } + + [Test] + public void TestWriteFileWithoutDescription() + { + DoTest(x => x.WriteFile("abc.txt"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='artifact' value='abc.txt']"); + } + + [Test] + public void TestWriteImage() + { + DoTest(x => x.WriteImage("abc.jpg", "abc"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='image' value='abc.jpg' name='abc']"); + } + + [Test] + public void TestWriteImageWithoutDescription() + { + DoTest(x => x.WriteImage("abc.jpg"), "##teamcity[testMetadata testName='BadaBumBigBadaBum' type='image' value='abc.jpg']"); + } } } \ No newline at end of file diff --git a/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityWriterBaseTest.cs b/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityWriterBaseTest.cs index af946f0..f2a9ca6 100644 --- a/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityWriterBaseTest.cs +++ b/TeamCity.ServiceMessages.Tests/Write/Specials/TeamCityWriterBaseTest.cs @@ -63,11 +63,11 @@ private void DoTestImpl(Action action, Func replace, string[] var actualText = "\r\n" + string.Join("\r\n", actual); var expected = preprocess(string.Join("\r\n", golds)); - if (actual.Count() != expected.Count()) + if (actual.Length != expected.Length) Assert.Fail("Incorrect number of messages. Was: " + actualText); for (var i = 0; i < actual.Count(); i++) - Assert.AreEqual(actual[i], expected[i], "Message {0} does not match. Was: {1}", i, actualText); + Assert.AreEqual(expected[i], actual[i], "Message {0} does not match. Was: {1}", i, actualText); } protected class ToStringProcessor : IServiceMessageProcessor diff --git a/TeamCity.ServiceMessages.sln.DotSettings b/TeamCity.ServiceMessages.sln.DotSettings index 1355e50..5ffefd7 100644 --- a/TeamCity.ServiceMessages.sln.DotSettings +++ b/TeamCity.ServiceMessages.sln.DotSettings @@ -180,6 +180,8 @@ </Patterns> True <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + True + True True True True diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityArtifactsWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityArtifactsWriter.cs index 54d1ad3..05f159e 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityArtifactsWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityArtifactsWriter.cs @@ -18,7 +18,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special { /// /// Service messages for dynamically publish artifacts. - /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-PublishingArtifactswhiletheBuildisStillinProgress + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-PublishingArtifactswhiletheBuildisStillinProgress /// /// /// Implementation is not thread-safe. Create an instance for each thread instead. @@ -27,7 +27,7 @@ public interface ITeamCityArtifactsWriter { /// /// attaches new artifact publishing rules as described in - /// http://confluence.jetbrains.net/display/TCD7/Build+Artifact + /// http://confluence.jetbrains.net/display/TCD18/Build+Artifact /// /// void PublishArtifact([NotNull] string rules); diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityBlockWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityBlockWriter.cs index 1711999..7e25295 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityBlockWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityBlockWriter.cs @@ -23,7 +23,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special ///
##teamcity[blockOpened name='<blockName>']
/// and ///
##teamcity[blockClosed name='<blockName>']
- /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-BlocksofServiceMessages + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-BlocksofServiceMessages /// /// /// Implementation is not thread-safe. Create an instance for each thread instead. diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityBuildStatusWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityBuildStatusWriter.cs index 9a802ec..f421bcd 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityBuildStatusWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityBuildStatusWriter.cs @@ -18,7 +18,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special { /// /// Interface for writing build-related messages - /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildNumber + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingBuildNumber /// /// /// Implementation is not thread-safe. Create an instance for each thread instead. @@ -41,7 +41,7 @@ public interface ITeamCityBuildStatusWriter /// /// Generates service message to update build parameter - /// http://confluence.jetbrains.net/display/TCD7/Configuring+Build+Parameters + /// http://confluence.jetbrains.net/display/TCD18/Configuring+Build+Parameters /// /// /// parameter name, could start with env. for environment, system. for system property, @@ -53,7 +53,7 @@ public interface ITeamCityBuildStatusWriter /// /// Generates service message to report build statistics values - /// http://confluence.jetbrains.net/display/TCD7/Customizing+Statistics+Charts#CustomizingStatisticsCharts-customCharts + /// http://confluence.jetbrains.net/display/TCD18/Customizing+Statistics+Charts#CustomizingStatisticsCharts-customCharts /// /// statistics report key /// statistics report values diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityCompilationBlockWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityCompilationBlockWriter.cs index e5f64ca..10438c9 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityCompilationBlockWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityCompilationBlockWriter.cs @@ -23,7 +23,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special ///
##teamcity[compilationStarted compiler='<compiler name>']
/// and ///
##teamcity[compilationFinished compiler='<compiler name>']
- /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingCompilationMessages + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingCompilationMessages /// /// /// Implementation is not thread-safe. Create an instance for each thread instead. diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityMessageWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityMessageWriter.cs index e95c15c..f10cbd2 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityMessageWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityMessageWriter.cs @@ -21,7 +21,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special ///
     ///         ##teamcity[message text='<message text>' errorDetails='<error details>' status='<status value>']
     ///     
- /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingMessagesForBuildLog + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ReportingMessagesForBuildLog /// /// /// Implementation is not thread-safe. Create an instance for each thread instead. diff --git a/TeamCity.ServiceMessages/Write/Special/ITeamCityTestWriter.cs b/TeamCity.ServiceMessages/Write/Special/ITeamCityTestWriter.cs index b783704..a0cc602 100644 --- a/TeamCity.ServiceMessages/Write/Special/ITeamCityTestWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/ITeamCityTestWriter.cs @@ -60,9 +60,44 @@ public interface ITeamCityTestWriter : IDisposable /// Specifies test duration /// /// - /// TeamCity may compute test duration inself, to provide precise data, you may set the duration explicitly + /// TeamCity may compute test duration itself, to provide precise data, you may set the duration explicitly /// /// time of test void WriteDuration(TimeSpan duration); + + /// + /// Writes image as test metadata. Supported in TeamCity 2018.2+ https://confluence.jetbrains.com/display/TCD18/Reporting+Test+Metadata. + /// + /// The URI to the image in the build artifacts directory. Should be relative to the build artifacts directory. + /// The image description. When showing images, TeamCity shows both the ‘description’ and the filename of the referenced image. + void WriteImage([NotNull] string teamCityArtifactUri, [NotNull] string description = ""); + + /// + /// Writes file as test metadata. Supported in TeamCity 2018.2+ https://confluence.jetbrains.com/display/TCD18/Reporting+Test+Metadata. + /// + /// The URI to the file in the build artifacts directory. Should be relative to the build artifacts directory. + /// The file description. TeamCity shows both the ‘description’ and the filename of the referenced file. If a description was autogenerated, it is not shown. + void WriteFile([NotNull] string teamCityArtifactUri, [NotNull] string description = ""); + + /// + /// Writes number as test metadata. Supported in TeamCity 2018.2+ https://confluence.jetbrains.com/display/TCD18/Reporting+Test+Metadata. + /// + /// The metadata value. + /// The metadata name. + void WriteValue([NotNull] double value, [NotNull] string name); + + /// + /// Writes string value as test metadata. Supported in TeamCity 2018.2+ https://confluence.jetbrains.com/display/TCD18/Reporting+Test+Metadata. + /// + /// The metadata value. + /// The metadata name. + void WriteValue([NotNull] string value, [NotNull] string name); + + /// + /// Writes link as test metadata. Supported in TeamCity 2018.2+ https://confluence.jetbrains.com/display/TCD18/Reporting+Test+Metadata. + /// + /// The metadata URI. + /// The metadata name. + void WriteLink([NotNull] string linkUri, [NotNull] string name); } } \ No newline at end of file diff --git a/TeamCity.ServiceMessages/Write/Special/Impl/TeamCityWriterFacade.cs b/TeamCity.ServiceMessages/Write/Special/Impl/TeamCityWriterFacade.cs index a59b733..e8e18d5 100644 --- a/TeamCity.ServiceMessages/Write/Special/Impl/TeamCityWriterFacade.cs +++ b/TeamCity.ServiceMessages/Write/Special/Impl/TeamCityWriterFacade.cs @@ -129,7 +129,6 @@ public virtual void Dispose() public void PublishArtifact(string rules) { if (rules == null) throw new ArgumentNullException(nameof(rules)); - CheckConsistency(); _artifactsWriter.PublishArtifact(rules); } diff --git a/TeamCity.ServiceMessages/Write/Special/Impl/Updater/FlowMessageUpdater.cs b/TeamCity.ServiceMessages/Write/Special/Impl/Updater/FlowMessageUpdater.cs index 0ddaa86..ebe84c9 100644 --- a/TeamCity.ServiceMessages/Write/Special/Impl/Updater/FlowMessageUpdater.cs +++ b/TeamCity.ServiceMessages/Write/Special/Impl/Updater/FlowMessageUpdater.cs @@ -20,20 +20,17 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special.Impl.Updater /// /// Service message updater that adds FlowId to service message according to - /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity /// public class FlowMessageUpdater : IServiceMessageUpdater { - private readonly IFlowIdGenerator _flowId; - /// - /// Custructs updater + /// Constructs updater /// /// flowId set to all messages public FlowMessageUpdater([NotNull] string flowId) { - if (flowId == null) throw new ArgumentNullException(nameof(flowId)); - FlowId = flowId; + FlowId = flowId ?? throw new ArgumentNullException(nameof(flowId)); } /// @@ -42,16 +39,13 @@ public FlowMessageUpdater([NotNull] string flowId) /// public FlowMessageUpdater([NotNull] IFlowIdGenerator flowId) : this(flowId.NewFlowId()) { - if (flowId == null) throw new ArgumentNullException(nameof(flowId)); - _flowId = flowId; } public string FlowId { [NotNull] get; } public IServiceMessage UpdateServiceMessage(IServiceMessage message) { - if (message.DefaultValue != null) return message; - return new PatchedServiceMessage(message) {{"flowId", FlowId}}; + return message.DefaultValue != null ? message : new PatchedServiceMessage(message) {{"flowId", FlowId}}; } } } \ No newline at end of file diff --git a/TeamCity.ServiceMessages/Write/Special/Impl/Updater/TimestampUpdater.cs b/TeamCity.ServiceMessages/Write/Special/Impl/Updater/TimestampUpdater.cs index d5c984e..570681d 100644 --- a/TeamCity.ServiceMessages/Write/Special/Impl/Updater/TimestampUpdater.cs +++ b/TeamCity.ServiceMessages/Write/Special/Impl/Updater/TimestampUpdater.cs @@ -21,7 +21,7 @@ namespace JetBrains.TeamCity.ServiceMessages.Write.Special.Impl.Updater /// /// Service message updater that adds Timestamp to service message according to - /// http://confluence.jetbrains.net/display/TCD7/Build+Script+Interaction+with+TeamCity + /// http://confluence.jetbrains.net/display/TCD18/Build+Script+Interaction+with+TeamCity /// public class TimestampUpdater : IServiceMessageUpdater { @@ -29,8 +29,7 @@ public class TimestampUpdater : IServiceMessageUpdater public TimestampUpdater([NotNull] Func timeService) { - if (timeService == null) throw new ArgumentNullException(nameof(timeService)); - _timeService = timeService; + _timeService = timeService ?? throw new ArgumentNullException(nameof(timeService)); } public IServiceMessage UpdateServiceMessage(IServiceMessage message) diff --git a/TeamCity.ServiceMessages/Write/Special/Impl/Writer/TeamCityTestWriter.cs b/TeamCity.ServiceMessages/Write/Special/Impl/Writer/TeamCityTestWriter.cs index 2e1bd5b..dee3047 100644 --- a/TeamCity.ServiceMessages/Write/Special/Impl/Writer/TeamCityTestWriter.cs +++ b/TeamCity.ServiceMessages/Write/Special/Impl/Writer/TeamCityTestWriter.cs @@ -24,13 +24,12 @@ public class TeamCityTestWriter : BaseDisposableWriter private readonly string _testName; private TimeSpan? _duration; - public TeamCityTestWriter([NotNull] IServiceMessageProcessor target, [NotNull] string testName, [NotNull] IDisposable disposableHander) - : base(target, disposableHander) + public TeamCityTestWriter([NotNull] IServiceMessageProcessor target, [NotNull] string testName, [NotNull] IDisposable disposableHandler) + : base(target, disposableHandler) { if (target == null) throw new ArgumentNullException(nameof(target)); - if (testName == null) throw new ArgumentNullException(nameof(testName)); - if (disposableHander == null) throw new ArgumentNullException(nameof(disposableHander)); - _testName = testName; + if (disposableHandler == null) throw new ArgumentNullException(nameof(disposableHandler)); + _testName = testName ?? throw new ArgumentNullException(nameof(testName)); } public void WriteStdOutput(string text) @@ -70,6 +69,51 @@ public void WriteDuration(TimeSpan span) _duration = span; } + public void WriteImage(string teamCityArtifactUri, string description = "") + { + if (string.IsNullOrEmpty(teamCityArtifactUri)) throw new ArgumentException(nameof(teamCityArtifactUri)); + if (description == null) throw new ArgumentNullException(nameof(description)); + var message = new ServiceMessage("testMetadata") {{"testName", _testName}, {"type", "image"}, {"value", teamCityArtifactUri}}; + if (!string.IsNullOrEmpty(description)) + { + message.Add("name", description); + } + + PostMessage(message); + } + + public void WriteFile(string teamCityArtifactUri, string description = "") + { + if (string.IsNullOrEmpty(teamCityArtifactUri)) throw new ArgumentException(nameof(teamCityArtifactUri)); + if (description == null) throw new ArgumentNullException(nameof(description)); + var message = new ServiceMessage("testMetadata") {{"testName", _testName}, {"type", "artifact"}, {"value", teamCityArtifactUri}}; + if (!string.IsNullOrEmpty(description)) + { + message.Add("name", description); + } + + PostMessage(message); + } + + public void WriteValue(double value, string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); + PostMessage(new ServiceMessage("testMetadata") {{"testName", _testName}, {"type", "number"}, {"value", value.ToString(CultureInfo.InvariantCulture)}, {"name", name}}); + } + + public void WriteValue(string value, string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); + PostMessage(new ServiceMessage("testMetadata") {{"testName", _testName}, {"value", value}, {"name", name}}); + } + + public void WriteLink(string linkUri, string name) + { + if (string.IsNullOrEmpty(linkUri)) throw new ArgumentException(nameof(linkUri)); + if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); + PostMessage(new ServiceMessage("testMetadata") {{"testName", _testName}, {"type", "link"}, {"value", linkUri}, {"name", name}}); + } + public void OpenTest() { PostMessage(new ServiceMessage("testStarted") {{"name", _testName}, {"captureStandardOutput", "false"}});