Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
2 changes: 1 addition & 1 deletion src/coverlet.core/Coverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ public CoverageResult GetCoverageResult()
}
}

var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results };
var coverageResult = new CoverageResult { Identifier = _identifier, Modules = modules, InstrumentedResults = _results, UseSourceLink = _useSourceLink };

if (!string.IsNullOrEmpty(_mergeWith) && !string.IsNullOrWhiteSpace(_mergeWith) && _fileSystem.Exists(_mergeWith))
{
Expand Down
1 change: 1 addition & 0 deletions src/coverlet.core/CoverageResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class CoverageResult
{
public string Identifier;
public Modules Modules;
public bool UseSourceLink;
internal List<InstrumenterResult> InstrumentedResults;

internal CoverageResult() { }
Expand Down
37 changes: 35 additions & 2 deletions src/coverlet.core/Reporters/CoberturaReporter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
Expand Down Expand Up @@ -30,7 +32,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");
sources.Add(new XElement("source", string.Empty));
var rootDirs = GetRootDirs(result.Modules, result.UseSourceLink).ToList();
rootDirs.ForEach(x => sources.Add(new XElement("source", x)));

XElement packages = new XElement("packages");
foreach (var module in result.Modules)
Expand All @@ -48,7 +51,7 @@ public string Report(CoverageResult result)
{
XElement @class = new XElement("class");
@class.Add(new XAttribute("name", cls.Key));
@class.Add(new XAttribute("filename", document.Key));
@class.Add(new XAttribute("filename", GetRelativePathFromBase(rootDirs, 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)));
Expand Down Expand Up @@ -129,5 +132,35 @@ public string Report(CoverageResult result)

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

private static IEnumerable<string> GetRootDirs(Modules modules, bool useSourceLink)
{
if (useSourceLink)
{
return new[] { string.Empty };
}

return modules.Values.SelectMany(k => k.Keys).Select(Directory.GetDirectoryRoot).Distinct();
}

private static string GetRelativePathFromBase(IEnumerable<string> rootPaths, string path, bool useSourceLink)
{
if (useSourceLink)
{
return path;
}

foreach (var root in rootPaths)
{
if (path.StartsWith(root))
{
return path.Substring(root.Length);
}
}

Debug.Assert(false, "Unexpected, we should find at least one path starts with one pre-build roots list");

return path;
}
}
}
84 changes: 80 additions & 4 deletions test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using Xunit;

Expand Down Expand Up @@ -37,7 +36,15 @@ public void TestReport()
classes.Add("Coverlet.Core.Reporters.Tests.CoberturaReporterTests", 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 Expand Up @@ -102,7 +109,14 @@ public void TestEnsureParseMethodStringCorrectly(
classes.Add("Google.Protobuf.Reflection.MessageDescriptor", 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 All @@ -120,5 +134,67 @@ public void TestEnsureParseMethodStringCorrectly(
Assert.Equal(expectedMethodName, methodAttrs["name"]);
Assert.Equal(expectedSignature, methodAttrs["signature"]);
}

[Fact]
public void TestReportWithTwoDifferentDirectories()
{
CoverageResult result = new CoverageResult();
result.Identifier = Guid.NewGuid().ToString();

var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

string absolutePath1;
string absolutePath2;

if (isWindows)
{
absolutePath1 = @"C:\projA\file.cs";
absolutePath2 = @"E:\projB\file.cs";
}
else
{
absolutePath1 = @"/projA/file.cs";
absolutePath2 = @"/projB/file.cs";
}

var classes = new Classes { { "Class", new Methods() } };
var documents = new Documents { { absolutePath1, classes }, { absolutePath2, classes } };

result.Modules = new Modules { { "Module", documents } };

CoberturaReporter reporter = new CoberturaReporter();
string report = reporter.Report(result);

var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(report)));
Copy link
Collaborator

@MarcoRossignoli MarcoRossignoli Dec 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about

List<string> rootPaths = doc.Element("coverage").Element("sources").Elements().Select(e => e.Value).ToList();
List<string> relativePaths = doc.Element("coverage").Element("packages").Element("package").Element("classes").Elements().Select(e => e.Attribute("filename").Value).ToList();
List<string> possiblePaths = new List<string>();
foreach (string root in rootPaths)
{
    foreach (string relativePath in relativePaths)
    {
        possiblePaths.Add(Path.Combine(root, relativePath));
    }
}
            
Assert.Contains(absolutePath1, possiblePaths);
Assert.Contains(absolutePath2, possiblePaths);

This should be the expected alg of every reporter that can read a cobertura format...we try every permutation root+path and check if exists

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I changed it that way.

var rootPaths = doc.Descendants().Elements().Where(tag => tag.Name.LocalName.Equals("source"))
.Select(elem => elem.Value).ToList();
var relativePaths = doc.Descendants().Elements().Where(tag => tag.Name.LocalName.Equals("class"))
.Attributes("filename").Select(attr => attr.Value).ToList();

Assert.Equal(absolutePath1, Path.Combine(rootPaths[0], relativePaths[0]));
Assert.Equal(absolutePath2, Path.Combine(isWindows ? rootPaths[1] : rootPaths[0], relativePaths[1]));
}

[Fact]
public void TestReportWithSourcelinkPaths()
{
CoverageResult result = new CoverageResult { UseSourceLink = true, Identifier = Guid.NewGuid().ToString() };

var absolutePath = @"https://raw.githubusercontent.com/johndoe/Coverlet/02c09baa8bfdee3b6cdf4be89bd98c8157b0bc08/Demo.cs";

var classes = new Classes { { "Class", new Methods() } };
var documents = new Documents { { absolutePath, classes } };

result.Modules = new Modules { { "Module", documents } };

CoberturaReporter reporter = new CoberturaReporter();
string report = reporter.Report(result);

var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(report)));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok maybe cleanup a bit with doc.Element("coverage").Element("packages").Element("package").Element("classes").Elements().Select(e => e.Attribute("filename").Value).Single();

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var fileNames = doc.Descendants().Elements().Where(tag => tag.Name.LocalName.Equals("class"))
.Attributes("filename").Select(attr => attr.Value).ToList();

Assert.Equal(absolutePath, fileNames[0]);
}
}
}