diff --git a/TestPlatform.sln b/TestPlatform.sln index 2553cb8a81..d917217c24 100644 --- a/TestPlatform.sln +++ b/TestPlatform.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26326.0 +VisualStudioVersion = 15.0.26605.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED0C35EB-7F31-4841-A24F-8EB708FFA959}" EndProject @@ -151,6 +151,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.TestHostProvider", "src\Microsoft.TestPlatform.TestHostProvider\Microsoft.TestPlatform.TestHostProvider.csproj", "{11ECCB8B-6958-42A7-BD58-88C09CB149B2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.BlameDataCollector", "src\Microsoft.TestPlatform.Extensions.BlameDataCollector\Microsoft.TestPlatform.Extensions.BlameDataCollector.csproj", "{76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests", "test\Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests\Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests.csproj", "{488675EC-C8BB-40E0-AD4F-91F623D548B3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlameUnitTestProject", "test\TestAssets\BlameUnitTestProject\BlameUnitTestProject.csproj", "{6B2B841C-CCFF-469A-9939-EB07EA0401AE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -785,6 +791,42 @@ Global {11ECCB8B-6958-42A7-BD58-88C09CB149B2}.Release|x64.Build.0 = Release|Any CPU {11ECCB8B-6958-42A7-BD58-88C09CB149B2}.Release|x86.ActiveCfg = Release|Any CPU {11ECCB8B-6958-42A7-BD58-88C09CB149B2}.Release|x86.Build.0 = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|x64.ActiveCfg = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|x64.Build.0 = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|x86.ActiveCfg = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Debug|x86.Build.0 = Debug|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|Any CPU.Build.0 = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|x64.ActiveCfg = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|x64.Build.0 = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|x86.ActiveCfg = Release|Any CPU + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A}.Release|x86.Build.0 = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|x64.ActiveCfg = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|x64.Build.0 = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Debug|x86.Build.0 = Debug|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|Any CPU.Build.0 = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|x64.ActiveCfg = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|x64.Build.0 = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|x86.ActiveCfg = Release|Any CPU + {488675EC-C8BB-40E0-AD4F-91F623D548B3}.Release|x86.Build.0 = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|x64.ActiveCfg = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|x64.Build.0 = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|x86.ActiveCfg = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Debug|x86.Build.0 = Debug|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|Any CPU.Build.0 = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|x64.ActiveCfg = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|x64.Build.0 = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|x86.ActiveCfg = Release|Any CPU + {6B2B841C-CCFF-469A-9939-EB07EA0401AE}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -851,5 +893,8 @@ Global {7B48115A-B766-4B55-93A8-C08A42C37710} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {FBF74C8F-695C-4967-84AC-358EEFB1376D} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} {11ECCB8B-6958-42A7-BD58-88C09CB149B2} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959} + {76D4BB7E-D981-42D5-BE96-6FAD8DEF9A4A} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959} + {488675EC-C8BB-40E0-AD4F-91F623D548B3} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6} + {6B2B841C-CCFF-469A-9939-EB07EA0401AE} = {8DA7CBD9-F17E-41B6-90C4-CFF55848A25A} EndGlobalSection EndGlobal diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 6657baa09a..d0cbb71257 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -266,6 +266,14 @@ function Publish-Package Write-Verbose "Move-Item $coreCLR20PackageDir\$file $coreCLRExtensionsDir -Force" Move-Item $coreCLR20PackageDir\$file $coreCLRExtensionsDir -Force } + + # Publish Datacollector + $TPB_TargetFrameworkStandard = "netstandard1.5" + $blameDataCollector = Join-Path $env:TP_ROOT_DIR "src\Microsoft.TestPlatform.Extensions.BlameDataCollector\bin\$TPB_Configuration" + $blameDataCollectorNet46 = Join-Path $blameDataCollector $TPB_TargetFramework + $blameDataCollectorNetStandard = Join-Path $blameDataCollector $TPB_TargetFrameworkStandard + Copy-Item $blameDataCollectorNet46\* $fullCLRExtensionsDir -Force + Copy-Item $blameDataCollectorNetStandard\* $coreCLRExtensionsDir -Force # Note Note: If there are some dependencies for the TestHostRuntimeProvider assemblies, those need to be moved too. $runtimeproviders = @("Microsoft.TestPlatform.TestHostRuntimeProvider.dll", "Microsoft.TestPlatform.TestHostRuntimeProvider.pdb") diff --git a/scripts/build/TestPlatform.Dependencies.props b/scripts/build/TestPlatform.Dependencies.props index d08e7c79c4..73d2cc3e35 100644 --- a/scripts/build/TestPlatform.Dependencies.props +++ b/scripts/build/TestPlatform.Dependencies.props @@ -4,7 +4,7 @@ 15.0.26201 - 15.0.0 + 15.3.0-preview-20170628-02 1.2.0-beta 1.2.0-beta diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs index 25d279a652..df3350d09f 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs @@ -147,7 +147,6 @@ private void ProcessRequests(ITestRequestManager testRequestManager) case MessageType.StartDiscovery: { - var discoveryPayload = this.dataSerializer.DeserializePayload(message); this.StartDiscovery(discoveryPayload, testRequestManager); break; diff --git a/src/Microsoft.TestPlatform.Common/Friends.cs b/src/Microsoft.TestPlatform.Common/Friends.cs index 1655273bda..950f802108 100644 --- a/src/Microsoft.TestPlatform.Common/Friends.cs +++ b/src/Microsoft.TestPlatform.Common/Friends.cs @@ -22,5 +22,6 @@ [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] [assembly: InternalsVisibleTo("Microsoft.TestPlatform.CommunicationUtilities.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.TestPlatform.Client.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] #endregion \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Common/Logging/TestLoggerManager.cs b/src/Microsoft.TestPlatform.Common/Logging/TestLoggerManager.cs index c33b2c9100..f81a326cee 100644 --- a/src/Microsoft.TestPlatform.Common/Logging/TestLoggerManager.cs +++ b/src/Microsoft.TestPlatform.Common/Logging/TestLoggerManager.cs @@ -18,7 +18,6 @@ namespace Microsoft.VisualStudio.TestPlatform.Common.Logging using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using CommonResources = Microsoft.VisualStudio.TestPlatform.Common.Resources.Resources; - using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; /// /// Responsible for managing logger extensions and broadcasting results diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs new file mode 100644 index 0000000000..59631fa941 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameCollector.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Xml; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + + /// + /// The blame collector. + /// + [DataCollectorFriendlyName("Blame")] + [DataCollectorTypeUri("datacollector://Microsoft/TestPlatform/Extensions/Blame/v1")] + public class BlameCollector : DataCollector, ITestExecutionEnvironmentSpecifier + { + private DataCollectionSink dataCollectionSink; + private DataCollectionEnvironmentContext context; + private DataCollectionEvents events; + private List testSequence; + private IBlameReaderWriter blameReaderWriter; + private XmlElement configurationElement; + private int testStartCount; + private int testEndCount; + + /// + /// Initializes a new instance of the class. + /// Using XmlReaderWriter by default + /// + public BlameCollector() + : this(new XmlReaderWriter()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// BlameReaderWriter instance. + /// + protected BlameCollector(IBlameReaderWriter blameReaderWriter) + { + this.blameReaderWriter = blameReaderWriter; + } + + /// + /// Gets environment variables that should be set in the test execution environment + /// + /// Environment variables that should be set in the test execution environment + public IEnumerable> GetTestExecutionEnvironmentVariables() + { + return Enumerable.Empty>(); + } + + /// + /// Initializes parameters for the new instance of the class + /// + /// The Xml Element to save to + /// Data collection events to which methods subscribe + /// A data collection sink for data transfer + /// Data Collection Logger to send messages to the client + /// Context of data collector environment + public override void Initialize( + XmlElement configurationElement, + DataCollectionEvents events, + DataCollectionSink dataSink, + DataCollectionLogger logger, + DataCollectionEnvironmentContext environmentContext) + { + ValidateArg.NotNull(logger, nameof(logger)); + + this.events = events; + this.dataCollectionSink = dataSink; + this.context = environmentContext; + this.configurationElement = configurationElement; + this.testSequence = new List(); + + // Subscribing to events + this.events.SessionEnd += this.SessionEnded_Handler; + this.events.TestCaseStart += this.EventsTestCaseStart; + this.events.TestCaseEnd += this.EventsTestCaseEnd; + } + + /// + /// Called when Test Case Start event is invoked + /// + /// Sender + /// TestCaseStartEventArgs + private void EventsTestCaseStart(object sender, TestCaseStartEventArgs e) + { + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("Blame Collector : Test Case Start"); + } + + this.testSequence.Add(e.TestElement); + this.testStartCount++; + } + + /// + /// Called when Test Case End event is invoked + /// + /// Sender + /// TestCaseEndEventArgs + private void EventsTestCaseEnd(object sender, TestCaseEndEventArgs e) + { + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("Blame Collector : Test Case End"); + } + + this.testEndCount++; + } + + /// + /// Called when Session End event is invoked + /// + /// Sender + /// SessionEndEventArgs + private void SessionEnded_Handler(object sender, SessionEndEventArgs args) + { + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("Blame Collector : Session End"); + } + + // If the last test crashes, it will not invoke a test case end and therefore + // In case of crash testStartCount will be greater than testEndCount and we need to write the sequence + // And send the attachment + if (this.testStartCount > this.testEndCount) + { + var filepath = Path.Combine(this.GetResultsDirectory(), Constants.AttachmentFileName); + filepath = this.blameReaderWriter.WriteTestSequence(this.testSequence, filepath); + var fileTranferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, filepath, true); + this.dataCollectionSink.SendFileAsync(fileTranferInformation); + } + + this.DeregisterEvents(); + } + + /// + /// Method to deregister handlers and cleanup + /// + private void DeregisterEvents() + { + this.events.SessionEnd -= this.SessionEnded_Handler; + this.events.TestCaseStart -= this.EventsTestCaseStart; + this.events.TestCaseEnd -= this.EventsTestCaseEnd; + } + + private string GetResultsDirectory() + { + try + { + XmlElement resultsDirectoryElement = this.configurationElement["ResultsDirectory"]; + string resultsDirectory = resultsDirectoryElement != null ? resultsDirectoryElement.InnerText : string.Empty; + return resultsDirectory; + } + catch (NullReferenceException exception) + { + if (EqtTrace.IsErrorEnabled) + { + EqtTrace.Error("Blame Collector : " + exception); + } + + return string.Empty; + } + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameLogger.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameLogger.cs new file mode 100644 index 0000000000..add5ff88a5 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/BlameLogger.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector +{ + using System; + using System.Linq; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.Utilities; + + /// + /// The blame logger. + /// + [FriendlyName(BlameLogger.FriendlyName)] + [ExtensionUri(BlameLogger.ExtensionUri)] + public class BlameLogger : ITestLogger + { + #region Constants + + /// + /// Uri used to uniquely identify the Blame logger. + /// + public const string ExtensionUri = "logger://Microsoft/TestPlatform/Extensions/Blame/v1"; + + /// + /// Alternate user friendly string to uniquely identify the Blame logger. + /// + public const string FriendlyName = "Blame"; + + /// + /// The blame reader writer. + /// + private readonly IBlameReaderWriter blameReaderWriter; + + /// + /// The output. + /// + private readonly IOutput output; + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + public BlameLogger() + : this(ConsoleOutput.Instance, new XmlReaderWriter()) + { + } + + /// + /// Initializes a new instance of the class. + /// Constructor added for testing purpose + /// + /// Output Instance + /// BlameReaderWriter Instance + protected BlameLogger(IOutput output, IBlameReaderWriter blameReaderWriter) + { + this.output = output; + this.blameReaderWriter = blameReaderWriter; + } + + #endregion + + #region ITestLogger + + /// + /// Initializes the Logger. + /// + /// Events that can be registered for. + /// Test Run Directory + public void Initialize(TestLoggerEvents events, string testRunDictionary) + { + if (events == null) + { + throw new ArgumentNullException(nameof(events)); + } + + events.TestRunComplete += this.TestRunCompleteHandler; + } + + /// + /// Called when a test run is complete. + /// + /// Sender + /// TestRunCompleteEventArgs + private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) + { + if (sender == null) + { + throw new ArgumentNullException(nameof(sender)); + } + + ValidateArg.NotNull(sender, "sender"); + ValidateArg.NotNull(e, "e"); + + if (!e.IsAborted) + { + return; + } + + this.output.WriteLine(string.Empty, OutputLevel.Information); + + // Gets the faulty test case if test aborted + var testCaseName = this.GetFaultyTestCase(e); + if (testCaseName == string.Empty) + { + return; + } + + var reason = Resources.Resources.AbortedTestRun + testCaseName; + this.output.Error(reason); + } + + #endregion + + #region Faulty test case fetch + + /// + /// Fetches faulty test case + /// + /// + /// The TestRunCompleteEventArgs. + /// + /// + /// Faulty test case name + /// + private string GetFaultyTestCase(TestRunCompleteEventArgs e) + { + foreach (var attachmentSet in e.AttachmentSets) + { + if (attachmentSet.DisplayName.Equals(Constants.BlameDataCollectorName)) + { + var uriDataAttachment = attachmentSet.Attachments.LastOrDefault(); + if (uriDataAttachment != null) + { + var filepath = uriDataAttachment.Uri.LocalPath; + var testCaseList = this.blameReaderWriter.ReadTestSequence(filepath); + if (testCaseList.Count > 0) + { + var testcase = testCaseList.Last(); + return testcase.FullyQualifiedName; + } + } + + return string.Empty; + } + } + + return string.Empty; + } + + #endregion + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Constants.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Constants.cs new file mode 100644 index 0000000000..d4ea047a87 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Constants.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector +{ + /// + /// Class for constants used across the files. + /// + internal static class Constants + { + /// + /// Root node name for Xml file. + /// + public const string BlameRootNode = "TestSequence"; + + /// + /// Node name for each Xml node. + /// + public const string BlameTestNode = "Test"; + + /// + /// Attachment File name. + /// + public const string AttachmentFileName = "Sequence"; + + /// + /// Test Name Attribute. + /// + public const string TestNameAttribute = "Name"; + + /// + /// Test Source Attribute. + /// + public const string TestSourceAttribute = "Source"; + + /// + /// Friendly name of the data collector + /// + public const string BlameDataCollectorName = "Blame"; + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Interfaces/IBlameReaderWriter.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Interfaces/IBlameReaderWriter.cs new file mode 100644 index 0000000000..e8b2d3a996 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Interfaces/IBlameReaderWriter.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector +{ + using System.Collections.Generic; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + + public interface IBlameReaderWriter + { + /// + /// Writes tests to document + /// + /// List of tests in sequence + /// The path of file + /// File Path + string WriteTestSequence(List testSequence, string filePath); + + /// + /// Reads all tests from file + /// + /// The path of saved file + /// All tests + List ReadTestSequence(string filePath); + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Microsoft.TestPlatform.Extensions.BlameDataCollector.csproj b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Microsoft.TestPlatform.Extensions.BlameDataCollector.csproj new file mode 100644 index 0000000000..b68ec21728 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Microsoft.TestPlatform.Extensions.BlameDataCollector.csproj @@ -0,0 +1,55 @@ + + + + ..\..\ + + + + Microsoft.TestPlatform.Extensions.BlameDataCollector + + + Microsoft.TestPlatform.Extensions.BlameDataCollector + netstandard1.5;net46 + true + true + + + + + + + + + + + 4.3.0 + + + + + 4.3.0 + + + 4.3.0 + + + + + + + + + + True + True + Resources.resx + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs new file mode 100644 index 0000000000..6f8a01bbff --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector.Resources { + using System; + using System.Reflection; + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.TestPlatform.Extensions.BlameDataCollector.Resources.Resources", typeof(Resources).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The active Test Run was aborted because the host process exited unexpectedly while executing test.. + /// + internal static string AbortedTestRun { + get { + return ResourceManager.GetString("AbortedTestRun", resourceCulture); + } + } + } +} diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx new file mode 100644 index 0000000000..200337636f --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/Resources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The active Test Run was aborted because the host process exited unexpectedly while executing test + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf new file mode 100644 index 0000000000..f318e18ba5 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.cs.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf new file mode 100644 index 0000000000..17d1941606 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.de.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf new file mode 100644 index 0000000000..1122dc2222 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.es.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf new file mode 100644 index 0000000000..6d17bfc40e --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.fr.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf new file mode 100644 index 0000000000..4a2e407400 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.it.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf new file mode 100644 index 0000000000..8f6ab5d58b --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ja.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf new file mode 100644 index 0000000000..4f55dfddee --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ko.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf new file mode 100644 index 0000000000..596179e346 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pl.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf new file mode 100644 index 0000000000..84b0fbd3e6 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.pt-BR.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf new file mode 100644 index 0000000000..0e28adb505 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.ru.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf new file mode 100644 index 0000000000..8b2b9962d3 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.tr.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf new file mode 100644 index 0000000000..550fadc5b3 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.xlf @@ -0,0 +1,12 @@ + + + + + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf new file mode 100644 index 0000000000..edd8b8990d --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hans.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf new file mode 100644 index 0000000000..d4240dbed0 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/Resources/xlf/Resources.zh-Hant.xlf @@ -0,0 +1,30 @@ + + + +
+ + 27 + 22.95 + 22.95 + 0 + 0 + + + 0 + 0 + 0 + 0 + 0 + 27 + 0 + +
+ + + The active Test Run was aborted because the host process exited unexpectedly while executing test + The active Test Run was aborted because the host process exited unexpectedly while executing test + + + +
+
\ No newline at end of file diff --git a/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/XmlReaderWriter.cs b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/XmlReaderWriter.cs new file mode 100644 index 0000000000..a29f8a4872 --- /dev/null +++ b/src/Microsoft.TestPlatform.Extensions.BlameDataCollector/XmlReaderWriter.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector +{ + using System.Collections.Generic; + using System.IO; + using System.Xml; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + + /// + /// XmlReaderWriter class for reading and writing test sequences to file + /// + public class XmlReaderWriter : IBlameReaderWriter + { + /// + /// The file helper. + /// + private readonly IFileHelper fileHelper; + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + internal XmlReaderWriter() + : this(new FileHelper()) + { + } + + /// + /// Initializes a new instance of the class. + /// Protected for testing purposes + /// + /// + /// The file helper. + /// + protected XmlReaderWriter(IFileHelper fileHelper) + { + this.fileHelper = fileHelper; + } + + #endregion + + /// + /// Adds tests to document and saves document to file + /// + /// + /// The test Sequence. + /// + /// + /// The file Path. + /// + /// File path + public string WriteTestSequence(List testSequence, string filePath) + { + ValidateArg.NotNull(testSequence, nameof(testSequence)); + ValidateArg.NotNullOrEmpty(filePath, nameof(filePath)); + + filePath = filePath + ".xml"; + + // Writing test sequence + var xmlDocument = new XmlDocument(); + var xmlDeclaration = xmlDocument.CreateNode(XmlNodeType.XmlDeclaration, string.Empty, string.Empty); + var blameTestRoot = xmlDocument.CreateElement(Constants.BlameRootNode); + xmlDocument.AppendChild(xmlDeclaration); + + foreach (var testCase in testSequence) + { + var testElement = xmlDocument.CreateElement(Constants.BlameTestNode); + testElement.SetAttribute(Constants.TestNameAttribute, testCase.FullyQualifiedName); + testElement.SetAttribute(Constants.TestSourceAttribute, testCase.Source); + + blameTestRoot.AppendChild(testElement); + } + + xmlDocument.AppendChild(blameTestRoot); + using (var stream = this.fileHelper.GetStream(filePath, FileMode.Create)) + { + xmlDocument.Save(stream); + } + + return filePath; + } + + /// + /// Reads All test case from file + /// + /// The path of test sequence file + /// Test Case List + public List ReadTestSequence(string filePath) + { + ValidateArg.NotNull(filePath, nameof(filePath)); + + if (!this.fileHelper.Exists(filePath)) + { + throw new FileNotFoundException(); + } + + var testCaseList = new List(); + try + { + // Reading test sequence + var xmlDocument = new XmlDocument(); + using (var stream = this.fileHelper.GetStream(filePath, FileMode.Open)) + { + xmlDocument.Load(stream); + } + + var root = xmlDocument.LastChild; + foreach (XmlNode node in root) + { + var testCase = new TestCase + { + FullyQualifiedName = + node.Attributes[Constants.TestNameAttribute].Value, + Source = node.Attributes[Constants.TestSourceAttribute].Value + }; + testCaseList.Add(testCase); + } + } + catch (XmlException xmlException) + { + EqtTrace.Warning("XmlReaderWriter : Exception ", xmlException); + } + + return testCaseList; + } + } +} diff --git a/src/package/sign/sign.proj b/src/package/sign/sign.proj index de78067e4a..a5932ae87c 100644 --- a/src/package/sign/sign.proj +++ b/src/package/sign/sign.proj @@ -56,6 +56,7 @@ + @@ -95,6 +96,7 @@ + @@ -129,6 +131,7 @@ + diff --git a/src/vstest.console/Internal/ConsoleLogger.cs b/src/vstest.console/Internal/ConsoleLogger.cs index 4174183fb3..7ebbe94c00 100644 --- a/src/vstest.console/Internal/ConsoleLogger.cs +++ b/src/vstest.console/Internal/ConsoleLogger.cs @@ -432,6 +432,5 @@ private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) } } #endregion - } } diff --git a/src/vstest.console/Processors/CollectArgumentProcessor.cs b/src/vstest.console/Processors/CollectArgumentProcessor.cs index 69d8ccefad..97e40171f3 100644 --- a/src/vstest.console/Processors/CollectArgumentProcessor.cs +++ b/src/vstest.console/Processors/CollectArgumentProcessor.cs @@ -114,31 +114,7 @@ public void Initialize(string argument) argument)); } - EnabledDataCollectors.Add(argument.ToLower()); - - var settings = this.runSettingsManager.ActiveRunSettings?.SettingsXml; - if (settings == null) - { - this.runSettingsManager.AddDefaultRunSettings(); - settings = this.runSettingsManager.ActiveRunSettings?.SettingsXml; - } - - var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settings); - if (dataCollectionRunSettings == null) - { - dataCollectionRunSettings = new DataCollectionRunSettings(); - } - else - { - // By default, all data collectors present in run settings are enabled, if enabled attribute is not specified. - // So explicitely disable those data collectors and enable those which are specified. - DisableUnConfiguredDataCollectors(dataCollectionRunSettings); - } - - // Add data collectors if not already present, enable if already present. - EnableDataCollectorUsingFriendlyName(argument, dataCollectionRunSettings); - - this.runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.DataCollectionRunSettingsName, dataCollectionRunSettings.ToXml().InnerXml); + AddDataCollectorToRunSettings(argument, this.runSettingsManager); } /// @@ -195,5 +171,34 @@ private static bool DoesDataCollectorSettingsExist(string friendlyName, return false; } + + internal static void AddDataCollectorToRunSettings(string argument, IRunSettingsProvider runSettingsManager) + { + EnabledDataCollectors.Add(argument.ToLower()); + + var settings = runSettingsManager.ActiveRunSettings?.SettingsXml; + if (settings == null) + { + runSettingsManager.AddDefaultRunSettings(); + settings = runSettingsManager.ActiveRunSettings?.SettingsXml; + } + + var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settings); + if (dataCollectionRunSettings == null) + { + dataCollectionRunSettings = new DataCollectionRunSettings(); + } + else + { + // By default, all data collectors present in run settings are enabled, if enabled attribute is not specified. + // So explicitely disable those data collectors and enable those which are specified. + DisableUnConfiguredDataCollectors(dataCollectionRunSettings); + } + + // Add data collectors if not already present, enable if already present. + EnableDataCollectorUsingFriendlyName(argument, dataCollectionRunSettings); + + runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.DataCollectionRunSettingsName, dataCollectionRunSettings.ToXml().InnerXml); + } } } diff --git a/src/vstest.console/Processors/EnableBlameArgumentProcessor.cs b/src/vstest.console/Processors/EnableBlameArgumentProcessor.cs new file mode 100644 index 0000000000..d26296785f --- /dev/null +++ b/src/vstest.console/Processors/EnableBlameArgumentProcessor.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors +{ + using System; + using System.Diagnostics.Contracts; + + using Microsoft.VisualStudio.TestPlatform.Common; + using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; + using Microsoft.VisualStudio.TestPlatform.Common.Logging; + + using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; + using Microsoft.VisualStudio.TestPlatform.Common.Utilities; + using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities; + using System.Xml; + + internal class EnableBlameArgumentProcessor : IArgumentProcessor + { + /// + /// The name of the command line argument that the ListTestsArgumentExecutor handles. + /// + public const string CommandName = "/Blame"; + + private Lazy metadata; + + private Lazy executor; + + /// + /// Initializes a new instance of the class. + /// + public EnableBlameArgumentProcessor() + { + } + + public Lazy Metadata + { + get + { + if (this.metadata == null) + { + this.metadata = new Lazy(() => new EnableBlameArgumentProcessorCapabilities()); + } + + return this.metadata; + } + } + + /// + /// Gets or sets the executor. + /// + public Lazy Executor + { + get + { + if (this.executor == null) + { + this.executor = new Lazy(() => new EnableBlameArgumentExecutor(RunSettingsManager.Instance, TestLoggerManager.Instance)); + } + + return this.executor; + } + set + { + this.executor = value; + } + } + } + + /// + /// The argument capabilities. + /// + internal class EnableBlameArgumentProcessorCapabilities : BaseArgumentProcessorCapabilities + { + public override string CommandName => EnableBlameArgumentProcessor.CommandName; + + public override bool AllowMultiple => false; + + public override bool IsAction => false; + + public override ArgumentProcessorPriority Priority => ArgumentProcessorPriority.Logging; + + public override string HelpContentResourceName => CommandLineResources.EnableBlameUsage; + + public override HelpContentPriority HelpPriority => HelpContentPriority.EnableDiagArgumentProcessorHelpPriority; + } + + /// + /// The argument executor. + /// + internal class EnableBlameArgumentExecutor : IArgumentExecutor + { + /// + /// Blame logger and data collector friendly name + /// + private static string BlameFriendlyName = "blame"; + + /// + /// Test logger manager instance + /// + private readonly TestLoggerManager loggerManager; + + /// + /// Run settings manager + /// + private IRunSettingsProvider runSettingsManager; + + #region Constructor + + internal EnableBlameArgumentExecutor(IRunSettingsProvider runSettingsManager, TestLoggerManager loggerManager) + { + Contract.Requires(loggerManager != null); + + this.runSettingsManager = runSettingsManager; + this.loggerManager = loggerManager; + } + #endregion + + #region IArgumentExecutor + + /// + /// Initializes with the argument that was provided with the command. + /// + /// Argument that was provided with the command. + public void Initialize(string argument) + { + // Add Blame Logger + this.loggerManager.UpdateLoggerList(BlameFriendlyName, BlameFriendlyName, null); + + // Add Blame Data Collector + CollectArgumentExecutor.AddDataCollectorToRunSettings(BlameFriendlyName, this.runSettingsManager); + + // Get results directory from RunSettingsManager + var runSettings = this.runSettingsManager.ActiveRunSettings; + string resultsDirectory = null; + if (runSettings != null) + { + try + { + RunConfiguration runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runSettings.SettingsXml); + resultsDirectory = RunSettingsUtilities.GetTestResultsDirectory(runConfiguration); + } + catch (SettingsException se) + { + if (EqtTrace.IsErrorEnabled) + { + EqtTrace.Error("EnableBlameArgumentProcessor: Unable to get the test results directory: Error {0}", se); + } + } + } + + // Add configuration element + var settings = runSettings?.SettingsXml; + if (settings == null) + { + runSettingsManager.AddDefaultRunSettings(); + settings = runSettings?.SettingsXml; + } + + var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settings); + if (dataCollectionRunSettings == null) + { + dataCollectionRunSettings = new DataCollectionRunSettings(); + } + + var XmlDocument = new XmlDocument(); + var outernode = XmlDocument.CreateElement("Configuration"); + var node = XmlDocument.CreateElement("ResultsDirectory"); + outernode.AppendChild(node); + node.InnerText = resultsDirectory; + + foreach(var item in dataCollectionRunSettings.DataCollectorSettingsList) + { + if( item.FriendlyName.Equals(BlameFriendlyName)) + { + item.Configuration = outernode; + } + } + + runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.DataCollectionRunSettingsName, dataCollectionRunSettings.ToXml().InnerXml); + } + + /// + /// Executes the argument processor. + /// + /// The . + public ArgumentProcessorResult Execute() + { + // Nothing to do since we updated the logger and data collector list in initialize + return ArgumentProcessorResult.Success; + } + + #endregion + } +} diff --git a/src/vstest.console/Processors/EnableCodeCoverageArgumentProcessor.cs b/src/vstest.console/Processors/EnableCodeCoverageArgumentProcessor.cs index ea044fe577..1992d398a0 100644 --- a/src/vstest.console/Processors/EnableCodeCoverageArgumentProcessor.cs +++ b/src/vstest.console/Processors/EnableCodeCoverageArgumentProcessor.cs @@ -4,12 +4,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors { using System; - - using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities; using Microsoft.VisualStudio.TestPlatform.Common; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; - using Microsoft.VisualStudio.TestPlatform.ObjectModel; - using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities; using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources; @@ -103,24 +99,7 @@ internal EnableCodeCoverageArgumentExecutor(IRunSettingsProvider runSettingsMana public void Initialize(string argument) { // Add this enabled data collectors list, this will ensure Code Coverage isn't disabled when other DCs are configured using /Collect. - CollectArgumentExecutor.EnabledDataCollectors.Add(FriendlyName.ToLower()); - - var settings = this.runSettingsManager.ActiveRunSettings?.SettingsXml; - if (settings == null) - { - this.runSettingsManager.AddDefaultRunSettings(); - settings = this.runSettingsManager.ActiveRunSettings?.SettingsXml; - } - - var dataCollectionRunSettings = XmlRunSettingsUtilities.GetDataCollectionRunSettings(settings); - if (dataCollectionRunSettings == null) - { - dataCollectionRunSettings = new DataCollectionRunSettings(); - } - - CollectArgumentExecutor.EnableDataCollectorUsingFriendlyName(FriendlyName, dataCollectionRunSettings); - - this.runSettingsManager.UpdateRunSettingsNodeInnerXml(Constants.DataCollectionRunSettingsName, dataCollectionRunSettings.ToXml().InnerXml); + CollectArgumentExecutor.AddDataCollectorToRunSettings(argument, this.runSettingsManager); } /// diff --git a/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs b/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs index 0a7db269f6..f59370e320 100644 --- a/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs +++ b/src/vstest.console/Processors/Utilities/ArgumentProcessorFactory.cs @@ -234,7 +234,8 @@ public IEnumerable GetArgumentProcessorsToAlwaysExecute() new InIsolationArgumentProcessor(), new CollectArgumentProcessor(), new EnableCodeCoverageArgumentProcessor(), - new DisableAutoFakesArgumentProcessor() + new DisableAutoFakesArgumentProcessor(), + new EnableBlameArgumentProcessor() }; /// diff --git a/src/vstest.console/Resources/Resources.Designer.cs b/src/vstest.console/Resources/Resources.Designer.cs index 101f81b179..099108bb6f 100644 --- a/src/vstest.console/Resources/Resources.Designer.cs +++ b/src/vstest.console/Resources/Resources.Designer.cs @@ -475,6 +475,18 @@ public static string EnableDiagUsage } } + /// + /// Looks up a localized string similar to --Blame|/Blame + /// Enable Blame mode for diagnosis of faulty test case + /// + public static string EnableBlameUsage + { + get + { + return ResourceManager.GetString("EnableBlameUsage", resourceCulture); + } + } + /// /// Looks up a localized string similar to --logger|/logger:<Logger Uri/FriendlyName> /// Specify a logger for test results. For example, to log results into a diff --git a/src/vstest.console/Resources/Resources.resx b/src/vstest.console/Resources/Resources.resx index 2c5be05a89..f9c2f18ee2 100644 --- a/src/vstest.console/Resources/Resources.resx +++ b/src/vstest.console/Resources/Resources.resx @@ -655,4 +655,8 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.cs.xlf b/src/vstest.console/Resources/xlf/Resources.cs.xlf index 1c5202aec6..f2c5f3dbf1 100644 --- a/src/vstest.console/Resources/xlf/Resources.cs.xlf +++ b/src/vstest.console/Resources/xlf/Resources.cs.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.de.xlf b/src/vstest.console/Resources/xlf/Resources.de.xlf index 7ddeb1f4a3..c1839dc36d 100644 --- a/src/vstest.console/Resources/xlf/Resources.de.xlf +++ b/src/vstest.console/Resources/xlf/Resources.de.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.es.xlf b/src/vstest.console/Resources/xlf/Resources.es.xlf index 36f8d204f2..7ced1c1b1b 100644 --- a/src/vstest.console/Resources/xlf/Resources.es.xlf +++ b/src/vstest.console/Resources/xlf/Resources.es.xlf @@ -1560,6 +1560,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.fr.xlf b/src/vstest.console/Resources/xlf/Resources.fr.xlf index 7fec6c8d8e..06693c9632 100644 --- a/src/vstest.console/Resources/xlf/Resources.fr.xlf +++ b/src/vstest.console/Resources/xlf/Resources.fr.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.it.xlf b/src/vstest.console/Resources/xlf/Resources.it.xlf index 0132ea055f..452bccb0a5 100644 --- a/src/vstest.console/Resources/xlf/Resources.it.xlf +++ b/src/vstest.console/Resources/xlf/Resources.it.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.ja.xlf b/src/vstest.console/Resources/xlf/Resources.ja.xlf index 21cc8cb657..b39b445f03 100644 --- a/src/vstest.console/Resources/xlf/Resources.ja.xlf +++ b/src/vstest.console/Resources/xlf/Resources.ja.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.ko.xlf b/src/vstest.console/Resources/xlf/Resources.ko.xlf index 7fe4b0cad0..bdb5830b6e 100644 --- a/src/vstest.console/Resources/xlf/Resources.ko.xlf +++ b/src/vstest.console/Resources/xlf/Resources.ko.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.pl.xlf b/src/vstest.console/Resources/xlf/Resources.pl.xlf index 1bf0bc2884..353aa36b5b 100644 --- a/src/vstest.console/Resources/xlf/Resources.pl.xlf +++ b/src/vstest.console/Resources/xlf/Resources.pl.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.pt-BR.xlf b/src/vstest.console/Resources/xlf/Resources.pt-BR.xlf index 3e99c1570e..f4160d4f6e 100644 --- a/src/vstest.console/Resources/xlf/Resources.pt-BR.xlf +++ b/src/vstest.console/Resources/xlf/Resources.pt-BR.xlf @@ -1551,6 +1551,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.ru.xlf b/src/vstest.console/Resources/xlf/Resources.ru.xlf index afec058776..ce358f9dfd 100644 --- a/src/vstest.console/Resources/xlf/Resources.ru.xlf +++ b/src/vstest.console/Resources/xlf/Resources.ru.xlf @@ -1553,6 +1553,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.tr.xlf b/src/vstest.console/Resources/xlf/Resources.tr.xlf index c185f57dab..dc86d329dd 100644 --- a/src/vstest.console/Resources/xlf/Resources.tr.xlf +++ b/src/vstest.console/Resources/xlf/Resources.tr.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.xlf b/src/vstest.console/Resources/xlf/Resources.xlf index e53b389f12..a6258e13c6 100644 --- a/src/vstest.console/Resources/xlf/Resources.xlf +++ b/src/vstest.console/Resources/xlf/Resources.xlf @@ -700,6 +700,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.zh-Hans.xlf b/src/vstest.console/Resources/xlf/Resources.zh-Hans.xlf index 8ccb60f667..ccf5bef667 100644 --- a/src/vstest.console/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/vstest.console/Resources/xlf/Resources.zh-Hans.xlf @@ -1551,6 +1551,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/src/vstest.console/Resources/xlf/Resources.zh-Hant.xlf b/src/vstest.console/Resources/xlf/Resources.zh-Hant.xlf index 51b23c5e59..eda664c342 100644 --- a/src/vstest.console/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/vstest.console/Resources/xlf/Resources.zh-Hant.xlf @@ -1552,6 +1552,13 @@ Debug Traces Messages: + + --Blame|/Blame + Runs the test in blame mode. This option is helpful in isolating the problematic test causing test host crash. It creates an output file in the current directory as "Sequence.xml", that captures the order of execution of test before the crash. + --Blame|/Blame + Enable Blame mode to get name of the faulty test case in event of a test host crash. + + \ No newline at end of file diff --git a/test/Microsoft.TestPlatform.AcceptanceTests/BlameDataCollectorTests.cs b/test/Microsoft.TestPlatform.AcceptanceTests/BlameDataCollectorTests.cs new file mode 100644 index 0000000000..16f9dd4297 --- /dev/null +++ b/test/Microsoft.TestPlatform.AcceptanceTests/BlameDataCollectorTests.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.AcceptanceTests +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.IO; + using System.Xml; + + [TestClass] + public class BlameDataCollectorTests : AcceptanceTestBase + { + private readonly string resultsDir; + + public BlameDataCollectorTests() + { + this.resultsDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + } + + [TestCleanup] + public void Cleanup() + { + if (Directory.Exists(this.resultsDir)) + { + Directory.Delete(this.resultsDir, true); + } + } + + [CustomDataTestMethod] + [NET46TargetFramework] + [NETCORETargetFramework] + public void BlameDataCollectorShouldGiveCorrectTestCaseName(string runnerFramework, string targetFramework, string targetRuntime) + { + + SetTestEnvironment(this.testEnvironment, runnerFramework, targetFramework, targetRuntime); + var assemblyPaths = this.BuildMultipleAssemblyPath("BlameUnitTestProject.dll").Trim('\"'); + var arguments = PrepareArguments(assemblyPaths, this.GetTestAdapterPath(), string.Empty, this.FrameworkArgValue); + arguments = string.Concat(arguments, $" /Blame"); + arguments = string.Concat(arguments, $" /ResultsDirectory:{resultsDir}"); + this.InvokeVsTest(arguments); + + this.VaildateOutput(); + } + + private void VaildateOutput() + { + bool isAttachmentReceived = false; + bool isValid = false; + this.StdErrorContains("BlameUnitTestProject.UnitTest1.TestMethod2"); + this.StdOutputContains("Sequence.xml"); + var resultFiles = Directory.GetFiles(this.resultsDir, "*", SearchOption.AllDirectories); + + foreach(var file in resultFiles) + { + if(file.Contains("Sequence.xml")) + { + isAttachmentReceived = true; + isValid = IsValidXml(file); + break; + } + } + Assert.IsTrue(isAttachmentReceived); + Assert.IsTrue(isValid); + } + + private bool IsValidXml(string xmlFilePath) + { + var file = File.OpenRead(xmlFilePath); + var reader = XmlReader.Create(file); + try + { + while (reader.Read()) + { + } + file.Dispose(); + return true; + } + catch (XmlException) + { + file.Dispose(); + return false; + } + } + } +} diff --git a/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameCollectorTests.cs b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameCollectorTests.cs new file mode 100644 index 0000000000..b1d75c5cee --- /dev/null +++ b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameCollectorTests.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Xml; + + using Microsoft.TestPlatform.Extensions.BlameDataCollector; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + /// + /// The blame collector tests. + /// + [TestClass] + public class BlameCollectorTests + { + private DataCollectionEnvironmentContext context; + private DataCollectionContext dataCollectionContext; + private BlameCollector blameDataCollector; + private Mock mockLogger; + private Mock mockDataColectionEvents; + private Mock mockDataCollectionSink; + private Mock mockBlameReaderWriter; + private XmlElement configurationElement; + private string filepath; + + /// + /// Initializes a new instance of the class. + /// + public BlameCollectorTests() + { + // Initializing mocks + this.mockLogger = new Mock(); + this.mockDataColectionEvents = new Mock(); + this.mockDataCollectionSink = new Mock(); + this.mockBlameReaderWriter = new Mock(); + this.blameDataCollector = new TestableBlameCollector(this.mockBlameReaderWriter.Object); + + // Initializing members + TestCase testcase = new TestCase { Id = Guid.NewGuid() }; + this.dataCollectionContext = new DataCollectionContext(testcase); + this.configurationElement = null; + this.context = new DataCollectionEnvironmentContext(this.dataCollectionContext); + + this.filepath = Path.Combine(Path.GetTempPath(), "Test"); + FileStream stream = File.Create(this.filepath); + stream.Dispose(); + } + + /// + /// The initialize should throw exception if data collection logger is null. + /// + [TestMethod] + public void InitializeShouldThrowExceptionIfDataCollectionLoggerIsNull() + { + Assert.ThrowsException(() => + { + this.blameDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + null, + null); + }); + } + + /// + /// The trigger session ended handler should write to file if test start count is greater. + /// + [TestMethod] + public void TriggerSessionEndedHandlerShouldWriteToFileIfTestHostCrash() + { + // Initializing Blame Data Collector + this.blameDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + this.mockLogger.Object, + this.context); + + TestCase testcase = new TestCase("TestProject.UnitTest.TestMethod", new Uri("test:/abc"), "abc.dll"); + + // Setup and Raise TestCaseStart and Session End Event + this.mockBlameReaderWriter.Setup(x => x.WriteTestSequence(It.IsAny>(), It.IsAny())) + .Returns(this.filepath); + + this.mockDataColectionEvents.Raise(x => x.TestCaseStart += null, new TestCaseStartEventArgs(testcase)); + this.mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs(this.dataCollectionContext)); + + // Verify WriteTestSequence Call + this.mockBlameReaderWriter.Verify(x => x.WriteTestSequence(It.IsAny>(), It.IsAny()), Times.Once); + } + + /// + /// The trigger session ended handler should not write to file if test start count is same as test end count. + /// + [TestMethod] + public void TriggerSessionEndedHandlerShouldNotWriteToFileIfNoTestHostCrash() + { + // Initializing Blame Data Collector + this.blameDataCollector.Initialize( + this.configurationElement, + this.mockDataColectionEvents.Object, + this.mockDataCollectionSink.Object, + this.mockLogger.Object, + this.context); + + TestCase testcase = new TestCase("TestProject.UnitTest.TestMethod", new Uri("test:/abc"), "abc.dll"); + + // Setup and Raise TestCaseStart and Session End Event + this.mockBlameReaderWriter.Setup(x => x.WriteTestSequence(It.IsAny>(), It.IsAny())).Returns(this.filepath); + this.mockDataColectionEvents.Raise(x => x.TestCaseStart += null, new TestCaseStartEventArgs(testcase)); + this.mockDataColectionEvents.Raise(x => x.TestCaseEnd += null, new TestCaseEndEventArgs(testcase, TestOutcome.Passed)); + this.mockDataColectionEvents.Raise(x => x.SessionEnd += null, new SessionEndEventArgs(this.dataCollectionContext)); + + // Verify WriteTestSequence Call + this.mockBlameReaderWriter.Verify(x => x.WriteTestSequence(It.IsAny>(), this.filepath), Times.Never); + } + + [TestCleanup] + public void CleanUp() + { + File.Delete(this.filepath); + } + + /// + /// The testable blame collector. + /// + internal class TestableBlameCollector : BlameCollector + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The blame reader writer. + /// + internal TestableBlameCollector(IBlameReaderWriter blameReaderWriter) + : base(blameReaderWriter) + { + } + } + } +} diff --git a/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameLoggerTests.cs b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameLoggerTests.cs new file mode 100644 index 0000000000..9ad7ab37b7 --- /dev/null +++ b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/BlameLoggerTests.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + using Microsoft.TestPlatform.Extensions.BlameDataCollector; + using Microsoft.VisualStudio.TestPlatform.Common.Logging; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.Utilities; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + /// + /// The blame logger tests. + /// + [TestClass] + public class BlameLoggerTests + { + private Mock testRunRequest; + private Mock events; + private Mock mockOutput; + private Mock mockBlameReaderWriter; + private TestLoggerManager testLoggerManager; + private BlameLogger blameLogger; + + /// + /// Initializes a new instance of the class. + /// + public BlameLoggerTests() + { + // Mock for ITestRunRequest + this.testRunRequest = new Mock(); + this.events = new Mock(); + this.mockOutput = new Mock(); + this.mockBlameReaderWriter = new Mock(); + this.blameLogger = new TestableBlameLogger(this.mockOutput.Object, this.mockBlameReaderWriter.Object); + + // Create Instance of TestLoggerManager + this.testLoggerManager = new TestableTestLoggerManager(); + this.testLoggerManager.AddLogger(this.blameLogger, BlameLogger.ExtensionUri, null); + this.testLoggerManager.EnableLogging(); + + // Register TestRunRequest object + this.testLoggerManager.RegisterTestRunEvents(this.testRunRequest.Object); + } + + /// + /// The initialize should throw exception if events is null. + /// + [TestMethod] + public void InitializeShouldThrowExceptionIfEventsIsNull() + { + Assert.ThrowsException(() => + { + this.blameLogger.Initialize(null, string.Empty); + }); + } + + /// + /// The test result complete handler should throw exception if event args is null. + /// + [TestMethod] + public void TestResultCompleteHandlerShouldThrowExceptionIfEventArgsIsNull() + { + // Raise an event on mock object + Assert.ThrowsException(() => + { + this.testRunRequest.Raise(m => m.OnRunCompletion += null, default(TestRunCompleteEventArgs)); + }); + } + + /// + /// The test run complete handler should get faulty test run if test run aborted. + /// + [TestMethod] + public void TestRunCompleteHandlerShouldGetFaultyTestRunIfTestRunAborted() + { + // Initialize + var attachmentSet = new AttachmentSet(new Uri("test://uri"), "Blame"); + var uriDataAttachment = new UriDataAttachment(new Uri("C:/folder1/sequence.xml"), "description"); + attachmentSet.Attachments.Add(uriDataAttachment); + var attachmentSetList = new List { attachmentSet }; + + // Initialize Blame Logger + this.blameLogger.Initialize(this.events.Object, null); + + var testCaseList = + new List + { + new TestCase("ABC.UnitTestMethod1", new Uri("test://uri"), "C://test/filepath"), + new TestCase("ABC.UnitTestMethod2", new Uri("test://uri"), "C://test/filepath") + }; + + // Setup and Raise event + this.mockBlameReaderWriter.Setup(x => x.ReadTestSequence(It.IsAny())).Returns(testCaseList); + this.testRunRequest.Raise( + m => m.OnRunCompletion += null, + new TestRunCompleteEventArgs(stats: null, isCanceled: false, isAborted: true, error: null, attachmentSets: new Collection(attachmentSetList), elapsedTime: new TimeSpan(1, 0, 0, 0))); + + // Verify Call + this.mockBlameReaderWriter.Verify(x => x.ReadTestSequence(It.IsAny()), Times.Once); + } + + /// + /// The test run complete handler should not read file if test run not aborted. + /// + [TestMethod] + public void TestRunCompleteHandlerShouldNotReadFileIfTestRunNotAborted() + { + // Initialize Blame Logger + this.blameLogger.Initialize(this.events.Object, null); + + // Setup and Raise event + this.mockBlameReaderWriter.Setup(x => x.ReadTestSequence(It.IsAny())); + this.testRunRequest.Raise( + m => m.OnRunCompletion += null, + new TestRunCompleteEventArgs(stats: null, isCanceled: false, isAborted: false, error: null, attachmentSets: null, elapsedTime: new TimeSpan(1, 0, 0, 0))); + + // Verify Call + this.mockBlameReaderWriter.Verify(x => x.ReadTestSequence(It.IsAny()), Times.Never); + } + + /// + /// The test run complete handler should return if uri attachment is null. + /// + [TestMethod] + public void TestRunCompleteHandlerShouldReturnIfUriAttachmentIsNull() + { + // Initialize + var attachmentSet = new AttachmentSet(new Uri("test://uri"), "Blame"); + var attachmentSetList = new List { attachmentSet }; + + // Initialize Blame Logger + this.blameLogger.Initialize(this.events.Object, null); + + // Setup and Raise event + this.testRunRequest.Raise( + m => m.OnRunCompletion += null, + new TestRunCompleteEventArgs(stats: null, isCanceled: false, isAborted: true, error: null, attachmentSets: new Collection(attachmentSetList), elapsedTime: new TimeSpan(1, 0, 0, 0))); + + // Verify Call + this.mockBlameReaderWriter.Verify(x => x.ReadTestSequence(It.IsAny()), Times.Never); + } + + /// + /// The testable test logger manager. + /// + internal class TestableTestLoggerManager : TestLoggerManager + { + /// + /// Initializes a new instance of the class. + /// + internal TestableTestLoggerManager() + : base(TestSessionMessageLogger.Instance, new InternalTestLoggerEvents(TestSessionMessageLogger.Instance)) + { + } + } + + /// + /// The testable blame logger. + /// + internal class TestableBlameLogger : BlameLogger + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The output. + /// + /// + /// The blame Reader Writer. + /// + internal TestableBlameLogger(IOutput output, IBlameReaderWriter blameReaderWriter) + : base(output, blameReaderWriter) + { + } + } + } +} diff --git a/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests.csproj b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests.csproj new file mode 100644 index 0000000000..3be756b688 --- /dev/null +++ b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests.csproj @@ -0,0 +1,41 @@ + + + + ..\..\ + true + + + + Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests + + + netcoreapp1.0;net46 + Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests + true + true + + + + + true + + + + + + + + + + + 4.3.0 + + + + + + + + + + diff --git a/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/XmlReaderWriterTests.cs b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/XmlReaderWriterTests.cs new file mode 100644 index 0000000000..1104be02ae --- /dev/null +++ b/test/Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests/XmlReaderWriterTests.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.Extensions.BlameDataCollector.UnitTests +{ + using System; + using System.Collections.Generic; + using System.IO; + + using Microsoft.TestPlatform.Extensions.BlameDataCollector; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + using Moq; + + /// + /// The xml reader writer tests. + /// + [TestClass] + public class XmlReaderWriterTests + { + private TestableXmlReaderWriter xmlReaderWriter; + private Mock mockFileHelper; + private Mock mockStream; + private List testCaseList; + private TestCase testcase; + + /// + /// Initializes a new instance of the class. + /// + public XmlReaderWriterTests() + { + this.mockFileHelper = new Mock(); + this.xmlReaderWriter = new TestableXmlReaderWriter(this.mockFileHelper.Object); + this.mockStream = new Mock(); + this.testCaseList = new List(); + this.testcase = new TestCase + { + Id = Guid.NewGuid(), + FullyQualifiedName = "TestProject.UnitTest.TestMethod", + Source = "abc.dll" + }; + } + + /// + /// The write test sequence should throw exception if file path is null. + /// + [TestMethod] + public void WriteTestSequenceShouldThrowExceptionIfFilePathIsNull() + { + this.testCaseList.Add(this.testcase); + + Assert.ThrowsException(() => + { + this.xmlReaderWriter.WriteTestSequence(this.testCaseList, null); + }); + } + + /// + /// The write test sequence should throw exception if file path is empty. + /// + [TestMethod] + public void WriteTestSequenceShouldThrowExceptionIfFilePathIsEmpty() + { + this.testCaseList.Add(this.testcase); + + Assert.ThrowsException(() => + { + this.xmlReaderWriter.WriteTestSequence(this.testCaseList, string.Empty); + }); + } + + /// + /// The read test sequence should throw exception if file path is null. + /// + [TestMethod] + public void ReadTestSequenceShouldThrowExceptionIfFilePathIsNull() + { + Assert.ThrowsException(() => + { + this.xmlReaderWriter.ReadTestSequence(null); + }); + } + + /// + /// The read test sequence should throw exception if file not found. + /// + [TestMethod] + public void ReadTestSequenceShouldThrowExceptionIfFileNotFound() + { + this.mockFileHelper.Setup(m => m.Exists(It.IsAny())).Returns(false); + + Assert.ThrowsException(() => + { + this.xmlReaderWriter.ReadTestSequence(string.Empty); + }); + } + + /// + /// The read test sequence should read file stream. + /// + [TestMethod] + public void ReadTestSequenceShouldReadFileStream() + { + // Setup + this.mockFileHelper.Setup(m => m.Exists(It.IsAny())).Returns(true); + this.mockFileHelper.Setup(m => m.GetStream("path.xml", FileMode.Open, FileAccess.ReadWrite)).Returns(this.mockStream.Object); + + // Call to Read Test Sequence + this.xmlReaderWriter.ReadTestSequence("path.xml"); + + // Verify Call to fileHelper + this.mockFileHelper.Verify(x => x.GetStream("path.xml", FileMode.Open, FileAccess.ReadWrite)); + + // Verify Call to stream read + this.mockStream.Verify(x => x.Read(It.IsAny(), It.IsAny(), It.IsAny())); + } + + /// + /// The write test sequence should write file stream. + /// + [TestMethod] + public void WriteTestSequenceShouldWriteFileStream() + { + // Setup + this.mockFileHelper.Setup(m => m.Exists(It.IsAny())).Returns(true); + this.mockFileHelper.Setup(m => m.GetStream("path.xml", FileMode.Create, FileAccess.ReadWrite)).Returns(this.mockStream.Object); + this.mockStream.Setup(x => x.CanWrite).Returns(true); + this.mockStream.Setup(x => x.Write(It.IsAny(), It.IsAny(), It.IsAny())); + + this.xmlReaderWriter.WriteTestSequence(this.testCaseList, "path"); + + // Verify Call to fileHelper + this.mockFileHelper.Verify(x => x.GetStream("path.xml", FileMode.Create, FileAccess.ReadWrite)); + + // Verify Call to stream write + this.mockStream.Verify(x => x.Write(It.IsAny(), It.IsAny(), It.IsAny())); + } + + /// + /// The testable xml reader writer. + /// + internal class TestableXmlReaderWriter : XmlReaderWriter + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The file helper. + /// + internal TestableXmlReaderWriter(IFileHelper fileHelper) + : base(fileHelper) + { + } + } + } +} diff --git a/test/TestAssets/BlameUnitTestProject/BlameUnitTestProject.csproj b/test/TestAssets/BlameUnitTestProject/BlameUnitTestProject.csproj new file mode 100644 index 0000000000..8ff8ab5f79 --- /dev/null +++ b/test/TestAssets/BlameUnitTestProject/BlameUnitTestProject.csproj @@ -0,0 +1,31 @@ + + + + + + + + + BlameUnitTestProject + netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;net46 + + + + + + + $(MSTestFrameworkVersion) + + + $(MSTestAdapterVersion) + + + $(NETTestSdkPreviousVersion) + + + + + + diff --git a/test/TestAssets/BlameUnitTestProject/UnitTest1.cs b/test/TestAssets/BlameUnitTestProject/UnitTest1.cs new file mode 100644 index 0000000000..83e741270f --- /dev/null +++ b/test/TestAssets/BlameUnitTestProject/UnitTest1.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace BlameUnitTestProject +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System; + using System.Threading; + + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + } + + [TestMethod] + public void TestMethod2() + { + Environment.Exit(1); + } + } +} diff --git a/test/vstest.console.UnitTests/Processors/EnableBlameArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/EnableBlameArgumentProcessorTests.cs new file mode 100644 index 0000000000..2a11638683 --- /dev/null +++ b/test/vstest.console.UnitTests/Processors/EnableBlameArgumentProcessorTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace vstest.console.UnitTests.Processors +{ + using System; + using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors; + using Microsoft.VisualStudio.TestPlatform.Common; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using vstest.console.UnitTests.TestDoubles; + using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources; + + [TestClass] + public class EnableBlameArgumentProcessorTests + { + private TestableRunSettingsProvider settingsProvider; + private EnableBlameArgumentExecutor executor; + private DummyTestLoggerManager testloggerManager; + private const string DefaultRunSettings = "\r\n\r\n \r\n \r\n \r\n C:\\dir\\TestResults\r\n "; + + public EnableBlameArgumentProcessorTests() + { + this.settingsProvider = new TestableRunSettingsProvider(); + this.testloggerManager = new DummyTestLoggerManager(); + this.executor = new EnableBlameArgumentExecutor(this.settingsProvider,this.testloggerManager); + CollectArgumentExecutor.EnabledDataCollectors.Clear(); + } + + [TestMethod] + public void GetMetadataShouldReturnEnableBlameArgumentProcessorCapabilities() + { + var processor = new EnableBlameArgumentProcessor(); + Assert.IsTrue(processor.Metadata.Value is EnableBlameArgumentProcessorCapabilities); + } + + [TestMethod] + public void GetExecuterShouldReturnEnableBlameArgumentProcessorCapabilities() + { + var processor = new EnableBlameArgumentProcessor(); + Assert.IsTrue(processor.Executor.Value is EnableBlameArgumentExecutor); + } + + [TestMethod] + public void CapabilitiesShouldReturnAppropriateProperties() + { + var capabilities = new EnableBlameArgumentProcessorCapabilities(); + + Assert.AreEqual("/Blame", capabilities.CommandName); + Assert.AreEqual(false, capabilities.IsAction); + Assert.AreEqual(ArgumentProcessorPriority.Logging, capabilities.Priority); + Assert.AreEqual(HelpContentPriority.EnableDiagArgumentProcessorHelpPriority, capabilities.HelpPriority); + Assert.AreEqual(CommandLineResources.EnableBlameUsage, capabilities.HelpContentResourceName); + + Assert.AreEqual(false, capabilities.AllowMultiple); + Assert.AreEqual(false, capabilities.AlwaysExecute); + Assert.AreEqual(false, capabilities.IsSpecialCommand); + } + + [TestMethod] + public void InitializeShouldCreateEntryForBlameInRunSettingsIfNotAlreadyPresent() + { + var runsettingsString = string.Format(DefaultRunSettings, ""); + var runsettings = new RunSettings(); + runsettings.LoadSettingsXml(DefaultRunSettings); + this.settingsProvider.SetActiveRunSettings(runsettings); + + this.executor.Initialize(""); + + Assert.IsNotNull(this.settingsProvider.ActiveRunSettings); + Assert.AreEqual("\r\n\r\n \r\n \r\n \r\n \r\n C:\\dir\\TestResults\r\n \r\n \r\n \r\n \r\n \r\n C:\\dir\\TestResults\r\n \r\n", this.settingsProvider.ActiveRunSettings.SettingsXml); + } + + [TestMethod] + public void ExecutorInitializeShouldAddBlameLoggerInLoggerList() + { + executor.Initialize(String.Empty); + + Assert.IsTrue(testloggerManager.LoggerExist("blame")); + } + } +} diff --git a/test/vstest.console.UnitTests/Processors/EnableCodeCoverageArgumentProcessorTests.cs b/test/vstest.console.UnitTests/Processors/EnableCodeCoverageArgumentProcessorTests.cs index 4bfc6efa32..8427155cfb 100644 --- a/test/vstest.console.UnitTests/Processors/EnableCodeCoverageArgumentProcessorTests.cs +++ b/test/vstest.console.UnitTests/Processors/EnableCodeCoverageArgumentProcessorTests.cs @@ -87,6 +87,7 @@ public void InitializeShouldEnableCodeCoverageIfDisabledInRunSettings() [TestMethod] public void InitializeShouldNotDisableOtherDataCollectors() { + CollectArgumentExecutor.EnabledDataCollectors.Add("mydatacollector1"); var runsettingsString = string.Format(DefaultRunSettings, ""); var runsettings = new RunSettings(); runsettings.LoadSettingsXml(runsettingsString);