Skip to content
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

Change logic of code coverage attachments merging to merge only files with coverage extension #2671

Merged
merged 1 commit into from
Dec 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ namespace Microsoft.VisualStudio.TestPlatform.Utilities
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.VisualStudio.Coverage.CoreLib.Net;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;

public class CodeCoverageDataAttachmentsHandler : IDataCollectorAttachmentProcessor
{
Expand All @@ -37,17 +35,41 @@ public async Task<ICollection<AttachmentSet>> ProcessAttachmentSetsAsync(ICollec
{
if (attachments != null && attachments.Any())
{
var codeCoverageFiles = attachments.Select(coverageAttachment => coverageAttachment.Attachments[0].Uri.LocalPath).ToArray();
var outputFile = await this.MergeCodeCoverageFilesAsync(codeCoverageFiles, progressReporter, cancellationToken).ConfigureAwait(false);
var attachmentSet = new AttachmentSet(CodeCoverageDataCollectorUri, CoverageFriendlyName);
var coverageReportFilePaths = new List<string>();
var coverageOtherFilePaths = new List<string>();

if (!string.IsNullOrEmpty(outputFile))
foreach (var attachmentSet in attachments)
{
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri(outputFile), CoverageFriendlyName));
return new Collection<AttachmentSet> { attachmentSet };
foreach (var attachment in attachmentSet.Attachments)
{
if (attachment.Uri.LocalPath.EndsWith(CoverageFileExtension, StringComparison.OrdinalIgnoreCase))
{
coverageReportFilePaths.Add(attachment.Uri.LocalPath);
}
else
{
coverageOtherFilePaths.Add(attachment.Uri.LocalPath);
}
}
}

if(coverageReportFilePaths.Count > 1)
{
var mergedCoverageReportFilePath = await this.MergeCodeCoverageFilesAsync(coverageReportFilePaths, progressReporter, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(mergedCoverageReportFilePath))
{
var resultAttachmentSet = new AttachmentSet(CodeCoverageDataCollectorUri, CoverageFriendlyName);
resultAttachmentSet.Attachments.Add(new UriDataAttachment(new Uri(mergedCoverageReportFilePath), CoverageFriendlyName));

foreach (var coverageOtherFilePath in coverageOtherFilePaths)
{
resultAttachmentSet.Attachments.Add(new UriDataAttachment(new Uri(coverageOtherFilePath), string.Empty));
}

return new Collection<AttachmentSet> { resultAttachmentSet };
}
}

// In case merging fails(esp in dotnet core we cannot merge), so return filtered list of Code Coverage Attachments
return attachments;
}

Expand All @@ -56,20 +78,16 @@ public async Task<ICollection<AttachmentSet>> ProcessAttachmentSetsAsync(ICollec

private async Task<string> MergeCodeCoverageFilesAsync(IList<string> files, IProgress<int> progressReporter, CancellationToken cancellationToken)
{
if (files.Count == 1)
jakubch1 marked this conversation as resolved.
Show resolved Hide resolved
{
return files[0];
}

try
{
// Warning: Don't remove this method call.
//
// We took a dependency on Coverage.CoreLib.Net. In the unlikely case it cannot be
// resolved, this method call will throw an exception that will be caught and
// absorbed here.
await this.MergeCodeCoverageFilesAsync(files, cancellationToken).ConfigureAwait(false);
var result = await this.MergeCodeCoverageFilesAsync(files, cancellationToken).ConfigureAwait(false);
progressReporter?.Report(100);
return result;
}
catch (OperationCanceledException)
{
Expand All @@ -83,10 +101,10 @@ private async Task<string> MergeCodeCoverageFilesAsync(IList<string> files, IPro
ex.ToString());
}

return files[0];
return null;
}

private async Task MergeCodeCoverageFilesAsync(IList<string> files, CancellationToken cancellationToken)
private async Task<string> MergeCodeCoverageFilesAsync(IList<string> files, CancellationToken cancellationToken)
{
var coverageUtility = new CoverageFileUtility();

Expand All @@ -95,6 +113,20 @@ private async Task MergeCodeCoverageFilesAsync(IList<string> files, Cancellation
cancellationToken).ConfigureAwait(false);

coverageUtility.WriteCoverageFile(files[0], coverageData);

foreach(var file in files.Skip(1))
{
try
{
File.Delete(file);
fhnaseer marked this conversation as resolved.
Show resolved Hide resolved
}
catch (Exception ex)
{
EqtTrace.Error($"CodeCoverageDataCollectorAttachmentsHandler: Failed to remove {file}. Error: {ex}");
}
}

return files[0];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,62 @@ public async Task HandleDataCollectionAttachmentSetsShouldReturnEmptySetWhenNoAt
[TestMethod]
public async Task HandleDataCollectionAttachmentSetsShouldReturnInputIfOnly1Attachment()
{
var attachmentSet = new AttachmentSet(new Uri("//badrui//"), string.Empty);
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa"), "coverage"));
var attachmentSet = new AttachmentSet(new Uri("datacollector://microsoft/CodeCoverage/2.0"), string.Empty);
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa.coverage"), "coverage"));

Collection<AttachmentSet> attachment = new Collection<AttachmentSet> { attachmentSet };
ICollection<AttachmentSet> resultAttachmentSets = await
coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, CancellationToken.None);

Assert.IsNotNull(resultAttachmentSets);
Assert.IsTrue(resultAttachmentSets.Count == 1);
Assert.IsTrue(resultAttachmentSets.First().Attachments.Count == 1);
Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", resultAttachmentSets.First().Uri.AbsoluteUri);
Assert.AreEqual("file:///C:/temp/aa", resultAttachmentSets.First().Attachments.First().Uri.AbsoluteUri);
Assert.AreEqual("file:///C:/temp/aa.coverage", resultAttachmentSets.First().Attachments.First().Uri.AbsoluteUri);
}

[TestMethod]
public async Task HandleDataCollectionAttachmentSetsShouldReturnInputIfOnly1LogsAttachment()
{
var attachmentSet = new AttachmentSet(new Uri("datacollector://microsoft/CodeCoverage/2.0"), string.Empty);
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa.logs"), "coverage"));

Collection<AttachmentSet> attachment = new Collection<AttachmentSet> { attachmentSet };
ICollection<AttachmentSet> resultAttachmentSets = await
coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, CancellationToken.None);

Assert.IsNotNull(resultAttachmentSets);
Assert.IsTrue(resultAttachmentSets.Count == 1);
Assert.IsTrue(resultAttachmentSets.First().Attachments.Count == 1);
Assert.AreEqual("datacollector://microsoft/CodeCoverage/2.0", resultAttachmentSets.First().Uri.AbsoluteUri);
Assert.AreEqual("file:///C:/temp/aa.logs", resultAttachmentSets.First().Attachments.First().Uri.AbsoluteUri);
}

[TestMethod]
public async Task HandleDataCollectionAttachmentSetsShouldReturnInputIfOnlySeveralLogsAttachmentAnd1Report()
{
var attachmentSet = new AttachmentSet(new Uri("datacollector://microsoft/CodeCoverage/2.0"), string.Empty);
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa.coverage"), "coverage"));

var attachmentSet1 = new AttachmentSet(new Uri("datacollector://microsoft/CodeCoverage/2.0"), string.Empty);
attachmentSet1.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa.logs"), "coverage"));
attachmentSet1.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\bb.logs"), "coverage"));

Collection<AttachmentSet> attachment = new Collection<AttachmentSet> { attachmentSet, attachmentSet1 };
ICollection<AttachmentSet> resultAttachmentSets = await
coverageDataAttachmentsHandler.ProcessAttachmentSetsAsync(attachment, mockProgressReporter.Object, null, CancellationToken.None);

Assert.IsNotNull(resultAttachmentSets);
Assert.IsTrue(resultAttachmentSets.Count == 2);
Assert.IsTrue(resultAttachmentSets.First().Attachments.Count == 1);
Assert.IsTrue(resultAttachmentSets.Last().Attachments.Count == 2);
}

[TestMethod]
public async Task HandleDataCollectionAttachmentSetsShouldThrowIfCancellationRequested()
{
var attachmentSet = new AttachmentSet(new Uri("//badrui//"), string.Empty);
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa"), "coverage"));
attachmentSet.Attachments.Add(new UriDataAttachment(new Uri("C:\\temp\\aa.coverage"), "coverage"));
CancellationTokenSource cts = new CancellationTokenSource();
cts.Cancel();

Expand Down