From d4eb476a300d343f0c30904c417318c773151eef Mon Sep 17 00:00:00 2001 From: Phani Vanka Date: Sun, 18 Oct 2020 20:11:19 -0400 Subject: [PATCH 1/3] Refactored functions to determine base paths from CoberturaReporter into helper class (#263) Refactored functions to determine base paths from CoberturaReporter into helper class --- .../Abstractions/IFilePathHelper.cs | 10 ++ src/coverlet.core/Helpers/FilePathHelper.cs | 93 +++++++++++++++++++ .../Reporters/CoberturaReporter.cs | 87 +---------------- .../Helpers/FilePathHelperTests.cs | 89 ++++++++++++++++++ 4 files changed, 197 insertions(+), 82 deletions(-) create mode 100644 src/coverlet.core/Abstractions/IFilePathHelper.cs create mode 100644 src/coverlet.core/Helpers/FilePathHelper.cs create mode 100644 test/coverlet.core.tests/Helpers/FilePathHelperTests.cs diff --git a/src/coverlet.core/Abstractions/IFilePathHelper.cs b/src/coverlet.core/Abstractions/IFilePathHelper.cs new file mode 100644 index 000000000..6bf8f24e1 --- /dev/null +++ b/src/coverlet.core/Abstractions/IFilePathHelper.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Coverlet.Core.Abstractions +{ + interface IFilePathHelper + { + IEnumerable GetBasePaths(IEnumerable paths, bool useSourceLink); + string GetRelativePathFromBase(IEnumerable basePaths, string path, bool useSourceLink); + } +} diff --git a/src/coverlet.core/Helpers/FilePathHelper.cs b/src/coverlet.core/Helpers/FilePathHelper.cs new file mode 100644 index 000000000..15286cc75 --- /dev/null +++ b/src/coverlet.core/Helpers/FilePathHelper.cs @@ -0,0 +1,93 @@ +using Coverlet.Core.Abstractions; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; + + +namespace Coverlet.Core.Helpers +{ + internal class FilePathHelper : IFilePathHelper + { + public IEnumerable GetBasePaths(IEnumerable paths, bool useSourceLink) + { + /* + Workflow + + Path1 c:\dir1\dir2\file1.cs + Path2 c:\dir1\file2.cs + Path3 e:\dir1\file2.cs + + 1) Search for root dir + c:\ -> c:\dir1\dir2\file1.cs + c:\dir1\file2.cs + e:\ -> e:\dir1\file2.cs + + 2) Split path on directory separator i.e. for record c:\ ordered ascending by fragment elements + Path1 = [c:|dir1|file2.cs] + Path2 = [c:|dir1|dir2|file1.cs] + + 3) Find longest shared path comparing indexes + Path1[0] = Path2[0], ..., PathY[0] -> add to final fragment list + Path1[n] = Path2[n], ..., PathY[n] -> add to final fragment list + Path1[n+1] != Path2[n+1], ..., PathY[n+1] -> break, Path1[n] was last shared fragment + + 4) Concat created fragment list + */ + if (useSourceLink) + { + return new[] { string.Empty }; + } + + return paths.GroupBy(Directory.GetDirectoryRoot).Select(group => + { + var splittedPaths = group.Select(absolutePath => absolutePath.Split(Path.DirectorySeparatorChar)) + .OrderBy(absolutePath => absolutePath.Length).ToList(); + if (splittedPaths.Count == 1) + { + return group.Key; + } + + var basePathFragments = new List(); + bool stopSearch = false; + splittedPaths[0].Select((value, index) => (value, index)).ToList().ForEach(fragmentIndexPair => + { + if (stopSearch) + { + return; + } + + if (splittedPaths.All(sp => fragmentIndexPair.value.Equals(sp[fragmentIndexPair.index]))) + { + basePathFragments.Add(fragmentIndexPair.value); + } + else + { + stopSearch = true; + } + }); + return string.Concat(string.Join(Path.DirectorySeparatorChar.ToString(), basePathFragments), Path.DirectorySeparatorChar); + }); + } + + public string GetRelativePathFromBase(IEnumerable basePaths, string path, bool useSourceLink) + { + if (useSourceLink) + { + return path; + } + + foreach (var basePath in basePaths) + { + if (path.StartsWith(basePath)) + { + return path.Substring(basePath.Length); + } + } + + Debug.Assert(false, "Unexpected, we should find at least one path starts with one pre-build roots list"); + + return path; + } + } +} diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 291462299..cc77841e7 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -1,3 +1,4 @@ +using Coverlet.Core.Helpers; using System; using System.Collections.Generic; using System.Diagnostics; @@ -32,7 +33,9 @@ public string Report(CoverageResult result) coverage.Add(new XAttribute("timestamp", (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds)); XElement sources = new XElement("sources"); - var absolutePaths = GetBasePaths(result.Modules, result.UseSourceLink).ToList(); + FilePathHelper pathHelper = new FilePathHelper(); + + var absolutePaths = pathHelper.GetBasePaths(result.Modules.Values.SelectMany(k => k.Keys), result.UseSourceLink).ToList(); absolutePaths.ForEach(x => sources.Add(new XElement("source", x))); XElement packages = new XElement("packages"); @@ -51,7 +54,7 @@ public string Report(CoverageResult result) { XElement @class = new XElement("class"); @class.Add(new XAttribute("name", cls.Key)); - @class.Add(new XAttribute("filename", GetRelativePathFromBase(absolutePaths, document.Key, result.UseSourceLink))); + @class.Add(new XAttribute("filename", pathHelper.GetRelativePathFromBase(absolutePaths, document.Key, result.UseSourceLink))); @class.Add(new XAttribute("line-rate", (summary.CalculateLineCoverage(cls.Value).Percent / 100).ToString(CultureInfo.InvariantCulture))); @class.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(cls.Value).Percent / 100).ToString(CultureInfo.InvariantCulture))); @class.Add(new XAttribute("complexity", summary.CalculateCyclomaticComplexity(cls.Value))); @@ -134,85 +137,5 @@ public string Report(CoverageResult result) return Encoding.UTF8.GetString(stream.ToArray()); } - private static IEnumerable GetBasePaths(Modules modules, bool useSourceLink) - { - /* - Workflow - - Path1 c:\dir1\dir2\file1.cs - Path2 c:\dir1\file2.cs - Path3 e:\dir1\file2.cs - - 1) Search for root dir - c:\ -> c:\dir1\dir2\file1.cs - c:\dir1\file2.cs - e:\ -> e:\dir1\file2.cs - - 2) Split path on directory separator i.e. for record c:\ ordered ascending by fragment elements - Path1 = [c:|dir1|file2.cs] - Path2 = [c:|dir1|dir2|file1.cs] - - 3) Find longest shared path comparing indexes - Path1[0] = Path2[0], ..., PathY[0] -> add to final fragment list - Path1[n] = Path2[n], ..., PathY[n] -> add to final fragment list - Path1[n+1] != Path2[n+1], ..., PathY[n+1] -> break, Path1[n] was last shared fragment - - 4) Concat created fragment list - */ - if (useSourceLink) - { - return new[] { string.Empty }; - } - - return modules.Values.SelectMany(k => k.Keys).GroupBy(Directory.GetDirectoryRoot).Select(group => - { - var splittedPaths = group.Select(absolutePath => absolutePath.Split(Path.DirectorySeparatorChar)) - .OrderBy(absolutePath => absolutePath.Length).ToList(); - if (splittedPaths.Count == 1) - { - return group.Key; - } - - var basePathFragments = new List(); - bool stopSearch = false; - splittedPaths[0].Select((value, index) => (value, index)).ToList().ForEach(fragmentIndexPair => - { - if (stopSearch) - { - return; - } - - if (splittedPaths.All(sp => fragmentIndexPair.value.Equals(sp[fragmentIndexPair.index]))) - { - basePathFragments.Add(fragmentIndexPair.value); - } - else - { - stopSearch = true; - } - }); - return string.Concat(string.Join(Path.DirectorySeparatorChar.ToString(), basePathFragments), Path.DirectorySeparatorChar); - }); - } - - private static string GetRelativePathFromBase(IEnumerable basePaths, string path, bool useSourceLink) - { - if (useSourceLink) - { - return path; - } - - foreach (var basePath in basePaths) - { - if (path.StartsWith(basePath)) - { - return path.Substring(basePath.Length); - } - } - - Debug.Assert(false, "Unexpected, we should find at least one path starts with one pre-build roots list"); - - return path; - } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/Helpers/FilePathHelperTests.cs b/test/coverlet.core.tests/Helpers/FilePathHelperTests.cs new file mode 100644 index 000000000..5691d7bb8 --- /dev/null +++ b/test/coverlet.core.tests/Helpers/FilePathHelperTests.cs @@ -0,0 +1,89 @@ +using Coverlet.Core.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Xunit; + +namespace Coverlet.Core.Helpers.Tests +{ + public class FilePathHelperTests + { + + + [Fact] + public void TestGetBasePaths_UseSourceLink_False() + { + var absolutePaths = new List(); + var expectedBasePaths = new List(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + absolutePaths.Add(@"C:\projA\dir1\dir10\file1.cs"); + absolutePaths.Add(@"C:\projA\dir1\dir10\file2.cs"); + absolutePaths.Add(@"C:\projA\dir1\file3.cs"); + absolutePaths.Add(@"E:\projB\dir1\dir10\file4.cs"); + absolutePaths.Add(@"E:\projB\dir2\file5.cs"); + absolutePaths.Add(@"F:\file6.cs"); + absolutePaths.Add(@"F:\"); + absolutePaths.Add(@"c:\git\coverletissue\localpackagetest\deterministicbuild\ClassLibrary1\Class1.cs"); + absolutePaths.Add(@"c:\git\coverletissue\localpackagetest\deterministicbuild\ClassLibrary2\Class1.cs"); + + expectedBasePaths.Add(@"C:\projA\dir1\"); + expectedBasePaths.Add(@"E:\projB\"); + expectedBasePaths.Add(@"F:\"); + expectedBasePaths.Add(@"c:\git\coverletissue\localpackagetest\deterministicbuild\"); + } + else + { + absolutePaths.Add(@"/projA/dir1/dir10/file1.cs"); + absolutePaths.Add(@"/projA/dir1/file2.cs"); + absolutePaths.Add(@"/projA/dir1/file3.cs"); + absolutePaths.Add(@"/projA/dir2/file4.cs"); + absolutePaths.Add(@"/projA/dir2/file5.cs"); + absolutePaths.Add(@"/file1.cs"); + absolutePaths.Add(@"/"); + absolutePaths.Add(@"/git/coverletissue/localpackagetest/deterministicbuild/ClassLibrary1/Class1.cs"); + absolutePaths.Add(@"/git/coverletissue/localpackagetest/deterministicbuild/ClassLibrary2/Class1.cs"); + + + expectedBasePaths.Add(@"/"); + } + + var filePathHelper = new FilePathHelper(); + + var basePaths = filePathHelper.GetBasePaths(absolutePaths, false); + + Assert.Equal(expectedBasePaths.OrderBy(x => x), basePaths.OrderBy(x => x)); + } + + [Fact] + public void TestGetBasePaths_UseSourceLink_True() + { + var absolutePaths = new List(); + var expectedBasePaths = new List() { string.Empty }; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + absolutePaths.Add(@"C:\projA\dir1\dir10\file1.cs"); + absolutePaths.Add(@"E:\projB\dir1\dir10\file4.cs"); + absolutePaths.Add(@"F:\"); + absolutePaths.Add(@"c:\git\coverletissue\localpackagetest\deterministicbuild\ClassLibrary1\Class1.cs"); + } + else + { + absolutePaths.Add(@"/projA/dir1/dir10/file1.cs"); + absolutePaths.Add(@"/file1.cs"); + absolutePaths.Add(@"/"); + absolutePaths.Add(@"/git/coverletissue/localpackagetest/deterministicbuild/ClassLibrary1/Class1.cs"); + } + + var filePathHelper = new FilePathHelper(); + + var basePaths = filePathHelper.GetBasePaths(absolutePaths, true); + + Assert.Equal(expectedBasePaths, basePaths); + + } + + } +} From 0e97f5d6338ee27dacded22ce3a5b42ed377e4d9 Mon Sep 17 00:00:00 2001 From: Phani Vanka Date: Sun, 18 Oct 2020 22:26:19 -0400 Subject: [PATCH 2/3] Added dependency on IFilePathHelper for ReporterFactory (#263) --- .../DataCollection/CoverageManager.cs | 5 +++-- .../DataCollection/CoverletCoverageCollector.cs | 4 +++- src/coverlet.console/Program.cs | 3 ++- src/coverlet.core/Reporters/CoberturaReporter.cs | 16 +++++++++++----- src/coverlet.core/Reporters/ReporterFactory.cs | 9 ++++++--- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 2 +- .../InstrumentationTask.cs | 1 + .../CoverletCoverageDataCollectorTests.cs | 7 ++++++- .../Coverage/InstrumenterHelper.cs | 2 +- .../Reporters/CoberturaReporterTests.cs | 9 +++++---- .../Reporters/ReporterFactoryTests.cs | 14 ++++++++------ test/coverlet.core.tests/Reporters/Reporters.cs | 3 ++- 12 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/coverlet.collector/DataCollection/CoverageManager.cs b/src/coverlet.collector/DataCollection/CoverageManager.cs index 41801ff1f..bb6afa1bd 100644 --- a/src/coverlet.collector/DataCollection/CoverageManager.cs +++ b/src/coverlet.collector/DataCollection/CoverageManager.cs @@ -23,11 +23,12 @@ internal class CoverageManager public IReporter[] Reporters { get; } public CoverageManager(CoverletSettings settings, TestPlatformEqtTrace eqtTrace, TestPlatformLogger logger, ICoverageWrapper coverageWrapper, - IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper) + IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper, + IFilePathHelper filePathHelper) : this(settings, settings.ReportFormats.Select(format => { - var reporterFactory = new ReporterFactory(format); + var reporterFactory = new ReporterFactory(format, filePathHelper); if (!reporterFactory.IsValidFormat()) { eqtTrace.Warning($"Invalid report format '{format}'"); diff --git a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs index d5094dc91..7f5b1b2e0 100644 --- a/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs +++ b/src/coverlet.collector/DataCollection/CoverletCoverageCollector.cs @@ -134,7 +134,8 @@ private void OnSessionStart(object sender, SessionStartEventArgs sessionStartEve // Get coverage and attachment managers _coverageManager = new CoverageManager(coverletSettings, _eqtTrace, _logger, _coverageWrapper, _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService(), - _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService()); + _serviceProvider.GetRequiredService(), _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService()); // Instrument modules _coverageManager.InstrumentModules(); @@ -223,6 +224,7 @@ private static IServiceCollection GetDefaultServiceCollection(TestPlatformEqtTra serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); // We need to keep singleton/static semantics serviceCollection.AddSingleton(); diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 1b4cbf219..fea5427dc 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -27,6 +27,7 @@ static int Main(string[] args) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); // We need to keep singleton/static semantics serviceCollection.AddSingleton(); serviceCollection.AddSingleton(provider => new SourceRootTranslator(provider.GetRequiredService(), provider.GetRequiredService())); @@ -148,7 +149,7 @@ static int Main(string[] args) foreach (var format in (formats.HasValue() ? formats.Values : new List(new string[] { "json" }))) { - var reporter = new ReporterFactory(format).CreateReporter(); + var reporter = new ReporterFactory(format, serviceProvider.GetRequiredService()).CreateReporter(); if (reporter == null) { throw new Exception($"Specified output format '{format}' is not supported"); diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index cc77841e7..e4f2dbcb0 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -1,4 +1,4 @@ -using Coverlet.Core.Helpers; +using Coverlet.Core.Abstractions; using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,12 +12,19 @@ namespace Coverlet.Core.Reporters { internal class CoberturaReporter : IReporter { + private IFilePathHelper _filePathHelper; + public ReporterOutputType OutputType => ReporterOutputType.File; public string Format => "cobertura"; public string Extension => "cobertura.xml"; + public CoberturaReporter(IFilePathHelper filePathHelper) + { + _filePathHelper = filePathHelper; + } + public string Report(CoverageResult result) { CoverageSummary summary = new CoverageSummary(); @@ -33,9 +40,8 @@ public string Report(CoverageResult result) coverage.Add(new XAttribute("timestamp", (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds)); XElement sources = new XElement("sources"); - FilePathHelper pathHelper = new FilePathHelper(); - - var absolutePaths = pathHelper.GetBasePaths(result.Modules.Values.SelectMany(k => k.Keys), result.UseSourceLink).ToList(); + + var absolutePaths = _filePathHelper.GetBasePaths(result.Modules.Values.SelectMany(k => k.Keys), result.UseSourceLink).ToList(); absolutePaths.ForEach(x => sources.Add(new XElement("source", x))); XElement packages = new XElement("packages"); @@ -54,7 +60,7 @@ public string Report(CoverageResult result) { XElement @class = new XElement("class"); @class.Add(new XAttribute("name", cls.Key)); - @class.Add(new XAttribute("filename", pathHelper.GetRelativePathFromBase(absolutePaths, document.Key, result.UseSourceLink))); + @class.Add(new XAttribute("filename", _filePathHelper.GetRelativePathFromBase(absolutePaths, document.Key, result.UseSourceLink))); @class.Add(new XAttribute("line-rate", (summary.CalculateLineCoverage(cls.Value).Percent / 100).ToString(CultureInfo.InvariantCulture))); @class.Add(new XAttribute("branch-rate", (summary.CalculateBranchCoverage(cls.Value).Percent / 100).ToString(CultureInfo.InvariantCulture))); @class.Add(new XAttribute("complexity", summary.CalculateCyclomaticComplexity(cls.Value))); diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs index 7ac72337b..edb887b9f 100644 --- a/src/coverlet.core/Reporters/ReporterFactory.cs +++ b/src/coverlet.core/Reporters/ReporterFactory.cs @@ -1,3 +1,4 @@ +using Coverlet.Core.Abstractions; using System; using System.Linq; @@ -7,13 +8,15 @@ internal class ReporterFactory { private string _format; private IReporter[] _reporters; - - public ReporterFactory(string format) + private IFilePathHelper _filePathHelper; + + public ReporterFactory(string format, IFilePathHelper filePathHelper) { _format = format; + _filePathHelper = filePathHelper; _reporters = new IReporter[] { new JsonReporter(), new LcovReporter(), - new OpenCoverReporter(), new CoberturaReporter(), + new OpenCoverReporter(), new CoberturaReporter(_filePathHelper), new TeamCityReporter() }; } diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index fd3cdd9ca..684a2a336 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -95,7 +95,7 @@ public override bool Execute() var coverageReportPaths = new List(formats.Length); foreach (var format in formats) { - var reporter = new ReporterFactory(format).CreateReporter(); + var reporter = new ReporterFactory(format, ServiceProvider.GetRequiredService()).CreateReporter(); if (reporter == null) { throw new Exception($"Specified output format '{format}' is not supported"); diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 11bfbc6db..2f258812d 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -77,6 +77,7 @@ public override bool Execute() serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => _logger); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); // We cache resolutions serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(Path, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); // We need to keep singleton/static semantics diff --git a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs index 3c0346d20..5919e7882 100644 --- a/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs +++ b/test/coverlet.collector.tests/CoverletCoverageDataCollectorTests.cs @@ -60,6 +60,7 @@ public void OnSessionStartShouldInitializeCoverageWithCorrectCoverletSettings() serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); @@ -94,6 +95,7 @@ public void OnSessionStartShouldPrepareModulesForCoverage() serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); @@ -147,6 +149,7 @@ public void OnSessionEndShouldSendGetCoverageReportToTestPlatform() serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); @@ -195,6 +198,7 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); @@ -202,7 +206,7 @@ public void OnSessionEndShouldSendCoverageReportsForMultipleFormatsToTestPlatfor }; _coverletCoverageDataCollector = new CoverletCoverageCollector(new TestPlatformEqtTrace(), new CoverageWrapper(), _mockCountDownEventFactory.Object, serviceCollectionFactory); - IList reporters = formats.Split(',').Select(f => new ReporterFactory(f).CreateReporter()).Where(x => x != null).ToList(); + IList reporters = formats.Split(',').Select(f => new ReporterFactory(f, new FilePathHelper()).CreateReporter()).Where(x => x != null).ToList(); Mock mockDataCollectionSink = new Mock(); mockDataCollectionSink.Setup(m => m.SendFileAsync(It.IsAny())).Callback(fti => { @@ -248,6 +252,7 @@ public void OnSessionStartShouldLogWarningIfInstrumentationFailed() serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(_ => new CoverletLogger(eqtTrace, logger)); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(serviceProvider => new SourceRootTranslator(testModule, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService())); serviceCollection.AddSingleton(); diff --git a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs index e548996f1..02caf9af5 100644 --- a/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs +++ b/test/coverlet.core.tests/Coverage/InstrumenterHelper.cs @@ -29,7 +29,7 @@ static class TestInstrumentationHelper public static void GenerateHtmlReport(CoverageResult coverageResult, IReporter reporter = null, string sourceFileFilter = "", [CallerMemberName] string directory = "") { JsonReporter defaultReporter = new JsonReporter(); - reporter ??= new CoberturaReporter(); + reporter ??= new CoberturaReporter(new FilePathHelper()); DirectoryInfo dir = Directory.CreateDirectory(directory); dir.Delete(true); dir.Create(); diff --git a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs index db70e16bd..7b3e330e8 100644 --- a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs @@ -1,3 +1,4 @@ +using Coverlet.Core.Helpers; using System; using System.Collections.Generic; using System.Globalization; @@ -58,7 +59,7 @@ public void TestReport() // where decimal char is comma. Assert.Equal("1,5", (1.5).ToString()); - CoberturaReporter reporter = new CoberturaReporter(); + CoberturaReporter reporter = new CoberturaReporter(new FilePathHelper()); string report = reporter.Report(result); Assert.NotEmpty(report); @@ -139,7 +140,7 @@ public void TestEnsureParseMethodStringCorrectly( result.Modules = new Modules(); result.Modules.Add("module", documents); - CoberturaReporter reporter = new CoberturaReporter(); + CoberturaReporter reporter = new CoberturaReporter(new FilePathHelper()); string report = reporter.Report(result); Assert.NotEmpty(report); @@ -209,7 +210,7 @@ public void TestReportWithDifferentDirectories() result.Modules = new Modules { { "Module", documents } }; - CoberturaReporter reporter = new CoberturaReporter(); + CoberturaReporter reporter = new CoberturaReporter(new FilePathHelper()); string report = reporter.Report(result); var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(report))); @@ -251,7 +252,7 @@ public void TestReportWithSourcelinkPaths() result.Modules = new Modules { { "Module", documents } }; - CoberturaReporter reporter = new CoberturaReporter(); + CoberturaReporter reporter = new CoberturaReporter(new FilePathHelper()); string report = reporter.Report(result); var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(report))); diff --git a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs index e148fa378..6e806d699 100644 --- a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs +++ b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs @@ -1,3 +1,4 @@ +using Coverlet.Core.Helpers; using Coverlet.Core.Reporters; using Xunit; @@ -8,12 +9,13 @@ public class ReporterFactoryTests [Fact] public void TestCreateReporter() { - Assert.Equal(typeof(JsonReporter), new ReporterFactory("json").CreateReporter().GetType()); - Assert.Equal(typeof(LcovReporter), new ReporterFactory("lcov").CreateReporter().GetType()); - Assert.Equal(typeof(OpenCoverReporter), new ReporterFactory("opencover").CreateReporter().GetType()); - Assert.Equal(typeof(CoberturaReporter), new ReporterFactory("cobertura").CreateReporter().GetType()); - Assert.Equal(typeof(TeamCityReporter), new ReporterFactory("teamcity").CreateReporter().GetType()); - Assert.Null(new ReporterFactory("").CreateReporter()); + var filePathHelper = new FilePathHelper(); + Assert.Equal(typeof(JsonReporter), new ReporterFactory("json", filePathHelper).CreateReporter().GetType()); + Assert.Equal(typeof(LcovReporter), new ReporterFactory("lcov", filePathHelper).CreateReporter().GetType()); + Assert.Equal(typeof(OpenCoverReporter), new ReporterFactory("opencover", filePathHelper).CreateReporter().GetType()); + Assert.Equal(typeof(CoberturaReporter), new ReporterFactory("cobertura", filePathHelper).CreateReporter().GetType()); + Assert.Equal(typeof(TeamCityReporter), new ReporterFactory("teamcity", filePathHelper).CreateReporter().GetType()); + Assert.Null(new ReporterFactory("", filePathHelper).CreateReporter()); } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/Reporters/Reporters.cs b/test/coverlet.core.tests/Reporters/Reporters.cs index 1c516aeb0..51f92f893 100644 --- a/test/coverlet.core.tests/Reporters/Reporters.cs +++ b/test/coverlet.core.tests/Reporters/Reporters.cs @@ -1,6 +1,7 @@ using System.IO; using Coverlet.Core.Abstractions; +using Coverlet.Core.Helpers; using Coverlet.MSbuild.Tasks; using Moq; using Xunit; @@ -37,7 +38,7 @@ public void Msbuild_ReportWriter(string coverletMultiTargetFrameworksCurrentTFM, // mimic code inside CoverageResultTask.cs Path.GetDirectoryName(coverletOutput), coverletOutput, - new ReporterFactory(reportFormat).CreateReporter(), + new ReporterFactory(reportFormat, new FilePathHelper()).CreateReporter(), fileSystem.Object, console.Object, new CoverageResult() { Modules = new Modules() }); From 6454217d298a7fe76dfb1b64e1a708e1b1655219 Mon Sep 17 00:00:00 2001 From: Phani Vanka Date: Sun, 18 Oct 2020 22:55:48 -0400 Subject: [PATCH 3/3] Updated LcovReporter to include only relative paths for source files in report (#263) Updated LcovReporter to include only relative paths for source files in report --- src/coverlet.core/Reporters/LcovReporter.cs | 12 +++++++++++- src/coverlet.core/Reporters/ReporterFactory.cs | 2 +- .../Reporters/LcovReporterTests.cs | 14 ++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs index e8e94b68d..20e2348d2 100644 --- a/src/coverlet.core/Reporters/LcovReporter.cs +++ b/src/coverlet.core/Reporters/LcovReporter.cs @@ -1,3 +1,4 @@ +using Coverlet.Core.Abstractions; using System; using System.Linq; using System.Collections.Generic; @@ -6,17 +7,26 @@ namespace Coverlet.Core.Reporters { internal class LcovReporter : IReporter { + private IFilePathHelper _filePathHelper; + public ReporterOutputType OutputType => ReporterOutputType.File; public string Format => "lcov"; public string Extension => "info"; + public LcovReporter(IFilePathHelper filePathHelper) + { + _filePathHelper = filePathHelper; + } + public string Report(CoverageResult result) { CoverageSummary summary = new CoverageSummary(); List lcov = new List(); + var absolutePaths = _filePathHelper.GetBasePaths(result.Modules.Values.SelectMany(k => k.Keys), result.UseSourceLink).ToList(); + foreach (var module in result.Modules) { foreach (var doc in module.Value) @@ -25,7 +35,7 @@ public string Report(CoverageResult result) var docBranchCoverage = summary.CalculateBranchCoverage(doc.Value); var docMethodCoverage = summary.CalculateMethodCoverage(doc.Value); - lcov.Add("SF:" + doc.Key); + lcov.Add("SF:" + _filePathHelper.GetRelativePathFromBase(absolutePaths, doc.Key, result.UseSourceLink)); foreach (var @class in doc.Value) { foreach (var method in @class.Value) diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs index edb887b9f..384c99e97 100644 --- a/src/coverlet.core/Reporters/ReporterFactory.cs +++ b/src/coverlet.core/Reporters/ReporterFactory.cs @@ -15,7 +15,7 @@ public ReporterFactory(string format, IFilePathHelper filePathHelper) _format = format; _filePathHelper = filePathHelper; _reporters = new IReporter[] { - new JsonReporter(), new LcovReporter(), + new JsonReporter(), new LcovReporter(_filePathHelper), new OpenCoverReporter(), new CoberturaReporter(_filePathHelper), new TeamCityReporter() }; diff --git a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs index f5c888bf1..c9fa53a4b 100644 --- a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs @@ -1,5 +1,7 @@ +using Coverlet.Core.Helpers; using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using Xunit; namespace Coverlet.Core.Reporters.Tests @@ -30,11 +32,19 @@ public void TestReport() classes.Add("Coverlet.Core.Reporters.Tests.LcovReporterTests", methods); Documents documents = new Documents(); - documents.Add("doc.cs", classes); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + documents.Add(@"C:\doc.cs", classes); + } + else + { + documents.Add(@"/doc.cs", classes); + } result.Modules = new Modules(); result.Modules.Add("module", documents); - LcovReporter reporter = new LcovReporter(); + LcovReporter reporter = new LcovReporter(new FilePathHelper()); string report = reporter.Report(result); Assert.NotEmpty(report);