From b0170ef9bdda828f2037a5eae5e80b9e1c0cf7f2 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 31 May 2023 11:38:48 +0200 Subject: [PATCH 01/28] use System.CommandLine for coverlet.console --- .editorconfig | 3 + Directory.Build.props | 10 +- Directory.Build.targets | 3 +- exclusion.dic | 0 src/coverlet.console/Program.cs | 198 ++++++++++--------- src/coverlet.console/coverlet.console.csproj | 3 +- 6 files changed, 122 insertions(+), 95 deletions(-) create mode 100644 exclusion.dic diff --git a/.editorconfig b/.editorconfig index 1227fcc9b..b60f1f34e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,9 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true +spelling_exclusion_path = ./exclusion.dic +spelling_languages = en-us + # XML project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 diff --git a/Directory.Build.props b/Directory.Build.props index a075f6f8b..123e8ebac 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,7 +25,13 @@ - - + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/Directory.Build.targets b/Directory.Build.targets index d970b1531..3d2910136 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,6 +1,5 @@ - @@ -25,6 +24,8 @@ + + diff --git a/exclusion.dic b/exclusion.dic new file mode 100644 index 000000000..e69de29bb diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index d967f90d6..39a115224 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -6,9 +6,12 @@ using System.ComponentModel; using System.Diagnostics; using System.Globalization; +using System.Threading.Tasks; using System.IO; using System.Linq; using System.Text; +using System.CommandLine; +using System.CommandLine.NamingConventionBinder; using ConsoleTables; using Coverlet.Console.Logging; using Coverlet.Core; @@ -17,15 +20,70 @@ using Coverlet.Core.Helpers; using Coverlet.Core.Reporters; using Coverlet.Core.Symbols; -using McMaster.Extensions.CommandLineUtils; using Microsoft.Extensions.DependencyInjection; +using System.Reflection; +using Newtonsoft.Json; namespace Coverlet.Console { - class Program + public static class Program { - static int Main(string[] args) + public static async Task Main(string[] args) { + var root = new RootCommand + { + new Argument("path", "Path to the test assembly or application directory."), + new Option(new[] { "--target", "-t" }, "Path to the test runner application."){Arity = ArgumentArity.ZeroOrOne,IsRequired = true}, + new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner."){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--output", "-o" }, "Output of the generated coverage report"){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed."){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--formats", "-f" }, () => new[] {"json"} , "Format of the generated coverage report."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--threshold", "Exits with error if the coverage % is below value."){Arity = ArgumentArity.ZeroOrOne}, + new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.") {Arity = ArgumentArity.ZeroOrMore}, + new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude", "Filter expressions to exclude specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--include", "Filter expressions to include only specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-by-file", "Glob patterns specifying source files to exclude."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--include-directory", "Include directories containing additional assemblies to be instrumented."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }, + new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }, + new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location"){ Arity = ArgumentArity.Zero }, + new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }, + new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }, + new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }, + new Option("--does-not-return-attribute", "Attributes that mark methods that do not return"){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents."){ Arity = ArgumentArity.ZeroOrOne } + }.WithHandler(nameof(HandleCommandAsync)); + + root.Description = "Cross platform .NET Core code coverage tool"; + return await root.InvokeAsync(args); + } + private static async Task HandleCommandAsync(string moduleOrAppDirectory, + string target, + string targetargs, + string output, + LogLevel verbosity, + string[] formats, + string threshold, + List thresholdType, + ThresholdStatistic thresholdStat, + string[] exclude, + string[] include, + string[] excludeByFile, + string[] includeDirectory, + string[] excludeByAttribute, + bool includeTestAssembly, + bool singleHit, + bool skipautoprops, + string mergeWith, + bool useSourceLink, + string[] doesNotReturnAttribute, + string excludeAssembliesWithoutSources + ) + { + + + IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -41,70 +99,29 @@ static int Main(string[] args) var logger = (ConsoleLogger)serviceProvider.GetService(); IFileSystem fileSystem = serviceProvider.GetService(); - var app = new CommandLineApplication - { - Name = "coverlet", - FullName = "Cross platform .NET Core code coverage tool" - }; - app.HelpOption("-h|--help"); - app.VersionOption("-v|--version", GetAssemblyVersion()); + // Adjust log level based on user input. + logger.Level = verbosity; int exitCode = (int)CommandExitCodes.Success; - - CommandArgument moduleOrAppDirectory = app.Argument("", "Path to the test assembly or application directory."); - CommandOption target = app.Option("-t|--target", "Path to the test runner application.", CommandOptionType.SingleValue); - CommandOption targs = app.Option("-a|--targetargs", "Arguments to be passed to the test runner.", CommandOptionType.SingleValue); - CommandOption output = app.Option("-o|--output", "Output of the generated coverage report", CommandOptionType.SingleValue); - CommandOption verbosity = app.Option("-v|--verbosity", "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.", CommandOptionType.SingleValue); - CommandOption formats = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue); - CommandOption threshold = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue); - CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue); - CommandOption thresholdStat = app.Option("--threshold-stat", "Coverage statistic used to enforce the threshold value.", CommandOptionType.SingleValue); - CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue); - CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue); - CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue); - CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue); - CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue); - CommandOption includeTestAssembly = app.Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.", CommandOptionType.NoValue); - CommandOption singleHit = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue); - CommandOption skipAutoProp = app.Option("--skipautoprops", "Neither track nor record auto-implemented properties.", CommandOptionType.NoValue); - CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue); - CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue); - CommandOption doesNotReturnAttributes = app.Option("--does-not-return-attribute", "Attributes that mark methods that do not return.", CommandOptionType.MultipleValue); - CommandOption excludeAssembliesWithoutSources = app.Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.", CommandOptionType.SingleValue); - - app.OnExecute(() => + try { - if (string.IsNullOrEmpty(moduleOrAppDirectory.Value) || string.IsNullOrWhiteSpace(moduleOrAppDirectory.Value)) - throw new CommandParsingException(app, "No test assembly or application directory specified."); - - if (!target.HasValue()) - throw new CommandParsingException(app, "Target must be specified."); - - if (verbosity.HasValue()) - { - // Adjust log level based on user input. - logger.Level = verbosity.ParsedValue; - } - CoverageParameters parameters = new() { - IncludeFilters = includeFilters.Values.ToArray(), - IncludeDirectories = includeDirectories.Values.ToArray(), - ExcludeFilters = excludeFilters.Values.ToArray(), - ExcludedSourceFiles = excludedSourceFiles.Values.ToArray(), - ExcludeAttributes = excludeAttributes.Values.ToArray(), - IncludeTestAssembly = includeTestAssembly.HasValue(), - SingleHit = singleHit.HasValue(), - MergeWith = mergeWith.Value(), - UseSourceLink = useSourceLink.HasValue(), - SkipAutoProps = skipAutoProp.HasValue(), - DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray(), - ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources.Value() + IncludeFilters = include, + IncludeDirectories = includeDirectory, + ExcludeFilters = exclude, + ExcludedSourceFiles = excludeByFile, + ExcludeAttributes = excludeByAttribute, + IncludeTestAssembly = includeTestAssembly, + SingleHit = singleHit, + MergeWith = mergeWith, + UseSourceLink = useSourceLink, + SkipAutoProps = skipautoprops, + DoesNotReturnAttributes = doesNotReturnAttribute, + ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources }; - ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService(); - Coverage coverage = new(moduleOrAppDirectory.Value, + Coverage coverage = new(moduleOrAppDirectory, parameters, logger, serviceProvider.GetRequiredService(), @@ -114,8 +131,8 @@ static int Main(string[] args) coverage.PrepareModules(); Process process = new(); - process.StartInfo.FileName = target.Value(); - process.StartInfo.Arguments = targs.HasValue() ? targs.Value() : string.Empty; + process.StartInfo.FileName = target; + process.StartInfo.Arguments = targetargs != null ? targetargs : string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; @@ -138,9 +155,7 @@ static int Main(string[] args) process.WaitForExit(); - string dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString(); - List dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List(new string[] { "line", "branch", "method" }); - ThresholdStatistic dThresholdStat = thresholdStat.HasValue() ? Enum.Parse(thresholdStat.Value(), true) : Enum.Parse("minimum", true); + string dOutput = output != null ? output : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString(); logger.LogInformation("\nCalculating coverage result..."); @@ -156,12 +171,12 @@ static int Main(string[] args) Directory.CreateDirectory(directory); } - foreach (string format in formats.HasValue() ? formats.Values : new List(new string[] { "json" })) + foreach (string fmt in formats) { - Core.Abstractions.IReporter reporter = new ReporterFactory(format).CreateReporter(); + Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{format}' is not supported"); + throw new Exception($"Specified output format '{formats}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) @@ -185,26 +200,26 @@ static int Main(string[] args) var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdType in dThresholdTypes) + foreach (string thresholdTyp in thresholdType) { - if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdTyp.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdTyp.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdTyp.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } } var thresholdTypeFlagValues = new Dictionary(); - if (threshold.HasValue() && threshold.Value().Contains(',')) + if (!string.IsNullOrEmpty(threshold) && threshold.Contains(',')) { - IEnumerable thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()); + IEnumerable thresholdValues = threshold.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()); if (thresholdValues.Count() != thresholdTypeFlagQueue.Count) { throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match"); @@ -224,7 +239,7 @@ static int Main(string[] args) } else { - double thresholdValue = threshold.HasValue() ? double.Parse(threshold.Value()) : 0; + double thresholdValue = threshold != null ? double.Parse(threshold) : 0; while (thresholdTypeFlagQueue.Any()) { @@ -271,44 +286,36 @@ static int Main(string[] args) exitCode += (int)CommandExitCodes.TestFailed; } - ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat); + ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat); if (thresholdTypeFlags != ThresholdTypeFlags.None) { exitCode += (int)CommandExitCodes.CoverageBelowThreshold; var exceptionMessageBuilder = new StringBuilder(); if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}"); } if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}"); } if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}"); } throw new Exception(exceptionMessageBuilder.ToString()); } return exitCode; - }); - try - { - return app.Execute(args); - } - catch (CommandParsingException ex) - { - logger.LogError(ex.Message); - app.ShowHelp(); - return (int)CommandExitCodes.CommandParsingException; + } + catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process") { - logger.LogError($"Start process '{target.Value()}' failed with '{we.Message}'"); + logger.LogError($"Start process '{target}' failed with '{we.Message}'"); return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; } catch (Exception ex) @@ -316,9 +323,18 @@ static int Main(string[] args) logger.LogError(ex.Message); return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; } + + } + + private static Command WithHandler(this Command command, string methodName) + { + var method = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); + var handler = CommandHandler.Create(method!); + command.Handler = handler; + return command; } - static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); +static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture); } diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj index b44e05dff..60076dc70 100644 --- a/src/coverlet.console/coverlet.console.csproj +++ b/src/coverlet.console/coverlet.console.csproj @@ -22,7 +22,8 @@ - + + From 0504ca3e92c6f1fc17b5c5eff87d2f1cfc112d6a Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 26 Jun 2023 08:36:32 +0200 Subject: [PATCH 02/28] resolve review comments and add DotnetTool tests --- Directory.Build.props | 12 +- Directory.Build.targets | 1 - Documentation/GlobalTool.md | 10 +- global.json | 2 +- .../DataCollection/CoverageWrapper.cs | 4 + .../coverlet.collector.csproj | 2 +- src/coverlet.console/Program.cs | 194 ++++++++++++------ src/coverlet.console/coverlet.console.csproj | 8 +- .../Reporters/CoberturaReporter.cs | 1 - src/coverlet.core/coverlet.core.csproj | 2 +- .../coverlet.msbuild.tasks.csproj | 2 +- .../coverlet.collector.tests.csproj | 2 +- .../Helpers/InstrumentationHelperTests.cs | 1 - .../ModuleTrackerTemplateTests.cs | 26 ++- .../coverlet.core.tests.csproj | 3 +- test/coverlet.integration.tests/DotnetTool.cs | 51 +++++ 16 files changed, 215 insertions(+), 106 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 123e8ebac..7a716a812 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ preview true preview - $(NoWarn);NU5105 + $(NoWarn);NU5105;CS1591 https://api.nuget.org/v3/index.json; @@ -25,13 +25,7 @@ - - all - runtime; build; native; contentfiles; analyzers - - - all - runtime; build; native; contentfiles; analyzers - + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 3d2910136..aea1e02e7 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,7 +25,6 @@ - diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md index 455f34b39..1bea4da74 100644 --- a/Documentation/GlobalTool.md +++ b/Documentation/GlobalTool.md @@ -17,16 +17,14 @@ Arguments: Path to the test assembly or application directory. Options: - -h|--help Show help information - -v|--version Show version information - -t|--target Path to the test runner application. + -t|--target (REQUIRED) Path to the test runner application. -a|--targetargs Arguments to be passed to the test runner. -o|--output Output of the generated coverage report -v|--verbosity Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed. - -f|--format Format of the generated coverage report. + -f|--format Format of the generated coverage report. [default: json] --threshold Exits with error if the coverage % is below value. --threshold-type Coverage type to apply the threshold to. - --threshold-stat Coverage statistic used to enforce the threshold value. + --threshold-stat Coverage statistic used to enforce the threshold value. [default: Minimum] --exclude Filter expressions to exclude specific modules and types. --include Filter expressions to include only specific modules and types. --exclude-by-file Glob patterns specifying source files to exclude. @@ -39,6 +37,8 @@ Options: --use-source-link Specifies whether to use SourceLink URIs in place of file system paths. --does-not-return-attribute Attributes that mark methods that do not return. --exclude-assemblies-without-sources Specifies behaviour of heuristic to ignore assemblies with missing source documents. + --version Show version information + -?, -h, --help Show help and usage information ``` NB. For a [multiple value] options you have to specify values multiple times i.e. diff --git a/global.json b/global.json index 2dbcd442b..ccc54128c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.408", + "version": "6.0.410", "rollForward": "latestMajor" } } diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs index 0569ab277..ffd7d8a41 100644 --- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs +++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs @@ -17,6 +17,10 @@ internal class CoverageWrapper : ICoverageWrapper /// /// Coverlet settings /// Coverlet logger + /// + /// + /// + /// /// Coverage object public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper) { diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj index ba206705c..bdae19ae6 100644 --- a/src/coverlet.collector/coverlet.collector.csproj +++ b/src/coverlet.collector/coverlet.collector.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 coverlet.collector diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 39a115224..44de62cdb 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Text; using System.CommandLine; -using System.CommandLine.NamingConventionBinder; using ConsoleTables; using Coverlet.Console.Logging; using Coverlet.Core; @@ -21,69 +20,139 @@ using Coverlet.Core.Reporters; using Coverlet.Core.Symbols; using Microsoft.Extensions.DependencyInjection; -using System.Reflection; -using Newtonsoft.Json; namespace Coverlet.Console { public static class Program { - public static async Task Main(string[] args) + static int Main(string[] args) { - var root = new RootCommand + var moduleOrAppDirectory = new Argument("path", "Path to the test assembly or application directory."); + var target = new Option(new[] { "--target", "-t" }, "Path to the test runner application.") { Arity = ArgumentArity.ZeroOrOne, IsRequired = true }; + var targs = new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner.") { Arity = ArgumentArity.ZeroOrOne }; + var output = new Option(new[] { "--output", "-o" }, "Output of the generated coverage report") { Arity = ArgumentArity.ZeroOrOne }; + var verbosity = new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.") { Arity = ArgumentArity.ZeroOrOne }; + var formats = new Option(new[] { "--format", "-f" }, () => new[] { "json" }, "Format of the generated coverage report.") { Arity = ArgumentArity.ZeroOrMore }; + var threshold = new Option("--threshold", "Exits with error if the coverage % is below value.") { Arity = ArgumentArity.ZeroOrOne }; + var thresholdTypes = new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.").FromAmong("line", "branch", "method"); + var thresholdStat = new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value.") { Arity = ArgumentArity.ZeroOrMore }; + var excludeFilters = new Option("--exclude", "Filter expressions to exclude specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; + var includeFilters = new Option("--include", "Filter expressions to include only specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; + var excludedSourceFiles = new Option("--exclude-by-file", "Glob patterns specifying source files to exclude.") { Arity = ArgumentArity.ZeroOrMore }; + var includeDirectories = new Option("--include-directory", "Include directories containing additional assemblies to be instrumented.") { Arity = ArgumentArity.ZeroOrMore }; + var excludeAttributes = new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }; + var includeTestAssembly = new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }; + var singleHit = new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location") { Arity = ArgumentArity.Zero }; + var skipAutoProp = new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }; + var mergeWith = new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }; + var useSourceLink = new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }; + var doesNotReturnAttributes = new Option("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore }; + var excludeAssembliesWithoutSources = new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.") { Arity = ArgumentArity.ZeroOrOne }; + + RootCommand rootCommand = new () { - new Argument("path", "Path to the test assembly or application directory."), - new Option(new[] { "--target", "-t" }, "Path to the test runner application."){Arity = ArgumentArity.ZeroOrOne,IsRequired = true}, - new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner."){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--output", "-o" }, "Output of the generated coverage report"){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed."){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--formats", "-f" }, () => new[] {"json"} , "Format of the generated coverage report."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--threshold", "Exits with error if the coverage % is below value."){Arity = ArgumentArity.ZeroOrOne}, - new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.") {Arity = ArgumentArity.ZeroOrMore}, - new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude", "Filter expressions to exclude specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--include", "Filter expressions to include only specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-by-file", "Glob patterns specifying source files to exclude."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--include-directory", "Include directories containing additional assemblies to be instrumented."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }, - new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }, - new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location"){ Arity = ArgumentArity.Zero }, - new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }, - new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }, - new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }, - new Option("--does-not-return-attribute", "Attributes that mark methods that do not return"){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents."){ Arity = ArgumentArity.ZeroOrOne } - }.WithHandler(nameof(HandleCommandAsync)); - - root.Description = "Cross platform .NET Core code coverage tool"; - return await root.InvokeAsync(args); + moduleOrAppDirectory, + target, + targs, + output, + verbosity, + formats, + threshold, + thresholdTypes, + thresholdStat, + excludeFilters, + includeFilters, + excludedSourceFiles, + includeDirectories, + excludeAttributes, + includeTestAssembly, + singleHit, + skipAutoProp, + mergeWith, + useSourceLink, + doesNotReturnAttributes, + excludeAssembliesWithoutSources + }; + + rootCommand.Description = "Cross platform .NET Core code coverage tool"; + + rootCommand.SetHandler(async (context) => + { + string moduleOrAppDirectoryValue = context.ParseResult.GetValueForArgument(moduleOrAppDirectory); + string targetValue = context.ParseResult.GetValueForOption(target); + string targsValue = context.ParseResult.GetValueForOption(targs); + string outputValue = context.ParseResult.GetValueForOption(output); + LogLevel verbosityValue = context.ParseResult.GetValueForOption(verbosity); + string[] formatsValue = context.ParseResult.GetValueForOption(formats); + string thresholdValue = context.ParseResult.GetValueForOption(threshold); + List thresholdTypesValue = context.ParseResult.GetValueForOption(thresholdTypes); + ThresholdStatistic thresholdStatValue = context.ParseResult.GetValueForOption(thresholdStat); + string[] excludeFiltersValue = context.ParseResult.GetValueForOption(excludeFilters); + string[] includeFiltersValue = context.ParseResult.GetValueForOption(includeFilters); + string[] excludedSourceFilesValue = context.ParseResult.GetValueForOption(excludedSourceFiles); + string[] includeDirectoriesValue = context.ParseResult.GetValueForOption(includeDirectories); + string[] excludeAttributesValue = context.ParseResult.GetValueForOption(excludeAttributes); + bool includeTestAssemblyValue = context.ParseResult.GetValueForOption(includeTestAssembly); + bool singleHitValue = context.ParseResult.GetValueForOption(singleHit); + bool skipAutoPropValue = context.ParseResult.GetValueForOption(skipAutoProp); + string mergeWithValue = context.ParseResult.GetValueForOption(mergeWith); + bool useSourceLinkValue = context.ParseResult.GetValueForOption(useSourceLink); + string[] doesNotReturnAttributesValue = context.ParseResult.GetValueForOption(doesNotReturnAttributes); + string excludeAssembliesWithoutSourcesValue = context.ParseResult.GetValueForOption(excludeAssembliesWithoutSources); + + if (string.IsNullOrEmpty(moduleOrAppDirectoryValue) || string.IsNullOrWhiteSpace(moduleOrAppDirectoryValue)) + throw new ArgumentException("No test assembly or application directory specified."); + + var taskStatus = await HandleCommand(moduleOrAppDirectoryValue, + targetValue, + targsValue, + outputValue, + verbosityValue, + formatsValue, + thresholdValue, + thresholdTypesValue, + thresholdStatValue, + excludeFiltersValue, + includeFiltersValue, + excludedSourceFilesValue, + includeDirectoriesValue, + excludeAttributesValue, + includeTestAssemblyValue, + singleHitValue, + skipAutoPropValue, + mergeWithValue, + useSourceLinkValue, + doesNotReturnAttributesValue, + excludeAssembliesWithoutSourcesValue); + context.ExitCode = taskStatus; + + }); + return rootCommand.Invoke(args); } - private static async Task HandleCommandAsync(string moduleOrAppDirectory, + private static Task HandleCommand(string moduleOrAppDirectory, string target, - string targetargs, + string targs, string output, LogLevel verbosity, string[] formats, string threshold, List thresholdType, ThresholdStatistic thresholdStat, - string[] exclude, - string[] include, - string[] excludeByFile, - string[] includeDirectory, - string[] excludeByAttribute, + string[] excludeFilters, + string[] includeFilters, + string[] excludedSourceFiles, + string[] includeDirectories, + string[] excludeAttributes, bool includeTestAssembly, bool singleHit, - bool skipautoprops, + bool skipAutoProp, string mergeWith, bool useSourceLink, - string[] doesNotReturnAttribute, + string[] doesNotReturnAttributes, string excludeAssembliesWithoutSources ) { - - IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -102,21 +171,22 @@ string excludeAssembliesWithoutSources // Adjust log level based on user input. logger.Level = verbosity; int exitCode = (int)CommandExitCodes.Success; + try { CoverageParameters parameters = new() { - IncludeFilters = include, - IncludeDirectories = includeDirectory, - ExcludeFilters = exclude, - ExcludedSourceFiles = excludeByFile, - ExcludeAttributes = excludeByAttribute, + IncludeFilters = includeFilters, + IncludeDirectories = includeDirectories, + ExcludeFilters = excludeFilters, + ExcludedSourceFiles = excludedSourceFiles, + ExcludeAttributes = excludeAttributes, IncludeTestAssembly = includeTestAssembly, SingleHit = singleHit, MergeWith = mergeWith, UseSourceLink = useSourceLink, - SkipAutoProps = skipautoprops, - DoesNotReturnAttributes = doesNotReturnAttribute, + SkipAutoProps = skipAutoProp, + DoesNotReturnAttributes = doesNotReturnAttributes, ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources }; ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService(); @@ -132,7 +202,7 @@ string excludeAssembliesWithoutSources Process process = new(); process.StartInfo.FileName = target; - process.StartInfo.Arguments = targetargs != null ? targetargs : string.Empty; + process.StartInfo.Arguments = targs != null ? targs : string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; @@ -176,7 +246,7 @@ string excludeAssembliesWithoutSources Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{formats}' is not supported"); + throw new Exception($"Specified output format '{fmt}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) @@ -200,17 +270,17 @@ string excludeAssembliesWithoutSources var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdTyp in thresholdType) + foreach (string thresholdMode in thresholdType) { - if (thresholdTyp.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdMode.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdTyp.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdMode.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdTyp.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdMode.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } @@ -308,7 +378,7 @@ string excludeAssembliesWithoutSources throw new Exception(exceptionMessageBuilder.ToString()); } - return exitCode; + return Task.FromResult(exitCode); } @@ -316,26 +386,16 @@ string excludeAssembliesWithoutSources catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process") { logger.LogError($"Start process '{target}' failed with '{we.Message}'"); - return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; + return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception); } catch (Exception ex) { logger.LogError(ex.Message); - return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; + return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception); } } - private static Command WithHandler(this Command command, string methodName) - { - var method = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); - var handler = CommandHandler.Create(method!); - command.Handler = handler; - return command; - } - -static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); - static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture); } } diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj index 60076dc70..d4d110fa8 100644 --- a/src/coverlet.console/coverlet.console.csproj +++ b/src/coverlet.console/coverlet.console.csproj @@ -14,16 +14,17 @@ tonerdo Coverlet is a cross platform code coverage tool for .NET, with support for line, branch and method coverage. coverage;testing;unit-test;lcov;opencover;quality + GlobalTool.md + https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true coverlet-icon.png https://github.com/coverlet-coverage/coverlet MIT git - + - @@ -32,6 +33,9 @@ + + true + diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index e3d299fda..3bbb67aaa 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 93a6c972d..34b6b5777 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -1,4 +1,4 @@ - + Library diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 80cbb6b21..0dbef0361 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -1,4 +1,4 @@ - + Library diff --git a/test/coverlet.collector.tests/coverlet.collector.tests.csproj b/test/coverlet.collector.tests/coverlet.collector.tests.csproj index fa828394a..87826ac9d 100644 --- a/test/coverlet.collector.tests/coverlet.collector.tests.csproj +++ b/test/coverlet.collector.tests/coverlet.collector.tests.csproj @@ -1,4 +1,4 @@ - + diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 8c6917042..71d88bfb8 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -6,7 +6,6 @@ using Xunit; using System.Collections.Generic; using System.Linq; -using Castle.Core.Internal; using Moq; using Coverlet.Core.Abstractions; using Coverlet.Core.Enums; diff --git a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs index 7cd5f2943..74957858e 100644 --- a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs +++ b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs @@ -123,26 +123,24 @@ public void MutexBlocksMultipleWriters() FunctionExecutor.Run(async () => { using var ctx = new TrackerContext(); - using (var mutex = new Mutex( - true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew)) - { - Assert.True(createdNew); + using var mutex = new Mutex( + true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew); + Assert.True(createdNew); - ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 }; - var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null)); + ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 }; + var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null)); - Assert.False(unloadTask.Wait(5)); + Assert.False(unloadTask.Wait(5)); - WriteHitsFile(new[] { 0, 3, 2, 1 }); + WriteHitsFile(new[] { 0, 3, 2, 1 }); - Assert.False(unloadTask.Wait(5)); + Assert.False(unloadTask.Wait(5)); - mutex.ReleaseMutex(); - await unloadTask; + mutex.ReleaseMutex(); + await unloadTask; - int[] expectedHitsArray = new[] { 0, 4, 4, 4 }; - Assert.Equal(expectedHitsArray, ReadHitsFile()); - } + int[] expectedHitsArray = new[] { 0, 4, 4, 4 }; + Assert.Equal(expectedHitsArray, ReadHitsFile()); return 0; }); diff --git a/test/coverlet.core.tests/coverlet.core.tests.csproj b/test/coverlet.core.tests/coverlet.core.tests.csproj index f040aa9c3..a882b8e90 100644 --- a/test/coverlet.core.tests/coverlet.core.tests.csproj +++ b/test/coverlet.core.tests/coverlet.core.tests.csproj @@ -1,4 +1,4 @@ - + @@ -8,6 +8,7 @@ NU1702 true + diff --git a/test/coverlet.integration.tests/DotnetTool.cs b/test/coverlet.integration.tests/DotnetTool.cs index 58f2521ac..86c473314 100644 --- a/test/coverlet.integration.tests/DotnetTool.cs +++ b/test/coverlet.integration.tests/DotnetTool.cs @@ -46,5 +46,56 @@ public void StandAlone() Assert.Contains("Hello World!", standardOutput); AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThreshold() + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False( RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.Contains("The minimum method coverage is below the specified 80", standardOutput); + } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThresholdLine() + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False(RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --threshold-type line --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.DoesNotContain("The minimum method coverage is below the specified 80", standardOutput); + } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThresholdLineAndMethod () + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False(RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --threshold-type line --threshold-type method --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.Contains("The minimum method coverage is below the specified 80", standardOutput); + } } } From 468de3ca289c217a453943267c8a6e524f4deb83 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 23 Jul 2023 09:48:04 -0700 Subject: [PATCH 03/28] add space between CLI argument (#1499) matches the same argument further down --- Documentation/GlobalTool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md index 455f34b39..fb3dcb40c 100644 --- a/Documentation/GlobalTool.md +++ b/Documentation/GlobalTool.md @@ -43,7 +43,7 @@ Options: NB. For a [multiple value] options you have to specify values multiple times i.e. ``` ---exclude-by-attribute 'Obsolete' --exclude-by-attribute'GeneratedCode' --exclude-by-attribute 'CompilerGenerated' +--exclude-by-attribute 'Obsolete' --exclude-by-attribute 'GeneratedCode' --exclude-by-attribute 'CompilerGenerated' ``` For `--merge-with` [check the sample](Examples.md). From 26bd50b1c043ea149ff60ecd39fc37b3830602e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Gr=C3=BCtzmacher?= <44983012+lg2de@users.noreply.github.com> Date: Sun, 23 Jul 2023 18:55:09 +0200 Subject: [PATCH 04/28] Fix resolving assemblies from frameworks not referenced by coverlet itself (#1449) --- coverlet.sln | 349 ++++++++++-------- .../Instrumentation/CecilAssemblyResolver.cs | 77 +++- .../Instrumentation/InstrumenterResult.cs | 2 + src/coverlet.core/Properties/AssemblyInfo.cs | 5 +- .../Instrumentation/InstrumenterTests.cs | 32 +- .../ModuleTrackerTemplateTests.cs | 26 +- .../WpfResolverTests.cs | 44 +++ .../coverlet.integration.tests.csproj | 1 + .../AssemblyInfo.cs | 6 + .../ResolverTests.cs | 38 ++ ...t.tests.projectsample.aspnet6.tests.csproj | 20 + ...rlet.tests.projectsample.aspnet6.tests.snk | Bin 0 -> 596 bytes .../Program.cs | 23 ++ .../Startup.cs | 39 ++ .../appsettings.Development.json | 9 + .../appsettings.json | 10 + ...overlet.tests.projectsample.aspnet6.csproj | 10 + .../Program.cs | 9 + .../TestClass.cs | 12 + .../coverlet.tests.projectsample.wpf6.csproj | 12 + 20 files changed, 506 insertions(+), 218 deletions(-) create mode 100644 test/coverlet.integration.tests/WpfResolverTests.cs create mode 100644 test/coverlet.tests.projectsample.aspnet6.tests/AssemblyInfo.cs create mode 100644 test/coverlet.tests.projectsample.aspnet6.tests/ResolverTests.cs create mode 100644 test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.csproj create mode 100644 test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.snk create mode 100644 test/coverlet.tests.projectsample.aspnet6/Program.cs create mode 100644 test/coverlet.tests.projectsample.aspnet6/Startup.cs create mode 100644 test/coverlet.tests.projectsample.aspnet6/appsettings.Development.json create mode 100644 test/coverlet.tests.projectsample.aspnet6/appsettings.json create mode 100644 test/coverlet.tests.projectsample.aspnet6/coverlet.tests.projectsample.aspnet6.csproj create mode 100644 test/coverlet.tests.projectsample.wpf6/Program.cs create mode 100644 test/coverlet.tests.projectsample.wpf6/TestClass.cs create mode 100644 test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj diff --git a/coverlet.sln b/coverlet.sln index efccfa31f..48ebba9a3 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,164 +1,185 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.2.32208.508 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.empty", "test\coverlet.tests.projectsample.empty\coverlet.tests.projectsample.empty.csproj", "{085A3AFB-C086-4E98-86F1-1B481446EC5E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77A15177-8262-488F-AF2B-91B9055715DA}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - eng\azure-pipelines-nightly.yml = eng\azure-pipelines-nightly.yml - eng\azure-pipelines.yml = eng\azure-pipelines.yml - eng\build.yml = eng\build.yml - DeterministicBuild.targets = DeterministicBuild.targets - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - global.json = global.json - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.excludedbyattribute", "test\coverlet.tests.projectsample.excludedbyattribute\coverlet.tests.projectsample.excludedbyattribute.csproj", "{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.tests", "test\coverlet.integration.tests\coverlet.integration.tests.csproj", "{99B4059C-B25C-4B82-8117-A0E9DC9B0949}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.template", "test\coverlet.integration.template\coverlet.integration.template.csproj", "{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests.samples.netstandard", "test\coverlet.core.tests.samples.netstandard\coverlet.core.tests.samples.netstandard.csproj", "{5FF404AD-7C0B-465A-A1E9-558CDC642B0C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.xunit.extensions", "test\coverlet.tests.xunit.extensions\coverlet.tests.xunit.extensions.csproj", "{F8199E19-FA9A-4559-9101-CAD7028121B4}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4-4A24-4217-AEFE-159B68F029A1}" - ProjectSection(SolutionItems) = preProject - test\Directory.Build.props = test\Directory.Build.props - test\Directory.Build.targets = test\Directory.Build.targets - EndProjectSection -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}" -EndProject -Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU - {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.Build.0 = Release|Any CPU - {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU - {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU - {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU - {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.Build.0 = Release|Any CPU - {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.Build.0 = Release|Any CPU - {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.Build.0 = Release|Any CPU - {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.Build.0 = Release|Any CPU - {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.Build.0 = Release|Any CPU - {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.Build.0 = Release|Any CPU - {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.Build.0 = Release|Any CPU - {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.Build.0 = Release|Any CPU - {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} - {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} - {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} - {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} - {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {085A3AFB-C086-4E98-86F1-1B481446EC5E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {99B4059C-B25C-4B82-8117-A0E9DC9B0949} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {F6FE7678-C662-43D3-AC6A-64F6AC5A5935} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32208.508 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2FEBDE1B-83E3-445B-B9F8-5644B0E0E134}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector", "src\coverlet.collector\coverlet.collector.csproj", "{F5B2C45B-274B-43D6-9565-8B50659CFE56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.collector.tests", "test\coverlet.collector.tests\coverlet.collector.tests.csproj", "{5ED4FA81-8F8C-4211-BA88-7573BD63262E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.empty", "test\coverlet.tests.projectsample.empty\coverlet.tests.projectsample.empty.csproj", "{085A3AFB-C086-4E98-86F1-1B481446EC5E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77A15177-8262-488F-AF2B-91B9055715DA}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitignore = .gitignore + eng\azure-pipelines-nightly.yml = eng\azure-pipelines-nightly.yml + eng\azure-pipelines.yml = eng\azure-pipelines.yml + eng\build.yml = eng\build.yml + DeterministicBuild.targets = DeterministicBuild.targets + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + global.json = global.json + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.excludedbyattribute", "test\coverlet.tests.projectsample.excludedbyattribute\coverlet.tests.projectsample.excludedbyattribute.csproj", "{D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.tests", "test\coverlet.integration.tests\coverlet.integration.tests.csproj", "{99B4059C-B25C-4B82-8117-A0E9DC9B0949}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.integration.template", "test\coverlet.integration.template\coverlet.integration.template.csproj", "{F6FE7678-C662-43D3-AC6A-64F6AC5A5935}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests.samples.netstandard", "test\coverlet.core.tests.samples.netstandard\coverlet.core.tests.samples.netstandard.csproj", "{5FF404AD-7C0B-465A-A1E9-558CDC642B0C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.xunit.extensions", "test\coverlet.tests.xunit.extensions\coverlet.tests.xunit.extensions.csproj", "{F8199E19-FA9A-4559-9101-CAD7028121B4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4-4A24-4217-AEFE-159B68F029A1}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + test\Directory.Build.targets = test\Directory.Build.targets + EndProjectSection +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspnet6", "test\coverlet.tests.projectsample.aspnet6\coverlet.tests.projectsample.aspnet6.csproj", "{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspnet6.tests", "test\coverlet.tests.projectsample.aspnet6.tests\coverlet.tests.projectsample.aspnet6.tests.csproj", "{8EC065A4-7700-45E6-8B90-0182E3649DEA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.wpf6", "test\coverlet.tests.projectsample.wpf6\coverlet.tests.projectsample.wpf6.csproj", "{988A5FF0-4326-4F5B-9F05-CB165543A555}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|Any CPU.Build.0 = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|Any CPU.Build.0 = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|Any CPU.Build.0 = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|Any CPU.Build.0 = Release|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|Any CPU.Build.0 = Release|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|Any CPU.Build.0 = Release|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5B2C45B-274B-43D6-9565-8B50659CFE56}.Release|Any CPU.Build.0 = Release|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5ED4FA81-8F8C-4211-BA88-7573BD63262E}.Release|Any CPU.Build.0 = Release|Any CPU + {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {085A3AFB-C086-4E98-86F1-1B481446EC5E}.Release|Any CPU.Build.0 = Release|Any CPU + {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6}.Release|Any CPU.Build.0 = Release|Any CPU + {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99B4059C-B25C-4B82-8117-A0E9DC9B0949}.Release|Any CPU.Build.0 = Release|Any CPU + {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6FE7678-C662-43D3-AC6A-64F6AC5A5935}.Release|Any CPU.Build.0 = Release|Any CPU + {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FF404AD-7C0B-465A-A1E9-558CDC642B0C}.Release|Any CPU.Build.0 = Release|Any CPU + {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8199E19-FA9A-4559-9101-CAD7028121B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8199E19-FA9A-4559-9101-CAD7028121B4}.Release|Any CPU.Build.0 = Release|Any CPU + {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9B7DC34-3E04-4F20-AED4-73791AF8020D}.Release|Any CPU.Build.0 = Release|Any CPU + {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1CBF6966-2A67-4D2C-8598-D174B83072F4}.Release|Any CPU.Build.0 = Release|Any CPU + {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E69D68C9-78ED-4076-A14B-D07295A4B2A5}.Release|Any CPU.Build.0 = Release|Any CPU + {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}.Release|Any CPU.Build.0 = Release|Any CPU + {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EC065A4-7700-45E6-8B90-0182E3649DEA}.Release|Any CPU.Build.0 = Release|Any CPU + {988A5FF0-4326-4F5B-9F05-CB165543A555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {988A5FF0-4326-4F5B-9F05-CB165543A555}.Debug|Any CPU.Build.0 = Debug|Any CPU + {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.ActiveCfg = Release|Any CPU + {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {FA73E423-9790-4F35-B018-3C4E3CA338BA} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {AE117FAA-C21D-4F23-917E-0C8050614750} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F5B2C45B-274B-43D6-9565-8B50659CFE56} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} + {5ED4FA81-8F8C-4211-BA88-7573BD63262E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {085A3AFB-C086-4E98-86F1-1B481446EC5E} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {D6B14F2F-9E7D-4D2C-BAC8-48834F853ED6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {99B4059C-B25C-4B82-8117-A0E9DC9B0949} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F6FE7678-C662-43D3-AC6A-64F6AC5A5935} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {8EC065A4-7700-45E6-8B90-0182E3649DEA} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {988A5FF0-4326-4F5B-9F05-CB165543A555} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} + EndGlobalSection +EndGlobal diff --git a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs index 6a781485d..0bf729810 100644 --- a/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs +++ b/src/coverlet.core/Instrumentation/CecilAssemblyResolver.cs @@ -10,6 +10,8 @@ using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel.Resolution; using Mono.Cecil; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Coverlet.Core.Instrumentation { @@ -70,14 +72,14 @@ public NetstandardAwareAssemblyResolver(string modulePath, ILogger logger) _modulePath = modulePath; _logger = logger; - // this is lazy because we cannot create AspNetCoreSharedFrameworkResolver if not on .NET Core runtime, + // this is lazy because we cannot create NetCoreSharedFrameworkResolver if not on .NET Core runtime, // runtime folders are different _compositeResolver = new Lazy(() => new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[] { new AppBaseCompilationAssemblyResolver(), - new ReferenceAssemblyPathResolver(), new PackageCompilationAssemblyResolver(), - new AspNetCoreSharedFrameworkResolver(_logger) + new NetCoreSharedFrameworkResolver(modulePath, _logger), + new ReferenceAssemblyPathResolver(), }), true); } @@ -216,23 +218,37 @@ internal AssemblyDefinition TryWithCustomResolverOnDotNetCore(AssemblyNameRefere } } - internal class AspNetCoreSharedFrameworkResolver : ICompilationAssemblyResolver + internal class NetCoreSharedFrameworkResolver : ICompilationAssemblyResolver { - private readonly string[] _aspNetSharedFrameworkDirs; + private readonly List _aspNetSharedFrameworkDirs = new(); private readonly ILogger _logger; - public AspNetCoreSharedFrameworkResolver(ILogger logger) + public NetCoreSharedFrameworkResolver(string modulePath, ILogger logger) { _logger = logger; - string runtimeRootPath = Path.GetDirectoryName(typeof(object).Assembly.Location); - string runtimeVersion = runtimeRootPath.Substring(runtimeRootPath.LastIndexOf(Path.DirectorySeparatorChar) + 1); - _aspNetSharedFrameworkDirs = new string[] + + string runtimeConfigFile = Path.Combine( + Path.GetDirectoryName(modulePath)!, + Path.GetFileNameWithoutExtension(modulePath) + ".runtimeconfig.json"); + if (!File.Exists(runtimeConfigFile)) + { + return; + } + + var reader = new RuntimeConfigurationReader(runtimeConfigFile); + IEnumerable<(string Name, string Version)> referencedFrameworks = reader.GetFrameworks(); + string runtimePath = Path.GetDirectoryName(typeof(object).Assembly.Location); + string runtimeRootPath = Path.Combine(runtimePath!, "../.."); + foreach ((string frameworkName, string frameworkVersion) in referencedFrameworks) { - Path.GetFullPath(Path.Combine(runtimeRootPath,"../../Microsoft.AspNetCore.All", runtimeVersion)), - Path.GetFullPath(Path.Combine(runtimeRootPath, "../../Microsoft.AspNetCore.App", runtimeVersion)) - }; + var majorVersion = string.Join(".", frameworkVersion.Split('.').Take(2)) + "."; + var directory = new DirectoryInfo(Path.Combine(runtimeRootPath, frameworkName)); + var latestVersion = directory.GetDirectories().Where(x => x.Name.StartsWith(majorVersion)) + .Select(x => Convert.ToUInt32(x.Name.Substring(majorVersion.Length))).Max(); + _aspNetSharedFrameworkDirs.Add(Path.Combine(directory.FullName, majorVersion + latestVersion)); + } - _logger.LogVerbose("AspNetCoreSharedFrameworkResolver search paths:"); + _logger.LogVerbose("NetCoreSharedFrameworkResolver search paths:"); foreach (string searchPath in _aspNetSharedFrameworkDirs) { _logger.LogVerbose(searchPath); @@ -250,7 +266,8 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List ass continue; } - foreach (string file in Directory.GetFiles(sharedFrameworkPath)) + string[] files = Directory.GetFiles(sharedFrameworkPath); + foreach (string file in files) { if (Path.GetFileName(file).Equals(dllName, StringComparison.OrdinalIgnoreCase)) { @@ -264,4 +281,36 @@ public bool TryResolveAssemblyPaths(CompilationLibrary library, List ass return false; } } + + internal class RuntimeConfigurationReader + { + private readonly string _runtimeConfigFile; + + public RuntimeConfigurationReader(string runtimeConfigFile) + { + _runtimeConfigFile = runtimeConfigFile; + } + + public IEnumerable<(string Name, string Version)> GetFrameworks() + { + JObject configuration = + new JsonSerializer().Deserialize( + new JsonTextReader(new StringReader(File.ReadAllText(_runtimeConfigFile)))); + + JToken runtimeOptions = configuration["runtimeOptions"]; + JToken framework = runtimeOptions?["framework"]; + if (framework != null) + { + return new[] {(framework["name"].Value(), framework["version"].Value())}; + } + + JToken frameworks = runtimeOptions?["frameworks"]; + if (frameworks != null) + { + return frameworks.Select(x => (x["name"].Value(), x["version"].Value())); + } + + throw new InvalidOperationException($"Unable to read runtime configuration from {_runtimeConfigFile}."); + } + } } diff --git a/src/coverlet.core/Instrumentation/InstrumenterResult.cs b/src/coverlet.core/Instrumentation/InstrumenterResult.cs index 69a6ab24a..f93b66866 100644 --- a/src/coverlet.core/Instrumentation/InstrumenterResult.cs +++ b/src/coverlet.core/Instrumentation/InstrumenterResult.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; namespace Coverlet.Core.Instrumentation @@ -79,6 +80,7 @@ public Document() [DebuggerDisplay("isBranch = {isBranch} docIndex = {docIndex} start = {start} end = {end}")] [DataContract] + [SuppressMessage("Style", "IDE1006", Justification = "suppress casing error for API compatibility")] internal class HitCandidate { public HitCandidate(bool isBranch, int docIndex, int start, int end) => (this.isBranch, this.docIndex, this.start, this.end) = (isBranch, docIndex, start, end); diff --git a/src/coverlet.core/Properties/AssemblyInfo.cs b/src/coverlet.core/Properties/AssemblyInfo.cs index 7e65be514..38d48cf07 100644 --- a/src/coverlet.core/Properties/AssemblyInfo.cs +++ b/src/coverlet.core/Properties/AssemblyInfo.cs @@ -13,5 +13,8 @@ [assembly: InternalsVisibleTo("coverlet.core.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] [assembly: InternalsVisibleTo("coverlet.collector.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ed0ed6af9693182615b8dcadc83c918b8d36312f86cefc69539d67d4189cd1b89420e7c3871802ffef7f5ca7816c68ad856c77bf7c230cc07824d96aa5d1237eebd30e246b9a14e22695fb26b40c800f74ea96619092cbd3a5d430d6c003fc7a82e8ccd1e315b935105d9232fe9e99e8d7ff54bba6f191959338d4a3169df9b3")] [assembly: InternalsVisibleTo("coverlet.integration.tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d24efbe9cbc2dc49b7a3d2ae34ca37cfb69b4f450acd768a22ce5cd021c8a38ae7dc68b2809a1ac606ad531b578f192a5690b2986990cbda4dd84ec65a3a4c1c36f6d7bb18f08592b93091535eaee2f0c8e48763ed7f190db2008e1f9e0facd5c0df5aaab74febd3430e09a428a72e5e6b88357f92d78e47512d46ebdc3cbb")] +[assembly: InternalsVisibleTo("coverlet.tests.projectsample.aspnet6.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] +[assembly: InternalsVisibleTo("coverlet.tests.projectsample.wpf6.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] + // Needed to mock internal type https://github.com/Moq/moq4/wiki/Quickstart#advanced-features -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index a8c31da33..c561e3a5f 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -619,24 +619,6 @@ public int SampleMethod() if (expectedExcludes) { loggerMock.Verify(l => l.LogVerbose(It.IsAny())); } } - [Fact] - public void TestInstrument_AspNetCoreSharedFrameworkResolver() - { - var resolver = new AspNetCoreSharedFrameworkResolver(_mockLogger.Object); - var compilationLibrary = new CompilationLibrary( - "package", - "Microsoft.Extensions.Logging.Abstractions", - "2.2.0", - "sha512-B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A==", - Enumerable.Empty(), - Enumerable.Empty(), - true); - - var assemblies = new List(); - Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies)); - Assert.NotEmpty(assemblies); - } - [Fact] public void TestInstrument_NetstandardAwareAssemblyResolver_PreserveCompilationContext() { @@ -740,15 +722,15 @@ public void TestReachabilityHelper() new[] { // Throws - 7, 8, + 7, 8, // NoBranches - 12, 13, 14, 15, 16, + 12, 13, 14, 15, 16, // If - 19, 20, 22, 23, 24, 25, 26, 27, 29, 30, + 19, 20, 22, 23, 24, 25, 26, 27, 29, 30, // Switch - 33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69, + 33, 34, 36, 39, 40, 41, 42, 44, 45, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 68, 69, // Subtle - 72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103, + 72, 73, 75, 78, 79, 80, 82, 83, 86, 87, 88, 91, 92, 95, 96, 98, 99, 101, 102, 103, // UnreachableBranch 106, 107, 108, 110, 111, 112, 113, 114, // ThrowsGeneric @@ -774,7 +756,7 @@ public void TestReachabilityHelper() // Switch 41, 42, // Subtle - 79, 80, 88, 96, 98, 99, + 79, 80, 88, 96, 98, 99, // UnreachableBranch 110, 111, 112, 113, 114, // CallsGenericMethodDoesNotReturn @@ -822,7 +804,7 @@ public void Instrumenter_MethodsWithoutReferenceToSource_AreSkipped() var instrumenter = new Instrumenter(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace", parameters, loggerMock.Object, instrumentationHelper, new FileSystem(), new SourceRootTranslator(Path.Combine(directory.FullName, Path.GetFileName(module)), loggerMock.Object, new FileSystem(), new AssemblyAdapter()), new CecilSymbolHelper()); - + instrumentationHelper.BackupOriginalModule(Path.Combine(directory.FullName, Path.GetFileName(module)), "_coverlet_tests_projectsample_vbmynamespace"); InstrumenterResult result = instrumenter.Instrument(); diff --git a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs index 7cd5f2943..677ead26b 100644 --- a/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs +++ b/test/coverlet.core.tests/Instrumentation/ModuleTrackerTemplateTests.cs @@ -123,26 +123,24 @@ public void MutexBlocksMultipleWriters() FunctionExecutor.Run(async () => { using var ctx = new TrackerContext(); - using (var mutex = new Mutex( - true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew)) - { - Assert.True(createdNew); + using var mutex = new Mutex( + true, Path.GetFileNameWithoutExtension(ModuleTrackerTemplate.HitsFilePath) + "_Mutex", out bool createdNew); + Assert.True(createdNew); - ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 }; - var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null)); + ModuleTrackerTemplate.HitsArray = new[] { 0, 1, 2, 3 }; + var unloadTask = Task.Run(() => ModuleTrackerTemplate.UnloadModule(null, null)); - Assert.False(unloadTask.Wait(5)); + Assert.False(unloadTask.Wait(5)); - WriteHitsFile(new[] { 0, 3, 2, 1 }); + WriteHitsFile(new[] { 0, 3, 2, 1 }); - Assert.False(unloadTask.Wait(5)); + Assert.False(unloadTask.Wait(5)); - mutex.ReleaseMutex(); - await unloadTask; + mutex.ReleaseMutex(); + await unloadTask; - int[] expectedHitsArray = new[] { 0, 4, 4, 4 }; - Assert.Equal(expectedHitsArray, ReadHitsFile()); - } + int[] expectedHitsArray = new[] { 0, 4, 4, 4 }; + Assert.Equal(expectedHitsArray, ReadHitsFile()); return 0; }); diff --git a/test/coverlet.integration.tests/WpfResolverTests.cs b/test/coverlet.integration.tests/WpfResolverTests.cs new file mode 100644 index 000000000..6ba05e1d8 --- /dev/null +++ b/test/coverlet.integration.tests/WpfResolverTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Coverlet.Core.Abstractions; +using Coverlet.Core.Instrumentation; +using Coverlet.Tests.Xunit.Extensions; +using Microsoft.Extensions.DependencyModel; +using Moq; +using Xunit; + +namespace Coverlet.Integration.Tests +{ + public class WpfResolverTests : BaseTest + { + [ConditionalFact] + [SkipOnOS(OS.Linux, "WPF only runs on Windows")] + [SkipOnOS(OS.MacOS, "WPF only runs on Windows")] + public void TestInstrument_NetCoreSharedFrameworkResolver() + { + string wpfProjectPath = "../../../../coverlet.tests.projectsample.wpf6"; + Assert.True(DotnetCli($"build \"{wpfProjectPath}\"", out string output, out string error)); + string assemblyLocation = Directory.GetFiles($"{wpfProjectPath}/bin", "coverlet.tests.projectsample.wpf6.dll", SearchOption.AllDirectories).First(); + + var mockLogger = new Mock(); + var resolver = new NetCoreSharedFrameworkResolver(assemblyLocation, mockLogger.Object); + var compilationLibrary = new CompilationLibrary( + "package", + "System.Drawing", + "0.0.0.0", + "sha512-not-relevant", + Enumerable.Empty(), + Enumerable.Empty(), + true); + + var assemblies = new List(); + Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies), + "sample assembly shall be resolved"); + Assert.NotEmpty(assemblies); + } + } +} diff --git a/test/coverlet.integration.tests/coverlet.integration.tests.csproj b/test/coverlet.integration.tests/coverlet.integration.tests.csproj index 6de30285a..2f0b3bacc 100644 --- a/test/coverlet.integration.tests/coverlet.integration.tests.csproj +++ b/test/coverlet.integration.tests/coverlet.integration.tests.csproj @@ -8,6 +8,7 @@ + diff --git a/test/coverlet.tests.projectsample.aspnet6.tests/AssemblyInfo.cs b/test/coverlet.tests.projectsample.aspnet6.tests/AssemblyInfo.cs new file mode 100644 index 000000000..2b6ab18ca --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6.tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +[assembly: AssemblyKeyFile("coverlet.tests.projectsample.aspnet6.tests.snk")] diff --git a/test/coverlet.tests.projectsample.aspnet6.tests/ResolverTests.cs b/test/coverlet.tests.projectsample.aspnet6.tests/ResolverTests.cs new file mode 100644 index 000000000..216f1a624 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6.tests/ResolverTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Coverlet.Core.Abstractions; +using Coverlet.Core.Instrumentation; +using Microsoft.Extensions.DependencyModel; +using Moq; +using Xunit; + +namespace coverlet.tests.projectsample.aspnet6.tests +{ + public class ResolverTests + { + [Fact] + public void TestInstrument_NetCoreSharedFrameworkResolver() + { + Assembly assembly = GetType().Assembly; + var mockLogger = new Mock(); + var resolver = new NetCoreSharedFrameworkResolver(assembly.Location, mockLogger.Object); + var compilationLibrary = new CompilationLibrary( + "package", + "Microsoft.Extensions.Logging.Abstractions", + "0.0.0.0", + "sha512-not-relevant", + Enumerable.Empty(), + Enumerable.Empty(), + true); + + var assemblies = new List(); + Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies), + "sample assembly shall be resolved"); + Assert.NotEmpty(assemblies); + } + } +} diff --git a/test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.csproj b/test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.csproj new file mode 100644 index 000000000..ade1c3c97 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + false + + + + + + + + + + + + + + + diff --git a/test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.snk b/test/coverlet.tests.projectsample.aspnet6.tests/coverlet.tests.projectsample.aspnet6.tests.snk new file mode 100644 index 0000000000000000000000000000000000000000..9e007898b54599bc8a77f98b601b9dc05591dcf0 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097XeEBIIc&IL0wTgmzJVwI#SuukrtuWEG zE|G&F|Gk8NvK3{xS8XafBvvYu=xleQn6+e}@wty<*P%ojI*ZI8ZKLf8)!NK<;Ub|4 zsne`rjv=5_)@P!+>4gxX@b*){=Jk(4w6l{>U=sE0N(r;y)8S1+e%=%$y|*H+tz@mA zX+_07fMQLKaP7!3t*el!1Pr0eatN2ScEC4In}@tC$1f@F>m~RNA}Z=sTPi~SASR!N z2BK{p8v!Va4Q$72Nhnt7C9-ey&2T~|#vkpsYPH_@jVd3kt45})`+5j+_lP3AOYzP- z#_(UOwVG2={VwN=Z0OH8b2kj)DVE+BEwUn@L^bx#blR<_D&JGO2$+$Bf2fPzSA4DX zqy^KhFQLFn3n}U80Cjgh3Jy%TvEC>m8d}j$vMJF8Yz>6(wOy@nY9x@0qHTA6-S*#l zmb!ewwR$Urib?u<#q<&V2$~=60O$m8uz`=rQq^I|@`uL1Yl4(Dx2Etz={USc^Ym85 z#piE9H5c&6o_cx%oo@qx(UPV)vT#$3k}B@@U64RD8BB;n#VNe(fGnhlmIsNXXEF&_ zTo}Z;JPeS9tEybm8I+RFl>?0^n<0ZjeDk}O+hQYvSmV9BW@h zM}7567D^tzfyW+DXu literal 0 HcmV?d00001 diff --git a/test/coverlet.tests.projectsample.aspnet6/Program.cs b/test/coverlet.tests.projectsample.aspnet6/Program.cs new file mode 100644 index 000000000..dba32bba8 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6/Program.cs @@ -0,0 +1,23 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace coverlet.tests.projectsample.aspnet6 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/test/coverlet.tests.projectsample.aspnet6/Startup.cs b/test/coverlet.tests.projectsample.aspnet6/Startup.cs new file mode 100644 index 000000000..7c22a4c60 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6/Startup.cs @@ -0,0 +1,39 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace coverlet.tests.projectsample.aspnet6 +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync("Hello World!"); + }); + }); + } + } +} diff --git a/test/coverlet.tests.projectsample.aspnet6/appsettings.Development.json b/test/coverlet.tests.projectsample.aspnet6/appsettings.Development.json new file mode 100644 index 000000000..8983e0fc1 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/test/coverlet.tests.projectsample.aspnet6/appsettings.json b/test/coverlet.tests.projectsample.aspnet6/appsettings.json new file mode 100644 index 000000000..d9d9a9bff --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/test/coverlet.tests.projectsample.aspnet6/coverlet.tests.projectsample.aspnet6.csproj b/test/coverlet.tests.projectsample.aspnet6/coverlet.tests.projectsample.aspnet6.csproj new file mode 100644 index 000000000..19ead24ac --- /dev/null +++ b/test/coverlet.tests.projectsample.aspnet6/coverlet.tests.projectsample.aspnet6.csproj @@ -0,0 +1,10 @@ + + + + net6.0 + false + false + false + + + diff --git a/test/coverlet.tests.projectsample.wpf6/Program.cs b/test/coverlet.tests.projectsample.wpf6/Program.cs new file mode 100644 index 000000000..205a96743 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf6/Program.cs @@ -0,0 +1,9 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace coverlet.tests.projectsample.wpf6; + +public static class Program +{ + public static void Main() { } +} diff --git a/test/coverlet.tests.projectsample.wpf6/TestClass.cs b/test/coverlet.tests.projectsample.wpf6/TestClass.cs new file mode 100644 index 000000000..9299936c9 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf6/TestClass.cs @@ -0,0 +1,12 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Windows.Controls; + +namespace coverlet.tests.projectsample.wpf6 +{ + public class TestClass + { + public UserControl? Control { get; set; } + } +} diff --git a/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj b/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj new file mode 100644 index 000000000..7b2242d01 --- /dev/null +++ b/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj @@ -0,0 +1,12 @@ + + + + WinExe + net6.0-windows + enable + true + false + true + + + From 95b5cdfae7a9dd48440b37b03c00f657654c2fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Sun, 23 Jul 2023 19:07:57 +0200 Subject: [PATCH 05/28] Make tests run on all platforms (#1492) --- .../Instrumentation/InstrumenterTests.cs | 18 +++++++----------- test/coverlet.integration.tests/DotnetTool.cs | 14 +++++--------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index c561e3a5f..ef5065f55 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Toni Solarin-Sodara +// Copyright (c) Toni Solarin-Sodara // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; @@ -34,9 +34,7 @@ public void Dispose() _disposeAction?.Invoke(); } - [ConditionalFact] - [SkipOnOS(OS.Linux)] - [SkipOnOS(OS.MacOS)] + [Fact] public void TestCoreLibInstrumentation() { DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), nameof(TestCoreLibInstrumentation))); @@ -55,7 +53,7 @@ public void TestCoreLibInstrumentation() partialMockFileSystem.CallBase = true; partialMockFileSystem.Setup(fs => fs.OpenRead(It.IsAny())).Returns((string path) => { - if (Path.GetFileName(path) == files[1]) + if (Path.GetFileName(path.Replace(@"\", @"/")) == files[1]) { return File.OpenRead(Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), files[1])); } @@ -66,7 +64,7 @@ public void TestCoreLibInstrumentation() }); partialMockFileSystem.Setup(fs => fs.Exists(It.IsAny())).Returns((string path) => { - if (Path.GetFileName(path) == files[1]) + if (Path.GetFileName(path.Replace(@"\", @"/")) == files[1]) { return File.Exists(Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), files[1])); } @@ -446,9 +444,7 @@ public void SkipEmbeddedPpdbWithoutLocalSource() loggerMock.VerifyNoOtherCalls(); } - [ConditionalFact] - [SkipOnOS(OS.MacOS)] - [SkipOnOS(OS.Linux)] + [Fact] public void SkipPpdbWithoutLocalSource() { string dllFileName = "75d9f96508d74def860a568f426ea4a4.dll"; @@ -458,7 +454,7 @@ public void SkipPpdbWithoutLocalSource() partialMockFileSystem.CallBase = true; partialMockFileSystem.Setup(fs => fs.OpenRead(It.IsAny())).Returns((string path) => { - if (Path.GetFileName(path) == pdbFileName) + if (Path.GetFileName(path.Replace(@"\", @"/")) == pdbFileName) { return File.OpenRead(Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), pdbFileName)); } @@ -469,7 +465,7 @@ public void SkipPpdbWithoutLocalSource() }); partialMockFileSystem.Setup(fs => fs.Exists(It.IsAny())).Returns((string path) => { - if (Path.GetFileName(path) == pdbFileName) + if (Path.GetFileName(path.Replace(@"\", @"/")) == pdbFileName) { return File.Exists(Path.Combine(Path.Combine(Directory.GetCurrentDirectory(), "TestAssets"), pdbFileName)); } diff --git a/test/coverlet.integration.tests/DotnetTool.cs b/test/coverlet.integration.tests/DotnetTool.cs index 58f2521ac..a947391c3 100644 --- a/test/coverlet.integration.tests/DotnetTool.cs +++ b/test/coverlet.integration.tests/DotnetTool.cs @@ -14,12 +14,10 @@ private string InstallTool(string projectPath) { _ = DotnetCli($"tool install coverlet.console --version {GetPackageVersion("*console*.nupkg")} --tool-path \"{Path.Combine(projectPath, "coverletTool")}\"", out string standardOutput, out _, projectPath); Assert.Contains("was successfully installed.", standardOutput); - return Path.Combine(projectPath, "coverletTool", "coverlet "); + return Path.Combine(projectPath, "coverletTool", "coverlet"); } - [ConditionalFact] - [SkipOnOS(OS.Linux)] - [SkipOnOS(OS.MacOS)] + [Fact] public void DotnetTool() { using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); @@ -27,14 +25,12 @@ public void DotnetTool() string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); - RunCommand(coverletToolCommandPath, $"\"{publishedTestFile}\" --target \"dotnet\" --targetargs \"test {Path.Combine(clonedTemplateProject.ProjectRootPath, ClonedTemplateProject.ProjectFileName)} --no-build\" --include-test-assembly --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError); + RunCommand(coverletToolCommandPath, $"\"{publishedTestFile}\" --target \"dotnet\" --targetargs \"test {Path.Combine(clonedTemplateProject.ProjectRootPath, ClonedTemplateProject.ProjectFileName)} --no-build\" --include-test-assembly --output \"{clonedTemplateProject.ProjectRootPath}\"{Path.DirectorySeparatorChar}", out standardOutput, out standardError); Assert.Contains("Passed!", standardOutput); AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); } - [ConditionalFact] - [SkipOnOS(OS.Linux)] - [SkipOnOS(OS.MacOS)] + [Fact] public void StandAlone() { using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); @@ -42,7 +38,7 @@ public void StandAlone() string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); - RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError); + RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --output \"{clonedTemplateProject.ProjectRootPath}\"{Path.DirectorySeparatorChar}", out standardOutput, out standardError); Assert.Contains("Hello World!", standardOutput); AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); } From 5bc18ec26d8a9c941b0699d7a8dcd9f5d7af0a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Wed, 26 Jul 2023 09:07:11 +0200 Subject: [PATCH 06/28] Integration test for assembly resolver for mvc.razor (#1502) --- Documentation/Changelog.md | 6 +++ coverlet.sln | 29 ++++++++++--- src/coverlet.core/Properties/AssemblyInfo.cs | 3 +- .../AssemblyInfo.cs | 6 +++ .../ResolverTests.cs | 38 ++++++++++++++++++ ...sts.projectsample.aspmvcrazor.tests.csproj | 19 +++++++++ ....tests.projectsample.aspmvcrazor.tests.snk | Bin 0 -> 596 bytes .../Class.cs | 16 ++++++++ .../Program.cs | 28 +++++++++++++ .../appsettings.json | 9 +++++ ...let.tests.projectsample.aspmvcrazor.csproj | 11 +++++ .../coverlet.tests.projectsample.wpf6.csproj | 1 + 12 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor.tests/AssemblyInfo.cs create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor.tests/ResolverTests.cs create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.csproj create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.snk create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor/Class.cs create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor/Program.cs create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor/appsettings.json create mode 100644 test/coverlet.tests.projectsample.aspmvcrazor/coverlet.tests.projectsample.aspmvcrazor.csproj diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index e02e8fd33..6b33f89ee 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed +-Fix problem with coverage for .net5 WPF application [#1221](https://github.com/coverlet-coverage/coverlet/issues/1221) by https://github.com/lg2de +-Fix unable to instrument module for Microsoft.AspNetCore.Mvc.Razor [#1459](https://github.com/coverlet-coverage/coverlet/issues/1459) by https://github.com/lg2de + ## Release date 2023-05-21 ### Packages coverlet.msbuild 6.0.0 diff --git a/coverlet.sln b/coverlet.sln index 48ebba9a3..311b59387 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32208.508 @@ -54,17 +53,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{9A8B19D4 test\Directory.Build.targets = test\Directory.Build.targets EndProjectSection EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "coverlet.tests.projectsample.vbmynamespace", "test\coverlet.tests.projectsample.vbmynamespace\coverlet.tests.projectsample.vbmynamespace.vbproj", "{C9B7DC34-3E04-4F20-AED4-73791AF8020D}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "coverlet.tests.projectsample.fsharp", "test\coverlet.tests.projectsample.fsharp\coverlet.tests.projectsample.fsharp.fsproj", "{1CBF6966-2A67-4D2C-8598-D174B83072F4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.netframework", "test\coverlet.tests.projectsample.netframework\coverlet.tests.projectsample.netframework.csproj", "{E69D68C9-78ED-4076-A14B-D07295A4B2A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspnet6", "test\coverlet.tests.projectsample.aspnet6\coverlet.tests.projectsample.aspnet6.csproj", "{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet6", "test\coverlet.tests.projectsample.aspnet6\coverlet.tests.projectsample.aspnet6.csproj", "{1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspnet6.tests", "test\coverlet.tests.projectsample.aspnet6.tests\coverlet.tests.projectsample.aspnet6.tests.csproj", "{8EC065A4-7700-45E6-8B90-0182E3649DEA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.aspnet6.tests", "test\coverlet.tests.projectsample.aspnet6.tests\coverlet.tests.projectsample.aspnet6.tests.csproj", "{8EC065A4-7700-45E6-8B90-0182E3649DEA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.wpf6", "test\coverlet.tests.projectsample.wpf6\coverlet.tests.projectsample.wpf6.csproj", "{988A5FF0-4326-4F5B-9F05-CB165543A555}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tests.projectsample.wpf6", "test\coverlet.tests.projectsample.wpf6\coverlet.tests.projectsample.wpf6.csproj", "{988A5FF0-4326-4F5B-9F05-CB165543A555}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspmvcrazor", "test\coverlet.tests.projectsample.aspmvcrazor\coverlet.tests.projectsample.aspmvcrazor.csproj", "{6ACF69B1-C01F-44A4-8F8E-2501884238D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tests.projectsample.aspmvcrazor.tests", "test\coverlet.tests.projectsample.aspmvcrazor.tests\coverlet.tests.projectsample.aspmvcrazor.tests.csproj", "{F508CCDD-5BC8-4AB6-97B3-D37498813C41}" + ProjectSection(ProjectDependencies) = postProject + {31084026-D563-4B91-BE71-174C4270CCF4} = {31084026-D563-4B91-BE71-174C4270CCF4} + {6ACF69B1-C01F-44A4-8F8E-2501884238D4} = {6ACF69B1-C01F-44A4-8F8E-2501884238D4} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -152,6 +159,14 @@ Global {988A5FF0-4326-4F5B-9F05-CB165543A555}.Debug|Any CPU.Build.0 = Debug|Any CPU {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.ActiveCfg = Release|Any CPU {988A5FF0-4326-4F5B-9F05-CB165543A555}.Release|Any CPU.Build.0 = Release|Any CPU + {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6ACF69B1-C01F-44A4-8F8E-2501884238D4}.Release|Any CPU.Build.0 = Release|Any CPU + {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F508CCDD-5BC8-4AB6-97B3-D37498813C41}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -172,12 +187,14 @@ Global {5FF404AD-7C0B-465A-A1E9-558CDC642B0C} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F8199E19-FA9A-4559-9101-CAD7028121B4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {9A8B19D4-4A24-4217-AEFE-159B68F029A1} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {1CBF6966-2A67-4D2C-8598-D174B83072F4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {E69D68C9-78ED-4076-A14B-D07295A4B2A5} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} - {C9B7DC34-3E04-4F20-AED4-73791AF8020D} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {1C3CA3F8-DF8C-433F-8A56-69102D2BBDF6} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {8EC065A4-7700-45E6-8B90-0182E3649DEA} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {988A5FF0-4326-4F5B-9F05-CB165543A555} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {6ACF69B1-C01F-44A4-8F8E-2501884238D4} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} + {F508CCDD-5BC8-4AB6-97B3-D37498813C41} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} diff --git a/src/coverlet.core/Properties/AssemblyInfo.cs b/src/coverlet.core/Properties/AssemblyInfo.cs index 38d48cf07..b97f3eafa 100644 --- a/src/coverlet.core/Properties/AssemblyInfo.cs +++ b/src/coverlet.core/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -// Copyright (c) Toni Solarin-Sodara +// Copyright (c) Toni Solarin-Sodara // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Reflection; @@ -15,6 +15,7 @@ [assembly: InternalsVisibleTo("coverlet.integration.tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010001d24efbe9cbc2dc49b7a3d2ae34ca37cfb69b4f450acd768a22ce5cd021c8a38ae7dc68b2809a1ac606ad531b578f192a5690b2986990cbda4dd84ec65a3a4c1c36f6d7bb18f08592b93091535eaee2f0c8e48763ed7f190db2008e1f9e0facd5c0df5aaab74febd3430e09a428a72e5e6b88357f92d78e47512d46ebdc3cbb")] [assembly: InternalsVisibleTo("coverlet.tests.projectsample.aspnet6.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] [assembly: InternalsVisibleTo("coverlet.tests.projectsample.wpf6.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] +[assembly: InternalsVisibleTo("coverlet.tests.projectsample.aspmvcrazor.tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a82e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e86c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722aead64ad")] // Needed to mock internal type https://github.com/Moq/moq4/wiki/Quickstart#advanced-features [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/test/coverlet.tests.projectsample.aspmvcrazor.tests/AssemblyInfo.cs b/test/coverlet.tests.projectsample.aspmvcrazor.tests/AssemblyInfo.cs new file mode 100644 index 000000000..4bd09b30b --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor.tests/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Reflection; + +[assembly: AssemblyKeyFile("coverlet.tests.projectsample.aspmvcrazor.tests.snk")] diff --git a/test/coverlet.tests.projectsample.aspmvcrazor.tests/ResolverTests.cs b/test/coverlet.tests.projectsample.aspmvcrazor.tests/ResolverTests.cs new file mode 100644 index 000000000..dadf5a7b3 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor.tests/ResolverTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Coverlet.Core.Abstractions; +using Coverlet.Core.Instrumentation; +using Microsoft.Extensions.DependencyModel; +using Moq; +using Xunit; + +namespace coverlet.tests.projectsample.aspmvcrazor.tests +{ + public class ResolverTests + { + [Fact] + public void TestInstrument_NetCoreSharedFrameworkResolver() + { + Assembly assembly = GetType().Assembly; + var mockLogger = new Mock(); + var resolver = new NetCoreSharedFrameworkResolver(assembly.Location, mockLogger.Object); + var compilationLibrary = new CompilationLibrary( + "package", + "Microsoft.AspNetCore.Mvc.Razor", + "0.0.0.0", + "sha512-not-relevant", + Enumerable.Empty(), + Enumerable.Empty(), + true); + + var assemblies = new List(); + Assert.True(resolver.TryResolveAssemblyPaths(compilationLibrary, assemblies), + "sample assembly shall be resolved"); + Assert.NotEmpty(assemblies); + } + } +} diff --git a/test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.csproj b/test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.csproj new file mode 100644 index 000000000..515684a89 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + false + + + + + + + + + + + + + + diff --git a/test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.snk b/test/coverlet.tests.projectsample.aspmvcrazor.tests/coverlet.tests.projectsample.aspmvcrazor.tests.snk new file mode 100644 index 0000000000000000000000000000000000000000..9e007898b54599bc8a77f98b601b9dc05591dcf0 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097XeEBIIc&IL0wTgmzJVwI#SuukrtuWEG zE|G&F|Gk8NvK3{xS8XafBvvYu=xleQn6+e}@wty<*P%ojI*ZI8ZKLf8)!NK<;Ub|4 zsne`rjv=5_)@P!+>4gxX@b*){=Jk(4w6l{>U=sE0N(r;y)8S1+e%=%$y|*H+tz@mA zX+_07fMQLKaP7!3t*el!1Pr0eatN2ScEC4In}@tC$1f@F>m~RNA}Z=sTPi~SASR!N z2BK{p8v!Va4Q$72Nhnt7C9-ey&2T~|#vkpsYPH_@jVd3kt45})`+5j+_lP3AOYzP- z#_(UOwVG2={VwN=Z0OH8b2kj)DVE+BEwUn@L^bx#blR<_D&JGO2$+$Bf2fPzSA4DX zqy^KhFQLFn3n}U80Cjgh3Jy%TvEC>m8d}j$vMJF8Yz>6(wOy@nY9x@0qHTA6-S*#l zmb!ewwR$Urib?u<#q<&V2$~=60O$m8uz`=rQq^I|@`uL1Yl4(Dx2Etz={USc^Ym85 z#piE9H5c&6o_cx%oo@qx(UPV)vT#$3k}B@@U64RD8BB;n#VNe(fGnhlmIsNXXEF&_ zTo}Z;JPeS9tEybm8I+RFl>?0^n<0ZjeDk}O+hQYvSmV9BW@h zM}7567D^tzfyW+DXu literal 0 HcmV?d00001 diff --git a/test/coverlet.tests.projectsample.aspmvcrazor/Class.cs b/test/coverlet.tests.projectsample.aspmvcrazor/Class.cs new file mode 100644 index 000000000..259b4655e --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor/Class.cs @@ -0,0 +1,16 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.AspNetCore.Mvc.Razor; + +namespace coverlet.tests.projectsample.aspmvcrazor +{ + public static class Class + { + public static IMvcBuilder AddLocalization(this IMvcBuilder mvcBuilder, + LanguageViewLocationExpanderFormat viewLocationExpanderFormat = LanguageViewLocationExpanderFormat.Suffix) + { + return mvcBuilder; + } + } +} diff --git a/test/coverlet.tests.projectsample.aspmvcrazor/Program.cs b/test/coverlet.tests.projectsample.aspmvcrazor/Program.cs new file mode 100644 index 000000000..12efc2fd4 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor/Program.cs @@ -0,0 +1,28 @@ +// Copyright (c) Toni Solarin-Sodara +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorPages(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); +app.UseStaticFiles(); + +app.UseRouting(); + +app.UseAuthorization(); + +app.MapRazorPages(); + +app.Run(); diff --git a/test/coverlet.tests.projectsample.aspmvcrazor/appsettings.json b/test/coverlet.tests.projectsample.aspmvcrazor/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/test/coverlet.tests.projectsample.aspmvcrazor/coverlet.tests.projectsample.aspmvcrazor.csproj b/test/coverlet.tests.projectsample.aspmvcrazor/coverlet.tests.projectsample.aspmvcrazor.csproj new file mode 100644 index 000000000..1806460da --- /dev/null +++ b/test/coverlet.tests.projectsample.aspmvcrazor/coverlet.tests.projectsample.aspmvcrazor.csproj @@ -0,0 +1,11 @@ + + + + net6.0 + enable + false + false + false + + + diff --git a/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj b/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj index 7b2242d01..be6ee0adf 100644 --- a/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj +++ b/test/coverlet.tests.projectsample.wpf6/coverlet.tests.projectsample.wpf6.csproj @@ -7,6 +7,7 @@ true false true + false From a63e4d1d602e6afb12b98cc36cf557ca6b7be361 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:19 +0200 Subject: [PATCH 07/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 44de62cdb..b16814150 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -202,7 +202,7 @@ string excludeAssembliesWithoutSources Process process = new(); process.StartInfo.FileName = target; - process.StartInfo.Arguments = targs != null ? targs : string.Empty; + process.StartInfo.Arguments = targs ?? string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; From f0f0688437a8f290009e03c098619d9907d9bee2 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:31 +0200 Subject: [PATCH 08/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index b16814150..55f0a9349 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -241,7 +241,7 @@ string excludeAssembliesWithoutSources Directory.CreateDirectory(directory); } - foreach (string fmt in formats) + foreach (string format in formats) { Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) From b1fcb6f1c4bc187f5278bc692ae25b6e36bb7023 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:52 +0200 Subject: [PATCH 09/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 55f0a9349..f62a62116 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -246,7 +246,7 @@ string excludeAssembliesWithoutSources Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{fmt}' is not supported"); + throw new Exception($"Specified output format '{format}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) From 065468c1715e928a057cf83310763ac4e0302279 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:08 +0200 Subject: [PATCH 10/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index f62a62116..10dc612d2 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -136,7 +136,7 @@ private static Task HandleCommand(string moduleOrAppDirectory, LogLevel verbosity, string[] formats, string threshold, - List thresholdType, + List thresholdTypes, ThresholdStatistic thresholdStat, string[] excludeFilters, string[] includeFilters, From 0c260f83012513adaea898d67c1fe0c2063bae62 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:19 +0200 Subject: [PATCH 11/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 10dc612d2..60c63556d 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -243,7 +243,7 @@ string excludeAssembliesWithoutSources foreach (string format in formats) { - Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); + IReporter reporter = new ReporterFactory(format).CreateReporter(); if (reporter == null) { throw new Exception($"Specified output format '{format}' is not supported"); From dc4db0608c7209f3fcdbf8572f3f03528fffb9f9 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:33 +0200 Subject: [PATCH 12/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 60c63556d..04571dc9f 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -272,7 +272,7 @@ string excludeAssembliesWithoutSources foreach (string thresholdMode in thresholdType) { - if (thresholdMode.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } From 81bd553b823d8cb9bf99a43446e792d2dbd3c19c Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:53 +0200 Subject: [PATCH 13/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 04571dc9f..7c8b8799a 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -276,7 +276,7 @@ string excludeAssembliesWithoutSources { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdMode.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } From 37372ac9bcaf242539214a950b9b00a4a4f5d258 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:56:02 +0200 Subject: [PATCH 14/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 7c8b8799a..fe7c2b5d4 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -280,7 +280,7 @@ string excludeAssembliesWithoutSources { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdMode.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } From b3bc7c7b60f690cdc2f2bff64f29d4eaa1da585c Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:57:39 +0200 Subject: [PATCH 15/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index fe7c2b5d4..73d848aaf 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -270,7 +270,7 @@ string excludeAssembliesWithoutSources var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdMode in thresholdType) + foreach (string thresholdType in thresholdTypes) { if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) { From 6b03f132534bef7b99e82c614282af4a6a3d2ebf Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 09:05:58 +0200 Subject: [PATCH 16/28] remove spelling configuration --- .editorconfig | 17 ++++++++++++----- exclusion.dic | 0 2 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 exclusion.dic diff --git a/.editorconfig b/.editorconfig index b60f1f34e..3cfc4f441 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,9 +13,6 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true -spelling_exclusion_path = ./exclusion.dic -spelling_languages = en-us - # XML project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 @@ -45,7 +42,7 @@ end_of_line = crlf # Code files [*.{cs,csx,vb,vbx}] -indent_size = 4 +indent_size = 2 insert_final_newline = true charset = utf-8-bom ############################### @@ -135,12 +132,15 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case dotnet_diagnostic.IDE1006.severity = warning # IDE0090: Use 'new(...)' dotnet_diagnostic.IDE0090.severity = warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 2 +end_of_line = crlf ############################### # C# Coding Conventions # ############################### [*.cs] # Organize usings -csharp_using_directive_placement = outside_namespace:warning +csharp_using_directive_placement = outside_namespace:silent # var preferences - use keywords instead of BCL types, and permit var only when the type is clear csharp_style_var_for_built_in_types = false:warning csharp_style_var_when_type_is_apparent = true:warning @@ -216,3 +216,10 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent diff --git a/exclusion.dic b/exclusion.dic deleted file mode 100644 index e69de29bb..000000000 From 675cd30cf5a23d3239cdbc4964b9aaae66dfa268 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 31 May 2023 11:38:48 +0200 Subject: [PATCH 17/28] use System.CommandLine for coverlet.console --- .editorconfig | 3 + Directory.Build.props | 10 +- Directory.Build.targets | 3 +- exclusion.dic | 0 src/coverlet.console/Program.cs | 198 ++++++++++--------- src/coverlet.console/coverlet.console.csproj | 3 +- 6 files changed, 122 insertions(+), 95 deletions(-) create mode 100644 exclusion.dic diff --git a/.editorconfig b/.editorconfig index 1227fcc9b..b60f1f34e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,9 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true +spelling_exclusion_path = ./exclusion.dic +spelling_languages = en-us + # XML project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 diff --git a/Directory.Build.props b/Directory.Build.props index a075f6f8b..123e8ebac 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,7 +25,13 @@ - - + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/Directory.Build.targets b/Directory.Build.targets index d970b1531..3d2910136 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,6 +1,5 @@ - @@ -25,6 +24,8 @@ + + diff --git a/exclusion.dic b/exclusion.dic new file mode 100644 index 000000000..e69de29bb diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index d967f90d6..39a115224 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -6,9 +6,12 @@ using System.ComponentModel; using System.Diagnostics; using System.Globalization; +using System.Threading.Tasks; using System.IO; using System.Linq; using System.Text; +using System.CommandLine; +using System.CommandLine.NamingConventionBinder; using ConsoleTables; using Coverlet.Console.Logging; using Coverlet.Core; @@ -17,15 +20,70 @@ using Coverlet.Core.Helpers; using Coverlet.Core.Reporters; using Coverlet.Core.Symbols; -using McMaster.Extensions.CommandLineUtils; using Microsoft.Extensions.DependencyInjection; +using System.Reflection; +using Newtonsoft.Json; namespace Coverlet.Console { - class Program + public static class Program { - static int Main(string[] args) + public static async Task Main(string[] args) { + var root = new RootCommand + { + new Argument("path", "Path to the test assembly or application directory."), + new Option(new[] { "--target", "-t" }, "Path to the test runner application."){Arity = ArgumentArity.ZeroOrOne,IsRequired = true}, + new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner."){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--output", "-o" }, "Output of the generated coverage report"){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed."){Arity = ArgumentArity.ZeroOrOne}, + new Option(new[] { "--formats", "-f" }, () => new[] {"json"} , "Format of the generated coverage report."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--threshold", "Exits with error if the coverage % is below value."){Arity = ArgumentArity.ZeroOrOne}, + new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.") {Arity = ArgumentArity.ZeroOrMore}, + new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude", "Filter expressions to exclude specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--include", "Filter expressions to include only specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-by-file", "Glob patterns specifying source files to exclude."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--include-directory", "Include directories containing additional assemblies to be instrumented."){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }, + new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }, + new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location"){ Arity = ArgumentArity.Zero }, + new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }, + new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }, + new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }, + new Option("--does-not-return-attribute", "Attributes that mark methods that do not return"){Arity = ArgumentArity.ZeroOrMore}, + new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents."){ Arity = ArgumentArity.ZeroOrOne } + }.WithHandler(nameof(HandleCommandAsync)); + + root.Description = "Cross platform .NET Core code coverage tool"; + return await root.InvokeAsync(args); + } + private static async Task HandleCommandAsync(string moduleOrAppDirectory, + string target, + string targetargs, + string output, + LogLevel verbosity, + string[] formats, + string threshold, + List thresholdType, + ThresholdStatistic thresholdStat, + string[] exclude, + string[] include, + string[] excludeByFile, + string[] includeDirectory, + string[] excludeByAttribute, + bool includeTestAssembly, + bool singleHit, + bool skipautoprops, + string mergeWith, + bool useSourceLink, + string[] doesNotReturnAttribute, + string excludeAssembliesWithoutSources + ) + { + + + IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -41,70 +99,29 @@ static int Main(string[] args) var logger = (ConsoleLogger)serviceProvider.GetService(); IFileSystem fileSystem = serviceProvider.GetService(); - var app = new CommandLineApplication - { - Name = "coverlet", - FullName = "Cross platform .NET Core code coverage tool" - }; - app.HelpOption("-h|--help"); - app.VersionOption("-v|--version", GetAssemblyVersion()); + // Adjust log level based on user input. + logger.Level = verbosity; int exitCode = (int)CommandExitCodes.Success; - - CommandArgument moduleOrAppDirectory = app.Argument("", "Path to the test assembly or application directory."); - CommandOption target = app.Option("-t|--target", "Path to the test runner application.", CommandOptionType.SingleValue); - CommandOption targs = app.Option("-a|--targetargs", "Arguments to be passed to the test runner.", CommandOptionType.SingleValue); - CommandOption output = app.Option("-o|--output", "Output of the generated coverage report", CommandOptionType.SingleValue); - CommandOption verbosity = app.Option("-v|--verbosity", "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.", CommandOptionType.SingleValue); - CommandOption formats = app.Option("-f|--format", "Format of the generated coverage report.", CommandOptionType.MultipleValue); - CommandOption threshold = app.Option("--threshold", "Exits with error if the coverage % is below value.", CommandOptionType.SingleValue); - CommandOption thresholdTypes = app.Option("--threshold-type", "Coverage type to apply the threshold to.", CommandOptionType.MultipleValue); - CommandOption thresholdStat = app.Option("--threshold-stat", "Coverage statistic used to enforce the threshold value.", CommandOptionType.SingleValue); - CommandOption excludeFilters = app.Option("--exclude", "Filter expressions to exclude specific modules and types.", CommandOptionType.MultipleValue); - CommandOption includeFilters = app.Option("--include", "Filter expressions to include only specific modules and types.", CommandOptionType.MultipleValue); - CommandOption excludedSourceFiles = app.Option("--exclude-by-file", "Glob patterns specifying source files to exclude.", CommandOptionType.MultipleValue); - CommandOption includeDirectories = app.Option("--include-directory", "Include directories containing additional assemblies to be instrumented.", CommandOptionType.MultipleValue); - CommandOption excludeAttributes = app.Option("--exclude-by-attribute", "Attributes to exclude from code coverage.", CommandOptionType.MultipleValue); - CommandOption includeTestAssembly = app.Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.", CommandOptionType.NoValue); - CommandOption singleHit = app.Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location", CommandOptionType.NoValue); - CommandOption skipAutoProp = app.Option("--skipautoprops", "Neither track nor record auto-implemented properties.", CommandOptionType.NoValue); - CommandOption mergeWith = app.Option("--merge-with", "Path to existing coverage result to merge.", CommandOptionType.SingleValue); - CommandOption useSourceLink = app.Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.", CommandOptionType.NoValue); - CommandOption doesNotReturnAttributes = app.Option("--does-not-return-attribute", "Attributes that mark methods that do not return.", CommandOptionType.MultipleValue); - CommandOption excludeAssembliesWithoutSources = app.Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.", CommandOptionType.SingleValue); - - app.OnExecute(() => + try { - if (string.IsNullOrEmpty(moduleOrAppDirectory.Value) || string.IsNullOrWhiteSpace(moduleOrAppDirectory.Value)) - throw new CommandParsingException(app, "No test assembly or application directory specified."); - - if (!target.HasValue()) - throw new CommandParsingException(app, "Target must be specified."); - - if (verbosity.HasValue()) - { - // Adjust log level based on user input. - logger.Level = verbosity.ParsedValue; - } - CoverageParameters parameters = new() { - IncludeFilters = includeFilters.Values.ToArray(), - IncludeDirectories = includeDirectories.Values.ToArray(), - ExcludeFilters = excludeFilters.Values.ToArray(), - ExcludedSourceFiles = excludedSourceFiles.Values.ToArray(), - ExcludeAttributes = excludeAttributes.Values.ToArray(), - IncludeTestAssembly = includeTestAssembly.HasValue(), - SingleHit = singleHit.HasValue(), - MergeWith = mergeWith.Value(), - UseSourceLink = useSourceLink.HasValue(), - SkipAutoProps = skipAutoProp.HasValue(), - DoesNotReturnAttributes = doesNotReturnAttributes.Values.ToArray(), - ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources.Value() + IncludeFilters = include, + IncludeDirectories = includeDirectory, + ExcludeFilters = exclude, + ExcludedSourceFiles = excludeByFile, + ExcludeAttributes = excludeByAttribute, + IncludeTestAssembly = includeTestAssembly, + SingleHit = singleHit, + MergeWith = mergeWith, + UseSourceLink = useSourceLink, + SkipAutoProps = skipautoprops, + DoesNotReturnAttributes = doesNotReturnAttribute, + ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources }; - ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService(); - Coverage coverage = new(moduleOrAppDirectory.Value, + Coverage coverage = new(moduleOrAppDirectory, parameters, logger, serviceProvider.GetRequiredService(), @@ -114,8 +131,8 @@ static int Main(string[] args) coverage.PrepareModules(); Process process = new(); - process.StartInfo.FileName = target.Value(); - process.StartInfo.Arguments = targs.HasValue() ? targs.Value() : string.Empty; + process.StartInfo.FileName = target; + process.StartInfo.Arguments = targetargs != null ? targetargs : string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; @@ -138,9 +155,7 @@ static int Main(string[] args) process.WaitForExit(); - string dOutput = output.HasValue() ? output.Value() : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString(); - List dThresholdTypes = thresholdTypes.HasValue() ? thresholdTypes.Values : new List(new string[] { "line", "branch", "method" }); - ThresholdStatistic dThresholdStat = thresholdStat.HasValue() ? Enum.Parse(thresholdStat.Value(), true) : Enum.Parse("minimum", true); + string dOutput = output != null ? output : Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar.ToString(); logger.LogInformation("\nCalculating coverage result..."); @@ -156,12 +171,12 @@ static int Main(string[] args) Directory.CreateDirectory(directory); } - foreach (string format in formats.HasValue() ? formats.Values : new List(new string[] { "json" })) + foreach (string fmt in formats) { - Core.Abstractions.IReporter reporter = new ReporterFactory(format).CreateReporter(); + Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{format}' is not supported"); + throw new Exception($"Specified output format '{formats}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) @@ -185,26 +200,26 @@ static int Main(string[] args) var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdType in dThresholdTypes) + foreach (string thresholdTyp in thresholdType) { - if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdTyp.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdTyp.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdTyp.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } } var thresholdTypeFlagValues = new Dictionary(); - if (threshold.HasValue() && threshold.Value().Contains(',')) + if (!string.IsNullOrEmpty(threshold) && threshold.Contains(',')) { - IEnumerable thresholdValues = threshold.Value().Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()); + IEnumerable thresholdValues = threshold.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()); if (thresholdValues.Count() != thresholdTypeFlagQueue.Count) { throw new Exception($"Threshold type flag count ({thresholdTypeFlagQueue.Count}) and values count ({thresholdValues.Count()}) doesn't match"); @@ -224,7 +239,7 @@ static int Main(string[] args) } else { - double thresholdValue = threshold.HasValue() ? double.Parse(threshold.Value()) : 0; + double thresholdValue = threshold != null ? double.Parse(threshold) : 0; while (thresholdTypeFlagQueue.Any()) { @@ -271,44 +286,36 @@ static int Main(string[] args) exitCode += (int)CommandExitCodes.TestFailed; } - ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, dThresholdStat); + ThresholdTypeFlags thresholdTypeFlags = result.GetThresholdTypesBelowThreshold(summary, thresholdTypeFlagValues, thresholdStat); if (thresholdTypeFlags != ThresholdTypeFlags.None) { exitCode += (int)CommandExitCodes.CoverageBelowThreshold; var exceptionMessageBuilder = new StringBuilder(); if ((thresholdTypeFlags & ThresholdTypeFlags.Line) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} line coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Line]}"); } if ((thresholdTypeFlags & ThresholdTypeFlags.Branch) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} branch coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Branch]}"); } if ((thresholdTypeFlags & ThresholdTypeFlags.Method) != ThresholdTypeFlags.None) { - exceptionMessageBuilder.AppendLine($"The {dThresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}"); + exceptionMessageBuilder.AppendLine($"The {thresholdStat.ToString().ToLower()} method coverage is below the specified {thresholdTypeFlagValues[ThresholdTypeFlags.Method]}"); } throw new Exception(exceptionMessageBuilder.ToString()); } return exitCode; - }); - try - { - return app.Execute(args); - } - catch (CommandParsingException ex) - { - logger.LogError(ex.Message); - app.ShowHelp(); - return (int)CommandExitCodes.CommandParsingException; + } + catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process") { - logger.LogError($"Start process '{target.Value()}' failed with '{we.Message}'"); + logger.LogError($"Start process '{target}' failed with '{we.Message}'"); return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; } catch (Exception ex) @@ -316,9 +323,18 @@ static int Main(string[] args) logger.LogError(ex.Message); return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; } + + } + + private static Command WithHandler(this Command command, string methodName) + { + var method = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); + var handler = CommandHandler.Create(method!); + command.Handler = handler; + return command; } - static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); +static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture); } diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj index b44e05dff..60076dc70 100644 --- a/src/coverlet.console/coverlet.console.csproj +++ b/src/coverlet.console/coverlet.console.csproj @@ -22,7 +22,8 @@ - + + From d4682375015dfe78e7dc4c03b4ec9f601b67d151 Mon Sep 17 00:00:00 2001 From: Bert Date: Mon, 26 Jun 2023 08:36:32 +0200 Subject: [PATCH 18/28] resolve review comments and add DotnetTool tests --- Directory.Build.props | 12 +- Directory.Build.targets | 1 - Documentation/GlobalTool.md | 10 +- global.json | 2 +- .../DataCollection/CoverageWrapper.cs | 4 + .../coverlet.collector.csproj | 2 +- src/coverlet.console/Program.cs | 194 ++++++++++++------ src/coverlet.console/coverlet.console.csproj | 8 +- .../Reporters/CoberturaReporter.cs | 1 - src/coverlet.core/coverlet.core.csproj | 2 +- .../coverlet.msbuild.tasks.csproj | 2 +- .../coverlet.collector.tests.csproj | 2 +- .../Helpers/InstrumentationHelperTests.cs | 1 - .../coverlet.core.tests.csproj | 3 +- test/coverlet.integration.tests/DotnetTool.cs | 51 +++++ 15 files changed, 203 insertions(+), 92 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 123e8ebac..7a716a812 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ preview true preview - $(NoWarn);NU5105 + $(NoWarn);NU5105;CS1591 https://api.nuget.org/v3/index.json; @@ -25,13 +25,7 @@ - - all - runtime; build; native; contentfiles; analyzers - - - all - runtime; build; native; contentfiles; analyzers - + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 3d2910136..aea1e02e7 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -25,7 +25,6 @@ - diff --git a/Documentation/GlobalTool.md b/Documentation/GlobalTool.md index fb3dcb40c..cf50232c3 100644 --- a/Documentation/GlobalTool.md +++ b/Documentation/GlobalTool.md @@ -17,16 +17,14 @@ Arguments: Path to the test assembly or application directory. Options: - -h|--help Show help information - -v|--version Show version information - -t|--target Path to the test runner application. + -t|--target (REQUIRED) Path to the test runner application. -a|--targetargs Arguments to be passed to the test runner. -o|--output Output of the generated coverage report -v|--verbosity Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed. - -f|--format Format of the generated coverage report. + -f|--format Format of the generated coverage report. [default: json] --threshold Exits with error if the coverage % is below value. --threshold-type Coverage type to apply the threshold to. - --threshold-stat Coverage statistic used to enforce the threshold value. + --threshold-stat Coverage statistic used to enforce the threshold value. [default: Minimum] --exclude Filter expressions to exclude specific modules and types. --include Filter expressions to include only specific modules and types. --exclude-by-file Glob patterns specifying source files to exclude. @@ -39,6 +37,8 @@ Options: --use-source-link Specifies whether to use SourceLink URIs in place of file system paths. --does-not-return-attribute Attributes that mark methods that do not return. --exclude-assemblies-without-sources Specifies behaviour of heuristic to ignore assemblies with missing source documents. + --version Show version information + -?, -h, --help Show help and usage information ``` NB. For a [multiple value] options you have to specify values multiple times i.e. diff --git a/global.json b/global.json index 2dbcd442b..ccc54128c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.408", + "version": "6.0.410", "rollForward": "latestMajor" } } diff --git a/src/coverlet.collector/DataCollection/CoverageWrapper.cs b/src/coverlet.collector/DataCollection/CoverageWrapper.cs index 0569ab277..ffd7d8a41 100644 --- a/src/coverlet.collector/DataCollection/CoverageWrapper.cs +++ b/src/coverlet.collector/DataCollection/CoverageWrapper.cs @@ -17,6 +17,10 @@ internal class CoverageWrapper : ICoverageWrapper /// /// Coverlet settings /// Coverlet logger + /// + /// + /// + /// /// Coverage object public Coverage CreateCoverage(CoverletSettings settings, ILogger coverletLogger, IInstrumentationHelper instrumentationHelper, IFileSystem fileSystem, ISourceRootTranslator sourceRootTranslator, ICecilSymbolHelper cecilSymbolHelper) { diff --git a/src/coverlet.collector/coverlet.collector.csproj b/src/coverlet.collector/coverlet.collector.csproj index ba206705c..bdae19ae6 100644 --- a/src/coverlet.collector/coverlet.collector.csproj +++ b/src/coverlet.collector/coverlet.collector.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 coverlet.collector diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 39a115224..44de62cdb 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Text; using System.CommandLine; -using System.CommandLine.NamingConventionBinder; using ConsoleTables; using Coverlet.Console.Logging; using Coverlet.Core; @@ -21,69 +20,139 @@ using Coverlet.Core.Reporters; using Coverlet.Core.Symbols; using Microsoft.Extensions.DependencyInjection; -using System.Reflection; -using Newtonsoft.Json; namespace Coverlet.Console { public static class Program { - public static async Task Main(string[] args) + static int Main(string[] args) { - var root = new RootCommand + var moduleOrAppDirectory = new Argument("path", "Path to the test assembly or application directory."); + var target = new Option(new[] { "--target", "-t" }, "Path to the test runner application.") { Arity = ArgumentArity.ZeroOrOne, IsRequired = true }; + var targs = new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner.") { Arity = ArgumentArity.ZeroOrOne }; + var output = new Option(new[] { "--output", "-o" }, "Output of the generated coverage report") { Arity = ArgumentArity.ZeroOrOne }; + var verbosity = new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed.") { Arity = ArgumentArity.ZeroOrOne }; + var formats = new Option(new[] { "--format", "-f" }, () => new[] { "json" }, "Format of the generated coverage report.") { Arity = ArgumentArity.ZeroOrMore }; + var threshold = new Option("--threshold", "Exits with error if the coverage % is below value.") { Arity = ArgumentArity.ZeroOrOne }; + var thresholdTypes = new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.").FromAmong("line", "branch", "method"); + var thresholdStat = new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value.") { Arity = ArgumentArity.ZeroOrMore }; + var excludeFilters = new Option("--exclude", "Filter expressions to exclude specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; + var includeFilters = new Option("--include", "Filter expressions to include only specific modules and types.") { Arity = ArgumentArity.ZeroOrMore }; + var excludedSourceFiles = new Option("--exclude-by-file", "Glob patterns specifying source files to exclude.") { Arity = ArgumentArity.ZeroOrMore }; + var includeDirectories = new Option("--include-directory", "Include directories containing additional assemblies to be instrumented.") { Arity = ArgumentArity.ZeroOrMore }; + var excludeAttributes = new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }; + var includeTestAssembly = new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }; + var singleHit = new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location") { Arity = ArgumentArity.Zero }; + var skipAutoProp = new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }; + var mergeWith = new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }; + var useSourceLink = new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }; + var doesNotReturnAttributes = new Option("--does-not-return-attribute", "Attributes that mark methods that do not return") { Arity = ArgumentArity.ZeroOrMore }; + var excludeAssembliesWithoutSources = new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents.") { Arity = ArgumentArity.ZeroOrOne }; + + RootCommand rootCommand = new () { - new Argument("path", "Path to the test assembly or application directory."), - new Option(new[] { "--target", "-t" }, "Path to the test runner application."){Arity = ArgumentArity.ZeroOrOne,IsRequired = true}, - new Option(new[] { "--targetargs", "-a" }, "Arguments to be passed to the test runner."){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--output", "-o" }, "Output of the generated coverage report"){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--verbosity", "-v" }, () => LogLevel.Normal, "Sets the verbosity level of the command. Allowed values are quiet, minimal, normal, detailed."){Arity = ArgumentArity.ZeroOrOne}, - new Option(new[] { "--formats", "-f" }, () => new[] {"json"} , "Format of the generated coverage report."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--threshold", "Exits with error if the coverage % is below value."){Arity = ArgumentArity.ZeroOrOne}, - new Option>("--threshold-type", () => new List(new string[] { "line", "branch", "method" }), "Coverage type to apply the threshold to.") {Arity = ArgumentArity.ZeroOrMore}, - new Option("--threshold-stat", () => ThresholdStatistic.Minimum, "Coverage statistic used to enforce the threshold value."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude", "Filter expressions to exclude specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--include", "Filter expressions to include only specific modules and types."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-by-file", "Glob patterns specifying source files to exclude."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--include-directory", "Include directories containing additional assemblies to be instrumented."){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-by-attribute", "Attributes to exclude from code coverage.") { Arity = ArgumentArity.ZeroOrOne }, - new Option("--include-test-assembly", "Specifies whether to report code coverage of the test assembly.") { Arity = ArgumentArity.Zero }, - new Option("--single-hit", "Specifies whether to limit code coverage hit reporting to a single hit for each location"){ Arity = ArgumentArity.Zero }, - new Option("--skipautoprops", "Neither track nor record auto-implemented properties.") { Arity = ArgumentArity.Zero }, - new Option("--merge-with", "Path to existing coverage result to merge.") { Arity = ArgumentArity.ZeroOrOne }, - new Option("--use-source-link", "Specifies whether to use SourceLink URIs in place of file system paths.") { Arity = ArgumentArity.Zero }, - new Option("--does-not-return-attribute", "Attributes that mark methods that do not return"){Arity = ArgumentArity.ZeroOrMore}, - new Option("--exclude-assemblies-without-sources", "Specifies behaviour of heuristic to ignore assemblies with missing source documents."){ Arity = ArgumentArity.ZeroOrOne } - }.WithHandler(nameof(HandleCommandAsync)); - - root.Description = "Cross platform .NET Core code coverage tool"; - return await root.InvokeAsync(args); + moduleOrAppDirectory, + target, + targs, + output, + verbosity, + formats, + threshold, + thresholdTypes, + thresholdStat, + excludeFilters, + includeFilters, + excludedSourceFiles, + includeDirectories, + excludeAttributes, + includeTestAssembly, + singleHit, + skipAutoProp, + mergeWith, + useSourceLink, + doesNotReturnAttributes, + excludeAssembliesWithoutSources + }; + + rootCommand.Description = "Cross platform .NET Core code coverage tool"; + + rootCommand.SetHandler(async (context) => + { + string moduleOrAppDirectoryValue = context.ParseResult.GetValueForArgument(moduleOrAppDirectory); + string targetValue = context.ParseResult.GetValueForOption(target); + string targsValue = context.ParseResult.GetValueForOption(targs); + string outputValue = context.ParseResult.GetValueForOption(output); + LogLevel verbosityValue = context.ParseResult.GetValueForOption(verbosity); + string[] formatsValue = context.ParseResult.GetValueForOption(formats); + string thresholdValue = context.ParseResult.GetValueForOption(threshold); + List thresholdTypesValue = context.ParseResult.GetValueForOption(thresholdTypes); + ThresholdStatistic thresholdStatValue = context.ParseResult.GetValueForOption(thresholdStat); + string[] excludeFiltersValue = context.ParseResult.GetValueForOption(excludeFilters); + string[] includeFiltersValue = context.ParseResult.GetValueForOption(includeFilters); + string[] excludedSourceFilesValue = context.ParseResult.GetValueForOption(excludedSourceFiles); + string[] includeDirectoriesValue = context.ParseResult.GetValueForOption(includeDirectories); + string[] excludeAttributesValue = context.ParseResult.GetValueForOption(excludeAttributes); + bool includeTestAssemblyValue = context.ParseResult.GetValueForOption(includeTestAssembly); + bool singleHitValue = context.ParseResult.GetValueForOption(singleHit); + bool skipAutoPropValue = context.ParseResult.GetValueForOption(skipAutoProp); + string mergeWithValue = context.ParseResult.GetValueForOption(mergeWith); + bool useSourceLinkValue = context.ParseResult.GetValueForOption(useSourceLink); + string[] doesNotReturnAttributesValue = context.ParseResult.GetValueForOption(doesNotReturnAttributes); + string excludeAssembliesWithoutSourcesValue = context.ParseResult.GetValueForOption(excludeAssembliesWithoutSources); + + if (string.IsNullOrEmpty(moduleOrAppDirectoryValue) || string.IsNullOrWhiteSpace(moduleOrAppDirectoryValue)) + throw new ArgumentException("No test assembly or application directory specified."); + + var taskStatus = await HandleCommand(moduleOrAppDirectoryValue, + targetValue, + targsValue, + outputValue, + verbosityValue, + formatsValue, + thresholdValue, + thresholdTypesValue, + thresholdStatValue, + excludeFiltersValue, + includeFiltersValue, + excludedSourceFilesValue, + includeDirectoriesValue, + excludeAttributesValue, + includeTestAssemblyValue, + singleHitValue, + skipAutoPropValue, + mergeWithValue, + useSourceLinkValue, + doesNotReturnAttributesValue, + excludeAssembliesWithoutSourcesValue); + context.ExitCode = taskStatus; + + }); + return rootCommand.Invoke(args); } - private static async Task HandleCommandAsync(string moduleOrAppDirectory, + private static Task HandleCommand(string moduleOrAppDirectory, string target, - string targetargs, + string targs, string output, LogLevel verbosity, string[] formats, string threshold, List thresholdType, ThresholdStatistic thresholdStat, - string[] exclude, - string[] include, - string[] excludeByFile, - string[] includeDirectory, - string[] excludeByAttribute, + string[] excludeFilters, + string[] includeFilters, + string[] excludedSourceFiles, + string[] includeDirectories, + string[] excludeAttributes, bool includeTestAssembly, bool singleHit, - bool skipautoprops, + bool skipAutoProp, string mergeWith, bool useSourceLink, - string[] doesNotReturnAttribute, + string[] doesNotReturnAttributes, string excludeAssembliesWithoutSources ) { - - IServiceCollection serviceCollection = new ServiceCollection(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -102,21 +171,22 @@ string excludeAssembliesWithoutSources // Adjust log level based on user input. logger.Level = verbosity; int exitCode = (int)CommandExitCodes.Success; + try { CoverageParameters parameters = new() { - IncludeFilters = include, - IncludeDirectories = includeDirectory, - ExcludeFilters = exclude, - ExcludedSourceFiles = excludeByFile, - ExcludeAttributes = excludeByAttribute, + IncludeFilters = includeFilters, + IncludeDirectories = includeDirectories, + ExcludeFilters = excludeFilters, + ExcludedSourceFiles = excludedSourceFiles, + ExcludeAttributes = excludeAttributes, IncludeTestAssembly = includeTestAssembly, SingleHit = singleHit, MergeWith = mergeWith, UseSourceLink = useSourceLink, - SkipAutoProps = skipautoprops, - DoesNotReturnAttributes = doesNotReturnAttribute, + SkipAutoProps = skipAutoProp, + DoesNotReturnAttributes = doesNotReturnAttributes, ExcludeAssembliesWithoutSources = excludeAssembliesWithoutSources }; ISourceRootTranslator sourceRootTranslator = serviceProvider.GetRequiredService(); @@ -132,7 +202,7 @@ string excludeAssembliesWithoutSources Process process = new(); process.StartInfo.FileName = target; - process.StartInfo.Arguments = targetargs != null ? targetargs : string.Empty; + process.StartInfo.Arguments = targs != null ? targs : string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; @@ -176,7 +246,7 @@ string excludeAssembliesWithoutSources Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{formats}' is not supported"); + throw new Exception($"Specified output format '{fmt}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) @@ -200,17 +270,17 @@ string excludeAssembliesWithoutSources var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdTyp in thresholdType) + foreach (string thresholdMode in thresholdType) { - if (thresholdTyp.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdMode.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdTyp.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdMode.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdTyp.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdMode.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } @@ -308,7 +378,7 @@ string excludeAssembliesWithoutSources throw new Exception(exceptionMessageBuilder.ToString()); } - return exitCode; + return Task.FromResult(exitCode); } @@ -316,26 +386,16 @@ string excludeAssembliesWithoutSources catch (Win32Exception we) when (we.Source == "System.Diagnostics.Process") { logger.LogError($"Start process '{target}' failed with '{we.Message}'"); - return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; + return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception); } catch (Exception ex) { logger.LogError(ex.Message); - return exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception; + return Task.FromResult(exitCode > 0 ? exitCode : (int)CommandExitCodes.Exception); } } - private static Command WithHandler(this Command command, string methodName) - { - var method = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static); - var handler = CommandHandler.Create(method!); - command.Handler = handler; - return command; - } - -static string GetAssemblyVersion() => typeof(Program).Assembly.GetName().Version.ToString(); - static string InvariantFormat(double value) => value.ToString(CultureInfo.InvariantCulture); } } diff --git a/src/coverlet.console/coverlet.console.csproj b/src/coverlet.console/coverlet.console.csproj index 60076dc70..d4d110fa8 100644 --- a/src/coverlet.console/coverlet.console.csproj +++ b/src/coverlet.console/coverlet.console.csproj @@ -14,16 +14,17 @@ tonerdo Coverlet is a cross platform code coverage tool for .NET, with support for line, branch and method coverage. coverage;testing;unit-test;lcov;opencover;quality + GlobalTool.md + https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md https://raw.githubusercontent.com/tonerdo/coverlet/master/_assets/coverlet-icon.svg?sanitize=true coverlet-icon.png https://github.com/coverlet-coverage/coverlet MIT git - + - @@ -32,6 +33,9 @@ + + true + diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index e3d299fda..3bbb67aaa 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 93a6c972d..34b6b5777 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -1,4 +1,4 @@ - + Library diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 80cbb6b21..0dbef0361 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -1,4 +1,4 @@ - + Library diff --git a/test/coverlet.collector.tests/coverlet.collector.tests.csproj b/test/coverlet.collector.tests/coverlet.collector.tests.csproj index fa828394a..87826ac9d 100644 --- a/test/coverlet.collector.tests/coverlet.collector.tests.csproj +++ b/test/coverlet.collector.tests/coverlet.collector.tests.csproj @@ -1,4 +1,4 @@ - + diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 8c6917042..71d88bfb8 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -6,7 +6,6 @@ using Xunit; using System.Collections.Generic; using System.Linq; -using Castle.Core.Internal; using Moq; using Coverlet.Core.Abstractions; using Coverlet.Core.Enums; diff --git a/test/coverlet.core.tests/coverlet.core.tests.csproj b/test/coverlet.core.tests/coverlet.core.tests.csproj index f040aa9c3..a882b8e90 100644 --- a/test/coverlet.core.tests/coverlet.core.tests.csproj +++ b/test/coverlet.core.tests/coverlet.core.tests.csproj @@ -1,4 +1,4 @@ - + @@ -8,6 +8,7 @@ NU1702 true + diff --git a/test/coverlet.integration.tests/DotnetTool.cs b/test/coverlet.integration.tests/DotnetTool.cs index a947391c3..8c87e33da 100644 --- a/test/coverlet.integration.tests/DotnetTool.cs +++ b/test/coverlet.integration.tests/DotnetTool.cs @@ -42,5 +42,56 @@ public void StandAlone() Assert.Contains("Hello World!", standardOutput); AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThreshold() + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False( RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.Contains("The minimum method coverage is below the specified 80", standardOutput); + } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThresholdLine() + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False(RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --threshold-type line --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.DoesNotContain("The minimum method coverage is below the specified 80", standardOutput); + } + + [ConditionalFact] + [SkipOnOS(OS.Linux)] + [SkipOnOS(OS.MacOS)] + public void StandAloneThresholdLineAndMethod () + { + using ClonedTemplateProject clonedTemplateProject = CloneTemplateProject(); + UpdateNugeConfigtWithLocalPackageFolder(clonedTemplateProject.ProjectRootPath!); + string coverletToolCommandPath = InstallTool(clonedTemplateProject.ProjectRootPath!); + DotnetCli($"build {clonedTemplateProject.ProjectRootPath}", out string standardOutput, out string standardError); + string publishedTestFile = clonedTemplateProject.GetFiles("*" + ClonedTemplateProject.AssemblyName + ".dll").Single(f => !f.Contains("obj") && !f.Contains("ref")); + Assert.False(RunCommand(coverletToolCommandPath, $"\"{Path.GetDirectoryName(publishedTestFile)}\" --target \"dotnet\" --targetargs \"{publishedTestFile}\" --threshold 80 --threshold-type line --threshold-type method --output \"{clonedTemplateProject.ProjectRootPath}\"\\", out standardOutput, out standardError)); + Assert.Contains("Hello World!", standardOutput); + AssertCoverage(clonedTemplateProject, standardOutput: standardOutput); + Assert.Contains("The minimum line coverage is below the specified 80", standardOutput); + Assert.Contains("The minimum method coverage is below the specified 80", standardOutput); + } } } From e83ab4d505dd74a9204e2a127c6400bd4d9465fe Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:19 +0200 Subject: [PATCH 19/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 44de62cdb..b16814150 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -202,7 +202,7 @@ string excludeAssembliesWithoutSources Process process = new(); process.StartInfo.FileName = target; - process.StartInfo.Arguments = targs != null ? targs : string.Empty; + process.StartInfo.Arguments = targs ?? string.Empty; process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; From 75f6fdb64afdacab13423434a7223423448e322f Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:31 +0200 Subject: [PATCH 20/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index b16814150..55f0a9349 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -241,7 +241,7 @@ string excludeAssembliesWithoutSources Directory.CreateDirectory(directory); } - foreach (string fmt in formats) + foreach (string format in formats) { Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) From 9d16c9d6d4d39dcefbb4c9068be57de12a4279b2 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:54:52 +0200 Subject: [PATCH 21/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 55f0a9349..f62a62116 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -246,7 +246,7 @@ string excludeAssembliesWithoutSources Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); if (reporter == null) { - throw new Exception($"Specified output format '{fmt}' is not supported"); + throw new Exception($"Specified output format '{format}' is not supported"); } if (reporter.OutputType == ReporterOutputType.Console) From 3d303c8d0d368db76d3fafaff87baa9cccc55994 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:08 +0200 Subject: [PATCH 22/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index f62a62116..10dc612d2 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -136,7 +136,7 @@ private static Task HandleCommand(string moduleOrAppDirectory, LogLevel verbosity, string[] formats, string threshold, - List thresholdType, + List thresholdTypes, ThresholdStatistic thresholdStat, string[] excludeFilters, string[] includeFilters, From 7facddd00e0a1ebe5394c37f75c391825f58b28b Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:19 +0200 Subject: [PATCH 23/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 10dc612d2..60c63556d 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -243,7 +243,7 @@ string excludeAssembliesWithoutSources foreach (string format in formats) { - Core.Abstractions.IReporter reporter = new ReporterFactory(fmt).CreateReporter(); + IReporter reporter = new ReporterFactory(format).CreateReporter(); if (reporter == null) { throw new Exception($"Specified output format '{format}' is not supported"); From 7f0b53ec37823dea3eb24aff5d3db0274522a82b Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:33 +0200 Subject: [PATCH 24/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 60c63556d..04571dc9f 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -272,7 +272,7 @@ string excludeAssembliesWithoutSources foreach (string thresholdMode in thresholdType) { - if (thresholdMode.Equals("line", StringComparison.OrdinalIgnoreCase)) + if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } From c5d03faf7393910b44afdcdbfc014d3d5f59fb45 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:55:53 +0200 Subject: [PATCH 25/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 04571dc9f..7c8b8799a 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -276,7 +276,7 @@ string excludeAssembliesWithoutSources { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Line); } - else if (thresholdMode.Equals("branch", StringComparison.OrdinalIgnoreCase)) + else if (thresholdType.Equals("branch", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } From 8d35fa132779c42ec253a9a3bd7654ad39f880b7 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:56:02 +0200 Subject: [PATCH 26/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index 7c8b8799a..fe7c2b5d4 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -280,7 +280,7 @@ string excludeAssembliesWithoutSources { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Branch); } - else if (thresholdMode.Equals("method", StringComparison.OrdinalIgnoreCase)) + else if (thresholdType.Equals("method", StringComparison.OrdinalIgnoreCase)) { thresholdTypeFlagQueue.Enqueue(ThresholdTypeFlags.Method); } From 6fdac0da94e23d47f8dc1233ddff62e576c04ef3 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 08:57:39 +0200 Subject: [PATCH 27/28] Update src/coverlet.console/Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Müller --- src/coverlet.console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.console/Program.cs b/src/coverlet.console/Program.cs index fe7c2b5d4..73d848aaf 100644 --- a/src/coverlet.console/Program.cs +++ b/src/coverlet.console/Program.cs @@ -270,7 +270,7 @@ string excludeAssembliesWithoutSources var thresholdTypeFlagQueue = new Queue(); - foreach (string thresholdMode in thresholdType) + foreach (string thresholdType in thresholdTypes) { if (thresholdType.Equals("line", StringComparison.OrdinalIgnoreCase)) { From ab171a2267540e3ab983fa1cebed36ff829c9c2b Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 2 Aug 2023 09:05:58 +0200 Subject: [PATCH 28/28] remove spelling configuration --- .editorconfig | 17 ++++++++++++----- exclusion.dic | 0 2 files changed, 12 insertions(+), 5 deletions(-) delete mode 100644 exclusion.dic diff --git a/.editorconfig b/.editorconfig index b60f1f34e..3cfc4f441 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,9 +13,6 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true -spelling_exclusion_path = ./exclusion.dic -spelling_languages = en-us - # XML project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 @@ -45,7 +42,7 @@ end_of_line = crlf # Code files [*.{cs,csx,vb,vbx}] -indent_size = 4 +indent_size = 2 insert_final_newline = true charset = utf-8-bom ############################### @@ -135,12 +132,15 @@ dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case dotnet_diagnostic.IDE1006.severity = warning # IDE0090: Use 'new(...)' dotnet_diagnostic.IDE0090.severity = warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 2 +end_of_line = crlf ############################### # C# Coding Conventions # ############################### [*.cs] # Organize usings -csharp_using_directive_placement = outside_namespace:warning +csharp_using_directive_placement = outside_namespace:silent # var preferences - use keywords instead of BCL types, and permit var only when the type is clear csharp_style_var_for_built_in_types = false:warning csharp_style_var_when_type_is_apparent = true:warning @@ -216,3 +216,10 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent diff --git a/exclusion.dic b/exclusion.dic deleted file mode 100644 index e69de29bb..000000000