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

[NOMERGE]Relative paths lcov #1120

Closed
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
91 changes: 5 additions & 86 deletions src/coverlet.core/Reporters/CoberturaReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@

namespace Coverlet.Core.Reporters
{
internal class CoberturaReporter : IReporter
internal class CoberturaReporter : ReporterBase
{
public ReporterOutputType OutputType => ReporterOutputType.File;
public override ReporterOutputType OutputType => ReporterOutputType.File;

public string Format => "cobertura";
public override string Format => "cobertura";

public string Extension => "cobertura.xml";
public override string Extension => "cobertura.xml";

public string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
public override string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
CoverageSummary summary = new CoverageSummary();

Expand Down Expand Up @@ -149,86 +149,5 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran

return Encoding.UTF8.GetString(stream.ToArray());
}

private static IEnumerable<string> 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<string>();
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<string> 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;
}
}
}
10 changes: 5 additions & 5 deletions src/coverlet.core/Reporters/JsonReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

namespace Coverlet.Core.Reporters
{
internal class JsonReporter : IReporter
internal class JsonReporter : ReporterBase
{
public ReporterOutputType OutputType => ReporterOutputType.File;
public override ReporterOutputType OutputType => ReporterOutputType.File;

public string Format => "json";
public override string Format => "json";

public string Extension => "json";
public override string Extension => "json";

public string Report(CoverageResult result, ISourceRootTranslator _)
public override string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
return JsonConvert.SerializeObject(result.Modules, Formatting.Indented);
}
Expand Down
13 changes: 7 additions & 6 deletions src/coverlet.core/Reporters/LcovReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

namespace Coverlet.Core.Reporters
{
internal class LcovReporter : IReporter
internal class LcovReporter : ReporterBase
{
public ReporterOutputType OutputType => ReporterOutputType.File;
public override ReporterOutputType OutputType => ReporterOutputType.File;

public string Format => "lcov";
public override string Format => "lcov";

public string Extension => "info";
public override string Extension => "info";

public string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
public override string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
if (result.Parameters.DeterministicReport)
{
Expand All @@ -23,6 +23,7 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran

CoverageSummary summary = new CoverageSummary();
List<string> lcov = new List<string>();
var absolutePaths = GetBasePaths(result.Modules, result.Parameters.UseSourceLink).ToList();

foreach (var module in result.Modules)
{
Expand All @@ -32,7 +33,7 @@ public string Report(CoverageResult result, ISourceRootTranslator sourceRootTran
var docBranchCoverage = summary.CalculateBranchCoverage(doc.Value);
var docMethodCoverage = summary.CalculateMethodCoverage(doc.Value);

lcov.Add("SF:" + doc.Key);
lcov.Add("SF:" + GetRelativePathFromBase(absolutePaths, doc.Key, result.Parameters.UseSourceLink));
foreach (var @class in doc.Value)
{
foreach (var method in @class.Value)
Expand Down
10 changes: 5 additions & 5 deletions src/coverlet.core/Reporters/OpenCoverReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

namespace Coverlet.Core.Reporters
{
internal class OpenCoverReporter : IReporter
internal class OpenCoverReporter : ReporterBase
{
public ReporterOutputType OutputType => ReporterOutputType.File;
public override ReporterOutputType OutputType => ReporterOutputType.File;

public string Format => "opencover";
public override string Format => "opencover";

public string Extension => "opencover.xml";
public override string Extension => "opencover.xml";

public string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
public override string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
if (result.Parameters.DeterministicReport)
{
Expand Down
100 changes: 100 additions & 0 deletions src/coverlet.core/Reporters/ReporterBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using Coverlet.Core.Abstractions;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Coverlet.Core.Reporters
{
internal abstract class ReporterBase : IReporter
{
public abstract ReporterOutputType OutputType { get; }

public abstract string Format { get; }

public abstract string Extension { get; }

public abstract string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator);

protected static IEnumerable<string> 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<string>();
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);
});
}

protected static string GetRelativePathFromBase(IEnumerable<string> 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;
}
}
}
10 changes: 5 additions & 5 deletions src/coverlet.core/Reporters/TeamCityReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

namespace Coverlet.Core.Reporters
{
internal class TeamCityReporter : IReporter
internal class TeamCityReporter : ReporterBase
{
public ReporterOutputType OutputType => ReporterOutputType.Console;
public override ReporterOutputType OutputType => ReporterOutputType.Console;

public string Format => "teamcity";
public override string Format => "teamcity";

public string Extension => null;
public override string Extension => null;

public string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
public override string Report(CoverageResult result, ISourceRootTranslator sourceRootTranslator)
{
if (result.Parameters.DeterministicReport)
{
Expand Down
10 changes: 9 additions & 1 deletion test/coverlet.core.tests/Reporters/LcovReporterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Moq;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Xunit;

namespace Coverlet.Core.Reporters.Tests
Expand Down Expand Up @@ -33,7 +34,14 @@ 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);

Expand Down