-
Notifications
You must be signed in to change notification settings - Fork 326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blame for vstest #915
Blame for vstest #915
Changes from 5 commits
d6ee1f4
af00f67
46f87dd
6922cb9
1e9b6ca
1d352f3
5663e13
35e5cf8
8599465
57c24b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// 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; | ||
|
||
/// <summary> | ||
/// The blame collector. | ||
/// </summary> | ||
[DataCollectorFriendlyName("Blame")] | ||
[DataCollectorTypeUri("datacollector://Microsoft/TestPlatform/Extensions/Blame")] | ||
public class BlameCollector : DataCollector, ITestExecutionEnvironmentSpecifier | ||
{ | ||
private DataCollectionSink dataCollectionSink; | ||
private DataCollectionEnvironmentContext context; | ||
private DataCollectionEvents events; | ||
private List<TestCase> testSequence; | ||
private IBlameReaderWriter blameReaderWriter; | ||
private int testStartCount; | ||
private int testEndCount; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BlameCollector"/> class. | ||
/// Using XmlReaderWriter by default | ||
/// </summary> | ||
public BlameCollector() | ||
: this(new XmlReaderWriter()) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BlameCollector"/> class. | ||
/// </summary> | ||
/// <param name="blameReaderWriter"> | ||
/// BlameReaderWriter instance. | ||
/// </param> | ||
protected BlameCollector(IBlameReaderWriter blameReaderWriter) | ||
{ | ||
this.blameReaderWriter = blameReaderWriter; | ||
} | ||
|
||
/// <summary> | ||
/// Gets environment variables that should be set in the test execution environment | ||
/// </summary> | ||
/// <returns>Environment variables that should be set in the test execution environment</returns> | ||
public IEnumerable<KeyValuePair<string, string>> GetTestExecutionEnvironmentVariables() | ||
{ | ||
return Enumerable.Empty<KeyValuePair<string, string>>(); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes parameters for the new instance of the class <see cref="BlameDataCollector"/> | ||
/// </summary> | ||
/// <param name="configurationElement">The Xml Element to save to</param> | ||
/// <param name="events">Data collection events to which methods subscribe</param> | ||
/// <param name="dataSink">A data collection sink for data transfer</param> | ||
/// <param name="logger">Data Collection Logger to send messages to the client </param> | ||
/// <param name="environmentContext">Context of data collector environment</param> | ||
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.testSequence = new List<TestCase>(); | ||
|
||
// Subscribing to events | ||
this.events.SessionEnd += this.SessionEnded_Handler; | ||
this.events.TestCaseStart += this.EventsTestCaseStart; | ||
this.events.TestCaseEnd += this.EventsTestCaseEnd; | ||
} | ||
|
||
/// <summary> | ||
/// Called when Test Case Start event is invoked | ||
/// </summary> | ||
private void EventsTestCaseStart(object sender, TestCaseStartEventArgs e) | ||
{ | ||
if(EqtTrace.IsInfoEnabled) | ||
{ | ||
EqtTrace.Info("Blame Collector : Test Case Start"); | ||
} | ||
this.testSequence.Add(e.TestElement); | ||
this.testStartCount++; | ||
} | ||
|
||
/// <summary> | ||
/// Called when Test Case End event is invoked | ||
/// </summary> | ||
private void EventsTestCaseEnd(object sender, TestCaseEndEventArgs e) | ||
{ | ||
if (EqtTrace.IsInfoEnabled) | ||
{ | ||
EqtTrace.Info("Blame Collector : Test Case End"); | ||
} | ||
this.testEndCount++; | ||
} | ||
|
||
/// <summary> | ||
/// Called when Session End event is invoked | ||
/// </summary> | ||
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 send write the sequence | ||
if (this.testStartCount > this.testEndCount) | ||
{ | ||
var filepath = Path.Combine(AppContext.BaseDirectory, Constants.AttachmentFileName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check filepath |
||
filepath = this.blameReaderWriter.WriteTestSequence(this.testSequence, filepath); | ||
var fileTranferInformation = new FileTransferInformation(this.context.SessionDataCollectionContext, filepath, true); | ||
this.dataCollectionSink.SendFileAsync(fileTranferInformation); | ||
} | ||
|
||
this.DeregisterEvents(); | ||
} | ||
|
||
/// <summary> | ||
/// Method to deregister handlers and cleanup | ||
/// </summary> | ||
private void DeregisterEvents() | ||
{ | ||
this.events.SessionEnd -= this.SessionEnded_Handler; | ||
this.events.TestCaseStart -= this.EventsTestCaseStart; | ||
this.events.TestCaseEnd -= this.EventsTestCaseEnd; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// 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; | ||
|
||
/// <summary> | ||
/// The blame logger. | ||
/// </summary> | ||
[FriendlyName(BlameLogger.FriendlyName)] | ||
[ExtensionUri(BlameLogger.ExtensionUri)] | ||
public class BlameLogger : ITestLogger | ||
{ | ||
#region Constants | ||
|
||
/// <summary> | ||
/// Uri used to uniquely identify the Blame logger. | ||
/// </summary> | ||
public const string ExtensionUri = "logger://Microsoft/TestPlatform/Extensions/Blame"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Version |
||
|
||
/// <summary> | ||
/// Alternate user friendly string to uniquely identify the Blame logger. | ||
/// </summary> | ||
public const string FriendlyName = "Blame"; | ||
|
||
/// <summary> | ||
/// The blame reader writer. | ||
/// </summary> | ||
private readonly IBlameReaderWriter blameReaderWriter; | ||
|
||
/// <summary> | ||
/// The output. | ||
/// </summary> | ||
private readonly IOutput output; | ||
|
||
#endregion | ||
|
||
#region Constructor | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="BlameLogger"/> class. | ||
/// </summary> | ||
public BlameLogger() | ||
: this(ConsoleOutput.Instance, new XmlReaderWriter()) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Constructor added for testing purpose | ||
/// </summary> | ||
/// <param name="output">Output Instance</param> | ||
/// <param name="blameReaderWriter">BlameReaderWriter Instance</param> | ||
protected BlameLogger(IOutput output, IBlameReaderWriter blameReaderWriter) | ||
{ | ||
this.output = output; | ||
this.blameReaderWriter = blameReaderWriter; | ||
} | ||
|
||
#endregion | ||
|
||
#region ITestLogger | ||
|
||
/// <summary> | ||
/// Initializes the Logger. | ||
/// </summary> | ||
/// <param name="events">Events that can be registered for.</param> | ||
/// <param name="testRunDictionary">Test Run Directory</param> | ||
public void Initialize(TestLoggerEvents events, string testRunDictionary) | ||
{ | ||
if (events == null) | ||
{ | ||
throw new ArgumentNullException(nameof(events)); | ||
} | ||
events.TestRunComplete += this.TestRunCompleteHandler; | ||
} | ||
|
||
/// <summary> | ||
/// Called when a test run is complete. | ||
/// </summary> | ||
private void TestRunCompleteHandler(object sender, TestRunCompleteEventArgs e) | ||
{ | ||
ValidateArg.NotNull<object>(sender, "sender"); | ||
ValidateArg.NotNull<TestRunCompleteEventArgs>(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 | ||
|
||
/// <summary> | ||
/// Fetches faulty test case | ||
/// </summary> | ||
/// <param name="e"> | ||
/// The TestRunCompleteEventArgs. | ||
/// </param> | ||
/// <returns> | ||
/// Faulty test case name | ||
/// </returns> | ||
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[testCaseList.Count - 1]; | ||
return testcase.FullyQualifiedName; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LastOrDefault |
||
} | ||
} | ||
|
||
return string.Empty; | ||
} | ||
} | ||
|
||
return string.Empty; | ||
} | ||
|
||
#endregion | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Require change in build.sh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this assembly be added for signing?