Generate code coverage output with coverlet (#1866)

* add support for Codecov reporting on Windows
This commit is contained in:
Stanley Goldman
2019-09-17 08:49:44 -04:00
committed by Brendan Forster
parent 60e60ca384
commit f70c402d78
12 changed files with 189 additions and 6 deletions

1
.gitignore vendored
View File

@@ -102,3 +102,4 @@ Backup/
tools/* tools/*
!tools/gitversion_wrapper.sh !tools/gitversion_wrapper.sh
!tools/LINQPad !tools/LINQPad
coverage-results/*

View File

@@ -2,6 +2,7 @@
[![Build status](https://ci.appveyor.com/api/projects/status/cego2g42yw26th26/branch/master?svg=true)](https://ci.appveyor.com/project/github-windows/octokit-net/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/cego2g42yw26th26/branch/master?svg=true)](https://ci.appveyor.com/project/github-windows/octokit-net/branch/master)
[![Build Status]( https://travis-ci.org/octokit/octokit.net.svg)]( https://travis-ci.org/octokit/octokit.net) [![Build Status]( https://travis-ci.org/octokit/octokit.net.svg)]( https://travis-ci.org/octokit/octokit.net)
[![codecov](https://codecov.io/gh/octokit/octokit.net/branch/master/graph/badge.svg)](https://codecov.io/gh/octokit/octokit.net)
[![Join the chat at https://gitter.im/octokit/octokit.net](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/octokit/octokit.net?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/octokit/octokit.net](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/octokit/octokit.net?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![NuGet](http://img.shields.io/nuget/v/Octokit.svg)](https://www.nuget.org/packages/Octokit) [![NuGet](http://img.shields.io/nuget/v/Octokit.svg)](https://www.nuget.org/packages/Octokit)
[![NuGet](http://img.shields.io/nuget/v/Octokit.Reactive.svg)](https://www.nuget.org/packages/Octokit.Reactive) [![NuGet](http://img.shields.io/nuget/v/Octokit.Reactive.svg)](https://www.nuget.org/packages/Octokit.Reactive)

View File

@@ -9,6 +9,7 @@ init:
build_script: build_script:
- dotnet --info - dotnet --info
- dotnet tool install --global coverlet.console
- ps: .\build.ps1 -LinkSources -Verbosity Verbose - ps: .\build.ps1 -LinkSources -Verbosity Verbose
test: off test: off

View File

@@ -6,7 +6,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Cake.Coverlet" Version="1.3.1" />
<PackageReference Include="Cake.Frosting" Version="0.34.1" /> <PackageReference Include="Cake.Frosting" Version="0.34.1" />
<PackageReference Include="Cake.Codecov" Version="0.4.0" />
<PackageReference Include="Codecov" Version="1.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -13,6 +13,7 @@ public class Context : FrostingContext
public BuildVersion Version { get; set; } public BuildVersion Version { get; set; }
public DirectoryPath Artifacts { get; set; } public DirectoryPath Artifacts { get; set; }
public DirectoryPath CodeCoverage { get; set; }
public bool IsLocalBuild { get; set; } public bool IsLocalBuild { get; set; }
public bool IsPullRequest { get; set; } public bool IsPullRequest { get; set; }

View File

@@ -15,6 +15,7 @@ public class Lifetime : FrostingLifetime<Context>
context.CoreOnly = context.Argument("CoreOnly", !context.IsRunningOnWindows()); context.CoreOnly = context.Argument("CoreOnly", !context.IsRunningOnWindows());
context.Artifacts = "./packaging/"; context.Artifacts = "./packaging/";
context.CodeCoverage = "./coverage-results/";
// Build system information. // Build system information.
var buildSystem = context.BuildSystem(); var buildSystem = context.BuildSystem();

View File

@@ -13,7 +13,8 @@ public sealed class Clean : FrostingTask<Context>
var directories = context.GetDirectories("./**/bin", globberSettings) var directories = context.GetDirectories("./**/bin", globberSettings)
+ context.GetDirectories("./**/obj", globberSettings) + context.GetDirectories("./**/obj", globberSettings)
+ context.Artifacts; + context.Artifacts
+ context.CodeCoverage;
foreach (var directory in directories) foreach (var directory in directories)
{ {

View File

@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Linq;
using Cake.Codecov;
using Cake.Common;
using Cake.Common.Build;
using Cake.Common.Diagnostics;
using Cake.Core.IO;
using Cake.Frosting;
[Dependency(typeof(Build))]
public sealed class CodeCoverage : FrostingTask<Context>
{
public override void Run(Context context)
{
var coverageFiles = new List<FilePath>();
if (context.AppVeyor)
{
foreach (var project in context.Projects.Where(x => x.UnitTests))
{
context.Information("Executing Code Coverage for Project {0}...", project.Name);
var dotNetCoreCoverage = context.CodeCoverage
.CombineWithFilePath(project.Name + "-netcoreapp2.0.xml");
coverageFiles.Add(dotNetCoreCoverage);
context.Coverlet(project, new CoverletToolSettings()
{
Configuration = context.Configuration,
Framework = "netcoreapp2.0",
Output = dotNetCoreCoverage.FullPath
});
if (context.IsRunningOnWindows())
{
var dotNetFrameworkCoverage = context.CodeCoverage
.CombineWithFilePath(project.Name + "-net452.xml");
coverageFiles.Add(dotNetFrameworkCoverage);
context.Coverlet(project, new CoverletToolSettings
{
Configuration = context.Configuration,
Framework = "net452",
Output = dotNetFrameworkCoverage.FullPath
});
}
context.Information("Uploading Coverage Files: {0}", string.Join(",", coverageFiles.Select(path => path.GetFilename().ToString())));
var buildVersion = $"{context.Version.FullSemVer}.build.{context.EnvironmentVariable("APPVEYOR_BUILD_NUMBER")}";
var userProfilePath = context.EnvironmentVariable("USERPROFILE");
var codecovPath = new DirectoryPath(userProfilePath)
.CombineWithFilePath(".nuget\\packages\\codecov\\1.1.0\\tools\\codecov.exe");
context.Tools.RegisterFile(codecovPath);
foreach (var coverageFile in coverageFiles)
{
var settings = new CodecovSettings
{
Files = new[] { coverageFile.MakeAbsolute(context.Environment).FullPath },
Verbose = true,
EnvironmentVariables = new Dictionary<string, string>()
{
{ "APPVEYOR_BUILD_VERSION", buildVersion}
}
};
context.Codecov(settings);
}
}
}
}
}

View File

@@ -6,6 +6,7 @@ using Cake.Frosting;
[Dependency(typeof(UnitTests))] [Dependency(typeof(UnitTests))]
[Dependency(typeof(ConventionTests))] [Dependency(typeof(ConventionTests))]
[Dependency(typeof(CodeCoverage))]
[Dependency(typeof(ValidateLINQPadSamples))] [Dependency(typeof(ValidateLINQPadSamples))]
public sealed class Package : FrostingTask<Context> public sealed class Package : FrostingTask<Context>
{ {

62
build/Tools/Coverlet.cs Normal file
View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using Cake.Common.Diagnostics;
using Cake.Core;
using Cake.Core.Annotations;
using Cake.Core.IO;
using Cake.Core.Tooling;
public class CoverletTool : Tool<CoverletToolSettings>
{
private readonly ICakeEnvironment _environment;
public CoverletTool(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner runner, IToolLocator tools)
: base(fileSystem, environment, runner, tools)
{
_environment = environment;
}
public void Coverlet(Project project, CoverletToolSettings settings)
{
var arguments = new ProcessArgumentBuilder();
var filePath = FilePath.FromString($"bin\\{settings.Configuration}\\{settings.Framework}\\{project.Name}.dll");
var fullPath = project.Path.GetDirectory().CombineWithFilePath(filePath).MakeAbsolute(_environment);
arguments.Append($"\"{fullPath}\" --target \"dotnet\" --targetargs \"test -c {settings.Configuration} {project.Path.FullPath} --no-build\" --format opencover --output \"{settings.Output}\"");
Run(settings, arguments);
}
protected override string GetToolName()
{
return "Coverlet";
}
protected override IEnumerable<string> GetToolExecutableNames()
{
return new[] { "coverlet", "coverlet.exe" };
}
}
public class CoverletToolSettings : ToolSettings
{
public string Configuration { get; set; }
public string Framework { get; set; }
public string Output { get; set; }
}
public static class CoverletAliases
{
[CakeMethodAlias]
public static void Coverlet(this ICakeContext context, Project project, CoverletToolSettings settings = null)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (settings == null)
throw new ArgumentNullException(nameof(settings));
new CoverletTool(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools).Coverlet(project, settings);
}
}

View File

@@ -6,11 +6,13 @@ public class BuildVersion
{ {
public string Prefix { get; set; } public string Prefix { get; set; }
public string Suffix { get; set; } public string Suffix { get; set; }
public string FullSemVer { get; set; }
public BuildVersion(string version, string suffix) public BuildVersion(string version, string suffix, string fullSemVer)
{ {
Prefix = version; Prefix = version;
Suffix = suffix; Suffix = suffix;
FullSemVer = fullSemVer;
if (string.IsNullOrWhiteSpace(Suffix)) if (string.IsNullOrWhiteSpace(Suffix))
{ {
@@ -29,6 +31,10 @@ public class BuildVersion
public static BuildVersion Calculate(Context context) public static BuildVersion Calculate(Context context)
{ {
string version = null;
string semVersion = null;
string fullSemVer = null;
context.Information("Calculating semantic version..."); context.Information("Calculating semantic version...");
if (!context.IsLocalBuild) if (!context.IsLocalBuild)
@@ -40,14 +46,15 @@ public class BuildVersion
// Run in interactive mode to get the properties for the rest of the script // Run in interactive mode to get the properties for the rest of the script
var assertedversions = GitVersionRunner.Run(context, GitVersionOutput.Json); var assertedversions = GitVersionRunner.Run(context, GitVersionOutput.Json);
var version = assertedversions.MajorMinorPatch; version = assertedversions.MajorMinorPatch;
var semVersion = assertedversions.LegacySemVerPadded; semVersion = assertedversions.LegacySemVerPadded;
fullSemVer = assertedversions.FullSemVer;
if (string.IsNullOrWhiteSpace(version)) if (string.IsNullOrWhiteSpace(version))
{ {
throw new CakeException("Could not calculate version of build."); throw new CakeException("Could not calculate version of build.");
} }
return new BuildVersion(version, semVersion.Substring(version.Length).TrimStart('-')); return new BuildVersion(version, semVersion.Substring(version.Length).TrimStart('-'), fullSemVer);
} }
} }

29
codecov.yml Normal file
View File

@@ -0,0 +1,29 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project: yes
patch: yes
changes: no
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "header, diff, files"
behavior: once
require_changes: no
fixes:
- "/C/projects/octokit-net/::"