From 89791e642550170f971b5ae0ef45516187bdab56 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 1 May 2018 21:25:06 -0400 Subject: [PATCH 01/62] add better branch support, change coverage calculation to be on actual points instead of averages --- .gitignore | 1 + src/coverlet.core/Coverage.cs | 104 +++++-- src/coverlet.core/CoverageResult.cs | 22 +- src/coverlet.core/CoverageSummary.cs | 255 +++++++++------ src/coverlet.core/CoverageTracker.cs | 1 + .../Extensions/HelperExtensions.cs | 16 + .../Instrumentation/Instrumenter.cs | 95 ++++-- .../Instrumentation/InstrumenterResult.cs | 16 +- .../Reporters/CoberturaReporter.cs | 61 ++-- src/coverlet.core/Reporters/LcovReporter.cs | 54 ++-- .../Reporters/OpenCoverReporter.cs | 108 ++++--- src/coverlet.core/Symbols/BranchPoint.cs | 47 +++ .../Symbols/CecilSymbolHelper.cs | 290 ++++++++++++++++++ .../CoverageResultTask.cs | 7 +- .../CoverageSummaryTests.cs | 36 ++- test/coverlet.core.tests/CoverageTests.cs | 18 +- .../InstrumenterResultTests.cs | 3 +- .../Instrumentation/InstrumenterTests.cs | 2 +- .../Reporters/CoberturaReporterTests.cs | 11 +- .../Reporters/JsonReporterTests.cs | 4 +- .../Reporters/LcovReporterTests.cs | 11 +- .../Reporters/OpenCoverReporterTests.cs | 15 +- test/coverlet.core.tests/Samples/Samples.cs | 164 ++++++++++ .../Symbols/CecilSymbolHelperTests.cs | 263 ++++++++++++++++ 24 files changed, 1332 insertions(+), 272 deletions(-) create mode 100644 src/coverlet.core/Extensions/HelperExtensions.cs create mode 100644 src/coverlet.core/Symbols/BranchPoint.cs create mode 100644 src/coverlet.core/Symbols/CecilSymbolHelper.cs create mode 100644 test/coverlet.core.tests/Samples/Samples.cs create mode 100644 test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs diff --git a/.gitignore b/.gitignore index b9830ce42..294c9a4cb 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,7 @@ _TeamCity* # Visual Studio code coverage results *.coverage *.coveragexml +lcov.info # NCrunch _NCrunch_* diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 0ec0b4c84..c68e2950d 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -49,35 +49,90 @@ public CoverageResult GetCoverageResult() Documents documents = new Documents(); foreach (var doc in result.Documents) { + // Construct Line Results foreach (var line in doc.Lines) { if (documents.TryGetValue(doc.Path, out Classes classes)) { if (classes.TryGetValue(line.Class, out Methods methods)) { - if (methods.TryGetValue(line.Method, out Lines lines)) + if (methods.TryGetValue(line.Method, out Method method)) { - documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget }); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); } else { - documents[doc.Path][line.Class].Add(line.Method, new Lines()); - documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget }); + documents[doc.Path][line.Class].Add(line.Method, new Method()); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); } } else { documents[doc.Path].Add(line.Class, new Methods()); - documents[doc.Path][line.Class].Add(line.Method, new Lines()); - documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget }); + documents[doc.Path][line.Class].Add(line.Method, new Method()); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); } } else { documents.Add(doc.Path, new Classes()); documents[doc.Path].Add(line.Class, new Methods()); - documents[doc.Path][line.Class].Add(line.Method, new Lines()); - documents[doc.Path][line.Class][line.Method].Add(line.Number, new LineInfo { Hits = line.Hits, IsBranchPoint = line.IsBranchTarget }); + documents[doc.Path][line.Class].Add(line.Method, new Method()); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); + } + } + + // Construct Branch Results + foreach (var branch in doc.Branches) + { + if (documents.TryGetValue(doc.Path, out Classes classes)) + { + if (classes.TryGetValue(branch.Class, out Methods methods)) + { + if (methods.TryGetValue(branch.Method, out Method method)) + { + if (method.Branches.TryGetValue(branch.Number, out List branchInfo)) + { + documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo + { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); + } + else + { + documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); + documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo + { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); + } + } + else + { + documents[doc.Path][branch.Class].Add(branch.Method, new Method()); + documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); + documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo + { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); + } + } + else + { + documents[doc.Path].Add(branch.Class, new Methods()); + documents[doc.Path][branch.Class].Add(branch.Method, new Method()); + documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); + documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo + { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); + } + } + else + { + documents.Add(doc.Path, new Classes()); + documents[doc.Path].Add(branch.Class, new Methods()); + documents[doc.Path][branch.Class].Add(branch.Method, new Method()); + documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); + documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo + { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); } } } @@ -99,28 +154,37 @@ private void CalculateCoverage() { if (!File.Exists(result.HitsFilePath)) { continue; } var lines = InstrumentationHelper.ReadHitsFile(result.HitsFilePath); - foreach (var line in lines) + foreach (var row in lines) { - var info = line.Split(','); + var info = row.Split(','); // Ignore malformed lines if (info.Length != 4) continue; - var document = result.Documents.FirstOrDefault(d => d.Path == info[0]); + bool isBranch = info[0] == "B"; + + var document = result.Documents.FirstOrDefault(d => d.Path == info[1]); if (document == null) continue; - int start = int.Parse(info[1]); - int end = int.Parse(info[2]); - bool target = info[3] == "B"; + int start = int.Parse(info[2]); - for (int j = start; j <= end; j++) + if (isBranch) { - var subLine = document.Lines.First(l => l.Number == j); - subLine.Hits = subLine.Hits + 1; - - if (j == start) - subLine.IsBranchTarget = target; + uint ordinal = uint.Parse(info[3]); + var branch = document.Branches.First(b => b.Number == start && b.Ordinal == ordinal); + if (branch.Hits != int.MaxValue) + branch.Hits += branch.Hits + 1; + } + else + { + int end = int.Parse(info[3]); + for (int j = start; j <= end; j++) + { + var line = document.Lines.First(l => l.Number == j); + if (line.Hits != int.MaxValue) + line.Hits = line.Hits + 1; + } } } diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs index 302361e66..a9a574ad0 100644 --- a/src/coverlet.core/CoverageResult.cs +++ b/src/coverlet.core/CoverageResult.cs @@ -8,11 +8,29 @@ namespace Coverlet.Core public class LineInfo { public int Hits { get; set; } - public bool IsBranchPoint { get; set; } + } + + public class BranchInfo : LineInfo + { + public int Offset { get; set; } + public int EndOffset { get; set; } + public int Path { get; set; } + public uint Ordinal { get; set; } } public class Lines : SortedDictionary { } - public class Methods : Dictionary { } + public class Branches : SortedDictionary> { } + public class Method + { + internal Method() + { + Lines = new Lines(); + Branches = new Branches(); + } + public Lines Lines; + public Branches Branches; + } + public class Methods : Dictionary { } public class Classes : Dictionary { } public class Documents : Dictionary { } public class Modules : Dictionary { } diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs index 670bfd51c..4bf5a1ff2 100644 --- a/src/coverlet.core/CoverageSummary.cs +++ b/src/coverlet.core/CoverageSummary.cs @@ -4,149 +4,232 @@ namespace Coverlet.Core { + public class CoverageDetails + { + public double Covered { get; set; } + public int Total { get; set; } + public double Percent { get; set; } + } public class CoverageSummary { - public double CalculateLineCoverage(Lines lines) + public CoverageDetails CalculateLineCoverage(Lines lines) { - double linesCovered = lines.Where(l => l.Value.Hits > 0).Count(); - double coverage = lines.Count == 0 ? lines.Count : linesCovered / lines.Count; - return Math.Round(coverage, 3); + var details = new CoverageDetails(); + details.Covered = lines.Where(l => l.Value.Hits > 0).Count(); + details.Total = lines.Count; + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateLineCoverage(Methods methods) + public CoverageDetails CalculateLineCoverage(Methods methods) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var method in methods) - total += CalculateLineCoverage(method.Value); - - average = total / methods.Count; - return Math.Round(average, 3); + { + var methodCoverage = CalculateLineCoverage(method.Value.Lines); + details.Covered += methodCoverage.Covered; + details.Total += methodCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateLineCoverage(Classes classes) + public CoverageDetails CalculateLineCoverage(Classes classes) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var @class in classes) - total += CalculateLineCoverage(@class.Value); - - average = total / classes.Count; - return Math.Round(average, 3); + { + var classCoverage = CalculateLineCoverage(@class.Value); + details.Covered += classCoverage.Covered; + details.Total += classCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateLineCoverage(Documents documents) + public CoverageDetails CalculateLineCoverage(Documents documents) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var document in documents) - total += CalculateLineCoverage(document.Value); - - average = total / documents.Count; - return Math.Round(average, 3); + { + var documentCoverage = CalculateLineCoverage(document.Value); + details.Covered += documentCoverage.Covered; + details.Total += documentCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateLineCoverage(Modules modules) + public CoverageDetails CalculateLineCoverage(Modules modules) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var module in modules) - total += CalculateLineCoverage(module.Value); + { + var moduleCoverage = CalculateLineCoverage(module.Value); + details.Covered += moduleCoverage.Covered; + details.Total += moduleCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; + } - average = total / modules.Count; - return Math.Round(average, 3); + public CoverageDetails CalculateBranchCoverage(List branchInfo) + { + var details = new CoverageDetails(); + details.Covered = branchInfo.Count(bi => bi.Hits > 0); + details.Total = branchInfo.Count; + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateBranchCoverage(Lines lines) + public CoverageDetails CalculateBranchCoverage(Branches branches) { - double pointsCovered = lines.Where(l => l.Value.Hits > 0 && l.Value.IsBranchPoint).Count(); - double totalPoints = lines.Where(l => l.Value.IsBranchPoint).Count(); - double coverage = totalPoints == 0 ? totalPoints : pointsCovered / totalPoints; - return Math.Round(coverage, 3); + var details = new CoverageDetails(); + details.Covered = branches.Sum(b => b.Value.Where(bi => bi.Hits > 0).Count()); + details.Total = branches.Sum(b => b.Value.Count()); + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateBranchCoverage(Methods methods) + public CoverageDetails CalculateBranchCoverage(Methods methods) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var method in methods) - total += CalculateBranchCoverage(method.Value); - - average = total / methods.Count; - return Math.Round(average, 3); + { + var methodCoverage = CalculateBranchCoverage(method.Value.Branches); + details.Covered += methodCoverage.Covered; + details.Total += methodCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateBranchCoverage(Classes classes) + public CoverageDetails CalculateBranchCoverage(Classes classes) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var @class in classes) - total += CalculateBranchCoverage(@class.Value); - - average = total / classes.Count; - return Math.Round(average, 3); + { + var classCoverage = CalculateBranchCoverage(@class.Value); + details.Covered += classCoverage.Covered; + details.Total += classCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateBranchCoverage(Documents documents) + public CoverageDetails CalculateBranchCoverage(Documents documents) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var document in documents) - total += CalculateBranchCoverage(document.Value); - - average = total / documents.Count; - return Math.Round(average, 3); + { + var documentCoverage = CalculateBranchCoverage(document.Value); + details.Covered += documentCoverage.Covered; + details.Total += documentCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateBranchCoverage(Modules modules) + public CoverageDetails CalculateBranchCoverage(Modules modules) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var module in modules) - total += CalculateBranchCoverage(module.Value); - - average = total / modules.Count; - return Math.Round(average, 3); + { + var moduleCoverage = CalculateBranchCoverage(module.Value); + details.Covered += moduleCoverage.Covered; + details.Total += moduleCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateMethodCoverage(Lines lines) + public CoverageDetails CalculateMethodCoverage(Lines lines) { - if (lines.Any(l => l.Value.Hits > 0)) - return 1; - - return 0; + var details = new CoverageDetails(); + details.Covered = lines.Any(l => l.Value.Hits > 0) ? 1 : 0; + details.Total = 1; + details.Percent = details.Covered; + return details; } - public double CalculateMethodCoverage(Methods methods) + public CoverageDetails CalculateMethodCoverage(Methods methods) { - double total = 0, average = 0; - foreach (var method in methods) - total += CalculateMethodCoverage(method.Value); - - average = total / methods.Count; - return Math.Round(average, 3); + var details = new CoverageDetails(); + var methodsWithLines = methods.Where(m => m.Value.Lines.Count > 0); + foreach (var method in methodsWithLines) + { + var methodCoverage = CalculateMethodCoverage(method.Value.Lines); + details.Covered += methodCoverage.Covered; + } + details.Total = methodsWithLines.Count(); + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateMethodCoverage(Classes classes) + public CoverageDetails CalculateMethodCoverage(Classes classes) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var @class in classes) - total += CalculateMethodCoverage(@class.Value); - - average = total / classes.Count; - return Math.Round(average, 3); + { + var classCoverage = CalculateMethodCoverage(@class.Value); + details.Covered += classCoverage.Covered; + details.Total += classCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateMethodCoverage(Documents documents) + public CoverageDetails CalculateMethodCoverage(Documents documents) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var document in documents) - total += CalculateMethodCoverage(document.Value); - - average = total / documents.Count; - return Math.Round(average, 3); + { + var documentCoverage = CalculateMethodCoverage(document.Value); + details.Covered += documentCoverage.Covered; + details.Total += documentCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } - public double CalculateMethodCoverage(Modules modules) + public CoverageDetails CalculateMethodCoverage(Modules modules) { - double total = 0, average = 0; + var details = new CoverageDetails(); foreach (var module in modules) - total += CalculateMethodCoverage(module.Value); - - average = total / modules.Count; - return Math.Round(average, 3); + { + var moduleCoverage = CalculateMethodCoverage(module.Value); + details.Covered += moduleCoverage.Covered; + details.Total += moduleCoverage.Total; + } + + double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; + details.Percent = Math.Round(coverage, 3); + return details; } } } \ No newline at end of file diff --git a/src/coverlet.core/CoverageTracker.cs b/src/coverlet.core/CoverageTracker.cs index 0ff0b4a69..9a5d64dbf 100644 --- a/src/coverlet.core/CoverageTracker.cs +++ b/src/coverlet.core/CoverageTracker.cs @@ -33,6 +33,7 @@ public static void MarkExecuted(string path, string marker) } } + [ExcludeFromCoverage] public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { foreach (var kvp in _markers) diff --git a/src/coverlet.core/Extensions/HelperExtensions.cs b/src/coverlet.core/Extensions/HelperExtensions.cs new file mode 100644 index 000000000..fe8f45cae --- /dev/null +++ b/src/coverlet.core/Extensions/HelperExtensions.cs @@ -0,0 +1,16 @@ + +using System; +using Coverlet.Core.Attributes; + +namespace Coverlet.Core.Extensions +{ + internal static class HelperExtensions + { + [ExcludeFromCoverage] + public static TRet Maybe(this T value, Func action, TRet defValue = default(TRet)) + where T : class + { + return (value != null) ? action(value) : defValue; + } + } +} \ No newline at end of file diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 1387546d0..72a4b5812 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -6,6 +6,7 @@ using Coverlet.Core.Attributes; using Coverlet.Core.Helpers; +using Coverlet.Core.Symbols; using Mono.Cecil; using Mono.Cecil.Cil; @@ -99,32 +100,59 @@ private void InstrumentMethod(MethodDefinition method) private void InstrumentIL(MethodDefinition method) { + method.Body.SimplifyMacros(); ILProcessor processor = method.Body.GetILProcessor(); var index = 0; var count = processor.Body.Instructions.Count; + var branchPoints = CecilSymbolHelper.GetBranchPoints(method); + for (int n = 0; n < count; n++) { var instruction = processor.Body.Instructions[index]; var sequencePoint = method.DebugInformation.GetSequencePoint(instruction); - if (sequencePoint == null || sequencePoint.IsHidden) + var targetedBranchPoints = branchPoints.Where(p => p.EndOffset == instruction.Offset); + + if (sequencePoint != null && !sequencePoint.IsHidden) { - index++; - continue; - } + var target = AddInstrumentationCode(method, processor, instruction, sequencePoint); + foreach (var _instruction in processor.Body.Instructions) + ReplaceInstructionTarget(_instruction, instruction, target); - var target = AddInstrumentationCode(method, processor, instruction, sequencePoint); - foreach (var _instruction in processor.Body.Instructions) - ReplaceInstructionTarget(_instruction, instruction, target); + foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers) + ReplaceExceptionHandlerBoundary(handler, instruction, target); - foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers) - ReplaceExceptionHandlerBoundary(handler, instruction, target); + index += 3; + } + + if (targetedBranchPoints.Count() > 0) + { + foreach (var _branchTarget in targetedBranchPoints) + { + /* + * Skip branches with no sequence point reference for now. + * In this case for an anonymous class the compiler will dynamically create an Equals 'utility' method. + * The CecilSymbolHelper will create branch points with a start line of -1 and no document, which + * I am currently not sure how to handle. + */ + if (_branchTarget.StartLine == -1 || _branchTarget.Document == null) + continue; + + var target = AddInstrumentationCode(method, processor, instruction, _branchTarget); + foreach (var _instruction in processor.Body.Instructions) + ReplaceInstructionTarget(_instruction, instruction, target); + + foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers) + ReplaceExceptionHandlerBoundary(handler, instruction, target); + + index += 3; + } + } - index += 4; + index++; } - method.Body.SimplifyMacros(); method.Body.OptimizeMacros(); } @@ -143,8 +171,8 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor document.Lines.Add(new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName }); } - string flag = IsBranchTarget(processor, instruction) ? "B" : "L"; - string marker = $"{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine},{flag}"; + // string flag = branchPoints.Count > 0 ? "B" : "L"; + string marker = $"L,{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine}"; var pathInstr = Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath); var markInstr = Instruction.Create(OpCodes.Ldstr, marker); @@ -157,21 +185,40 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor return pathInstr; } - private static bool IsBranchTarget(ILProcessor processor, Instruction instruction) + private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, BranchPoint branchPoint) { - foreach (var _instruction in processor.Body.Instructions) + var document = _result.Documents.FirstOrDefault(d => d.Path == branchPoint.Document); + if (document == null) { - if (_instruction.Operand is Instruction target) - { - if (target == instruction) - return true; - } - - if (_instruction.Operand is Instruction[] targets) - return targets.Any(t => t == instruction); + document = new Document { Path = branchPoint.Document }; + _result.Documents.Add(document); } - return false; + if (!document.Branches.Exists(l => l.Number == branchPoint.StartLine && l.Ordinal == branchPoint.Ordinal)) + document.Branches.Add( + new Branch + { + Number = branchPoint.StartLine, + Class = method.DeclaringType.FullName, + Method = method.FullName, + Offset = branchPoint.Offset, + EndOffset = branchPoint.EndOffset, + Path = branchPoint.Path, + Ordinal = branchPoint.Ordinal + } + ); + + string marker = $"B,{document.Path},{branchPoint.StartLine},{branchPoint.Ordinal}"; + + var pathInstr = Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath); + var markInstr = Instruction.Create(OpCodes.Ldstr, marker); + var callInstr = Instruction.Create(OpCodes.Call, processor.Body.Method.Module.ImportReference(_markExecutedMethodLoader.Value)); + + processor.InsertBefore(instruction, callInstr); + processor.InsertBefore(callInstr, markInstr); + processor.InsertBefore(markInstr, pathInstr); + + return pathInstr; } private static void ReplaceInstructionTarget(Instruction instruction, Instruction oldTarget, Instruction newTarget) diff --git a/src/coverlet.core/Instrumentation/InstrumenterResult.cs b/src/coverlet.core/Instrumentation/InstrumenterResult.cs index dd7c9aef0..a2b92cd36 100644 --- a/src/coverlet.core/Instrumentation/InstrumenterResult.cs +++ b/src/coverlet.core/Instrumentation/InstrumenterResult.cs @@ -7,16 +7,28 @@ internal class Line public int Number; public string Class; public string Method; - public bool IsBranchTarget; public int Hits; } + internal class Branch : Line + { + public int Offset; + public int EndOffset; + public int Path; + public uint Ordinal; + } + internal class Document { - public Document() => Lines = new List(); + public Document() + { + Lines = new List(); + Branches = new List(); + } public string Path; public List Lines { get; private set; } + public List Branches { get; private set; } } internal class InstrumenterResult diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 16f3c40d1..92048509a 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -18,12 +18,13 @@ public string Report(CoverageResult result) { CoverageSummary summary = new CoverageSummary(); - int totalLines = 0, coveredLines = 0, totalBranches = 0, coveredBranches = 0; + var lineCoverage = summary.CalculateLineCoverage(result.Modules); + var branchCoverage = summary.CalculateBranchCoverage(result.Modules); XDocument xml = new XDocument(); XElement coverage = new XElement("coverage"); - coverage.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(result.Modules).ToString())); - coverage.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(result.Modules).ToString())); + coverage.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(result.Modules).Percent.ToString())); + coverage.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(result.Modules).Percent.ToString())); coverage.Add(new XAttribute("version", "1.9")); coverage.Add(new XAttribute("timestamp", ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString())); @@ -36,8 +37,8 @@ public string Report(CoverageResult result) { XElement package = new XElement("package"); package.Add(new XAttribute("name", Path.GetFileNameWithoutExtension(module.Key))); - package.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(module.Value).ToString())); - package.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(module.Value).ToString())); + package.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(module.Value).Percent.ToString())); + package.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(module.Value).Percent.ToString())); package.Add(new XAttribute("complexity", "0")); XElement classes = new XElement("classes"); @@ -48,8 +49,8 @@ public string Report(CoverageResult result) XElement @class = new XElement("class"); @class.Add(new XAttribute("name", cls.Key)); @class.Add(new XAttribute("filename", GetRelativePathFromBase(basePath, document.Key))); - @class.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(cls.Value).ToString())); - @class.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(cls.Value).ToString())); + @class.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(cls.Value).Percent.ToString())); + @class.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(cls.Value).Percent.ToString())); @class.Add(new XAttribute("complexity", "0")); XElement classLines = new XElement("lines"); @@ -57,37 +58,39 @@ public string Report(CoverageResult result) foreach (var meth in cls.Value) { + // Skip all methods with no lines + if (meth.Value.Lines.Count == 0) + continue; + XElement method = new XElement("method"); method.Add(new XAttribute("name", meth.Key.Split(':')[2].Split('(')[0])); method.Add(new XAttribute("signature", "(" + meth.Key.Split(':')[2].Split('(')[1])); - method.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(meth.Value).ToString())); - method.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(meth.Value).ToString())); + method.Add(new XAttribute("line-rate", summary.CalculateLineCoverage(meth.Value.Lines).Percent.ToString())); + method.Add(new XAttribute("branch-rate", summary.CalculateBranchCoverage(meth.Value.Branches).Percent.ToString())); XElement lines = new XElement("lines"); - foreach (var ln in meth.Value) + foreach (var ln in meth.Value.Lines) { XElement line = new XElement("line"); line.Add(new XAttribute("number", ln.Key.ToString())); line.Add(new XAttribute("hits", ln.Value.Hits.ToString())); - line.Add(new XAttribute("branch", ln.Value.IsBranchPoint.ToString())); - - totalLines++; - if (ln.Value.Hits > 0) coveredLines++; + line.Add(new XAttribute("branch", meth.Value.Branches.ContainsKey(ln.Key).ToString())); - - if (ln.Value.IsBranchPoint) + if (meth.Value.Branches.TryGetValue(ln.Key, out List branches)) { - line.Add(new XAttribute("condition-coverage", "100% (1/1)")); + var branchInfoCoverage = summary.CalculateBranchCoverage(branches); + line.Add(new XAttribute("condition-coverage", $"{branchInfoCoverage.Percent*100}% ({branchInfoCoverage.Covered}/{branchInfoCoverage.Total})")); XElement conditions = new XElement("conditions"); - XElement condition = new XElement("condition"); - condition.Add(new XAttribute("number", "0")); - condition.Add(new XAttribute("type", "jump")); - condition.Add(new XAttribute("coverage", "100%")); - - totalBranches++; - if (ln.Value.Hits > 0) coveredBranches++; + var byOffset = branches.GroupBy(b => b.Offset).ToDictionary(b => b.Key, b => b.ToList()); + foreach (var entry in byOffset) + { + XElement condition = new XElement("condition"); + condition.Add(new XAttribute("number", entry.Key)); + condition.Add(new XAttribute("type", entry.Value.Count() > 2 ? "switch" : "jump")); // Just guessing here + condition.Add(new XAttribute("coverage", $"{summary.CalculateBranchCoverage(entry.Value).Percent * 100}%")); + conditions.Add(condition); + } - conditions.Add(condition); line.Add(conditions); } @@ -110,10 +113,10 @@ public string Report(CoverageResult result) packages.Add(package); } - coverage.Add(new XAttribute("lines-covered", coveredLines.ToString())); - coverage.Add(new XAttribute("lines-valid", totalLines.ToString())); - coverage.Add(new XAttribute("branches-covered", coveredBranches.ToString())); - coverage.Add(new XAttribute("branches-valid", totalBranches.ToString())); + coverage.Add(new XAttribute("lines-covered", lineCoverage.Covered.ToString())); + coverage.Add(new XAttribute("lines-valid", lineCoverage.Total.ToString())); + coverage.Add(new XAttribute("branches-covered", branchCoverage.Covered.ToString())); + coverage.Add(new XAttribute("branches-valid", branchCoverage.Total.ToString())); coverage.Add(sources); coverage.Add(packages); diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs index 327de30e5..0676d6edc 100644 --- a/src/coverlet.core/Reporters/LcovReporter.cs +++ b/src/coverlet.core/Reporters/LcovReporter.cs @@ -12,58 +12,48 @@ public class LcovReporter : IReporter public string Report(CoverageResult result) { + CoverageSummary summary = new CoverageSummary(); List lcov = new List(); - int numSequencePoints = 0, numBranchPoints = 0, numMethods = 0, numBlockBranch = 1; - int visitedSequencePoints = 0, visitedBranchPoints = 0, visitedMethods = 0; foreach (var module in result.Modules) { foreach (var doc in module.Value) { + var docLineCoverage = summary.CalculateLineCoverage(doc.Value); + var docBranchCoverage = summary.CalculateBranchCoverage(doc.Value); + var docMethodCoverage = summary.CalculateMethodCoverage(doc.Value); + lcov.Add("SF:" + doc.Key); foreach (var @class in doc.Value) { - bool methodVisited = false; foreach (var method in @class.Value) { - lcov.Add($"FN:{method.Value.First().Key - 1},{method.Key}"); - lcov.Add($"FNDA:{method.Value.First().Value.Hits},{method.Key}"); + // Skip all methods with no lines + if (method.Value.Lines.Count == 0) + continue; - foreach (var line in method.Value) - { - lcov.Add($"DA:{line.Key},{line.Value.Hits}"); - numSequencePoints++; + lcov.Add($"FN:{method.Value.Lines.First().Key - 1},{method.Key}"); + lcov.Add($"FNDA:{method.Value.Lines.First().Value.Hits},{method.Key}"); - if (line.Value.IsBranchPoint) - { - lcov.Add($"BRDA:{line.Key},{numBlockBranch},{numBlockBranch},{line.Value.Hits}"); - numBlockBranch++; - numBranchPoints++; - } + foreach (var line in method.Value.Lines) + lcov.Add($"DA:{line.Key},{line.Value.Hits}"); - if (line.Value.Hits > 0) - { - visitedSequencePoints++; - methodVisited = true; - if (line.Value.IsBranchPoint) - visitedBranchPoints++; - } + foreach (var branchs in method.Value.Branches) + { + foreach (var branch in branchs.Value) + lcov.Add($"BRDA:{branchs.Key},{branch.Offset},{branch.Path},{branch.Hits}"); } - - numMethods++; - if (methodVisited) - visitedMethods++; } } - lcov.Add($"LH:{visitedSequencePoints}"); - lcov.Add($"LF:{numSequencePoints}"); + lcov.Add($"LF:{docLineCoverage.Total}"); + lcov.Add($"LH:{docLineCoverage.Covered}"); - lcov.Add($"BRF:{numBranchPoints}"); - lcov.Add($"BRH:{visitedBranchPoints}"); + lcov.Add($"BRF:{docBranchCoverage.Total}"); + lcov.Add($"BRH:{docBranchCoverage.Covered}"); - lcov.Add($"FNF:{numMethods}"); - lcov.Add($"FNH:{visitedMethods}"); + lcov.Add($"FNF:{docMethodCoverage.Total}"); + lcov.Add($"FNH:{docMethodCoverage.Covered}"); lcov.Add("end_of_record"); } diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs index 9e4fb4694..cb2b60d74 100644 --- a/src/coverlet.core/Reporters/OpenCoverReporter.cs +++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs @@ -21,8 +21,8 @@ public string Report(CoverageResult result) XElement coverageSummary = new XElement("Summary"); XElement modules = new XElement("Modules"); - int numSequencePoints = 0, numBranchPoints = 0, numClasses = 0, numMethods = 0; - int visitedSequencePoints = 0, visitedBranchPoints = 0, visitedClasses = 0, visitedMethods = 0; + int numClasses = 0, numMethods = 0; + int visitedClasses = 0, visitedMethods = 0; int i = 1; @@ -62,12 +62,19 @@ public string Report(CoverageResult result) foreach (var meth in cls.Value) { + // Skip all methods with no lines + if (meth.Value.Lines.Count == 0) + continue; + + var methLineCoverage = summary.CalculateLineCoverage(meth.Value.Lines); + var methBranchCoverage = summary.CalculateBranchCoverage(meth.Value.Branches); + XElement method = new XElement("Method"); method.Add(new XAttribute("cyclomaticComplexity", "0")); method.Add(new XAttribute("nPathComplexity", "0")); - method.Add(new XAttribute("sequenceCoverage", summary.CalculateLineCoverage(meth.Value).ToString())); - method.Add(new XAttribute("branchCoverage", summary.CalculateBranchCoverage(meth.Value).ToString())); + method.Add(new XAttribute("sequenceCoverage", methLineCoverage.Percent.ToString())); + method.Add(new XAttribute("branchCoverage", methBranchCoverage.Percent.ToString())); method.Add(new XAttribute("isConstructor", meth.Key.Contains("ctor").ToString())); method.Add(new XAttribute("isGetter", meth.Key.Contains("get_").ToString())); method.Add(new XAttribute("isSetter", meth.Key.Contains("set_").ToString())); @@ -79,15 +86,15 @@ public string Report(CoverageResult result) fileRef.Add(new XAttribute("uid", i.ToString())); XElement methodPoint = new XElement("MethodPoint"); - methodPoint.Add(new XAttribute("vc", meth.Value.Select(l => l.Value.Hits).Sum().ToString())); + methodPoint.Add(new XAttribute("vc", methLineCoverage.Covered.ToString())); methodPoint.Add(new XAttribute("upsid", "0")); methodPoint.Add(new XAttribute(XName.Get("type", "xsi"), "SequencePoint")); methodPoint.Add(new XAttribute("ordinal", j.ToString())); methodPoint.Add(new XAttribute("offset", j.ToString())); methodPoint.Add(new XAttribute("sc", "0")); - methodPoint.Add(new XAttribute("sl", meth.Value.First().Key.ToString())); + methodPoint.Add(new XAttribute("sl", meth.Value.Lines.First().Key.ToString())); methodPoint.Add(new XAttribute("ec", "1")); - methodPoint.Add(new XAttribute("el", meth.Value.Last().Key.ToString())); + methodPoint.Add(new XAttribute("el", meth.Value.Lines.Last().Key.ToString())); methodPoint.Add(new XAttribute("bec", "0")); methodPoint.Add(new XAttribute("bev", "0")); methodPoint.Add(new XAttribute("fileid", i.ToString())); @@ -100,7 +107,7 @@ public string Report(CoverageResult result) int kBr = 0; var methodVisited = false; - foreach (var lines in meth.Value) + foreach (var lines in meth.Value.Lines) { XElement sequencePoint = new XElement("SequencePoint"); sequencePoint.Add(new XAttribute("vc", lines.Value.Hits.ToString())); @@ -115,45 +122,43 @@ public string Report(CoverageResult result) sequencePoint.Add(new XAttribute("fileid", i.ToString())); sequencePoints.Add(sequencePoint); - if (lines.Value.IsBranchPoint) - { - XElement branchPoint = new XElement("BranchPoint"); - branchPoint.Add(new XAttribute("vc", lines.Value.Hits.ToString())); - branchPoint.Add(new XAttribute("upsid", lines.Key.ToString())); - branchPoint.Add(new XAttribute("ordinal", kBr.ToString())); - branchPoint.Add(new XAttribute("path", "")); - branchPoint.Add(new XAttribute("offset", kBr.ToString())); - branchPoint.Add(new XAttribute("offsetend", kBr.ToString())); - branchPoint.Add(new XAttribute("sl", lines.Key.ToString())); - branchPoint.Add(new XAttribute("fileid", i.ToString())); - branchPoints.Add(branchPoint); - kBr++; - numBranchPoints++; - } - - numSequencePoints++; if (lines.Value.Hits > 0) { - visitedSequencePoints++; classVisited = true; methodVisited = true; - if (lines.Value.IsBranchPoint) - visitedBranchPoints++; } k++; } + foreach (var branches in meth.Value.Branches) + { + foreach (var branch in branches.Value) + { + XElement branchPoint = new XElement("BranchPoint"); + branchPoint.Add(new XAttribute("vc", branch.Hits.ToString())); + branchPoint.Add(new XAttribute("upsid", branches.Key.ToString())); + branchPoint.Add(new XAttribute("ordinal", branch.Ordinal.ToString())); + branchPoint.Add(new XAttribute("path", branch.Path.ToString())); + branchPoint.Add(new XAttribute("offset", branch.Offset.ToString())); + branchPoint.Add(new XAttribute("offsetend", branch.EndOffset.ToString())); + branchPoint.Add(new XAttribute("sl", branches.Key.ToString())); + branchPoint.Add(new XAttribute("fileid", i.ToString())); + branchPoints.Add(branchPoint); + kBr++; + } + } + numMethods++; if (methodVisited) visitedMethods++; - methodSummary.Add(new XAttribute("numSequencePoints", meth.Value.Count().ToString())); - methodSummary.Add(new XAttribute("visitedSequencePoints", meth.Value.Where(l => l.Value.Hits > 0).Count().ToString())); - methodSummary.Add(new XAttribute("numBranchPoints", meth.Value.Where(l => l.Value.IsBranchPoint).Count().ToString())); - methodSummary.Add(new XAttribute("visitedBranchPoints", meth.Value.Where(l => l.Value.IsBranchPoint && l.Value.Hits > 0).Count().ToString())); - methodSummary.Add(new XAttribute("sequenceCoverage", summary.CalculateLineCoverage(meth.Value).ToString())); - methodSummary.Add(new XAttribute("branchCoverage", summary.CalculateBranchCoverage(meth.Value).ToString())); + methodSummary.Add(new XAttribute("numSequencePoints", methLineCoverage.Total.ToString())); + methodSummary.Add(new XAttribute("visitedSequencePoints", methLineCoverage.Covered.ToString())); + methodSummary.Add(new XAttribute("numBranchPoints", methBranchCoverage.Total.ToString())); + methodSummary.Add(new XAttribute("visitedBranchPoints", methBranchCoverage.Covered.ToString())); + methodSummary.Add(new XAttribute("sequenceCoverage", methLineCoverage.Percent.ToString())); + methodSummary.Add(new XAttribute("branchCoverage", methBranchCoverage.Percent.ToString())); methodSummary.Add(new XAttribute("maxCyclomaticComplexity", "0")); methodSummary.Add(new XAttribute("minCyclomaticComplexity", "0")); methodSummary.Add(new XAttribute("visitedClasses", "0")); @@ -176,18 +181,22 @@ public string Report(CoverageResult result) if (classVisited) visitedClasses++; - classSummary.Add(new XAttribute("numSequencePoints", cls.Value.Select(c => c.Value.Count).Sum().ToString())); - classSummary.Add(new XAttribute("visitedSequencePoints", cls.Value.Select(c => c.Value.Where(l => l.Value.Hits > 0).Count()).Sum().ToString())); - classSummary.Add(new XAttribute("numBranchPoints", cls.Value.Select(c => c.Value.Count(l => l.Value.IsBranchPoint)).Sum().ToString())); - classSummary.Add(new XAttribute("visitedBranchPoints", cls.Value.Select(c => c.Value.Where(l => l.Value.Hits > 0 && l.Value.IsBranchPoint).Count()).Sum().ToString())); - classSummary.Add(new XAttribute("sequenceCoverage", summary.CalculateLineCoverage(cls.Value).ToString())); - classSummary.Add(new XAttribute("branchCoverage", summary.CalculateBranchCoverage(cls.Value).ToString())); + var classLineCoverage = summary.CalculateLineCoverage(cls.Value); + var classBranchCoverage = summary.CalculateBranchCoverage(cls.Value); + var classMethodCoverage = summary.CalculateMethodCoverage(cls.Value); + + classSummary.Add(new XAttribute("numSequencePoints", classLineCoverage.Total.ToString())); + classSummary.Add(new XAttribute("visitedSequencePoints", classLineCoverage.Covered.ToString())); + classSummary.Add(new XAttribute("numBranchPoints", classBranchCoverage.Total.ToString())); + classSummary.Add(new XAttribute("visitedBranchPoints", classBranchCoverage.Covered.ToString())); + classSummary.Add(new XAttribute("sequenceCoverage", classLineCoverage.Percent.ToString())); + classSummary.Add(new XAttribute("branchCoverage", classBranchCoverage.Percent.ToString())); classSummary.Add(new XAttribute("maxCyclomaticComplexity", "0")); classSummary.Add(new XAttribute("minCyclomaticComplexity", "0")); classSummary.Add(new XAttribute("visitedClasses", classVisited ? "1" : "0")); classSummary.Add(new XAttribute("numClasses", "1")); - classSummary.Add(new XAttribute("visitedMethods", "0")); - classSummary.Add(new XAttribute("numMethods", cls.Value.Count.ToString())); + classSummary.Add(new XAttribute("visitedMethods", classMethodCoverage.Covered.ToString())); + classSummary.Add(new XAttribute("numMethods", classMethodCoverage.Total.ToString())); @class.Add(classSummary); @class.Add(className); @@ -202,12 +211,15 @@ public string Report(CoverageResult result) modules.Add(module); } - coverageSummary.Add(new XAttribute("numSequencePoints", numSequencePoints.ToString())); - coverageSummary.Add(new XAttribute("visitedSequencePoints", visitedSequencePoints.ToString())); - coverageSummary.Add(new XAttribute("numBranchPoints", numBranchPoints.ToString())); - coverageSummary.Add(new XAttribute("visitedBranchPoints", visitedBranchPoints.ToString())); - coverageSummary.Add(new XAttribute("sequenceCoverage", summary.CalculateLineCoverage(result.Modules).ToString())); - coverageSummary.Add(new XAttribute("branchCoverage", summary.CalculateLineCoverage(result.Modules).ToString())); + var moduleLineCoverage = summary.CalculateLineCoverage(result.Modules); + var moduleBranchCoverage = summary.CalculateLineCoverage(result.Modules); + + coverageSummary.Add(new XAttribute("numSequencePoints", moduleLineCoverage.Total.ToString())); + coverageSummary.Add(new XAttribute("visitedSequencePoints", moduleLineCoverage.Covered.ToString())); + coverageSummary.Add(new XAttribute("numBranchPoints", moduleBranchCoverage.Total.ToString())); + coverageSummary.Add(new XAttribute("visitedBranchPoints", moduleBranchCoverage.Covered.ToString())); + coverageSummary.Add(new XAttribute("sequenceCoverage", moduleLineCoverage.Percent.ToString())); + coverageSummary.Add(new XAttribute("branchCoverage", moduleBranchCoverage.Percent.ToString())); coverageSummary.Add(new XAttribute("maxCyclomaticComplexity", "0")); coverageSummary.Add(new XAttribute("minCyclomaticComplexity", "0")); coverageSummary.Add(new XAttribute("visitedClasses", visitedClasses.ToString())); diff --git a/src/coverlet.core/Symbols/BranchPoint.cs b/src/coverlet.core/Symbols/BranchPoint.cs new file mode 100644 index 000000000..427aad61b --- /dev/null +++ b/src/coverlet.core/Symbols/BranchPoint.cs @@ -0,0 +1,47 @@ +using System; +using System.Text.RegularExpressions; + +namespace Coverlet.Core.Symbols +{ + /// + /// a branch point + /// + public class BranchPoint + { + /// + /// Line of the branching instruction + /// + public int StartLine { get; set; } + + /// + /// A path that can be taken + /// + public int Path { get; set; } + + /// + /// An order of the point within the method + /// + public UInt32 Ordinal { get; set; } + + /// + /// List of OffsetPoints between Offset and EndOffset (exclusive) + /// + public System.Collections.Generic.List OffsetPoints { get; set; } + + /// + /// The IL offset of the point + /// + public int Offset { get; set; } + + /// + /// Last Offset == EndOffset. + /// Can be same as Offset + /// + public int EndOffset { get; set; } + + /// + /// The url to the document if an entry was not mapped to an id + /// + public string Document { get; set; } + } +} \ No newline at end of file diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs new file mode 100644 index 000000000..592567331 --- /dev/null +++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs @@ -0,0 +1,290 @@ +// +// This class is based heavily on the work of the OpenCover +// team in OpenCover.Framework.Symbols.CecilSymbolManager +// +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +using Coverlet.Core.Extensions; + +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Collections.Generic; + +namespace Coverlet.Core.Symbols +{ + public static class CecilSymbolHelper + { + private const int StepOverLineCode = 0xFEEFEE; + private static readonly Regex IsMovenext = new Regex(@"\<[^\s>]+\>\w__\w(\w)?::MoveNext\(\)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + public static List GetBranchPoints(MethodDefinition methodDefinition) + { + var list = new List(); + GetBranchPoints(methodDefinition, list); + return list; + } + private static void GetBranchPoints(MethodDefinition methodDefinition, List list) + { + if (methodDefinition == null) + return; + try + { + UInt32 ordinal = 0; + var instructions = methodDefinition.Body.Instructions; + + // if method is a generated MoveNext skip first branch (could be a switch or a branch) + var skipFirstBranch = IsMovenext.IsMatch(methodDefinition.FullName); + + foreach (var instruction in instructions.Where(instruction => instruction.OpCode.FlowControl == FlowControl.Cond_Branch)) + { + if (skipFirstBranch) + { + skipFirstBranch = false; + continue; + } + + if (BranchIsInGeneratedFinallyBlock(instruction, methodDefinition)) + continue; + + var pathCounter = 0; + + // store branch origin offset + var branchOffset = instruction.Offset; + var closestSeqPt = FindClosestInstructionWithSequencePoint(methodDefinition.Body, instruction).Maybe(i => methodDefinition.DebugInformation.GetSequencePoint(i)); + var branchingInstructionLine = closestSeqPt.Maybe(x => x.StartLine, -1); + var document = closestSeqPt.Maybe(x => x.Document.Url); + + if (null == instruction.Next) + return; + + if (!BuildPointsForConditionalBranch(list, instruction, branchingInstructionLine, document, branchOffset, pathCounter, instructions, ref ordinal, methodDefinition)) + return; + } + } + catch (Exception ex) + { + throw new InvalidOperationException( + $"An error occurred with 'GetBranchPointsForToken' for method '{methodDefinition.FullName}'", ex); + } + } + + private static bool BuildPointsForConditionalBranch(List list, Instruction instruction, + int branchingInstructionLine, string document, int branchOffset, int pathCounter, + Collection instructions, ref uint ordinal, MethodDefinition methodDefinition) + { + // Add Default branch (Path=0) + + // Follow else/default instruction + var @else = instruction.Next; + + var pathOffsetList = GetBranchPath(@else); + + // add Path 0 + var path0 = new BranchPoint + { + StartLine = branchingInstructionLine, + Document = document, + Offset = branchOffset, + Ordinal = ordinal++, + Path = pathCounter++, + OffsetPoints = + pathOffsetList.Count > 1 + ? pathOffsetList.GetRange(0, pathOffsetList.Count - 1) + : new List(), + EndOffset = pathOffsetList.Last() + }; + + // Add Conditional Branch (Path=1) + if (instruction.OpCode.Code != Code.Switch) + { + // Follow instruction at operand + var @then = instruction.Operand as Instruction; + if (@then == null) + return false; + + ordinal = BuildPointsForBranch(list, then, branchingInstructionLine, document, branchOffset, + ordinal, pathCounter, path0, instructions, methodDefinition); + } + else // instruction.OpCode.Code == Code.Switch + { + var branchInstructions = instruction.Operand as Instruction[]; + if (branchInstructions == null || branchInstructions.Length == 0) + return false; + + ordinal = BuildPointsForSwitchCases(list, path0, branchInstructions, branchingInstructionLine, + document, branchOffset, ordinal, ref pathCounter); + } + return true; + } + + private static uint BuildPointsForBranch(List list, Instruction then, int branchingInstructionLine, string document, + int branchOffset, uint ordinal, int pathCounter, BranchPoint path0, Collection instructions, MethodDefinition methodDefinition) + { + var pathOffsetList1 = GetBranchPath(@then); + + // Add path 1 + var path1 = new BranchPoint + { + StartLine = branchingInstructionLine, + Document = document, + Offset = branchOffset, + Ordinal = ordinal++, + Path = pathCounter, + OffsetPoints = + pathOffsetList1.Count > 1 + ? pathOffsetList1.GetRange(0, pathOffsetList1.Count - 1) + : new List(), + EndOffset = pathOffsetList1.Last() + }; + + // only add branch if branch does not match a known sequence + // e.g. auto generated field assignment + // or encapsulates at least one sequence point + var offsets = new[] + { + path0.Offset, + path0.EndOffset, + path1.Offset, + path1.EndOffset + }; + + var ignoreSequences = new[] + { + // we may need other samples + new[] {Code.Brtrue_S, Code.Pop, Code.Ldsfld, Code.Ldftn, Code.Newobj, Code.Dup, Code.Stsfld, Code.Newobj}, // CachedAnonymousMethodDelegate field allocation + }; + + var bs = offsets.Min(); + var be = offsets.Max(); + + var range = instructions.Where(i => (i.Offset >= bs) && (i.Offset <= be)).ToList(); + + var match = ignoreSequences + .Where(ignoreSequence => range.Count >= ignoreSequence.Length) + .Any(ignoreSequence => range.Zip(ignoreSequence, (instruction, code) => instruction.OpCode.Code == code).All(x => x)); + + var count = range + .Count(i => methodDefinition.DebugInformation.GetSequencePoint(i) != null); + + if (!match || count > 0) + { + list.Add(path0); + list.Add(path1); + } + return ordinal; + } + + private static uint BuildPointsForSwitchCases(List list, BranchPoint path0, Instruction[] branchInstructions, + int branchingInstructionLine, string document, int branchOffset, uint ordinal, ref int pathCounter) + { + var counter = pathCounter; + list.Add(path0); + // Add Conditional Branches (Path>0) + list.AddRange(branchInstructions.Select(GetBranchPath) + .Select(pathOffsetList1 => new BranchPoint + { + StartLine = branchingInstructionLine, + Document = document, + Offset = branchOffset, + Ordinal = ordinal++, + Path = counter++, + OffsetPoints = + pathOffsetList1.Count > 1 + ? pathOffsetList1.GetRange(0, pathOffsetList1.Count - 1) + : new List(), + EndOffset = pathOffsetList1.Last() + })); + pathCounter = counter; + return ordinal; + } + + private static bool BranchIsInGeneratedFinallyBlock(Instruction branchInstruction, MethodDefinition methodDefinition) + { + if (!methodDefinition.Body.HasExceptionHandlers) + return false; + + // a generated finally block will have no sequence points in its range + var handlers = methodDefinition.Body.ExceptionHandlers + .Where(e => e.HandlerType == ExceptionHandlerType.Finally) + .ToList(); + + return handlers + .Where(e => branchInstruction.Offset >= e.HandlerStart.Offset) + .Where( e =>branchInstruction.Offset < e.HandlerEnd.Maybe(h => h.Offset, GetOffsetOfNextEndfinally(methodDefinition.Body, e.HandlerStart.Offset))) + .OrderByDescending(h => h.HandlerStart.Offset) // we need to work inside out + .Any(eh => !(methodDefinition.DebugInformation.GetSequencePointMapping() + .Where(i => i.Value.StartLine != StepOverLineCode) + .Any(i => i.Value.Offset >= eh.HandlerStart.Offset && i.Value.Offset < eh.HandlerEnd.Maybe(h => h.Offset, GetOffsetOfNextEndfinally(methodDefinition.Body, eh.HandlerStart.Offset))))); + } + + private static int GetOffsetOfNextEndfinally(MethodBody body, int startOffset) + { + var lastOffset = body.Instructions.LastOrDefault().Maybe(i => i.Offset, int.MaxValue); + return body.Instructions.FirstOrDefault(i => i.Offset >= startOffset && i.OpCode.Code == Code.Endfinally).Maybe(i => i.Offset, lastOffset); + } + + private static List GetBranchPath(Instruction instruction) + { + var offsetList = new List(); + + if (instruction != null) + { + var point = instruction; + offsetList.Add(point.Offset); + while ( point.OpCode == OpCodes.Br || point.OpCode == OpCodes.Br_S ) + { + var nextPoint = point.Operand as Instruction; + if (nextPoint != null) + { + point = nextPoint; + offsetList.Add(point.Offset); + } + else + { + break; + } + } + } + + return offsetList; + } + + private static Instruction FindClosestInstructionWithSequencePoint(MethodBody methodBody, Instruction instruction) + { + var sequencePointsInMethod = methodBody.Instructions.Where(i => HasValidSequencePoint(i, methodBody.Method)).ToList(); + if (!sequencePointsInMethod.Any()) + return null; + var idx = sequencePointsInMethod.BinarySearch(instruction, new InstructionByOffsetComparer()); + Instruction prev; + if (idx < 0) + { + // no exact match, idx corresponds to the next, larger element + var lower = Math.Max(~idx - 1, 0); + prev = sequencePointsInMethod[lower]; + } + else + { + // exact match, idx corresponds to the match + prev = sequencePointsInMethod[idx]; + } + + return prev; + } + + private static bool HasValidSequencePoint(Instruction instruction, MethodDefinition methodDefinition) + { + var sp = methodDefinition.DebugInformation.GetSequencePoint(instruction); + return sp != null && sp.StartLine != StepOverLineCode; + } + + private class InstructionByOffsetComparer : IComparer + { + public int Compare(Instruction x, Instruction y) + { + return x.Offset.CompareTo(y.Offset); + } + } + } +} \ No newline at end of file diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index e201d7433..dd0621684 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -59,12 +59,13 @@ public override bool Execute() double total = 0; CoverageSummary summary = new CoverageSummary(); - ConsoleTable table = new ConsoleTable("Module", "Coverage"); + ConsoleTable table = new ConsoleTable("Module", "Line Coverage", "Branch Coverage"); foreach (var module in result.Modules) { - double percent = summary.CalculateLineCoverage(module.Value) * 100; - table.AddRow(System.IO.Path.GetFileNameWithoutExtension(module.Key), $"{percent}%"); + double percent = summary.CalculateLineCoverage(module.Value).Percent * 100; + double branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; + table.AddRow(System.IO.Path.GetFileNameWithoutExtension(module.Key), $"{percent}%", $"{branchPercent}%"); total += percent; } diff --git a/test/coverlet.core.tests/CoverageSummaryTests.cs b/test/coverlet.core.tests/CoverageSummaryTests.cs index 7e1f29369..fab5f2763 100644 --- a/test/coverlet.core.tests/CoverageSummaryTests.cs +++ b/test/coverlet.core.tests/CoverageSummaryTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Coverlet.Core; @@ -14,11 +15,18 @@ public class CoverageSummaryTests public CoverageSummaryTests() { Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1, IsBranchPoint = true }); + lines.Add(1, new LineInfo { Hits = 1 }); lines.Add(2, new LineInfo { Hits = 0 }); + Branches branches = new Branches(); + branches.Add(1, new List()); + branches[1].Add(new BranchInfo { Hits = 1, Offset = 1, Path = 0, Ordinal = 1 }); + branches[1].Add(new BranchInfo { Hits = 1, Offset = 1, Path = 1, Ordinal = 2 }); Methods methods = new Methods(); - methods.Add("System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()", lines); + var methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; + methods[methodString].Branches = branches; Classes classes = new Classes(); classes.Add("Coverlet.Core.Tests.CoverageSummaryTests", methods); @@ -40,10 +48,10 @@ public void TestCalculateLineCoverage() var @class = document.Value.First(); var method = @class.Value.First(); - Assert.Equal(0.5, summary.CalculateLineCoverage(module.Value)); - Assert.Equal(0.5, summary.CalculateLineCoverage(document.Value)); - Assert.Equal(0.5, summary.CalculateLineCoverage(@class.Value)); - Assert.Equal(0.5, summary.CalculateLineCoverage(method.Value)); + Assert.Equal(0.5, summary.CalculateLineCoverage(module.Value).Percent); + Assert.Equal(0.5, summary.CalculateLineCoverage(document.Value).Percent); + Assert.Equal(0.5, summary.CalculateLineCoverage(@class.Value).Percent); + Assert.Equal(0.5, summary.CalculateLineCoverage(method.Value.Lines).Percent); } [Fact] @@ -56,10 +64,10 @@ public void TestCalculateBranchCoverage() var @class = document.Value.First(); var method = @class.Value.First(); - Assert.Equal(1, summary.CalculateBranchCoverage(module.Value)); - Assert.Equal(1, summary.CalculateBranchCoverage(document.Value)); - Assert.Equal(1, summary.CalculateBranchCoverage(@class.Value)); - Assert.Equal(1, summary.CalculateBranchCoverage(method.Value)); + Assert.Equal(1, summary.CalculateBranchCoverage(module.Value).Percent); + Assert.Equal(1, summary.CalculateBranchCoverage(document.Value).Percent); + Assert.Equal(1, summary.CalculateBranchCoverage(@class.Value).Percent); + Assert.Equal(1, summary.CalculateBranchCoverage(method.Value.Branches).Percent); } [Fact] @@ -72,10 +80,10 @@ public void TestCalculateMethodCoverage() var @class = document.Value.First(); var method = @class.Value.First(); - Assert.Equal(1, summary.CalculateMethodCoverage(module.Value)); - Assert.Equal(1, summary.CalculateMethodCoverage(document.Value)); - Assert.Equal(1, summary.CalculateMethodCoverage(@class.Value)); - Assert.Equal(1, summary.CalculateMethodCoverage(method.Value)); + Assert.Equal(1, summary.CalculateMethodCoverage(module.Value).Percent); + Assert.Equal(1, summary.CalculateMethodCoverage(document.Value).Percent); + Assert.Equal(1, summary.CalculateMethodCoverage(@class.Value).Percent); + Assert.Equal(1, summary.CalculateMethodCoverage(method.Value.Lines).Percent); } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs index e895a47ee..e153c474f 100644 --- a/test/coverlet.core.tests/CoverageTests.cs +++ b/test/coverlet.core.tests/CoverageTests.cs @@ -5,6 +5,7 @@ using Moq; using Coverlet.Core; +using System.Collections.Generic; namespace Coverlet.Core.Tests { @@ -13,19 +14,26 @@ public class CoverageTests [Fact] public void TestCoverage() { - string module = typeof(CoverageTests).Assembly.Location; + string module = GetType().Assembly.Location; + string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); string identifier = Guid.NewGuid().ToString(); var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); - var tempModule = Path.Combine(directory.FullName, Path.GetFileName(module)); - File.Copy(module, tempModule, true); + File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); + File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); - var coverage = new Coverage(tempModule, identifier); + // TODO: Find a way to mimick hits + + // Since Coverage only instruments dependancies, we need a fake module here + var testModule = Path.Combine(directory.FullName, "test.module.dll"); + + var coverage = new Coverage(testModule, identifier); coverage.PrepareModules(); var result = coverage.GetCoverageResult(); - Assert.Empty(result.Modules); + + Assert.NotEmpty(result.Modules); directory.Delete(true); } diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterResultTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterResultTests.cs index 4f4058899..4a5b85bbf 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterResultTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterResultTests.cs @@ -14,10 +14,11 @@ public void TestEnsureDocumentsPropertyNotNull() } [Fact] - public void TestEnsureLinesPropertyNotNull() + public void TestEnsureLinesAndBranchesPropertyNotNull() { Document document = new Document(); Assert.NotNull(document.Lines); + Assert.NotNull(document.Branches); } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index e206a7213..83f70a873 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -11,7 +11,7 @@ public class InstrumenterTests [Fact] public void TestInstrument() { - string module = typeof(InstrumenterTests).Assembly.Location; + string module = GetType().Assembly.Location; string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); string identifier = Guid.NewGuid().ToString(); diff --git a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs index 540894a5a..be7eba83c 100644 --- a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Xunit; namespace Coverlet.Core.Reporters.Tests @@ -13,8 +14,16 @@ public void TestReport() Lines lines = new Lines(); lines.Add(1, new LineInfo { Hits = 1 }); lines.Add(2, new LineInfo { Hits = 0 }); + Branches branches = new Branches(); + branches.Add(1, new List { + new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, + new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } + }); Methods methods = new Methods(); - methods.Add("System.Void Coverlet.Core.Reporters.Tests.CoberturaReporterTests::TestReport()", lines); + var methodString = "System.Void Coverlet.Core.Reporters.Tests.CoberturaReporterTests::TestReport()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; + methods[methodString].Branches = branches; Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.CoberturaReporterTests", methods); Documents documents = new Documents(); diff --git a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs index bc9511d1e..979bc1108 100644 --- a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs @@ -14,7 +14,9 @@ public void TestReport() lines.Add(1, new LineInfo { Hits = 1 }); lines.Add(2, new LineInfo { Hits = 0 }); Methods methods = new Methods(); - methods.Add("System.Void Coverlet.Core.Reporters.Tests.JsonReporterTests.TestReport()", lines); + var methodString = "System.Void Coverlet.Core.Reporters.Tests.JsonReporterTests.TestReport()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.JsonReporterTests", methods); Documents documents = new Documents(); diff --git a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs index c7e972ad4..c30cea209 100644 --- a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Xunit; namespace Coverlet.Core.Reporters.Tests @@ -13,8 +14,16 @@ public void TestReport() Lines lines = new Lines(); lines.Add(1, new LineInfo { Hits = 1 }); lines.Add(2, new LineInfo { Hits = 0 }); + Branches branches = new Branches(); + branches.Add(1, new List { + new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, + new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } + }); Methods methods = new Methods(); - methods.Add("System.Void Coverlet.Core.Reporters.Tests.LcovReporterTests.TestReport()", lines); + var methodString = "System.Void Coverlet.Core.Reporters.Tests.LcovReporterTests.TestReport()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; + methods[methodString].Branches = branches; Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.LcovReporterTests", methods); Documents documents = new Documents(); diff --git a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs index 4545cec1f..3643480f3 100644 --- a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Xunit; namespace Coverlet.Core.Reporters.Tests @@ -41,8 +42,16 @@ private static Documents CreateFirstDocuments() Lines lines = new Lines(); lines.Add(1, new LineInfo { Hits = 1 }); lines.Add(2, new LineInfo { Hits = 0 }); + Branches branches = new Branches(); + branches.Add(1, new List { + new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, + new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } + }); Methods methods = new Methods(); - methods.Add("System.Void Coverlet.Core.Reporters.Tests.OpenCoverReporterTests.TestReport()", lines); + var methodString = "System.Void Coverlet.Core.Reporters.Tests.OpenCoverReporterTests.TestReport()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; + methods[methodString].Branches = branches; Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.OpenCoverReporterTests", methods); Documents documents = new Documents(); @@ -57,7 +66,9 @@ private static Documents CreateSecondDocuments() lines.Add(2, new LineInfo { Hits = 0 }); Methods methods = new Methods(); - methods.Add("System.Void Some.Other.Module.TestClass.TestMethod()", lines); + var methodString = "System.Void Some.Other.Module.TestClass.TestMethod()"; + methods.Add(methodString, new Method()); + methods[methodString].Lines = lines; Classes classes2 = new Classes(); classes2.Add("Some.Other.Module.TestClass", methods); diff --git a/test/coverlet.core.tests/Samples/Samples.cs b/test/coverlet.core.tests/Samples/Samples.cs new file mode 100644 index 000000000..518a848fb --- /dev/null +++ b/test/coverlet.core.tests/Samples/Samples.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Coverlet.Core.Samples.Tests +{ + class ConstructorNotDeclaredClass + { + } + class DeclaredConstructorClass + { + DeclaredConstructorClass() { } + + public bool HasSingleDecision(string input) + { + if (input.Contains("test")) return true; + return false; + } + + public bool HasTwoDecisions(string input) + { + if (input.Contains("test")) return true; + if (input.Contains("xxx")) return true; + return false; + } + + public bool HasCompleteIf(string input) + { + if (input.Contains("test")) + { + return true; + } + else + { + return false; + } + } + + public bool HasSwitch(int input) + { + switch (input) + { + case 0: + return true; + case 1: + return false; + case 2: + return true; + } + return false; + } + + public bool HasSwitchWithDefault(int input) + { + switch (input) + { + case 1: + return true; + case 2: + return false; + case 3: + return true; + default: + return false; + } + } + + public bool HasSwitchWithBreaks(int input) + { + bool ret = false; + switch (input) + { + case 1: + ret = true; + break; + case 2: + ret = false; + break; + case 3: + ret = true; + break; + } + + return ret; + } + + public int HasSwitchWithMultipleCases(int input) + { + switch (input) + { + case 1: + return -1; + case 2: + return 2001; + case 3: + return -5001; + default: + return 7; + } + } + + public string HasSimpleUsingStatement() + { + string value; + try + { + + } + finally + { + using (var stream = new MemoryStream()) + { + var x = stream.Length; + value = x > 1000 ? "yes" : "no"; + } + } + return value; + } + + public void HasSimpleTaskWithLambda() + { + var t = new Task(() => { }); + } + + public string UsingWithException_Issue243() + { + using (var ms = new MemoryStream()) // IL generates a finally block for using to dispose the stream + { + throw new Exception(); + } + } + } + + public class LinqIssue + { + public void Method() + { + var s = new ObservableCollection(); + var x = (from a in s select new {a}); + } + + public object Property + { + get + { + var s = new ObservableCollection(); + var x = (from a in s select new { a }); + return x; + } + } + } + + public class Iterator + { + public IEnumerable Fetch() + { + yield return "one"; + yield return "two"; + } + } +} \ No newline at end of file diff --git a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs new file mode 100644 index 000000000..0cd079683 --- /dev/null +++ b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs @@ -0,0 +1,263 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +using Xunit; +using Coverlet.Core.Samples.Tests; + +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Coverlet.Core.Symbols.Tests +{ + public class CecilSymbolHelperTests + { + private ModuleDefinition _module; + public CecilSymbolHelperTests() + { + var location = GetType().Assembly.Location; + var resolver = new DefaultAssemblyResolver(); + resolver.AddSearchDirectory(Path.GetDirectoryName(location)); + var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; + _module = ModuleDefinition.ReadModule(location, parameters); + } + + [Fact] + public void GetBranchPoints_OneBranch() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSingleDecision")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(2, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(0, points[0].Path); + Assert.Equal(1, points[1].Path); + Assert.Equal(19, points[0].StartLine); + Assert.Equal(19, points[1].StartLine); + Assert.NotNull(points[1].Document); + Assert.Equal(points[0].Document, points[1].Document); + } + + [Fact] + public void GetBranchPoints_Using_Where_GeneratedBranchesIgnored() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSimpleUsingStatement")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + Assert.Equal(2, points.Count()); + } + + [Fact] + public void GetBranchPoints_GeneratedBranches_DueToCachedAnonymousMethodDelegate_Ignored() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSimpleTaskWithLambda")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + Assert.Empty(points); + } + + [Fact] + public void GetBranchPoints_TwoBranch() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasTwoDecisions")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(4, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(points[2].Offset, points[3].Offset); + Assert.Equal(25, points[0].StartLine); + Assert.Equal(26, points[2].StartLine); + } + + [Fact] + public void GetBranchPoints_CompleteIf() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasCompleteIf")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(2, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(32, points[0].StartLine); + Assert.Equal(32, points[1].StartLine); + } + + [Fact] + public void GetBranchPoints_Switch() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSwitch")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(4, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(points[0].Offset, points[2].Offset); + Assert.Equal(3, points[3].Path); + + Assert.Equal(44, points[0].StartLine); + Assert.Equal(44, points[1].StartLine); + Assert.Equal(44, points[2].StartLine); + Assert.Equal(44, points[3].StartLine); + } + + [Fact] + public void GetBranchPoints_SwitchWithDefault() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithDefault")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(4, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(points[0].Offset, points[2].Offset); + Assert.Equal(3, points[3].Path); + + Assert.Equal(58, points[0].StartLine); + Assert.Equal(58, points[1].StartLine); + Assert.Equal(58, points[2].StartLine); + Assert.Equal(58, points[3].StartLine); + } + + [Fact] + public void GetBranchPoints_SwitchWithBreaks() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithBreaks")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(4, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(points[0].Offset, points[2].Offset); + Assert.Equal(3, points[3].Path); + + Assert.Equal(74, points[0].StartLine); + Assert.Equal(74, points[1].StartLine); + Assert.Equal(74, points[2].StartLine); + Assert.Equal(74, points[3].StartLine); + } + + [Fact] + public void GetBranchPoints_SwitchWithMultipleCases() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::HasSwitchWithMultipleCases")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + Assert.Equal(4, points.Count()); + Assert.Equal(points[0].Offset, points[1].Offset); + Assert.Equal(points[0].Offset, points[2].Offset); + Assert.Equal(points[0].Offset, points[3].Offset); + Assert.Equal(3, points[3].Path); + + Assert.Equal(92, points[0].StartLine); + Assert.Equal(92, points[1].StartLine); + Assert.Equal(92, points[2].StartLine); + Assert.Equal(92, points[3].StartLine); + } + + [Fact] + public void GetBranchPoints_AssignsNegativeLineNumberToBranchesInMethodsThatHaveNoInstrumentablePoints() + { + /* + * Yes these actually exist - the compiler is very inventive + * in this case for an anonymous class the compiler will dynamically create an Equals 'utility' method. + */ + // arrange + var type = _module.Types.First(x => x.FullName.Contains("f__AnonymousType")); + var method = type.Methods.First(x => x.FullName.Contains("::Equals")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.NotNull(points); + foreach (var branchPoint in points) + Assert.Equal(-1, branchPoint.StartLine); + } + + [Fact] + public void GetBranchPoints_UsingWithException_Issue243_IgnoresBranchInFinallyBlock() + { + // arrange + var type = _module.Types.First(x => x.FullName == typeof(DeclaredConstructorClass).FullName); + var method = type.Methods.First(x => x.FullName.Contains("::UsingWithException_Issue243")); + + // check that the method is laid out the way we discovered it to be during the defect + // @see https://github.com/OpenCover/opencover/issues/243 + Assert.Single(method.Body.ExceptionHandlers); + Assert.NotNull(method.Body.ExceptionHandlers[0].HandlerStart); + Assert.Null(method.Body.ExceptionHandlers[0].HandlerEnd); + Assert.Equal(1, method.Body.Instructions.Count(i => i.OpCode.FlowControl == FlowControl.Cond_Branch)); + Assert.True(method.Body.Instructions.First(i => i.OpCode.FlowControl == FlowControl.Cond_Branch).Offset > method.Body.ExceptionHandlers[0].HandlerStart.Offset); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.Empty(points); + } + + [Fact] + public void GetBranchPoints_IgnoresSwitchIn_GeneratedMoveNext() + { + // arrange + var nestedName = typeof (Iterator).GetNestedTypes(BindingFlags.NonPublic).First().Name; + var type = _module.Types.FirstOrDefault(x => x.FullName == typeof(Iterator).FullName); + var nestedType = type.NestedTypes.FirstOrDefault(x => x.FullName.EndsWith(nestedName)); + var method = nestedType.Methods.First(x => x.FullName.EndsWith("::MoveNext()")); + + // act + var points = CecilSymbolHelper.GetBranchPoints(method); + + // assert + Assert.Empty(points); + + } + } +} \ No newline at end of file From 686394d7ec03f2c3bd6f234bd9ebf1f4a4a9c962 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Tue, 1 May 2018 21:33:17 -0400 Subject: [PATCH 02/62] save on disk space by compressing the hits file --- src/coverlet.core/Coverage.cs | 71 +++++++++++-------- src/coverlet.core/CoverageTracker.cs | 29 +++++++- .../Helpers/InstrumentationHelper.cs | 9 --- .../Helpers/InstrumentationHelperTests.cs | 10 --- 4 files changed, 68 insertions(+), 51 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index c68e2950d..9acb6dbbe 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; using Coverlet.Core.Helpers; @@ -152,43 +153,55 @@ private void CalculateCoverage() { foreach (var result in _results) { - if (!File.Exists(result.HitsFilePath)) { continue; } - var lines = InstrumentationHelper.ReadHitsFile(result.HitsFilePath); - foreach (var row in lines) + var i = 0; + while (true) { - var info = row.Split(','); - // Ignore malformed lines - if (info.Length != 4) - continue; + var file = $"{result.HitsFilePath}_compressed_{i}"; + if(!File.Exists(file)) break; + + using (var fs = new FileStream(file, FileMode.Open)) + using (var gz = new GZipStream(fs, CompressionMode.Decompress)) + using (var sr = new StreamReader(gz)) + { + string row; + while ((row = sr.ReadLine()) != null) + { + var info = row.Split(','); + // Ignore malformed lines + if (info.Length != 4) + continue; - bool isBranch = info[0] == "B"; + bool isBranch = info[0] == "B"; - var document = result.Documents.FirstOrDefault(d => d.Path == info[1]); - if (document == null) - continue; + var document = result.Documents.FirstOrDefault(d => d.Path == info[1]); + if (document == null) + continue; - int start = int.Parse(info[2]); + int start = int.Parse(info[2]); - if (isBranch) - { - uint ordinal = uint.Parse(info[3]); - var branch = document.Branches.First(b => b.Number == start && b.Ordinal == ordinal); - if (branch.Hits != int.MaxValue) - branch.Hits += branch.Hits + 1; - } - else - { - int end = int.Parse(info[3]); - for (int j = start; j <= end; j++) - { - var line = document.Lines.First(l => l.Number == j); - if (line.Hits != int.MaxValue) - line.Hits = line.Hits + 1; + if (isBranch) + { + uint ordinal = uint.Parse(info[3]); + var branch = document.Branches.First(b => b.Number == start && b.Ordinal == ordinal); + if (branch.Hits != int.MaxValue) + branch.Hits += branch.Hits + 1; + } + else + { + int end = int.Parse(info[3]); + for (int j = start; j <= end; j++) + { + var line = document.Lines.First(l => l.Number == j); + if (line.Hits != int.MaxValue) + line.Hits = line.Hits + 1; + } + } } } - } - InstrumentationHelper.DeleteHitsFile(result.HitsFilePath); + InstrumentationHelper.DeleteHitsFile(file); + i++; + } } } } diff --git a/src/coverlet.core/CoverageTracker.cs b/src/coverlet.core/CoverageTracker.cs index 9a5d64dbf..7d3a4b8c7 100644 --- a/src/coverlet.core/CoverageTracker.cs +++ b/src/coverlet.core/CoverageTracker.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.IO; - +using System.IO.Compression; using Coverlet.Core.Attributes; using Coverlet.Core.Extensions; @@ -10,11 +10,13 @@ namespace Coverlet.Core public static class CoverageTracker { private static Dictionary> _markers; + private static Dictionary _markerFileCount; [ExcludeFromCoverage] static CoverageTracker() { _markers = new Dictionary>(); + _markerFileCount = new Dictionary(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); } @@ -25,10 +27,20 @@ public static void MarkExecuted(string path, string marker) { _markers.TryAdd(path, new List()); _markers[path].Add(marker); + _markerFileCount.TryAdd(path, 0); if (_markers[path].Count >= 100000) { - File.AppendAllLines(path, _markers[path]); + using (var fs = new FileStream($"{path}_compressed_{_markerFileCount[path]}", FileMode.OpenOrCreate)) + using (var gz = new GZipStream(fs, CompressionMode.Compress)) + using (var sw = new StreamWriter(gz)) + { + foreach(var line in _markers[path]) + { + sw.WriteLine(line); + } + } _markers[path].Clear(); + _markerFileCount[path] = _markerFileCount[path] + 1; } } } @@ -37,7 +49,18 @@ public static void MarkExecuted(string path, string marker) public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { foreach (var kvp in _markers) - File.AppendAllLines(kvp.Key, kvp.Value); + { + _markerFileCount.TryAdd(kvp.Key, 0); + using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) + using (var gz = new GZipStream(fs, CompressionMode.Compress)) + using (var sw = new StreamWriter(gz)) + { + foreach(var line in kvp.Value) + { + sw.WriteLine(line); + } + } + } } } } \ No newline at end of file diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 96b140f03..dfd36cd68 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -71,15 +71,6 @@ public static void RestoreOriginalModule(string module, string identifier) }, retryStrategy, 10); } - public static IEnumerable ReadHitsFile(string path) - { - // Retry hitting the hits file - retry up to 10 times, since the file could be locked - // See: https://github.com/tonerdo/coverlet/issues/25 - var retryStrategy = CreateRetryStrategy(); - - return RetryHelper.Do(() => File.ReadLines(path), retryStrategy, 10); - } - public static void DeleteHitsFile(string path) { // Retry hitting the hits file - retry up to 10 times, since the file could be locked diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 9e10cb52b..b795f21b0 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -61,16 +61,6 @@ public void TestDontCopyCoverletDependency() Directory.Delete(directory.FullName, true); } - [Fact] - public void TestReadHitsFile() - { - var tempFile = Path.GetTempFileName(); - Assert.True(File.Exists(tempFile)); - - var lines = InstrumentationHelper.ReadHitsFile(tempFile); - Assert.NotNull(lines); - } - [Fact] public void TestDeleteHitsFile() { From dddcf265004dc26ce4bce0fe5e27999095457065 Mon Sep 17 00:00:00 2001 From: Patrick Dwyer Date: Thu, 3 May 2018 19:54:25 +1000 Subject: [PATCH 03/62] Add support for multiple output formats separated by a comma --- .../Reporters/ReporterFactory.cs | 13 +++--- .../CoverageResultTask.cs | 14 ++++--- .../Reporters/ReporterFactoryTests.cs | 40 ++++++++++++++++--- 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs index c61156b08..2518c0e4a 100644 --- a/src/coverlet.core/Reporters/ReporterFactory.cs +++ b/src/coverlet.core/Reporters/ReporterFactory.cs @@ -1,22 +1,25 @@ using System.Linq; +using System.Collections.Generic; namespace Coverlet.Core.Reporters { public class ReporterFactory { - private string _format; + private IEnumerable _formats; private IReporter[] _reporters; - public ReporterFactory(string format) + public ReporterFactory(string formats) { - _format = format; + _formats = formats.Split(','); _reporters = new IReporter[] { new JsonReporter(), new LcovReporter(), new OpenCoverReporter(), new CoberturaReporter() }; } - public IReporter CreateReporter() - => _reporters.FirstOrDefault(r => r.Format == _format); + public IEnumerable CreateReporters() + { + return _reporters.Where(r => _formats.Contains(r.Format)); + } } } \ No newline at end of file diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index e201d7433..46482d93f 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using ConsoleTables; @@ -49,13 +50,16 @@ public override bool Execute() if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); - IReporter reporter = new ReporterFactory(_format).CreateReporter(); - if (reporter == null) + var reporters = new List(new ReporterFactory(_format).CreateReporters()); + if (reporters.Count != _format.Split(',').Length) throw new Exception($"Specified output format '{_format}' is not supported"); - _filename = _filename + "." + reporter.Extension; - Console.WriteLine($" Generating report '{_filename}'"); - File.WriteAllText(_filename, reporter.Report(result)); + foreach(var reporter in reporters) + { + var reportFilename = _filename + "." + reporter.Extension; + Console.WriteLine($" Generating report '{reportFilename}'"); + File.WriteAllText(reportFilename, reporter.Report(result)); + } double total = 0; CoverageSummary summary = new CoverageSummary(); diff --git a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs index 4f5f463f4..14cac96ab 100644 --- a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs +++ b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs @@ -6,13 +6,41 @@ namespace Coverlet.Core.Reporters.Tests public class ReporterFactoryTests { [Fact] - public void TestCreateReporter() + public void TestCreateReportersWithSingleFormat() { - Assert.Equal(typeof(JsonReporter), new ReporterFactory("json").CreateReporter().GetType()); - Assert.Equal(typeof(LcovReporter), new ReporterFactory("lcov").CreateReporter().GetType()); - Assert.Equal(typeof(OpenCoverReporter), new ReporterFactory("opencover").CreateReporter().GetType()); - Assert.Equal(typeof(CoberturaReporter), new ReporterFactory("cobertura").CreateReporter().GetType()); - Assert.Null(new ReporterFactory("").CreateReporter()); + Assert.Collection( + new ReporterFactory("json").CreateReporters(), + reporter => Assert.IsType(reporter)); + Assert.Collection( + new ReporterFactory("lcov").CreateReporters(), + reporter => Assert.IsType(reporter)); + Assert.Collection( + new ReporterFactory("opencover").CreateReporters(), + reporter => Assert.IsType(reporter)); + Assert.Collection( + new ReporterFactory("cobertura").CreateReporters(), + reporter => Assert.IsType(reporter)); + Assert.Empty(new ReporterFactory("").CreateReporters()); + } + + [Fact] + public void TestCreateReportersWithMultipleFormats() + { + Assert.Collection( + new ReporterFactory("json,lcov").CreateReporters(), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter)); + Assert.Collection( + new ReporterFactory("json,lcov,opencover").CreateReporters(), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter)); + Assert.Collection( + new ReporterFactory("json,lcov,opencover,cobertura").CreateReporters(), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter), + reporter => Assert.IsType(reporter)); } } } \ No newline at end of file From 85fa0f35ebb9f5f80e3f952ebed7cdfff82fe574 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 5 May 2018 12:51:30 -0400 Subject: [PATCH 04/62] changes per feedback --- src/coverlet.core/CoverageDetails.cs | 14 ++++++ src/coverlet.core/CoverageSummary.cs | 49 ------------------- .../Instrumentation/Instrumenter.cs | 40 +++++++-------- .../Symbols/CecilSymbolHelper.cs | 3 +- 4 files changed, 34 insertions(+), 72 deletions(-) create mode 100644 src/coverlet.core/CoverageDetails.cs diff --git a/src/coverlet.core/CoverageDetails.cs b/src/coverlet.core/CoverageDetails.cs new file mode 100644 index 000000000..e83cfdb44 --- /dev/null +++ b/src/coverlet.core/CoverageDetails.cs @@ -0,0 +1,14 @@ +using System; + +namespace Coverlet.Core +{ + public class CoverageDetails + { + public double Covered { get; internal set; } + public int Total { get; internal set; } + public double Percent + { + get => Math.Round(Total == 0 ? Total : Covered / Total, 3); + } + } +} \ No newline at end of file diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs index 4bf5a1ff2..81155c636 100644 --- a/src/coverlet.core/CoverageSummary.cs +++ b/src/coverlet.core/CoverageSummary.cs @@ -4,12 +4,6 @@ namespace Coverlet.Core { - public class CoverageDetails - { - public double Covered { get; set; } - public int Total { get; set; } - public double Percent { get; set; } - } public class CoverageSummary { public CoverageDetails CalculateLineCoverage(Lines lines) @@ -17,8 +11,6 @@ public CoverageDetails CalculateLineCoverage(Lines lines) var details = new CoverageDetails(); details.Covered = lines.Where(l => l.Value.Hits > 0).Count(); details.Total = lines.Count; - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -31,9 +23,6 @@ public CoverageDetails CalculateLineCoverage(Methods methods) details.Covered += methodCoverage.Covered; details.Total += methodCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -46,9 +35,6 @@ public CoverageDetails CalculateLineCoverage(Classes classes) details.Covered += classCoverage.Covered; details.Total += classCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -61,9 +47,6 @@ public CoverageDetails CalculateLineCoverage(Documents documents) details.Covered += documentCoverage.Covered; details.Total += documentCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -76,9 +59,6 @@ public CoverageDetails CalculateLineCoverage(Modules modules) details.Covered += moduleCoverage.Covered; details.Total += moduleCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -87,8 +67,6 @@ public CoverageDetails CalculateBranchCoverage(List branchInfo) var details = new CoverageDetails(); details.Covered = branchInfo.Count(bi => bi.Hits > 0); details.Total = branchInfo.Count; - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -97,8 +75,6 @@ public CoverageDetails CalculateBranchCoverage(Branches branches) var details = new CoverageDetails(); details.Covered = branches.Sum(b => b.Value.Where(bi => bi.Hits > 0).Count()); details.Total = branches.Sum(b => b.Value.Count()); - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -111,9 +87,6 @@ public CoverageDetails CalculateBranchCoverage(Methods methods) details.Covered += methodCoverage.Covered; details.Total += methodCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -126,9 +99,6 @@ public CoverageDetails CalculateBranchCoverage(Classes classes) details.Covered += classCoverage.Covered; details.Total += classCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -141,9 +111,6 @@ public CoverageDetails CalculateBranchCoverage(Documents documents) details.Covered += documentCoverage.Covered; details.Total += documentCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -156,9 +123,6 @@ public CoverageDetails CalculateBranchCoverage(Modules modules) details.Covered += moduleCoverage.Covered; details.Total += moduleCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -167,7 +131,6 @@ public CoverageDetails CalculateMethodCoverage(Lines lines) var details = new CoverageDetails(); details.Covered = lines.Any(l => l.Value.Hits > 0) ? 1 : 0; details.Total = 1; - details.Percent = details.Covered; return details; } @@ -181,9 +144,6 @@ public CoverageDetails CalculateMethodCoverage(Methods methods) details.Covered += methodCoverage.Covered; } details.Total = methodsWithLines.Count(); - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -196,9 +156,6 @@ public CoverageDetails CalculateMethodCoverage(Classes classes) details.Covered += classCoverage.Covered; details.Total += classCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -211,9 +168,6 @@ public CoverageDetails CalculateMethodCoverage(Documents documents) details.Covered += documentCoverage.Covered; details.Total += documentCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } @@ -226,9 +180,6 @@ public CoverageDetails CalculateMethodCoverage(Modules modules) details.Covered += moduleCoverage.Covered; details.Total += moduleCoverage.Total; } - - double coverage = details.Total == 0 ? details.Total : details.Covered / details.Total; - details.Percent = Math.Round(coverage, 3); return details; } } diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 72a4b5812..d6dd0527f 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -126,28 +126,25 @@ private void InstrumentIL(MethodDefinition method) index += 3; } - if (targetedBranchPoints.Count() > 0) + foreach (var _branchTarget in targetedBranchPoints) { - foreach (var _branchTarget in targetedBranchPoints) - { - /* - * Skip branches with no sequence point reference for now. - * In this case for an anonymous class the compiler will dynamically create an Equals 'utility' method. - * The CecilSymbolHelper will create branch points with a start line of -1 and no document, which - * I am currently not sure how to handle. - */ - if (_branchTarget.StartLine == -1 || _branchTarget.Document == null) - continue; - - var target = AddInstrumentationCode(method, processor, instruction, _branchTarget); - foreach (var _instruction in processor.Body.Instructions) - ReplaceInstructionTarget(_instruction, instruction, target); - - foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers) - ReplaceExceptionHandlerBoundary(handler, instruction, target); - - index += 3; - } + /* + * Skip branches with no sequence point reference for now. + * In this case for an anonymous class the compiler will dynamically create an Equals 'utility' method. + * The CecilSymbolHelper will create branch points with a start line of -1 and no document, which + * I am currently not sure how to handle. + */ + if (_branchTarget.StartLine == -1 || _branchTarget.Document == null) + continue; + + var target = AddInstrumentationCode(method, processor, instruction, _branchTarget); + foreach (var _instruction in processor.Body.Instructions) + ReplaceInstructionTarget(_instruction, instruction, target); + + foreach (ExceptionHandler handler in processor.Body.ExceptionHandlers) + ReplaceExceptionHandlerBoundary(handler, instruction, target); + + index += 3; } index++; @@ -171,7 +168,6 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor document.Lines.Add(new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName }); } - // string flag = branchPoints.Count > 0 ? "B" : "L"; string marker = $"L,{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine}"; var pathInstr = Instruction.Create(OpCodes.Ldstr, _result.HitsFilePath); diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs index 592567331..7b36e57df 100644 --- a/src/coverlet.core/Symbols/CecilSymbolHelper.cs +++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs @@ -19,6 +19,7 @@ public static class CecilSymbolHelper { private const int StepOverLineCode = 0xFEEFEE; private static readonly Regex IsMovenext = new Regex(@"\<[^\s>]+\>\w__\w(\w)?::MoveNext\(\)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture); + public static List GetBranchPoints(MethodDefinition methodDefinition) { var list = new List(); @@ -56,7 +57,7 @@ private static void GetBranchPoints(MethodDefinition methodDefinition, List x.StartLine, -1); var document = closestSeqPt.Maybe(x => x.Document.Url); - if (null == instruction.Next) + if (instruction.Next == null) return; if (!BuildPointsForConditionalBranch(list, instruction, branchingInstructionLine, document, branchOffset, pathCounter, instructions, ref ordinal, methodDefinition)) From ad60a1ce91364afe829e0ca7c4e9dbb3f925e78a Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 5 May 2018 17:53:38 +0100 Subject: [PATCH 05/62] build list of reporters from factory function --- .../Reporters/ReporterFactory.cs | 12 +++--- .../CoverageResultTask.cs | 17 ++++---- src/coverlet.msbuild/coverlet.msbuild.props | 2 - .../Reporters/ReporterFactoryTests.cs | 40 +++---------------- 4 files changed, 20 insertions(+), 51 deletions(-) diff --git a/src/coverlet.core/Reporters/ReporterFactory.cs b/src/coverlet.core/Reporters/ReporterFactory.cs index 2518c0e4a..813d23970 100644 --- a/src/coverlet.core/Reporters/ReporterFactory.cs +++ b/src/coverlet.core/Reporters/ReporterFactory.cs @@ -5,21 +5,19 @@ namespace Coverlet.Core.Reporters { public class ReporterFactory { - private IEnumerable _formats; + private string _format; private IReporter[] _reporters; - public ReporterFactory(string formats) + public ReporterFactory(string format) { - _formats = formats.Split(','); + _format = format; _reporters = new IReporter[] { new JsonReporter(), new LcovReporter(), new OpenCoverReporter(), new CoberturaReporter() }; } - public IEnumerable CreateReporters() - { - return _reporters.Where(r => _formats.Contains(r.Format)); - } + public IReporter CreateReporter() + => _reporters.FirstOrDefault(r => r.Format == _format); } } \ No newline at end of file diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 46482d93f..16dc64b42 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -50,15 +50,16 @@ public override bool Execute() if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); - var reporters = new List(new ReporterFactory(_format).CreateReporters()); - if (reporters.Count != _format.Split(',').Length) - throw new Exception($"Specified output format '{_format}' is not supported"); - - foreach(var reporter in reporters) + var formats = _format.Split(','); + foreach(var format in formats) { - var reportFilename = _filename + "." + reporter.Extension; - Console.WriteLine($" Generating report '{reportFilename}'"); - File.WriteAllText(reportFilename, reporter.Report(result)); + var reporter = new ReporterFactory(format).CreateReporter(); + if (reporter == null) + throw new Exception($"Specified output format '{format}' is not supported"); + + var report = _filename + "." + reporter.Extension; + Console.WriteLine($" Generating report '{report}'"); + File.WriteAllText(report, reporter.Report(result)); } double total = 0; diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index 6f7c3a11b..f61e52104 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -1,7 +1,6 @@ - json $(MSBuildProjectDirectory) coverage @@ -9,7 +8,6 @@ 0 false - \ No newline at end of file diff --git a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs index 14cac96ab..4f5f463f4 100644 --- a/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs +++ b/test/coverlet.core.tests/Reporters/ReporterFactoryTests.cs @@ -6,41 +6,13 @@ namespace Coverlet.Core.Reporters.Tests public class ReporterFactoryTests { [Fact] - public void TestCreateReportersWithSingleFormat() + public void TestCreateReporter() { - Assert.Collection( - new ReporterFactory("json").CreateReporters(), - reporter => Assert.IsType(reporter)); - Assert.Collection( - new ReporterFactory("lcov").CreateReporters(), - reporter => Assert.IsType(reporter)); - Assert.Collection( - new ReporterFactory("opencover").CreateReporters(), - reporter => Assert.IsType(reporter)); - Assert.Collection( - new ReporterFactory("cobertura").CreateReporters(), - reporter => Assert.IsType(reporter)); - Assert.Empty(new ReporterFactory("").CreateReporters()); - } - - [Fact] - public void TestCreateReportersWithMultipleFormats() - { - Assert.Collection( - new ReporterFactory("json,lcov").CreateReporters(), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter)); - Assert.Collection( - new ReporterFactory("json,lcov,opencover").CreateReporters(), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter)); - Assert.Collection( - new ReporterFactory("json,lcov,opencover,cobertura").CreateReporters(), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter), - reporter => Assert.IsType(reporter)); + Assert.Equal(typeof(JsonReporter), new ReporterFactory("json").CreateReporter().GetType()); + Assert.Equal(typeof(LcovReporter), new ReporterFactory("lcov").CreateReporter().GetType()); + Assert.Equal(typeof(OpenCoverReporter), new ReporterFactory("opencover").CreateReporter().GetType()); + Assert.Equal(typeof(CoberturaReporter), new ReporterFactory("cobertura").CreateReporter().GetType()); + Assert.Null(new ReporterFactory("").CreateReporter()); } } } \ No newline at end of file From 7b6e9d7a7933151dcca4c403b52ee08e5dc481be Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sat, 5 May 2018 13:29:38 -0400 Subject: [PATCH 06/62] make requested changes to symbol helper --- .../Symbols/CecilSymbolHelper.cs | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/coverlet.core/Symbols/CecilSymbolHelper.cs b/src/coverlet.core/Symbols/CecilSymbolHelper.cs index 7b36e57df..e4c9ca3ae 100644 --- a/src/coverlet.core/Symbols/CecilSymbolHelper.cs +++ b/src/coverlet.core/Symbols/CecilSymbolHelper.cs @@ -23,22 +23,18 @@ public static class CecilSymbolHelper public static List GetBranchPoints(MethodDefinition methodDefinition) { var list = new List(); - GetBranchPoints(methodDefinition, list); - return list; - } - private static void GetBranchPoints(MethodDefinition methodDefinition, List list) - { if (methodDefinition == null) - return; - try - { - UInt32 ordinal = 0; - var instructions = methodDefinition.Body.Instructions; - - // if method is a generated MoveNext skip first branch (could be a switch or a branch) - var skipFirstBranch = IsMovenext.IsMatch(methodDefinition.FullName); + return list; + + UInt32 ordinal = 0; + var instructions = methodDefinition.Body.Instructions; + + // if method is a generated MoveNext skip first branch (could be a switch or a branch) + var skipFirstBranch = IsMovenext.IsMatch(methodDefinition.FullName); - foreach (var instruction in instructions.Where(instruction => instruction.OpCode.FlowControl == FlowControl.Cond_Branch)) + foreach (var instruction in instructions.Where(instruction => instruction.OpCode.FlowControl == FlowControl.Cond_Branch)) + { + try { if (skipFirstBranch) { @@ -58,17 +54,17 @@ private static void GetBranchPoints(MethodDefinition methodDefinition, List x.Document.Url); if (instruction.Next == null) - return; + return list; if (!BuildPointsForConditionalBranch(list, instruction, branchingInstructionLine, document, branchOffset, pathCounter, instructions, ref ordinal, methodDefinition)) - return; + return list; + } + catch (Exception) + { + continue; } } - catch (Exception ex) - { - throw new InvalidOperationException( - $"An error occurred with 'GetBranchPointsForToken' for method '{methodDefinition.FullName}'", ex); - } + return list; } private static bool BuildPointsForConditionalBranch(List list, Instruction instruction, From 60a484f7f1c2791855c5c115602bb4d97177b870 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 5 May 2018 18:30:00 +0100 Subject: [PATCH 07/62] add codecov integration --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9f9da7337..6c712a999 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,7 @@ env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 script: - dotnet msbuild build.proj - - dotnet msbuild build.proj /p:Configuration=Release \ No newline at end of file + - dotnet msbuild build.proj /p:Configuration=Release + - curl -s https://codecov.io/bash > codecov + - chmod +x codecov + - ./codecov -f ./test/coverlet.core.tests/coverage.xml \ No newline at end of file From 232fde695f7f225049e8d04dfe1864bd14ae9e4b Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 5 May 2018 18:36:34 +0100 Subject: [PATCH 08/62] remove coveralls integration --- appveyor.yml | 3 +-- test/coverlet.core.tests/coverlet.core.tests.csproj | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ba3e0dfda..2ce36d2df 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,5 +6,4 @@ build_script: - echo "Building for %CONFIGURATION%" - dotnet msbuild build.proj /p:Configuration=%CONFIGURATION% test_script: - - ps: if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - - cmd: IF "%CONFIGURATION%"=="Release" ( %USERPROFILE%\.nuget\packages\coveralls.net\0.7.0\tools\csmacnz.Coveralls.exe --opencover -i test\coverlet.core.tests\coverage.xml --useRelativePaths ) \ No newline at end of file + - ps: if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/test/coverlet.core.tests/coverlet.core.tests.csproj b/test/coverlet.core.tests/coverlet.core.tests.csproj index d80f0701e..a181353e8 100644 --- a/test/coverlet.core.tests/coverlet.core.tests.csproj +++ b/test/coverlet.core.tests/coverlet.core.tests.csproj @@ -7,7 +7,6 @@ - From 5aee3fd7613af58e664669d7b894804441d78698 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 5 May 2018 18:38:35 +0100 Subject: [PATCH 09/62] update README.md, add codecov badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2756802f1..03a391690 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# coverlet [![Build Status](https://www.travis-ci.org/tonerdo/coverlet.svg?branch=master)](https://www.travis-ci.org/tonerdo/coverlet) [![Build status](https://ci.appveyor.com/api/projects/status/6rdf00wufospr4r8/branch/master?svg=true)](https://ci.appveyor.com/project/tonerdo/coverlet) [![Coverage Status](https://coveralls.io/repos/github/tonerdo/coverlet/badge.svg?branch=master)](https://coveralls.io/github/tonerdo/coverlet?branch=master) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +# coverlet [![Build Status](https://www.travis-ci.org/tonerdo/coverlet.svg?branch=master)](https://www.travis-ci.org/tonerdo/coverlet) [![Build status](https://ci.appveyor.com/api/projects/status/6rdf00wufospr4r8/branch/master?svg=true)](https://ci.appveyor.com/project/tonerdo/coverlet) [![codecov](https://codecov.io/gh/tonerdo/coverlet/branch/master/graph/badge.svg)](https://codecov.io/gh/tonerdo/coverlet) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) Coverlet is a cross platform code coverage library for .NET Core, with support for line, branch and method coverage. From d2effb3a782bd324643537264396f29015577794 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 5 May 2018 21:16:52 +0100 Subject: [PATCH 10/62] display method coverage in console sumary output --- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 1d542647b..1a6b46c22 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -51,7 +51,7 @@ public override bool Execute() Directory.CreateDirectory(directory); var formats = _format.Split(','); - foreach(var format in formats) + foreach (var format in formats) { var reporter = new ReporterFactory(format).CreateReporter(); if (reporter == null) @@ -64,18 +64,19 @@ public override bool Execute() double total = 0; CoverageSummary summary = new CoverageSummary(); - ConsoleTable table = new ConsoleTable("Module", "Line Coverage", "Branch Coverage"); + ConsoleTable table = new ConsoleTable("Module", "Line", "Branch", "Method"); foreach (var module in result.Modules) { - double percent = summary.CalculateLineCoverage(module.Value).Percent * 100; + double linePercent = summary.CalculateLineCoverage(module.Value).Percent * 100; double branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; - table.AddRow(System.IO.Path.GetFileNameWithoutExtension(module.Key), $"{percent}%", $"{branchPercent}%"); - total += percent; + double methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; + table.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); + total += linePercent; } Console.WriteLine(); - Console.WriteLine(table.ToMarkDownString()); + Console.WriteLine(table.ToStringAlternative()); double average = total / result.Modules.Count; if (average < _threshold) From 3bc81adafc9c8d9944ba7dc61216b16871892c80 Mon Sep 17 00:00:00 2001 From: Jason Hunter Date: Sun, 6 May 2018 09:44:04 -0400 Subject: [PATCH 11/62] remove redundant line --- src/coverlet.core/CoverageTracker.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coverlet.core/CoverageTracker.cs b/src/coverlet.core/CoverageTracker.cs index 7d3a4b8c7..2975d0035 100644 --- a/src/coverlet.core/CoverageTracker.cs +++ b/src/coverlet.core/CoverageTracker.cs @@ -50,7 +50,6 @@ public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { foreach (var kvp in _markers) { - _markerFileCount.TryAdd(kvp.Key, 0); using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) using (var gz = new GZipStream(fs, CompressionMode.Compress)) using (var sw = new StreamWriter(gz)) From 6a6dc3a79f714ffed222e76fbd48c2b9e6d168a4 Mon Sep 17 00:00:00 2001 From: StephenMP <1.stephen.porter@gmail.com> Date: Fri, 11 May 2018 12:31:32 -0600 Subject: [PATCH 12/62] Issue #92 : Added a ThresholdType switch that accepts a comma separated string of coverage types to apply to the threshold. It defaults to all coverage types and will ignore any unsupported coverage type --- .../CoverageResultTask.cs | 78 +++++++++++++++---- src/coverlet.msbuild/coverlet.msbuild.props | 1 + src/coverlet.msbuild/coverlet.msbuild.targets | 3 +- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 1a6b46c22..4ad0dbd1b 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text; using ConsoleTables; - using Coverlet.Core; using Coverlet.Core.Reporters; - using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -16,6 +15,7 @@ public class CoverageResultTask : Task private string _filename; private string _format; private int _threshold; + private string _thresholdTypes; [Required] public string Output @@ -38,17 +38,26 @@ public int Threshold set { _threshold = value; } } + [Required] + public string ThresholdType + { + get { return _thresholdTypes; } + set { _thresholdTypes = value; } + } + public override bool Execute() { try { Console.WriteLine("\nCalculating coverage result..."); var coverage = InstrumentationTask.Coverage; - CoverageResult result = coverage.GetCoverageResult(); + var result = coverage.GetCoverageResult(); var directory = Path.GetDirectoryName(_filename); if (!Directory.Exists(directory)) + { Directory.CreateDirectory(directory); + } var formats = _format.Split(','); foreach (var format in formats) @@ -62,25 +71,66 @@ public override bool Execute() File.WriteAllText(report, reporter.Report(result)); } - double total = 0; - CoverageSummary summary = new CoverageSummary(); - ConsoleTable table = new ConsoleTable("Module", "Line", "Branch", "Method"); + var branchTotal = 0d; + var methodTotal = 0d; + var lineTotal = 0d; + var summary = new CoverageSummary(); + var table = new ConsoleTable("Module", "Line", "Branch", "Method"); foreach (var module in result.Modules) { - double linePercent = summary.CalculateLineCoverage(module.Value).Percent * 100; - double branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; - double methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; + var linePercent = summary.CalculateLineCoverage(module.Value).Percent * 100; + var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; + var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; table.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); - total += linePercent; + + lineTotal += linePercent; + branchTotal += branchPercent; + methodTotal += methodPercent; } Console.WriteLine(); Console.WriteLine(table.ToStringAlternative()); - double average = total / result.Modules.Count; - if (average < _threshold) - throw new Exception($"Overall average coverage '{average}%' is lower than specified threshold '{_threshold}%'"); + if (_threshold > 0) + { + var thresholdFailed = false; + var exceptionBuilder = new StringBuilder(); + var lineAverage = lineTotal / result.Modules.Count; + var branchAverage = branchTotal / result.Modules.Count; + var methodAverage = methodTotal / result.Modules.Count; + var thresholdTypes = _thresholdTypes.Split(',').Select(t => t.ToLower()); + foreach (var thresholdType in thresholdTypes) + { + if (thresholdType == "line" && lineAverage < _threshold) + { + thresholdFailed = true; + exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{lineAverage}%' is lower than specified threshold '{_threshold}%'"); + } + + else if (thresholdType == "branch" && branchAverage < _threshold) + { + thresholdFailed = true; + exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{branchAverage}%' is lower than specified threshold '{_threshold}%'"); + } + + else if (thresholdType == "method" && methodAverage < _threshold) + { + thresholdFailed = true; + exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{methodAverage}%' is lower than specified threshold '{_threshold}%'"); + } + + else if (thresholdType != "line" && thresholdType != "branch" && thresholdType != "method") + { + Console.WriteLine($"Threshold type of {thresholdType} is not recognized/supported and will be ignored."); + } + } + + if (thresholdFailed) + { + throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray())); + } + } } catch (Exception ex) { diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index f61e52104..84ea59347 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -7,6 +7,7 @@ $([MSBuild]::EnsureTrailingSlash('$(CoverletOutputDirectory)'))$(CoverletOutputName) 0 + line,branch,method false diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets index 5174fb836..96688bedd 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.targets +++ b/src/coverlet.msbuild/coverlet.msbuild.targets @@ -22,7 +22,8 @@ Condition="$(CollectCoverage) == 'true'" Output="$(CoverletOutput)" OutputFormat="$(CoverletOutputFormat)" - Threshold="$(Threshold)" /> + Threshold="$(Threshold)" + ThresholdType="$(ThresholdType)" /> From 002c0a65477de8f9d473aa18c584d54af32640bb Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 11:33:58 +0100 Subject: [PATCH 13/62] create backing fields for instrumentation task properties --- .../InstrumentationTask.cs | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 2ee289fae..bda9ba2d7 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -1,28 +1,43 @@ using System; using Coverlet.Core; using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - +using Microsoft.Build.Utilities; + namespace Coverlet.MSbuild.Tasks { public class InstrumentationTask : Task - { - internal static Coverage Coverage { get; private set; } + { + private static Coverage _coverage; + private string _path; + private string _exclude; + + internal static Coverage Coverage + { + get { return _coverage; } + } [Required] - public string Path { get; set; } - - public string Exclude { get; set; } - + public string Path + { + get { return _path; } + set { _path = value; } + } + + public string Exclude + { + get { return _path; } + set { _path = value; } + } + public override bool Execute() { try { - var excludeRules = Exclude?.Split(','); - Coverage = new Coverage(Path, Guid.NewGuid().ToString(), excludeRules); - Coverage.PrepareModules(); + var excludeRules = _exclude?.Split(','); + _coverage = new Coverage(_path, Guid.NewGuid().ToString(), excludeRules); + _coverage.PrepareModules(); } - catch(Exception ex) + catch (Exception ex) { Log.LogErrorFromException(ex); return false; From 1dd3b70b1143736e3e004131e52f9b873a8af550 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 17:17:19 +0100 Subject: [PATCH 14/62] add preliminary support for excluding assemblies from coverage --- src/coverlet.core/Coverage.cs | 18 ++- .../Helpers/InstrumentationHelper.cs | 138 ++++++++++++++---- .../Instrumentation/Instrumenter.cs | 11 +- .../InstrumentationTask.cs | 17 ++- src/coverlet.msbuild/coverlet.msbuild.props | 1 + src/coverlet.msbuild/coverlet.msbuild.targets | 1 + test/coverlet.core.tests/CoverageTests.cs | 2 +- .../Helpers/InstrumentationHelperTests.cs | 44 +++--- .../Instrumentation/InstrumenterTests.cs | 2 +- 9 files changed, 168 insertions(+), 66 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 9acb6dbbe..bbdd95535 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -13,24 +13,30 @@ public class Coverage { private string _module; private string _identifier; - private IEnumerable _excludeRules; + private string[] _filters; + private string[] _excludes; private List _results; - public Coverage(string module, string identifier, IEnumerable excludeRules = null) + public Coverage(string module, string identifier, string[] filters, string[] excludes) { _module = module; _identifier = identifier; - _excludeRules = excludeRules; + _filters = filters; + _excludes = excludes; _results = new List(); } public void PrepareModules() { - string[] modules = InstrumentationHelper.GetDependencies(_module); - var excludedFiles = InstrumentationHelper.GetExcludedFiles(_excludeRules); + string[] modules = InstrumentationHelper.GetCoverableModules(_module); + string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_excludes); + foreach (var module in modules) { - var instrumenter = new Instrumenter(module, _identifier, excludedFiles); + if (InstrumentationHelper.IsModuleExcluded(module, _filters)) + continue; + + var instrumenter = new Instrumenter(module, _identifier, _filters, excludedFiles); if (instrumenter.CanInstrument()) { InstrumentationHelper.BackupOriginalModule(module, _identifier); diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index dfd36cd68..8aec780e4 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Reflection.PortableExecutable; +using System.Text.RegularExpressions; using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.FileSystemGlobbing.Abstractions; @@ -12,10 +13,10 @@ namespace Coverlet.Core.Helpers { internal static class InstrumentationHelper { - public static string[] GetDependencies(string module) + public static string[] GetCoverableModules(string module) { IEnumerable modules = Directory.GetFiles(Path.GetDirectoryName(module), "*.dll"); - modules = modules.Where(a => IsAssembly(a) && Path.GetFileName(a) != Path.GetFileName(module)); + modules = modules.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module)); return modules.ToArray(); } @@ -65,7 +66,8 @@ public static void RestoreOriginalModule(string module, string identifier) // See: https://github.com/tonerdo/coverlet/issues/25 var retryStrategy = CreateRetryStrategy(); - RetryHelper.Retry(() => { + RetryHelper.Retry(() => + { File.Copy(backupPath, module, true); File.Delete(backupPath); }, retryStrategy, 10); @@ -76,34 +78,81 @@ public static void DeleteHitsFile(string path) // Retry hitting the hits file - retry up to 10 times, since the file could be locked // See: https://github.com/tonerdo/coverlet/issues/25 var retryStrategy = CreateRetryStrategy(); - RetryHelper.Retry(() => File.Delete(path), retryStrategy, 10); } - public static IEnumerable GetExcludedFiles(IEnumerable excludeRules, - string parentDir = null) + public static bool IsModuleExcluded(string module, string[] filters) + { + if (filters == null || !filters.Any()) + return false; + + module = Path.GetFileNameWithoutExtension(module); + bool isMatch = false; + + foreach (var filter in filters) + { + if (!IsValidFilterExpression(filter)) + continue; + + string pattern = filter.Substring(1, filter.IndexOf(']') - 1); + pattern = WildcardToRegex(pattern); + + var regex = new Regex(pattern); + isMatch = regex.IsMatch(module); + } + + return isMatch; + } + + public static bool IsTypeExcluded(string type, string[] filters) + { + if (filters == null || !filters.Any()) + return false; + + bool isMatch = false; + + foreach (var filter in filters) + { + if (!IsValidFilterExpression(filter)) + continue; + + string pattern = filter.Substring(filter.IndexOf(']') + 1); + pattern = WildcardToRegex(pattern); + + var regex = new Regex(pattern); + isMatch = regex.IsMatch(type); + } + + return isMatch; + } + + public static string[] GetExcludedFiles(string[] excludes) { const string RELATIVE_KEY = nameof(RELATIVE_KEY); - parentDir = string.IsNullOrWhiteSpace(parentDir)? Directory.GetCurrentDirectory() : parentDir; + string parentDir = Directory.GetCurrentDirectory(); - if (excludeRules == null || !excludeRules.Any()) return Enumerable.Empty(); + if (excludes == null || !excludes.Any()) return Array.Empty(); - var matcherDict = new Dictionary(){ {RELATIVE_KEY, new Matcher()}}; - foreach (var excludeRule in excludeRules) + var matcherDict = new Dictionary() { { RELATIVE_KEY, new Matcher() } }; + foreach (var excludeRule in excludes) { - if (Path.IsPathRooted(excludeRule)) { + if (Path.IsPathRooted(excludeRule)) + { var root = Path.GetPathRoot(excludeRule); - if (!matcherDict.ContainsKey(root)) { + if (!matcherDict.ContainsKey(root)) + { matcherDict.Add(root, new Matcher()); } matcherDict[root].AddInclude(excludeRule.Substring(root.Length)); - } else { + } + else + { matcherDict[RELATIVE_KEY].AddInclude(excludeRule); } } var files = new List(); - foreach(var entry in matcherDict) + foreach (var entry in matcherDict) { var root = entry.Key; var matcher = entry.Value; @@ -114,20 +163,7 @@ public static IEnumerable GetExcludedFiles(IEnumerable excludeRu files.AddRange(currentFiles); } - return files.Distinct(); - } - - private static bool IsAssembly(string filePath) - { - try - { - AssemblyName.GetAssemblyName(filePath); - return true; - } - catch - { - return false; - } + return files.Distinct().ToArray(); } private static string GetBackupPath(string module, string identifier) @@ -149,6 +185,52 @@ TimeSpan retryStrategy() return retryStrategy; } + + private static bool IsValidFilterExpression(string filter) + { + if (!filter.StartsWith("[")) + return false; + + if (!filter.Contains("]")) + return false; + + if (filter.Count(f => f == '[') > 1) + return false; + + if (filter.Count(f => f == ']') > 1) + return false; + + if (filter.IndexOf(']') < filter.IndexOf('[')) + return false; + + if (filter.IndexOf(']') - filter.IndexOf('[') == 1) + return false; + + if (new Regex(@"[^\w*]").IsMatch(filter.Replace("[", "").Replace("]", ""))) + return false; + + return true; + } + + private static string WildcardToRegex(string pattern) + { + return "^" + Regex.Escape(pattern). + Replace("\\*", ".*"). + Replace("\\?", ".") + "$"; + } + + private static bool IsAssembly(string filePath) + { + try + { + AssemblyName.GetAssemblyName(filePath); + return true; + } + catch + { + return false; + } + } } } diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index d6dd0527f..2900bb46a 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -18,15 +18,17 @@ internal class Instrumenter { private readonly string _module; private readonly string _identifier; - private readonly IEnumerable _excludedFiles; + private readonly string[] _filters; + private readonly string[] _excludedFiles; private readonly static Lazy _markExecutedMethodLoader = new Lazy(GetMarkExecutedMethod); private InstrumenterResult _result; - public Instrumenter(string module, string identifier, IEnumerable excludedFiles = null) + public Instrumenter(string module, string identifier, string[] filters, string[] excludedFiles) { _module = module; _identifier = identifier; - _excludedFiles = excludedFiles ?? Enumerable.Empty(); + _filters = filters; + _excludedFiles = excludedFiles ?? Array.Empty(); } public bool CanInstrument() => InstrumentationHelper.HasPdb(_module); @@ -72,7 +74,8 @@ private void InstrumentModule() private void InstrumentType(TypeDefinition type) { - if (type.CustomAttributes.Any(IsExcludeAttribute)) + if (type.CustomAttributes.Any(IsExcludeAttribute) + && InstrumentationHelper.IsTypeExcluded(type.FullName, _filters)) return; foreach (var method in type.Methods) diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index bda9ba2d7..b5e66f3b8 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -9,6 +9,7 @@ public class InstrumentationTask : Task { private static Coverage _coverage; private string _path; + private string _filter; private string _exclude; internal static Coverage Coverage @@ -22,19 +23,27 @@ public string Path get { return _path; } set { _path = value; } } + + public string Filter + { + get { return _filter; } + set { _filter = value; } + } public string Exclude { - get { return _path; } - set { _path = value; } + get { return _exclude; } + set { _exclude = value; } } public override bool Execute() { try { - var excludeRules = _exclude?.Split(','); - _coverage = new Coverage(_path, Guid.NewGuid().ToString(), excludeRules); + var excludes = _exclude?.Split(','); + var filters = _filter?.Split(','); + + _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, excludes); _coverage.PrepareModules(); } catch (Exception ex) diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index 84ea59347..ca421a614 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -5,6 +5,7 @@ $(MSBuildProjectDirectory) coverage $([MSBuild]::EnsureTrailingSlash('$(CoverletOutputDirectory)'))$(CoverletOutputName) + 0 line,branch,method diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets index 96688bedd..a2cdc7382 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.targets +++ b/src/coverlet.msbuild/coverlet.msbuild.targets @@ -13,6 +13,7 @@ diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs index e153c474f..5432826d6 100644 --- a/test/coverlet.core.tests/CoverageTests.cs +++ b/test/coverlet.core.tests/CoverageTests.cs @@ -28,7 +28,7 @@ public void TestCoverage() // Since Coverage only instruments dependancies, we need a fake module here var testModule = Path.Combine(directory.FullName, "test.module.dll"); - var coverage = new Coverage(testModule, identifier); + var coverage = new Coverage(testModule, identifier, Array.Empty(), Array.Empty()); coverage.PrepareModules(); var result = coverage.GetCoverageResult(); diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index b795f21b0..558ce2bbd 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -8,12 +8,12 @@ namespace Coverlet.Core.Helpers.Tests { public class InstrumentationHelperTests - { + { [Fact] public void TestGetDependencies() { string module = typeof(InstrumentationHelperTests).Assembly.Location; - var modules = InstrumentationHelper.GetDependencies(module); + var modules = InstrumentationHelper.GetCoverableModules(module); Assert.False(Array.Exists(modules, m => m == module)); } @@ -70,60 +70,60 @@ public void TestDeleteHitsFile() InstrumentationHelper.DeleteHitsFile(tempFile); Assert.False(File.Exists(tempFile)); } - - - public static IEnumerable GetExcludedFilesReturnsEmptyArgs => - new[] + + + public static IEnumerable GetExcludedFilesReturnsEmptyArgs => + new[] { new object[]{null}, - new object[]{new List()}, - new object[]{new List(){ Path.GetRandomFileName() }}, - new object[]{new List(){Path.GetRandomFileName(), + new object[]{new string[0]}, + new object[]{new string[]{ Path.GetRandomFileName() }}, + new object[]{new string[]{Path.GetRandomFileName(), Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName())} } }; - + [Theory] [MemberData(nameof(GetExcludedFilesReturnsEmptyArgs))] - public void TestGetExcludedFilesReturnsEmpty(IEnumerable excludedFiles) + public void TestGetExcludedFilesReturnsEmpty(string[] excludedFiles) { Assert.False(InstrumentationHelper.GetExcludedFiles(excludedFiles)?.Any()); } - + [Fact] - public void TestGetExcludedFilesUsingAbsFile() + public void TestGetExcludedFilesUsingAbsFile() { var file = Path.GetRandomFileName(); File.Create(file).Dispose(); var excludeFiles = InstrumentationHelper.GetExcludedFiles( - new List() {Path.Combine(Directory.GetCurrentDirectory(), file)} + new string[] { Path.Combine(Directory.GetCurrentDirectory(), file) } ); File.Delete(file); Assert.Single(excludeFiles); } - + [Fact] - public void TestGetExcludedFilesUsingGlobbing() + public void TestGetExcludedFilesUsingGlobbing() { var fileExtension = Path.GetRandomFileName(); var paths = new string[]{ $"{Path.GetRandomFileName()}.{fileExtension}", $"{Path.GetRandomFileName()}.{fileExtension}" }; - + foreach (var path in paths) { File.Create(path).Dispose(); } - + var excludeFiles = InstrumentationHelper.GetExcludedFiles( - new List(){$"*.{fileExtension}"}); - + new string[] { $"*.{fileExtension}" }); + foreach (var path in paths) - { + { File.Delete(path); } - + Assert.Equal(paths.Length, excludeFiles.Count()); } } diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 83f70a873..622cc1f7b 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -21,7 +21,7 @@ public void TestInstrument() File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); module = Path.Combine(directory.FullName, Path.GetFileName(module)); - Instrumenter instrumenter = new Instrumenter(module, identifier); + Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty(), Array.Empty()); var result = instrumenter.Instrument(); Assert.Equal(Path.GetFileNameWithoutExtension(module), result.Module); From 9d09fde6f79a30afcabc655402018c6cf048f0cd Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 18:42:56 +0100 Subject: [PATCH 15/62] complete filter expression evaluation --- .../Helpers/InstrumentationHelper.cs | 34 ++++++++++++------- .../Instrumentation/Instrumenter.cs | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 8aec780e4..2989fb0f7 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -83,44 +83,49 @@ public static void DeleteHitsFile(string path) public static bool IsModuleExcluded(string module, string[] filters) { - if (filters == null || !filters.Any()) + if (filters == null) return false; - module = Path.GetFileNameWithoutExtension(module); bool isMatch = false; + module = Path.GetFileNameWithoutExtension(module); foreach (var filter in filters) { if (!IsValidFilterExpression(filter)) continue; - string pattern = filter.Substring(1, filter.IndexOf(']') - 1); - pattern = WildcardToRegex(pattern); + string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1); + string typePattern = filter.Substring(filter.IndexOf(']') + 1); - var regex = new Regex(pattern); - isMatch = regex.IsMatch(module); + modulePattern = WildcardToRegex(modulePattern); + + var regex = new Regex(modulePattern); + isMatch = regex.IsMatch(module) && typePattern == "*"; } return isMatch; } - public static bool IsTypeExcluded(string type, string[] filters) + public static bool IsTypeExcluded(string module, string type, string[] filters) { - if (filters == null || !filters.Any()) + if (filters == null) return false; bool isMatch = false; + module = Path.GetFileNameWithoutExtension(module); foreach (var filter in filters) { if (!IsValidFilterExpression(filter)) continue; - string pattern = filter.Substring(filter.IndexOf(']') + 1); - pattern = WildcardToRegex(pattern); + string typePattern = filter.Substring(filter.IndexOf(']') + 1); + string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1); - var regex = new Regex(pattern); - isMatch = regex.IsMatch(type); + typePattern = WildcardToRegex(typePattern); + modulePattern = WildcardToRegex(modulePattern); + + isMatch = new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module); } return isMatch; @@ -206,7 +211,10 @@ private static bool IsValidFilterExpression(string filter) if (filter.IndexOf(']') - filter.IndexOf('[') == 1) return false; - if (new Regex(@"[^\w*]").IsMatch(filter.Replace("[", "").Replace("]", ""))) + if (filter.EndsWith("]")) + return false; + + if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", ""))) return false; return true; diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 2900bb46a..dfc853204 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -75,7 +75,7 @@ private void InstrumentModule() private void InstrumentType(TypeDefinition type) { if (type.CustomAttributes.Any(IsExcludeAttribute) - && InstrumentationHelper.IsTypeExcluded(type.FullName, _filters)) + || InstrumentationHelper.IsTypeExcluded(_module, type.FullName, _filters)) return; foreach (var method in type.Methods) From ebedd7078cbaca4cf71315318acfc96ed61e753e Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 18:46:50 +0100 Subject: [PATCH 16/62] rename Exclude property to ExcludeByFile --- .../InstrumentationTask.cs | 116 +++++++++--------- src/coverlet.msbuild/coverlet.msbuild.props | 4 +- src/coverlet.msbuild/coverlet.msbuild.targets | 4 +- 3 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index b5e66f3b8..87df68731 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -1,58 +1,58 @@ -using System; -using Coverlet.Core; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Coverlet.MSbuild.Tasks -{ - public class InstrumentationTask : Task - { - private static Coverage _coverage; - private string _path; - private string _filter; - private string _exclude; - - internal static Coverage Coverage - { - get { return _coverage; } - } - - [Required] - public string Path - { - get { return _path; } - set { _path = value; } - } - - public string Filter - { - get { return _filter; } - set { _filter = value; } - } - - public string Exclude - { - get { return _exclude; } - set { _exclude = value; } - } - - public override bool Execute() - { - try - { - var excludes = _exclude?.Split(','); - var filters = _filter?.Split(','); - - _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, excludes); - _coverage.PrepareModules(); - } - catch (Exception ex) - { - Log.LogErrorFromException(ex); - return false; - } - - return true; - } - } -} +using System; +using Coverlet.Core; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Coverlet.MSbuild.Tasks +{ + public class InstrumentationTask : Task + { + private static Coverage _coverage; + private string _path; + private string _filter; + private string _excludeByFile; + + internal static Coverage Coverage + { + get { return _coverage; } + } + + [Required] + public string Path + { + get { return _path; } + set { _path = value; } + } + + public string Filter + { + get { return _filter; } + set { _filter = value; } + } + + public string ExcludeByFile + { + get { return _excludeByFile; } + set { _excludeByFile = value; } + } + + public override bool Execute() + { + try + { + var excludes = _excludeByFile?.Split(','); + var filters = _filter?.Split(','); + + _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, excludes); + _coverage.PrepareModules(); + } + catch (Exception ex) + { + Log.LogErrorFromException(ex); + return false; + } + + return true; + } + } +} diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index ca421a614..53fea8e8c 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -6,10 +6,10 @@ coverage $([MSBuild]::EnsureTrailingSlash('$(CoverletOutputDirectory)'))$(CoverletOutputName) - + 0 line,branch,method false - \ No newline at end of file + diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets index a2cdc7382..8324ee7b1 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.targets +++ b/src/coverlet.msbuild/coverlet.msbuild.targets @@ -6,7 +6,7 @@ @@ -14,7 +14,7 @@ From 9ed086489eb17f2309eb3ae431ee2a821735ebd7 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 18:50:31 +0100 Subject: [PATCH 17/62] change Filter property to Exclude --- src/coverlet.core/Coverage.cs | 10 +++++----- src/coverlet.core/Helpers/InstrumentationHelper.cs | 6 +++--- src/coverlet.msbuild.tasks/InstrumentationTask.cs | 14 +++++++------- src/coverlet.msbuild/coverlet.msbuild.props | 2 +- src/coverlet.msbuild/coverlet.msbuild.targets | 3 ++- .../Helpers/InstrumentationHelperTests.cs | 8 ++++---- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index bbdd95535..b058eead8 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -14,22 +14,22 @@ public class Coverage private string _module; private string _identifier; private string[] _filters; - private string[] _excludes; + private string[] _rules; private List _results; - public Coverage(string module, string identifier, string[] filters, string[] excludes) + public Coverage(string module, string identifier, string[] filters, string[] rules) { _module = module; _identifier = identifier; _filters = filters; - _excludes = excludes; + _rules = rules; _results = new List(); } public void PrepareModules() { string[] modules = InstrumentationHelper.GetCoverableModules(_module); - string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_excludes); + string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules); foreach (var module in modules) { @@ -211,4 +211,4 @@ private void CalculateCoverage() } } } -} \ No newline at end of file +} diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 2989fb0f7..a6c9d7c5e 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -131,15 +131,15 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) return isMatch; } - public static string[] GetExcludedFiles(string[] excludes) + public static string[] GetExcludedFiles(string[] rules) { const string RELATIVE_KEY = nameof(RELATIVE_KEY); string parentDir = Directory.GetCurrentDirectory(); - if (excludes == null || !excludes.Any()) return Array.Empty(); + if (rules == null || !rules.Any()) return Array.Empty(); var matcherDict = new Dictionary() { { RELATIVE_KEY, new Matcher() } }; - foreach (var excludeRule in excludes) + foreach (var excludeRule in rules) { if (Path.IsPathRooted(excludeRule)) { diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 87df68731..12c641ce0 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -9,7 +9,7 @@ public class InstrumentationTask : Task { private static Coverage _coverage; private string _path; - private string _filter; + private string _exclude; private string _excludeByFile; internal static Coverage Coverage @@ -24,10 +24,10 @@ public string Path set { _path = value; } } - public string Filter + public string Exclude { - get { return _filter; } - set { _filter = value; } + get { return _exclude; } + set { _exclude = value; } } public string ExcludeByFile @@ -40,10 +40,10 @@ public override bool Execute() { try { - var excludes = _excludeByFile?.Split(','); - var filters = _filter?.Split(','); + var rules = _excludeByFile?.Split(','); + var filters = _exclude?.Split(','); - _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, excludes); + _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, rules); _coverage.PrepareModules(); } catch (Exception ex) diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index 53fea8e8c..dafc8382a 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -5,7 +5,7 @@ $(MSBuildProjectDirectory) coverage $([MSBuild]::EnsureTrailingSlash('$(CoverletOutputDirectory)'))$(CoverletOutputName) - + 0 line,branch,method diff --git a/src/coverlet.msbuild/coverlet.msbuild.targets b/src/coverlet.msbuild/coverlet.msbuild.targets index 8324ee7b1..e77b7a0c8 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.targets +++ b/src/coverlet.msbuild/coverlet.msbuild.targets @@ -6,6 +6,7 @@ @@ -13,7 +14,7 @@ diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 558ce2bbd..0bbaf3245 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -95,11 +95,11 @@ public void TestGetExcludedFilesUsingAbsFile() { var file = Path.GetRandomFileName(); File.Create(file).Dispose(); - var excludeFiles = InstrumentationHelper.GetExcludedFiles( + var excludedFiles = InstrumentationHelper.GetExcludedFiles( new string[] { Path.Combine(Directory.GetCurrentDirectory(), file) } ); File.Delete(file); - Assert.Single(excludeFiles); + Assert.Single(excludedFiles); } [Fact] @@ -116,7 +116,7 @@ public void TestGetExcludedFilesUsingGlobbing() File.Create(path).Dispose(); } - var excludeFiles = InstrumentationHelper.GetExcludedFiles( + var excludedFiles = InstrumentationHelper.GetExcludedFiles( new string[] { $"*.{fileExtension}" }); foreach (var path in paths) @@ -124,7 +124,7 @@ public void TestGetExcludedFilesUsingGlobbing() File.Delete(path); } - Assert.Equal(paths.Length, excludeFiles.Count()); + Assert.Equal(paths.Length, excludedFiles.Count()); } } } From 371112c218246cffd55571f648201580a2ce095a Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 13 May 2018 20:01:42 +0100 Subject: [PATCH 18/62] update README.md, add filter documentation --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 03a391690..845499398 100644 --- a/README.md +++ b/README.md @@ -87,18 +87,34 @@ You can ignore a method or an entire class from code coverage by creating and ap Coverlet just uses the type name, so the attributes can be created under any namespace of your choosing. #### Source Files -You can also ignore specific source files from code coverage using the `Exclude` property +You can also ignore specific source files from code coverage using the `ExcludeByFile` property - Use single or multiple paths (separate by comma) - Use absolute or relative paths (relative to the project directory) - Use file path or directory path with globbing (e.g `dir1/*.cs`) ```bash -dotnet test /p:CollectCoverage=true /p:Exclude=\"../dir1/class1.cs,../dir2/*.cs,../dir3/**/*.cs,\" +dotnet test /p:CollectCoverage=true /p:ExcludeByFile=\"../dir1/class1.cs,../dir2/*.cs,../dir3/**/*.cs,\" ``` +#### Filters +Coverlet gives the ability to have fine grained control over what gets excluded using "filter expressions". + +Syntax: `/p:Exclude=[Assembly-Filter]Type-Filter` + +Examples + - `/p:Exclude="[*]*"` => Excludes all types in all assemblies (nothing is instrumented) + - `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`) + - `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly + +```bash +dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage" +``` + +You can specify multiple fiter expressions by separting them with a comma (`,`). + ## Roadmap -* Filter modules to be instrumented +* Merging outputs (multiple test projects, one coverage result) * Support for more output formats (e.g. JaCoCo) * Console runner (removes the need for requiring a NuGet package) From 0b42b0350fdf2a178c474ed0f4115e1e74009435 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Tue, 15 May 2018 10:53:15 +0100 Subject: [PATCH 19/62] improve performance of filter expression validation --- src/coverlet.core/Coverage.cs | 1 + .../Helpers/InstrumentationHelper.cs | 67 +++++++++---------- .../Helpers/InstrumentationHelperTests.cs | 15 +++++ 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index b058eead8..1d065334c 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -30,6 +30,7 @@ public void PrepareModules() { string[] modules = InstrumentationHelper.GetCoverableModules(_module); string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules); + _filters = _filters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray(); foreach (var module in modules) { diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index a6c9d7c5e..7e8171657 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -81,6 +81,38 @@ public static void DeleteHitsFile(string path) RetryHelper.Retry(() => File.Delete(path), retryStrategy, 10); } + public static bool IsValidFilterExpression(string filter) + { + if (filter == null) + return false; + + if (!filter.StartsWith("[")) + return false; + + if (!filter.Contains("]")) + return false; + + if (filter.Count(f => f == '[') > 1) + return false; + + if (filter.Count(f => f == ']') > 1) + return false; + + if (filter.IndexOf(']') < filter.IndexOf('[')) + return false; + + if (filter.IndexOf(']') - filter.IndexOf('[') == 1) + return false; + + if (filter.EndsWith("]")) + return false; + + if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", ""))) + return false; + + return true; + } + public static bool IsModuleExcluded(string module, string[] filters) { if (filters == null) @@ -91,9 +123,6 @@ public static bool IsModuleExcluded(string module, string[] filters) foreach (var filter in filters) { - if (!IsValidFilterExpression(filter)) - continue; - string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1); string typePattern = filter.Substring(filter.IndexOf(']') + 1); @@ -116,9 +145,6 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) foreach (var filter in filters) { - if (!IsValidFilterExpression(filter)) - continue; - string typePattern = filter.Substring(filter.IndexOf(']') + 1); string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1); @@ -191,35 +217,6 @@ TimeSpan retryStrategy() return retryStrategy; } - private static bool IsValidFilterExpression(string filter) - { - if (!filter.StartsWith("[")) - return false; - - if (!filter.Contains("]")) - return false; - - if (filter.Count(f => f == '[') > 1) - return false; - - if (filter.Count(f => f == ']') > 1) - return false; - - if (filter.IndexOf(']') < filter.IndexOf('[')) - return false; - - if (filter.IndexOf(']') - filter.IndexOf('[') == 1) - return false; - - if (filter.EndsWith("]")) - return false; - - if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", ""))) - return false; - - return true; - } - private static string WildcardToRegex(string pattern) { return "^" + Regex.Escape(pattern). diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 0bbaf3245..4b6740944 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -50,6 +50,21 @@ public void TestCopyCoverletDependency() Directory.Delete(directory.FullName, true); } + [Fact] + public void TestIsValidFilterExpression() + { + Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]*")); + Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]*core")); + Assert.True(InstrumentationHelper.IsValidFilterExpression("[assembly]*")); + Assert.True(InstrumentationHelper.IsValidFilterExpression("[*]type")); + Assert.True(InstrumentationHelper.IsValidFilterExpression("[assembly]type")); + Assert.False(InstrumentationHelper.IsValidFilterExpression("[*]")); + Assert.False(InstrumentationHelper.IsValidFilterExpression("[-]*")); + Assert.False(InstrumentationHelper.IsValidFilterExpression("*")); + Assert.False(InstrumentationHelper.IsValidFilterExpression("][")); + Assert.False(InstrumentationHelper.IsValidFilterExpression(null)); + } + [Fact] public void TestDontCopyCoverletDependency() { From bdb1c5aec639db3e7aa79aed83be5d7e911a8a97 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 21:14:04 +0200 Subject: [PATCH 20/62] Fix "MSBUILD : error MSB1008 Only one project can be specified" when running 'dotnet msbuild build.proj' when the coverlet repository is cloned in a directory containing spaces. --- build.proj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.proj b/build.proj index b53aa9917..bb78914ce 100644 --- a/build.proj +++ b/build.proj @@ -8,11 +8,11 @@ - + - + @@ -24,11 +24,11 @@ - + - + From 5835a6e8d6e56748fd04f090f7a0d587c86be299 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 21:49:55 +0200 Subject: [PATCH 21/62] First add a number of test (including some failing) to describe expected behavior. --- .../Helpers/InstrumentationHelperTests.cs | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 4b6740944..a41c4646c 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -141,6 +141,106 @@ public void TestGetExcludedFilesUsingGlobbing() Assert.Equal(paths.Length, excludedFiles.Count()); } + + [Fact] + public void TestIsModuleExcludedWithoutFilter() + { + var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new string[0]); + + Assert.False(result); + } + + [Theory] + [InlineData("[Module]mismatch")] + [InlineData("[Mismatch]*")] + public void TestIsModuleExcludedWithSingleMismatchFilter(string filter) + { + var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter }); + + Assert.False(result); + } + + [Theory] + [MemberData(nameof(ValidModuleFilterData))] + public void TestIsModuleExcludedWithFilter(string filter) + { + var result = InstrumentationHelper.IsModuleExcluded("Module.dll", new[] { filter }); + + Assert.True(result); + } + + [Theory] + [MemberData(nameof(ValidModuleFilterData))] + public void TestIsModuleExcludedWithMatchingAndMismatchingFilter(string filter) + { + var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" }; + + var result = InstrumentationHelper.IsModuleExcluded("Module.dll", filters); + + Assert.True(result); + } + + [Fact] + public void TestIsTypeExcludedWithoutFilter() + { + var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new string[0]); + + Assert.False(result); + } + + [Theory] + [InlineData("[Module]mismatch")] + [InlineData("[Mismatch]*")] + [InlineData("[Mismatch]a.b.Dto")] + public void TestIsTypeExcludedWithSingleMismatchFilter(string filter) + { + var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter }); + + Assert.False(result); + } + + [Theory] + [MemberData(nameof(ValidModuleAndNamespaceFilterData))] + public void TestIsTypeExcludedWithFilter(string filter) + { + var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", new[] { filter }); + + Assert.True(result); + } + + [Theory] + [MemberData(nameof(ValidModuleAndNamespaceFilterData))] + public void TestIsTypeExcludedWithMatchingAndMismatchingFilter(string filter) + { + var filters = new[] { "[Mismatch]*", filter, "[Mismatch]*" }; + + var result = InstrumentationHelper.IsTypeExcluded("Module.dll", "a.b.Dto", filters); + + Assert.True(result); + } + + public static IEnumerable ValidModuleFilterData => + new List + { + new object[] { "[Module]*" }, + new object[] { "[Module*]*" }, + new object[] { "[Mod*ule]*" }, + new object[] { "[M*e]*" }, + new object[] { "[Mod*le*]*" }, + new object[] { "[Module?]*" }, + new object[] { "[ModuleX?]*" }, + }; + + public static IEnumerable ValidModuleAndNamespaceFilterData => + new List + { + new object[] { "[Module]a.b.Dto" }, + new object[] { "[Module]a.b.Dtos?" }, + new object[] { "[Module]a.*" }, + new object[] { "[Module]a*" }, + new object[] { "[Module]*b.*" }, + } + .Concat(ValidModuleFilterData); } } From 45eba432ba1f6e9f646d879746a6634095576264 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 21:54:29 +0200 Subject: [PATCH 22/62] Fix IsModuleExcluded and IsTypeExcluded methods to immediately return true if a filter matches the input. --- .../Helpers/InstrumentationHelper.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 7e8171657..0c4d7efdd 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -118,21 +118,27 @@ public static bool IsModuleExcluded(string module, string[] filters) if (filters == null) return false; - bool isMatch = false; module = Path.GetFileNameWithoutExtension(module); + if (module == null) + return false; foreach (var filter in filters) { string modulePattern = filter.Substring(1, filter.IndexOf(']') - 1); string typePattern = filter.Substring(filter.IndexOf(']') + 1); + if (typePattern != "*") + continue; + modulePattern = WildcardToRegex(modulePattern); var regex = new Regex(modulePattern); - isMatch = regex.IsMatch(module) && typePattern == "*"; + + if (regex.IsMatch(module)) + return true; } - return isMatch; + return false; } public static bool IsTypeExcluded(string module, string type, string[] filters) @@ -140,8 +146,9 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) if (filters == null) return false; - bool isMatch = false; module = Path.GetFileNameWithoutExtension(module); + if (module == null) + return false; foreach (var filter in filters) { @@ -151,10 +158,11 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) typePattern = WildcardToRegex(typePattern); modulePattern = WildcardToRegex(modulePattern); - isMatch = new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module); + if (new Regex(typePattern).IsMatch(type) && new Regex(modulePattern).IsMatch(module)) + return true; } - return isMatch; + return false; } public static string[] GetExcludedFiles(string[] rules) From 0f5e184d4afdd5bf472b149585b6fed22219fd8e Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 22:07:48 +0200 Subject: [PATCH 23/62] Fix usage of question mark. Not sure if this is what was intended but seemed logical. --- README.md | 6 ++++++ src/coverlet.core/Helpers/InstrumentationHelper.cs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 845499398..7ba379271 100644 --- a/README.md +++ b/README.md @@ -101,10 +101,16 @@ Coverlet gives the ability to have fine grained control over what gets excluded Syntax: `/p:Exclude=[Assembly-Filter]Type-Filter` +Wildcards +- `*` => matches zero or more characters +- `?` => the prefixed character is optional + Examples - `/p:Exclude="[*]*"` => Excludes all types in all assemblies (nothing is instrumented) - `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`) - `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly + - `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional) + - `/p:Exclude="[coverlet.*]*,[*]Coverlet..Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly ```bash dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage" diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 0c4d7efdd..5319da9a2 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -107,7 +107,7 @@ public static bool IsValidFilterExpression(string filter) if (filter.EndsWith("]")) return false; - if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("[", "").Replace("]", ""))) + if (new Regex(@"[^\w*]").IsMatch(filter.Replace(".", "").Replace("?", "").Replace("[", "").Replace("]", ""))) return false; return true; @@ -229,7 +229,7 @@ private static string WildcardToRegex(string pattern) { return "^" + Regex.Escape(pattern). Replace("\\*", ".*"). - Replace("\\?", ".") + "$"; + Replace("\\?", "?") + "$"; } private static bool IsAssembly(string filePath) From 9346a42f6fc99aadab14e38ac853613f058a4d1b Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Tue, 15 May 2018 22:09:58 +0200 Subject: [PATCH 24/62] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ba379271..f8298c0f9 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Examples - `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`) - `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly - `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional) - - `/p:Exclude="[coverlet.*]*,[*]Coverlet..Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly + - `/p:Exclude="[coverlet.*]*,[*]Coverlet.Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly ```bash dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage" From e589b2d97ebf14bebcb83872e62bae5aa57df1af Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 17 May 2018 11:02:59 +0100 Subject: [PATCH 25/62] change Threshold implementation and update documentation --- README.md | 12 +++- .../CoverageResultTask.cs | 58 ++++++------------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index f8298c0f9..ccdf137bd 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,17 @@ Coverlet allows you to specify a coverage threshold below which it fails the bui dotnet test /p:CollectCoverage=true /p:Threshold=80 ``` -The above command will automatically fail the build if the average code coverage of all instrumented modules falls below 80%. +The above command will automatically fail the build if the line, branch or method coverage of _any_ of the instrumented modules falls below 80%. You can specify what type of coverage to apply the threshold value to using the `ThresholdType` property. For example to apply the threshold check to only **line** coverage: + +```bash +dotnet test /p:CollectCoverage=true /p:Threshold=80 /p:ThresholdType=line +``` + +You can specify multiple values for `ThresholdType` by separating them with commas. Valid values include `line`, `branch` and `method`. ### Excluding From Coverage -#### Attributes +#### Attributes You can ignore a method or an entire class from code coverage by creating and applying any of the following attributes: * ExcludeFromCoverage @@ -86,7 +92,7 @@ You can ignore a method or an entire class from code coverage by creating and ap Coverlet just uses the type name, so the attributes can be created under any namespace of your choosing. -#### Source Files +#### Source Files You can also ignore specific source files from code coverage using the `ExcludeByFile` property - Use single or multiple paths (separate by comma) - Use absolute or relative paths (relative to the project directory) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 4ad0dbd1b..b3e86e34f 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -15,7 +15,7 @@ public class CoverageResultTask : Task private string _filename; private string _format; private int _threshold; - private string _thresholdTypes; + private string _thresholdType; [Required] public string Output @@ -41,8 +41,8 @@ public int Threshold [Required] public string ThresholdType { - get { return _thresholdTypes; } - set { _thresholdTypes = value; } + get { return _thresholdType; } + set { _thresholdType = value; } } public override bool Execute() @@ -71,10 +71,11 @@ public override bool Execute() File.WriteAllText(report, reporter.Report(result)); } - var branchTotal = 0d; - var methodTotal = 0d; - var lineTotal = 0d; + var thresholdFailed = false; + var thresholdTypes = _thresholdType.Split(',').Select(t => t.Trim()); + var summary = new CoverageSummary(); + var exceptionBuilder = new StringBuilder(); var table = new ConsoleTable("Module", "Line", "Branch", "Method"); foreach (var module in result.Modules) @@ -84,53 +85,32 @@ public override bool Execute() var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; table.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); - lineTotal += linePercent; - branchTotal += branchPercent; - methodTotal += methodPercent; - } - - Console.WriteLine(); - Console.WriteLine(table.ToStringAlternative()); - - if (_threshold > 0) - { - var thresholdFailed = false; - var exceptionBuilder = new StringBuilder(); - var lineAverage = lineTotal / result.Modules.Count; - var branchAverage = branchTotal / result.Modules.Count; - var methodAverage = methodTotal / result.Modules.Count; - var thresholdTypes = _thresholdTypes.Split(',').Select(t => t.ToLower()); - foreach (var thresholdType in thresholdTypes) + if (_threshold > 0) { - if (thresholdType == "line" && lineAverage < _threshold) + if (linePercent < _threshold && thresholdTypes.Contains("line")) { + exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(module.Key)}' has a line coverage '{linePercent}%' below specified threshold '{_threshold}%'"); thresholdFailed = true; - exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{lineAverage}%' is lower than specified threshold '{_threshold}%'"); } - else if (thresholdType == "branch" && branchAverage < _threshold) + if (branchPercent < _threshold && thresholdTypes.Contains("branch")) { + exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(module.Key)}' has a branch coverage '{branchPercent}%' below specified threshold '{_threshold}%'"); thresholdFailed = true; - exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{branchAverage}%' is lower than specified threshold '{_threshold}%'"); } - else if (thresholdType == "method" && methodAverage < _threshold) + if (methodPercent < _threshold && thresholdTypes.Contains("method")) { + exceptionBuilder.AppendLine($"'{Path.GetFileNameWithoutExtension(module.Key)}' has a method coverage '{methodPercent}%' below specified threshold '{_threshold}%'"); thresholdFailed = true; - exceptionBuilder.AppendLine($"Overall average '{thresholdType}' coverage '{methodAverage}%' is lower than specified threshold '{_threshold}%'"); - } - - else if (thresholdType != "line" && thresholdType != "branch" && thresholdType != "method") - { - Console.WriteLine($"Threshold type of {thresholdType} is not recognized/supported and will be ignored."); } } - - if (thresholdFailed) - { - throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray())); - } } + + Console.WriteLine(); + Console.WriteLine(table.ToStringAlternative()); + if (thresholdFailed) + throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray())); } catch (Exception ex) { From e97bfb395a1586282aca9b32ae1d5501b372671b Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 17 May 2018 11:05:01 +0100 Subject: [PATCH 26/62] bump version numbers --- coverlet.msbuild.nuspec | 2 +- src/coverlet.core/coverlet.core.csproj | 2 +- src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coverlet.msbuild.nuspec b/coverlet.msbuild.nuspec index dd875f2fc..f8e8909d0 100644 --- a/coverlet.msbuild.nuspec +++ b/coverlet.msbuild.nuspec @@ -2,7 +2,7 @@ coverlet.msbuild - 1.2.0 + 2.0.0 coverlet.msbuild tonerdo tonerdo diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index b0440d373..1562e3b3d 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.2.0 + 2.0.0 diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 6806737b6..230a27199 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 1.2.0 + 2.0.0 From a2448158aca1ec3e9e4822ee3716302bb4da8101 Mon Sep 17 00:00:00 2001 From: Coen Munckhof Date: Thu, 17 May 2018 23:03:16 +0200 Subject: [PATCH 27/62] Update Readme how to use multiple filter expressions. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ccdf137bd..c82cf4ee7 100644 --- a/README.md +++ b/README.md @@ -116,13 +116,15 @@ Examples - `/p:Exclude="[coverlet.*]Coverlet.Core.Coverage"` => Excludes the Coverage class in the `Coverlet.Core` namespace belonging to any assembly that matches `coverlet.*` (e.g `coverlet.core`) - `/p:Exclude="[*]Coverlet.Core.Instrumentation.*"` => Excludes all types belonging to `Coverlet.Core.Instrumentation` namespace in any assembly - `/p:Exclude="[coverlet.*.tests?]*"` => Excludes all types in any assembly starting with `coverlet.` and ending with `.test` or `.tests` (the `?` makes the `s` optional) - - `/p:Exclude="[coverlet.*]*,[*]Coverlet.Core*"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly + - `/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"` => Excludes assemblies matching `coverlet.*` and excludes all types belonging to the `Coverlet.Core` namespace in any assembly ```bash dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Coverage" ``` -You can specify multiple fiter expressions by separting them with a comma (`,`). + +You can specify multiple filter expressions by separting them with a comma (`,`). If you specify multiple filters, then [you'll have to escape the surrounding quotes](https://github.com/Microsoft/msbuild/issues/2999#issuecomment-366078677) like this: +`/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"`. ## Roadmap From 63e5f07a692024b697fe60127815b28768286c27 Mon Sep 17 00:00:00 2001 From: Maarten van Sambeek Date: Fri, 18 May 2018 18:04:46 +0200 Subject: [PATCH 28/62] Added DomainUnloaded event. When using the NUnit3 test adapter, the process doesn't exit, the app domain is simply unloaded. This means that the ProcessExit event is never raised. The DomainUnload event is called always. To make sure we don't add double hits, we clear the _markers collection after writing (in case the event handler is triggered twice). --- src/coverlet.core/CoverageTracker.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/coverlet.core/CoverageTracker.cs b/src/coverlet.core/CoverageTracker.cs index 2975d0035..09836a46e 100644 --- a/src/coverlet.core/CoverageTracker.cs +++ b/src/coverlet.core/CoverageTracker.cs @@ -18,6 +18,7 @@ static CoverageTracker() _markers = new Dictionary>(); _markerFileCount = new Dictionary(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); + AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_ProcessExit); } [ExcludeFromCoverage] @@ -48,17 +49,22 @@ public static void MarkExecuted(string path, string marker) [ExcludeFromCoverage] public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { - foreach (var kvp in _markers) + lock (_markers) { - using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) - using (var gz = new GZipStream(fs, CompressionMode.Compress)) - using (var sw = new StreamWriter(gz)) + foreach (var kvp in _markers) { - foreach(var line in kvp.Value) + using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) + using (var gz = new GZipStream(fs, CompressionMode.Compress)) + using (var sw = new StreamWriter(gz)) { - sw.WriteLine(line); + foreach(var line in kvp.Value) + { + sw.WriteLine(line); + } } } + + _markers.Clear(); } } } From d6ea4674ed441d3d5a8a171bbc51566514e47d9e Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 20 May 2018 18:16:43 +0100 Subject: [PATCH 29/62] ensure autogenerated delegate types are also excluded --- src/coverlet.core/Attributes/ExcludeFromCoverage.cs | 2 +- src/coverlet.core/Instrumentation/Instrumenter.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs index e2ac0e523..0fb1ef097 100644 --- a/src/coverlet.core/Attributes/ExcludeFromCoverage.cs +++ b/src/coverlet.core/Attributes/ExcludeFromCoverage.cs @@ -2,6 +2,6 @@ namespace Coverlet.Core.Attributes { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class)] public sealed class ExcludeFromCoverageAttribute : Attribute { } } \ No newline at end of file diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index dfc853204..65f1b6e57 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -74,8 +74,12 @@ private void InstrumentModule() private void InstrumentType(TypeDefinition type) { - if (type.CustomAttributes.Any(IsExcludeAttribute) - || InstrumentationHelper.IsTypeExcluded(_module, type.FullName, _filters)) + TypeDefinition actualType = type; + if (type.FullName.Contains("/")) + actualType = type.Module.GetTypes().FirstOrDefault(t => t.FullName == type.FullName.Split('/')[0]); + + if (actualType.CustomAttributes.Any(IsExcludeAttribute) + || InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters)) return; foreach (var method in type.Methods) @@ -116,7 +120,7 @@ private void InstrumentIL(MethodDefinition method) var instruction = processor.Body.Instructions[index]; var sequencePoint = method.DebugInformation.GetSequencePoint(instruction); var targetedBranchPoints = branchPoints.Where(p => p.EndOffset == instruction.Offset); - + if (sequencePoint != null && !sequencePoint.IsHidden) { var target = AddInstrumentationCode(method, processor, instruction, sequencePoint); @@ -139,7 +143,7 @@ private void InstrumentIL(MethodDefinition method) */ if (_branchTarget.StartLine == -1 || _branchTarget.Document == null) continue; - + var target = AddInstrumentationCode(method, processor, instruction, _branchTarget); foreach (var _instruction in processor.Body.Instructions) ReplaceInstructionTarget(_instruction, instruction, target); From 814a89755ba1e909c537773b8c82273505a10fd1 Mon Sep 17 00:00:00 2001 From: StephenMP <1.stephen.porter@gmail.com> Date: Sun, 20 May 2018 14:52:28 -0600 Subject: [PATCH 30/62] Issues #29 & #95 : Displays the average coverage % for all coverage types --- coverlet.sln | 82 ++++++++++--------- .../CoverageResultTask.cs | 22 ++++- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/coverlet.sln b/coverlet.sln index 3571bc819..1bc392aba 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,19 +1,18 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,58 +23,58 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - 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}.Debug|x64.ActiveCfg = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.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 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.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 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.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 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.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 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.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} @@ -83,4 +82,7 @@ Global {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {158BA28B-D48B-4149-A88B-3D6C933A1FB6} + EndGlobalSection EndGlobal diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index b3e86e34f..b211eafad 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -76,14 +76,23 @@ public override bool Execute() var summary = new CoverageSummary(); var exceptionBuilder = new StringBuilder(); - var table = new ConsoleTable("Module", "Line", "Branch", "Method"); + var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method"); + var averageTable = new ConsoleTable("", "Line", "Branch", "Method"); + var lineAverage = 0d; + var branchAverage = 0d; + var methodAverage = 0d; foreach (var module in result.Modules) { var linePercent = summary.CalculateLineCoverage(module.Value).Percent * 100; var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; - table.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); + + lineAverage += linePercent; + branchAverage += branchPercent; + methodAverage += methodPercent; + + coverageTable.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); if (_threshold > 0) { @@ -107,8 +116,15 @@ public override bool Execute() } } + lineAverage = lineAverage / result.Modules.Count; + branchAverage = branchAverage / result.Modules.Count; + methodAverage = methodAverage / result.Modules.Count; + averageTable.AddRow("Average", $"{lineAverage}%", $"{branchAverage}%", $"{methodAverage}%"); + Console.WriteLine(); - Console.WriteLine(table.ToStringAlternative()); + Console.WriteLine(coverageTable.ToStringAlternative()); + Console.WriteLine(averageTable.ToStringAlternative()); + if (thresholdFailed) throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray())); } From 23a780327f5c191231a50011cfd190afcabe59b8 Mon Sep 17 00:00:00 2001 From: StephenMP <1.stephen.porter@gmail.com> Date: Sun, 20 May 2018 14:55:49 -0600 Subject: [PATCH 31/62] Revert solution changes --- coverlet.sln | 82 +++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/coverlet.sln b/coverlet.sln index 1bc392aba..3571bc819 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,18 +1,19 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -23,58 +24,58 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + 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}.Debug|x64.ActiveCfg = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|x64 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|x64 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|x86 {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 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|Any CPU - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|x64 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|x64 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|x86 {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}.Debug|x64.ActiveCfg = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|x64 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|x64 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|x86 {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 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|Any CPU - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|x64 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|x64 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|x86 {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}.Debug|x64.ActiveCfg = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|x64 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|x64 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|x86 {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 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|Any CPU - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|x64 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|x64 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|x86 {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}.Debug|x64.ActiveCfg = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|x64 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|x64 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|x86 {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 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|x64 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|x64 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(NestedProjects) = preSolution {31084026-D563-4B91-BE71-174C4270CCF4} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} @@ -82,7 +83,4 @@ Global {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {158BA28B-D48B-4149-A88B-3D6C933A1FB6} - EndGlobalSection EndGlobal From 6dccea38def4ba20aede2a1925293e585370976c Mon Sep 17 00:00:00 2001 From: StephenMP <1.stephen.porter@gmail.com> Date: Wed, 23 May 2018 11:47:01 -0600 Subject: [PATCH 32/62] Benign whitespace change, let's see if it fixes whatever codecov's issue is by running again --- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index b211eafad..0bce1363e 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -73,7 +73,6 @@ public override bool Execute() var thresholdFailed = false; var thresholdTypes = _thresholdType.Split(',').Select(t => t.Trim()); - var summary = new CoverageSummary(); var exceptionBuilder = new StringBuilder(); var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method"); From e7e407c0dbc130b36622d32b1e5912112fe71a11 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 26 May 2018 22:43:09 +0100 Subject: [PATCH 33/62] exclude local methods in excluded methods --- .../Helpers/InstrumentationHelper.cs | 3 ++ .../Instrumentation/Instrumenter.cs | 29 +++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 5319da9a2..f870ef042 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -165,6 +165,9 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) return false; } + public static bool IsLocalMethod(string method) + => new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method); + public static string[] GetExcludedFiles(string[] rules) { const string RELATIVE_KEY = nameof(RELATIVE_KEY); diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 65f1b6e57..fdbd56c97 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -60,11 +60,19 @@ private void InstrumentModule() { resolver.AddSearchDirectory(Path.GetDirectoryName(_module)); var parameters = new ReaderParameters { ReadSymbols = true, AssemblyResolver = resolver }; + using (var module = ModuleDefinition.ReadModule(stream, parameters)) { - foreach (var type in module.GetTypes()) + var types = module.GetTypes(); + foreach (var type in types) { - InstrumentType(type); + TypeDefinition actualType = type; + if (type.FullName.Contains("/")) + actualType = types.FirstOrDefault(t => t.FullName == type.FullName.Split('/')[0]); + + if (!actualType.CustomAttributes.Any(IsExcludeAttribute) + && !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters)) + InstrumentType(type); } module.Write(stream); @@ -74,17 +82,14 @@ private void InstrumentModule() private void InstrumentType(TypeDefinition type) { - TypeDefinition actualType = type; - if (type.FullName.Contains("/")) - actualType = type.Module.GetTypes().FirstOrDefault(t => t.FullName == type.FullName.Split('/')[0]); - - if (actualType.CustomAttributes.Any(IsExcludeAttribute) - || InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters)) - return; - - foreach (var method in type.Methods) + var methods = type.GetMethods(); + foreach (var method in methods) { - if (!method.CustomAttributes.Any(IsExcludeAttribute)) + MethodDefinition actualMethod = method; + if (InstrumentationHelper.IsLocalMethod(method.Name)) + actualMethod = methods.FirstOrDefault(m => m.Name == method.Name.Split('>')[0].Substring(1)) ?? method; + + if (!actualMethod.CustomAttributes.Any(IsExcludeAttribute)) InstrumentMethod(method); } } From 2a2f75afbab397a7c3d1193d8baaabb254d4ce91 Mon Sep 17 00:00:00 2001 From: Zeid Derhally Date: Mon, 4 Jun 2018 08:43:03 -0400 Subject: [PATCH 34/62] Add support of ExcludeFromCodeCoverage attribute --- README.md | 2 + coverlet.sln | 82 ++++++++++--------- .../Instrumentation/Instrumenter.cs | 6 +- .../Instrumentation/InstrumenterTests.cs | 54 ++++++++++-- test/coverlet.core.tests/Samples/Samples.cs | 30 +++++++ .../Symbols/CecilSymbolHelperTests.cs | 44 +++++----- 6 files changed, 149 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index c82cf4ee7..0f8a796c6 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ You can ignore a method or an entire class from code coverage by creating and ap * ExcludeFromCoverage * ExcludeFromCoverageAttribute +* ExcludeFromCodeCoverage +* ExcludeFromCodeCoverageAttribute Coverlet just uses the type name, so the attributes can be created under any namespace of your choosing. diff --git a/coverlet.sln b/coverlet.sln index 3571bc819..f616006d5 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,19 +1,18 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E877EBA4-E78B-4F7D-A2D3-1E070FED04CD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core", "src\coverlet.core\coverlet.core.csproj", "{31084026-D563-4B91-BE71-174C4270CCF4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.msbuild.tasks", "src\coverlet.msbuild.tasks\coverlet.msbuild.tasks.csproj", "{FA73E423-9790-4F35-B018-3C4E3CA338BA}" +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test\coverlet.core.tests\coverlet.core.tests.csproj", "{E7637CC6-43F7-461A-A0BF-3C14562419BD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -24,58 +23,58 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - 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}.Debug|x64.ActiveCfg = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.Build.0 = Debug|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x64.Build.0 = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Debug|x86.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 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|x64 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|x86 - {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.Build.0 = Release|x86 + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x64.Build.0 = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.ActiveCfg = Release|Any CPU + {31084026-D563-4B91-BE71-174C4270CCF4}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.Build.0 = Debug|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x64.Build.0 = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Debug|x86.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 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|x64 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|x86 - {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.Build.0 = Release|x86 + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x64.Build.0 = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.ActiveCfg = Release|Any CPU + {FA73E423-9790-4F35-B018-3C4E3CA338BA}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.Build.0 = Debug|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x64.Build.0 = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.ActiveCfg = Debug|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Debug|x86.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 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|x64 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|x86 - {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.Build.0 = Release|x86 + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x64.Build.0 = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.ActiveCfg = Release|Any CPU + {E7637CC6-43F7-461A-A0BF-3C14562419BD}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.Build.0 = Debug|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x64.Build.0 = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Debug|x86.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 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|x64 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|x86 - {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|x86 + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU + {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.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} @@ -83,4 +82,7 @@ Global {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} + EndGlobalSection EndGlobal diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index fdbd56c97..2980b1f67 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -269,7 +270,10 @@ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, In private static bool IsExcludeAttribute(CustomAttribute customAttribute) { - return customAttribute.AttributeType.Name == nameof(ExcludeFromCoverageAttribute) || customAttribute.AttributeType.Name == "ExcludeFromCoverage"; + var attributeName = customAttribute.AttributeType.Name; + + return attributeName == nameof(ExcludeFromCoverageAttribute) || attributeName == "ExcludeFromCoverage" + || attributeName == nameof(ExcludeFromCodeCoverageAttribute) || attributeName == "ExcludeFromCodeCoverage"; } private static Mono.Cecil.Cil.MethodBody GetMethodBody(MethodDefinition method) diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 622cc1f7b..8f3c11b12 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -1,8 +1,9 @@ using System; using System.IO; - +using System.Linq; using Xunit; using Coverlet.Core.Instrumentation; +using Coverlet.Core.Samples.Tests; namespace Coverlet.Core.Instrumentation.Tests { @@ -10,24 +11,65 @@ public class InstrumenterTests { [Fact] public void TestInstrument() + { + var instrumenterTest = CreateInstrumentor(); + + var result = instrumenterTest.Instrumenter.Instrument(); + + Assert.Equal(Path.GetFileNameWithoutExtension(instrumenterTest.Module), result.Module); + Assert.Equal(instrumenterTest.Module, result.ModulePath); + + instrumenterTest.Directory.Delete(true); + } + + [Theory] + [InlineData(typeof(ClassExcludedByCodeAnalysisCodeCoverageAttr))] + [InlineData(typeof(ClassExcludedByCoverletCodeCoverageAttr))] + public void TestInstrument_ClassesWithExcludeAttributeAreExcluded(Type excludedType) + { + var instrumenterTest = CreateInstrumentor(); + var result = instrumenterTest.Instrumenter.Instrument(); + + var doc = result.Documents.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); + Assert.NotNull(doc); + + var found = doc.Lines.Any(l => l.Class == excludedType.FullName); + Assert.False(found, "Class decorated with with exclude attribute should be excluded"); + + instrumenterTest.Directory.Delete(true); + } + + private InstrumenterTest CreateInstrumentor() { string module = GetType().Assembly.Location; string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); string identifier = Guid.NewGuid().ToString(); - var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); + DirectoryInfo directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); module = Path.Combine(directory.FullName, Path.GetFileName(module)); Instrumenter instrumenter = new Instrumenter(module, identifier, Array.Empty(), Array.Empty()); - var result = instrumenter.Instrument(); + return new InstrumenterTest + { + Instrumenter = instrumenter, + Module = module, + Identifier = identifier, + Directory = directory + }; + } + + class InstrumenterTest + { + public Instrumenter Instrumenter { get; set; } + + public string Module { get; set; } - Assert.Equal(Path.GetFileNameWithoutExtension(module), result.Module); - Assert.Equal(module, result.ModulePath); + public string Identifier { get; set; } - directory.Delete(true); + public DirectoryInfo Directory { get; set; } } } } \ No newline at end of file diff --git a/test/coverlet.core.tests/Samples/Samples.cs b/test/coverlet.core.tests/Samples/Samples.cs index 518a848fb..31917b11c 100644 --- a/test/coverlet.core.tests/Samples/Samples.cs +++ b/test/coverlet.core.tests/Samples/Samples.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; +using Coverlet.Core.Attributes; namespace Coverlet.Core.Samples.Tests { @@ -161,4 +163,32 @@ public IEnumerable Fetch() yield return "two"; } } + + [ExcludeFromCoverage] + public class ClassExcludedByCoverletCodeCoverageAttr + { + + public string Method(string input) + { + if(string.IsNullOrEmpty(input)) + throw new ArgumentException("Cannot be empty", nameof(input)); + + return input; + } + } + + [ExcludeFromCodeCoverage] + public class ClassExcludedByCodeAnalysisCodeCoverageAttr + { + + public string Method(string input) + { + if (string.IsNullOrEmpty(input)) + throw new ArgumentException("Cannot be empty", nameof(input)); + + return input; + } + } + + } \ No newline at end of file diff --git a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs index 0cd079683..c37a1d2af 100644 --- a/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs +++ b/test/coverlet.core.tests/Symbols/CecilSymbolHelperTests.cs @@ -39,8 +39,8 @@ public void GetBranchPoints_OneBranch() Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(0, points[0].Path); Assert.Equal(1, points[1].Path); - Assert.Equal(19, points[0].StartLine); - Assert.Equal(19, points[1].StartLine); + Assert.Equal(21, points[0].StartLine); + Assert.Equal(21, points[1].StartLine); Assert.NotNull(points[1].Document); Assert.Equal(points[0].Document, points[1].Document); } @@ -86,8 +86,8 @@ public void GetBranchPoints_TwoBranch() Assert.Equal(4, points.Count()); Assert.Equal(points[0].Offset, points[1].Offset); Assert.Equal(points[2].Offset, points[3].Offset); - Assert.Equal(25, points[0].StartLine); - Assert.Equal(26, points[2].StartLine); + Assert.Equal(27, points[0].StartLine); + Assert.Equal(28, points[2].StartLine); } [Fact] @@ -104,8 +104,8 @@ public void GetBranchPoints_CompleteIf() Assert.NotNull(points); Assert.Equal(2, points.Count()); Assert.Equal(points[0].Offset, points[1].Offset); - Assert.Equal(32, points[0].StartLine); - Assert.Equal(32, points[1].StartLine); + Assert.Equal(34, points[0].StartLine); + Assert.Equal(34, points[1].StartLine); } [Fact] @@ -125,10 +125,10 @@ public void GetBranchPoints_Switch() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(44, points[0].StartLine); - Assert.Equal(44, points[1].StartLine); - Assert.Equal(44, points[2].StartLine); - Assert.Equal(44, points[3].StartLine); + Assert.Equal(46, points[0].StartLine); + Assert.Equal(46, points[1].StartLine); + Assert.Equal(46, points[2].StartLine); + Assert.Equal(46, points[3].StartLine); } [Fact] @@ -148,10 +148,10 @@ public void GetBranchPoints_SwitchWithDefault() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(58, points[0].StartLine); - Assert.Equal(58, points[1].StartLine); - Assert.Equal(58, points[2].StartLine); - Assert.Equal(58, points[3].StartLine); + Assert.Equal(60, points[0].StartLine); + Assert.Equal(60, points[1].StartLine); + Assert.Equal(60, points[2].StartLine); + Assert.Equal(60, points[3].StartLine); } [Fact] @@ -171,10 +171,10 @@ public void GetBranchPoints_SwitchWithBreaks() Assert.Equal(points[0].Offset, points[2].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(74, points[0].StartLine); - Assert.Equal(74, points[1].StartLine); - Assert.Equal(74, points[2].StartLine); - Assert.Equal(74, points[3].StartLine); + Assert.Equal(76, points[0].StartLine); + Assert.Equal(76, points[1].StartLine); + Assert.Equal(76, points[2].StartLine); + Assert.Equal(76, points[3].StartLine); } [Fact] @@ -195,10 +195,10 @@ public void GetBranchPoints_SwitchWithMultipleCases() Assert.Equal(points[0].Offset, points[3].Offset); Assert.Equal(3, points[3].Path); - Assert.Equal(92, points[0].StartLine); - Assert.Equal(92, points[1].StartLine); - Assert.Equal(92, points[2].StartLine); - Assert.Equal(92, points[3].StartLine); + Assert.Equal(94, points[0].StartLine); + Assert.Equal(94, points[1].StartLine); + Assert.Equal(94, points[2].StartLine); + Assert.Equal(94, points[3].StartLine); } [Fact] From 15dfa17f805ef267850bce3bce6ad937b708eeee Mon Sep 17 00:00:00 2001 From: Zeid Derhally Date: Tue, 5 Jun 2018 10:43:00 -0400 Subject: [PATCH 35/62] Put list of attribute names in array --- src/coverlet.core/Instrumentation/Instrumenter.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 2980b1f67..9d71473da 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -24,6 +24,14 @@ internal class Instrumenter private readonly static Lazy _markExecutedMethodLoader = new Lazy(GetMarkExecutedMethod); private InstrumenterResult _result; + private static readonly string[] _excludeAttributeNames = new[] + { + nameof(ExcludeFromCoverageAttribute), + "ExcludeFromCoverage", + nameof(ExcludeFromCodeCoverageAttribute), + "ExcludeFromCodeCoverage" + }; + public Instrumenter(string module, string identifier, string[] filters, string[] excludedFiles) { _module = module; @@ -271,9 +279,7 @@ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, In private static bool IsExcludeAttribute(CustomAttribute customAttribute) { var attributeName = customAttribute.AttributeType.Name; - - return attributeName == nameof(ExcludeFromCoverageAttribute) || attributeName == "ExcludeFromCoverage" - || attributeName == nameof(ExcludeFromCodeCoverageAttribute) || attributeName == "ExcludeFromCodeCoverage"; + return _excludeAttributeNames.Any(a => a.Equals(attributeName)); } private static Mono.Cecil.Cil.MethodBody GetMethodBody(MethodDefinition method) From c1916a9a915dfac7a3dfa4bb23a79044d87716bf Mon Sep 17 00:00:00 2001 From: Zeid Derhally Date: Tue, 5 Jun 2018 14:38:09 -0400 Subject: [PATCH 36/62] Made list of attribute names local variable --- .../Instrumentation/Instrumenter.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index 9d71473da..c94a92d32 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -24,14 +24,6 @@ internal class Instrumenter private readonly static Lazy _markExecutedMethodLoader = new Lazy(GetMarkExecutedMethod); private InstrumenterResult _result; - private static readonly string[] _excludeAttributeNames = new[] - { - nameof(ExcludeFromCoverageAttribute), - "ExcludeFromCoverage", - nameof(ExcludeFromCodeCoverageAttribute), - "ExcludeFromCodeCoverage" - }; - public Instrumenter(string module, string identifier, string[] filters, string[] excludedFiles) { _module = module; @@ -278,8 +270,16 @@ private static void ReplaceExceptionHandlerBoundary(ExceptionHandler handler, In private static bool IsExcludeAttribute(CustomAttribute customAttribute) { + var excludeAttributeNames = new[] + { + nameof(ExcludeFromCoverageAttribute), + "ExcludeFromCoverage", + nameof(ExcludeFromCodeCoverageAttribute), + "ExcludeFromCodeCoverage" + }; + var attributeName = customAttribute.AttributeType.Name; - return _excludeAttributeNames.Any(a => a.Equals(attributeName)); + return excludeAttributeNames.Any(a => a.Equals(attributeName)); } private static Mono.Cecil.Cil.MethodBody GetMethodBody(MethodDefinition method) From 7f190e4d79a2013bbc731d0fb22ddeb07e4dd351 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sun, 10 Jun 2018 08:53:27 +0100 Subject: [PATCH 37/62] update extension for cobetura and opencover extensions Fixes #111 --- src/coverlet.core/Reporters/CoberturaReporter.cs | 2 +- src/coverlet.core/Reporters/OpenCoverReporter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 92048509a..fce3643f6 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -12,7 +12,7 @@ public class CoberturaReporter : IReporter { public string Format => "cobertura"; - public string Extension => "xml"; + public string Extension => "cobertura.xml"; public string Report(CoverageResult result) { diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs index cb2b60d74..98653966d 100644 --- a/src/coverlet.core/Reporters/OpenCoverReporter.cs +++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs @@ -11,7 +11,7 @@ public class OpenCoverReporter : IReporter { public string Format => "opencover"; - public string Extension => "xml"; + public string Extension => "opencover.xml"; public string Report(CoverageResult result) { From c19a5c086b592f6d15f2cc005f436646e505e3f7 Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Mon, 11 Jun 2018 06:01:14 +0400 Subject: [PATCH 38/62] sign coverlet core and test assemblies --- src/coverlet.core/Properties/AssemblyInfo.cs | 8 +++++++- src/coverlet.core/coverlet.core.snk | Bin 0 -> 596 bytes .../Properties/AssemblyInfo.cs | 1 + test/coverlet.core.tests/coverlet.core.tests.snk | Bin 0 -> 596 bytes 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/coverlet.core/coverlet.core.snk create mode 100644 test/coverlet.core.tests/Properties/AssemblyInfo.cs create mode 100644 test/coverlet.core.tests/coverlet.core.tests.snk diff --git a/src/coverlet.core/Properties/AssemblyInfo.cs b/src/coverlet.core/Properties/AssemblyInfo.cs index fb93e0f80..ab633e6d9 100644 --- a/src/coverlet.core/Properties/AssemblyInfo.cs +++ b/src/coverlet.core/Properties/AssemblyInfo.cs @@ -1 +1,7 @@ -[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("Coverlet.Core.Tests")] \ No newline at end of file +[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.core.snk")] +[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("Coverlet.Core.Tests,PublicKey=" + +"0024000004800000940000000602000000240000525341310004000001000100757cf9291d78a8" + +"2e5bb58a827a3c46c2f959318327ad30d1b52e918321ffbd847fb21565b8576d2a3a24562a93e8" + +"6c77a298b564a0f1b98f63d7a1441a3a8bcc206da3ed09d5dacc76e122a109a9d3ac608e21a054" + +"d667a2bae98510a1f0f653c0e6f58f42b4b3934f6012f5ec4a09b3dfd3e14d437ede1424bdb722" + +"aead64ad")] \ No newline at end of file diff --git a/src/coverlet.core/coverlet.core.snk b/src/coverlet.core/coverlet.core.snk new file mode 100644 index 0000000000000000000000000000000000000000..65e3bf4933a086f2a15931344fcf2718f9a71cb0 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097npRh7&f9P<3LF^HWZT-d26Y1Kr7>#Mf z!Mtsg+6>1xAWv-1jT5|b9C7Y%)BPAh$i-m0q)YcfmE~yji!SwQR7+?eUcE69*Q$w0 zeBS3@U~ehhGMpWl`X|m0V91j~BG?sw_$tK~4icShtAv;PUyOP^5;1Rgwb*l(qF%N0 z9q2cCqFNUh-zn+v?rs1i-s*<~;itWJN$dxoa0%g%PO9W!aH zt!tD`{jnLOcI4*0NZn}0AVSk zCsANXvNpt)w@~-IgT$d>no;r@|IYCZp947cMRR3+yiazk83G&Gb?!$os7^qIa)P1q zuBCFx$N8QQ-tX*;`WMU+Dtz)w0GGv44u<6_2n`ib3IS`qp4f}vCLh8V+rTAle$L={6{ ieERWH#0kW@NJLliXC(6`!jhL9+cwEEJ0J!Wp-ZMk;2b;v literal 0 HcmV?d00001 diff --git a/test/coverlet.core.tests/Properties/AssemblyInfo.cs b/test/coverlet.core.tests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2357f78b3 --- /dev/null +++ b/test/coverlet.core.tests/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.core.tests.snk")] \ No newline at end of file diff --git a/test/coverlet.core.tests/coverlet.core.tests.snk b/test/coverlet.core.tests/coverlet.core.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 From 555bf1cf8ade7362ce2d2086cf54fbc5e676fb18 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Tue, 12 Jun 2018 08:06:46 +0000 Subject: [PATCH 39/62] bump version numbers --- coverlet.msbuild.nuspec | 2 +- src/coverlet.core/coverlet.core.csproj | 2 +- src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coverlet.msbuild.nuspec b/coverlet.msbuild.nuspec index f8e8909d0..979fb4737 100644 --- a/coverlet.msbuild.nuspec +++ b/coverlet.msbuild.nuspec @@ -2,7 +2,7 @@ coverlet.msbuild - 2.0.0 + 2.0.1 coverlet.msbuild tonerdo tonerdo diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 1562e3b3d..9459a519c 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.0 + 2.0.1 diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 230a27199..7bb9df822 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.0 + 2.0.1 From b1edbac46345f3094ff796c621d51a47af316a96 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Tue, 12 Jun 2018 08:20:45 +0000 Subject: [PATCH 40/62] update README.md --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 0f8a796c6..248dd4fef 100644 --- a/README.md +++ b/README.md @@ -85,14 +85,8 @@ You can specify multiple values for `ThresholdType` by separating them with comm ### Excluding From Coverage #### Attributes -You can ignore a method or an entire class from code coverage by creating and applying any of the following attributes: -* ExcludeFromCoverage -* ExcludeFromCoverageAttribute -* ExcludeFromCodeCoverage -* ExcludeFromCodeCoverageAttribute - -Coverlet just uses the type name, so the attributes can be created under any namespace of your choosing. +You can ignore a method or an entire class from code coverage by creating and applying the `ExcludeFromCodeCoverage` attribute present in the `System.Diagnostics.CodeAnalysis` namespace. #### Source Files You can also ignore specific source files from code coverage using the `ExcludeByFile` property From 627dbe129e87f4ef50f8c5937f012ae35a8135a0 Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Tue, 19 Jun 2018 23:56:29 +0100 Subject: [PATCH 41/62] add ubuntu linux image to Appveyor build matrix --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 2ce36d2df..9d78c769f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,7 @@ version: '{build}' +image: + - Visual Studio 2015 + - Ubuntu configuration: - Debug - Release From d2e579da3e6b2c24a44461b1b08256c7ccb7db95 Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Wed, 20 Jun 2018 00:07:36 +0100 Subject: [PATCH 42/62] use powershell in build_script section in appveyor config --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9d78c769f..8d53f1fe3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ configuration: - Debug - Release build_script: - - echo "Building for %CONFIGURATION%" - - dotnet msbuild build.proj /p:Configuration=%CONFIGURATION% + - ps: echo "Building for $env:CONFIGURATION" + - ps: dotnet msbuild build.proj /p:Configuration=$env:CONFIGURATION test_script: - ps: if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 80f44725b22b1450f4cf0bec5a487cf004c0112a Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Wed, 20 Jun 2018 00:15:08 +0100 Subject: [PATCH 43/62] remove travis support --- .travis.yml | 16 ---------------- README.md | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6c712a999..000000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: csharp -mono: none -dotnet: 2.0.0 -os: linux -dist: trusty -sudo: false -env: - global: - - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - - DOTNET_CLI_TELEMETRY_OPTOUT: 1 -script: - - dotnet msbuild build.proj - - dotnet msbuild build.proj /p:Configuration=Release - - curl -s https://codecov.io/bash > codecov - - chmod +x codecov - - ./codecov -f ./test/coverlet.core.tests/coverage.xml \ No newline at end of file diff --git a/README.md b/README.md index 248dd4fef..a0d42c82a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# coverlet [![Build Status](https://www.travis-ci.org/tonerdo/coverlet.svg?branch=master)](https://www.travis-ci.org/tonerdo/coverlet) [![Build status](https://ci.appveyor.com/api/projects/status/6rdf00wufospr4r8/branch/master?svg=true)](https://ci.appveyor.com/project/tonerdo/coverlet) [![codecov](https://codecov.io/gh/tonerdo/coverlet/branch/master/graph/badge.svg)](https://codecov.io/gh/tonerdo/coverlet) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +# coverlet [![Build status](https://ci.appveyor.com/api/projects/status/6rdf00wufospr4r8/branch/master?svg=true)](https://ci.appveyor.com/project/tonerdo/coverlet) [![codecov](https://codecov.io/gh/tonerdo/coverlet/branch/master/graph/badge.svg)](https://codecov.io/gh/tonerdo/coverlet) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) Coverlet is a cross platform code coverage library for .NET Core, with support for line, branch and method coverage. From c5a3f7726b1f649ea8bebdf094d2266ebd8dbf16 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Fri, 22 Jun 2018 10:42:52 +0200 Subject: [PATCH 44/62] add exe extensions --- .../Helpers/InstrumentationHelper.cs | 2 +- .../Helpers/InstrumentationHelperTests.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index f870ef042..2d43037e5 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -15,7 +15,7 @@ internal static class InstrumentationHelper { public static string[] GetCoverableModules(string module) { - IEnumerable modules = Directory.GetFiles(Path.GetDirectoryName(module), "*.dll"); + IEnumerable modules = Directory.EnumerateFiles(Path.GetDirectoryName(module)).Where(fileName => Path.HasExtension(fileName) && (Path.GetExtension(fileName) == ".exe" || Path.GetExtension(fileName) == ".dll")); modules = modules.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module)); return modules.ToArray(); } diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index a41c4646c..f88b35037 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -1,6 +1,5 @@ using System; using System.IO; - using Xunit; using System.Collections.Generic; using System.Linq; @@ -17,6 +16,21 @@ public void TestGetDependencies() Assert.False(Array.Exists(modules, m => m == module)); } + [Fact] + public void TestExeModulesCoverage() + { + string module = typeof(InstrumentationHelperTests).Assembly.Location; + string exeModule = Path.ChangeExtension(module, ".exe"); + string noExtModule = Path.ChangeExtension(module, "").TrimEnd('.'); + File.Delete(exeModule); + File.Copy(module, exeModule); + File.Delete(noExtModule); + File.Copy(module, noExtModule); + var modules = InstrumentationHelper.GetCoverableModules(module); + Assert.True(Array.Exists(modules, m => m == exeModule)); + Assert.False(Array.Exists(modules, m => m == noExtModule)); + } + [Fact] public void TestHasPdb() { From 554f0543579aa63fb1b7fb9cfcf55f9f882a2212 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 23 Jun 2018 15:05:02 +0100 Subject: [PATCH 45/62] update the line coverage information --- src/coverlet.core/Coverage.cs | 8 ++++---- src/coverlet.core/CoverageResult.cs | 10 +++------- src/coverlet.core/CoverageSummary.cs | 4 ++-- src/coverlet.core/Reporters/CoberturaReporter.cs | 2 +- src/coverlet.core/Reporters/LcovReporter.cs | 4 ++-- src/coverlet.core/Reporters/OpenCoverReporter.cs | 4 ++-- test/coverlet.core.tests/CoverageSummaryTests.cs | 4 ++-- .../Reporters/CoberturaReporterTests.cs | 4 ++-- .../coverlet.core.tests/Reporters/JsonReporterTests.cs | 4 ++-- .../coverlet.core.tests/Reporters/LcovReporterTests.cs | 4 ++-- .../Reporters/OpenCoverReporterTests.cs | 8 ++++---- 11 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 1d065334c..3596be2b8 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -66,19 +66,19 @@ public CoverageResult GetCoverageResult() { if (methods.TryGetValue(line.Method, out Method method)) { - documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits); } else { documents[doc.Path][line.Class].Add(line.Method, new Method()); - documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits); } } else { documents[doc.Path].Add(line.Class, new Methods()); documents[doc.Path][line.Class].Add(line.Method, new Method()); - documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits); } } else @@ -86,7 +86,7 @@ public CoverageResult GetCoverageResult() documents.Add(doc.Path, new Classes()); documents[doc.Path].Add(line.Class, new Methods()); documents[doc.Path][line.Class].Add(line.Method, new Method()); - documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, new LineInfo { Hits = line.Hits }); + documents[doc.Path][line.Class][line.Method].Lines.Add(line.Number, line.Hits); } } diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs index a9a574ad0..c9fba580c 100644 --- a/src/coverlet.core/CoverageResult.cs +++ b/src/coverlet.core/CoverageResult.cs @@ -5,20 +5,16 @@ namespace Coverlet.Core { - public class LineInfo - { - public int Hits { get; set; } - } - - public class BranchInfo : LineInfo + public class BranchInfo { public int Offset { get; set; } public int EndOffset { get; set; } public int Path { get; set; } public uint Ordinal { get; set; } + public int Hits { get; set; } } - public class Lines : SortedDictionary { } + public class Lines : SortedDictionary { } public class Branches : SortedDictionary> { } public class Method { diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs index 81155c636..1db52af0d 100644 --- a/src/coverlet.core/CoverageSummary.cs +++ b/src/coverlet.core/CoverageSummary.cs @@ -9,7 +9,7 @@ public class CoverageSummary public CoverageDetails CalculateLineCoverage(Lines lines) { var details = new CoverageDetails(); - details.Covered = lines.Where(l => l.Value.Hits > 0).Count(); + details.Covered = lines.Where(l => l.Value > 0).Count(); details.Total = lines.Count; return details; } @@ -129,7 +129,7 @@ public CoverageDetails CalculateBranchCoverage(Modules modules) public CoverageDetails CalculateMethodCoverage(Lines lines) { var details = new CoverageDetails(); - details.Covered = lines.Any(l => l.Value.Hits > 0) ? 1 : 0; + details.Covered = lines.Any(l => l.Value > 0) ? 1 : 0; details.Total = 1; return details; } diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index fce3643f6..309c1702a 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -73,7 +73,7 @@ public string Report(CoverageResult result) { XElement line = new XElement("line"); line.Add(new XAttribute("number", ln.Key.ToString())); - line.Add(new XAttribute("hits", ln.Value.Hits.ToString())); + line.Add(new XAttribute("hits", ln.Value.ToString())); line.Add(new XAttribute("branch", meth.Value.Branches.ContainsKey(ln.Key).ToString())); if (meth.Value.Branches.TryGetValue(ln.Key, out List branches)) diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs index 0676d6edc..e83ef71dd 100644 --- a/src/coverlet.core/Reporters/LcovReporter.cs +++ b/src/coverlet.core/Reporters/LcovReporter.cs @@ -33,10 +33,10 @@ public string Report(CoverageResult result) continue; lcov.Add($"FN:{method.Value.Lines.First().Key - 1},{method.Key}"); - lcov.Add($"FNDA:{method.Value.Lines.First().Value.Hits},{method.Key}"); + lcov.Add($"FNDA:{method.Value.Lines.First().Value},{method.Key}"); foreach (var line in method.Value.Lines) - lcov.Add($"DA:{line.Key},{line.Value.Hits}"); + lcov.Add($"DA:{line.Key},{line.Value}"); foreach (var branchs in method.Value.Branches) { diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs index 98653966d..ebd8b1d33 100644 --- a/src/coverlet.core/Reporters/OpenCoverReporter.cs +++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs @@ -110,7 +110,7 @@ public string Report(CoverageResult result) foreach (var lines in meth.Value.Lines) { XElement sequencePoint = new XElement("SequencePoint"); - sequencePoint.Add(new XAttribute("vc", lines.Value.Hits.ToString())); + sequencePoint.Add(new XAttribute("vc", lines.Value.ToString())); sequencePoint.Add(new XAttribute("upsid", lines.Key.ToString())); sequencePoint.Add(new XAttribute("ordinal", k.ToString())); sequencePoint.Add(new XAttribute("sl", lines.Key.ToString())); @@ -122,7 +122,7 @@ public string Report(CoverageResult result) sequencePoint.Add(new XAttribute("fileid", i.ToString())); sequencePoints.Add(sequencePoint); - if (lines.Value.Hits > 0) + if (lines.Value > 0) { classVisited = true; methodVisited = true; diff --git a/test/coverlet.core.tests/CoverageSummaryTests.cs b/test/coverlet.core.tests/CoverageSummaryTests.cs index fab5f2763..5ddc583e4 100644 --- a/test/coverlet.core.tests/CoverageSummaryTests.cs +++ b/test/coverlet.core.tests/CoverageSummaryTests.cs @@ -15,8 +15,8 @@ public class CoverageSummaryTests public CoverageSummaryTests() { Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Branches branches = new Branches(); branches.Add(1, new List()); branches[1].Add(new BranchInfo { Hits = 1, Offset = 1, Path = 0, Ordinal = 1 }); diff --git a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs index be7eba83c..b2c0210d4 100644 --- a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs @@ -12,8 +12,8 @@ public void TestReport() CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Branches branches = new Branches(); branches.Add(1, new List { new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, diff --git a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs index 979bc1108..ac0f31428 100644 --- a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs @@ -11,8 +11,8 @@ public void TestReport() CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Reporters.Tests.JsonReporterTests.TestReport()"; methods.Add(methodString, new Method()); diff --git a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs index c30cea209..588405c61 100644 --- a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs @@ -12,8 +12,8 @@ public void TestReport() CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Branches branches = new Branches(); branches.Add(1, new List { new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, diff --git a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs index 3643480f3..c26fc1a92 100644 --- a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs @@ -40,8 +40,8 @@ public void TestFilesHaveUniqueIdsOverMultipleModules() private static Documents CreateFirstDocuments() { Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Branches branches = new Branches(); branches.Add(1, new List { new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, @@ -62,8 +62,8 @@ private static Documents CreateFirstDocuments() private static Documents CreateSecondDocuments() { Lines lines = new Lines(); - lines.Add(1, new LineInfo { Hits = 1 }); - lines.Add(2, new LineInfo { Hits = 0 }); + lines.Add(1, 1); + lines.Add(2, 0); Methods methods = new Methods(); var methodString = "System.Void Some.Other.Module.TestClass.TestMethod()"; From 4f220f597c964d71d43e1ea5880978b95d838316 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 23 Jun 2018 20:24:46 +0100 Subject: [PATCH 46/62] update the branch coverage information --- src/coverlet.core/Coverage.cs | 31 ++++++------------- src/coverlet.core/CoverageResult.cs | 5 ++- src/coverlet.core/CoverageSummary.cs | 14 ++------- .../Reporters/CoberturaReporter.cs | 6 ++-- src/coverlet.core/Reporters/LcovReporter.cs | 5 ++- .../Reporters/OpenCoverReporter.cs | 27 +++++++--------- .../CoverageSummaryTests.cs | 5 ++- .../Reporters/CoberturaReporterTests.cs | 12 ++++--- .../Reporters/JsonReporterTests.cs | 5 +++ .../Reporters/LcovReporterTests.cs | 11 ++++--- .../Reporters/OpenCoverReporterTests.cs | 17 +++++----- 11 files changed, 66 insertions(+), 72 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 3596be2b8..f41755fbe 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -99,26 +99,15 @@ public CoverageResult GetCoverageResult() { if (methods.TryGetValue(branch.Method, out Method method)) { - if (method.Branches.TryGetValue(branch.Number, out List branchInfo)) - { - documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo - { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } - ); - } - else - { - documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); - documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo - { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } - ); - } + method.Branches.Add(new BranchInfo + { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + ); } else { documents[doc.Path][branch.Class].Add(branch.Method, new Method()); - documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); - documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo - { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo + { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } ); } } @@ -126,9 +115,8 @@ public CoverageResult GetCoverageResult() { documents[doc.Path].Add(branch.Class, new Methods()); documents[doc.Path][branch.Class].Add(branch.Method, new Method()); - documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); - documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo - { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo + { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } ); } } @@ -137,9 +125,8 @@ public CoverageResult GetCoverageResult() documents.Add(doc.Path, new Classes()); documents[doc.Path].Add(branch.Class, new Methods()); documents[doc.Path][branch.Class].Add(branch.Method, new Method()); - documents[doc.Path][branch.Class][branch.Method].Branches.Add(branch.Number, new List()); - documents[doc.Path][branch.Class][branch.Method].Branches[branch.Number].Add(new BranchInfo - { Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } + documents[doc.Path][branch.Class][branch.Method].Branches.Add(new BranchInfo + { Line = branch.Number, Hits = branch.Hits, Offset = branch.Offset, EndOffset = branch.EndOffset, Path = branch.Path, Ordinal = branch.Ordinal } ); } } diff --git a/src/coverlet.core/CoverageResult.cs b/src/coverlet.core/CoverageResult.cs index c9fba580c..dc70d9c79 100644 --- a/src/coverlet.core/CoverageResult.cs +++ b/src/coverlet.core/CoverageResult.cs @@ -7,6 +7,7 @@ namespace Coverlet.Core { public class BranchInfo { + public int Line { get; set; } public int Offset { get; set; } public int EndOffset { get; set; } public int Path { get; set; } @@ -15,7 +16,9 @@ public class BranchInfo } public class Lines : SortedDictionary { } - public class Branches : SortedDictionary> { } + + public class Branches : List { } + public class Method { internal Method() diff --git a/src/coverlet.core/CoverageSummary.cs b/src/coverlet.core/CoverageSummary.cs index 1db52af0d..36b29c12e 100644 --- a/src/coverlet.core/CoverageSummary.cs +++ b/src/coverlet.core/CoverageSummary.cs @@ -62,19 +62,11 @@ public CoverageDetails CalculateLineCoverage(Modules modules) return details; } - public CoverageDetails CalculateBranchCoverage(List branchInfo) + public CoverageDetails CalculateBranchCoverage(IList branches) { var details = new CoverageDetails(); - details.Covered = branchInfo.Count(bi => bi.Hits > 0); - details.Total = branchInfo.Count; - return details; - } - - public CoverageDetails CalculateBranchCoverage(Branches branches) - { - var details = new CoverageDetails(); - details.Covered = branches.Sum(b => b.Value.Where(bi => bi.Hits > 0).Count()); - details.Total = branches.Sum(b => b.Value.Count()); + details.Covered = branches.Count(bi => bi.Hits > 0); + details.Total = branches.Count; return details; } diff --git a/src/coverlet.core/Reporters/CoberturaReporter.cs b/src/coverlet.core/Reporters/CoberturaReporter.cs index 309c1702a..e2f1772b0 100644 --- a/src/coverlet.core/Reporters/CoberturaReporter.cs +++ b/src/coverlet.core/Reporters/CoberturaReporter.cs @@ -71,13 +71,15 @@ public string Report(CoverageResult result) XElement lines = new XElement("lines"); foreach (var ln in meth.Value.Lines) { + bool isBranchPoint = meth.Value.Branches.Any(b => b.Line == ln.Key); XElement line = new XElement("line"); line.Add(new XAttribute("number", ln.Key.ToString())); line.Add(new XAttribute("hits", ln.Value.ToString())); - line.Add(new XAttribute("branch", meth.Value.Branches.ContainsKey(ln.Key).ToString())); + line.Add(new XAttribute("branch", isBranchPoint.ToString())); - if (meth.Value.Branches.TryGetValue(ln.Key, out List branches)) + if (isBranchPoint) { + var branches = meth.Value.Branches.Where(b => b.Line == ln.Key).ToList(); var branchInfoCoverage = summary.CalculateBranchCoverage(branches); line.Add(new XAttribute("condition-coverage", $"{branchInfoCoverage.Percent*100}% ({branchInfoCoverage.Covered}/{branchInfoCoverage.Total})")); XElement conditions = new XElement("conditions"); diff --git a/src/coverlet.core/Reporters/LcovReporter.cs b/src/coverlet.core/Reporters/LcovReporter.cs index e83ef71dd..0def9057d 100644 --- a/src/coverlet.core/Reporters/LcovReporter.cs +++ b/src/coverlet.core/Reporters/LcovReporter.cs @@ -38,10 +38,9 @@ public string Report(CoverageResult result) foreach (var line in method.Value.Lines) lcov.Add($"DA:{line.Key},{line.Value}"); - foreach (var branchs in method.Value.Branches) + foreach (var branch in method.Value.Branches) { - foreach (var branch in branchs.Value) - lcov.Add($"BRDA:{branchs.Key},{branch.Offset},{branch.Path},{branch.Hits}"); + lcov.Add($"BRDA:{branch.Line},{branch.Offset},{branch.Path},{branch.Hits}"); } } } diff --git a/src/coverlet.core/Reporters/OpenCoverReporter.cs b/src/coverlet.core/Reporters/OpenCoverReporter.cs index ebd8b1d33..1464fc152 100644 --- a/src/coverlet.core/Reporters/OpenCoverReporter.cs +++ b/src/coverlet.core/Reporters/OpenCoverReporter.cs @@ -131,22 +131,19 @@ public string Report(CoverageResult result) k++; } - foreach (var branches in meth.Value.Branches) + foreach (var branche in meth.Value.Branches) { - foreach (var branch in branches.Value) - { - XElement branchPoint = new XElement("BranchPoint"); - branchPoint.Add(new XAttribute("vc", branch.Hits.ToString())); - branchPoint.Add(new XAttribute("upsid", branches.Key.ToString())); - branchPoint.Add(new XAttribute("ordinal", branch.Ordinal.ToString())); - branchPoint.Add(new XAttribute("path", branch.Path.ToString())); - branchPoint.Add(new XAttribute("offset", branch.Offset.ToString())); - branchPoint.Add(new XAttribute("offsetend", branch.EndOffset.ToString())); - branchPoint.Add(new XAttribute("sl", branches.Key.ToString())); - branchPoint.Add(new XAttribute("fileid", i.ToString())); - branchPoints.Add(branchPoint); - kBr++; - } + XElement branchPoint = new XElement("BranchPoint"); + branchPoint.Add(new XAttribute("vc", branche.Hits.ToString())); + branchPoint.Add(new XAttribute("upsid", branche.Line.ToString())); + branchPoint.Add(new XAttribute("ordinal", branche.Ordinal.ToString())); + branchPoint.Add(new XAttribute("path", branche.Path.ToString())); + branchPoint.Add(new XAttribute("offset", branche.Offset.ToString())); + branchPoint.Add(new XAttribute("offsetend", branche.EndOffset.ToString())); + branchPoint.Add(new XAttribute("sl", branche.Line.ToString())); + branchPoint.Add(new XAttribute("fileid", i.ToString())); + branchPoints.Add(branchPoint); + kBr++; } numMethods++; diff --git a/test/coverlet.core.tests/CoverageSummaryTests.cs b/test/coverlet.core.tests/CoverageSummaryTests.cs index 5ddc583e4..04cd2e78f 100644 --- a/test/coverlet.core.tests/CoverageSummaryTests.cs +++ b/test/coverlet.core.tests/CoverageSummaryTests.cs @@ -18,9 +18,8 @@ public CoverageSummaryTests() lines.Add(1, 1); lines.Add(2, 0); Branches branches = new Branches(); - branches.Add(1, new List()); - branches[1].Add(new BranchInfo { Hits = 1, Offset = 1, Path = 0, Ordinal = 1 }); - branches[1].Add(new BranchInfo { Hits = 1, Offset = 1, Path = 1, Ordinal = 2 }); + branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 0, Ordinal = 1 }); + branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 1, Path = 1, Ordinal = 2 }); Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Tests.CoverageSummaryTests::TestCalculateSummary()"; diff --git a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs index b2c0210d4..92b5d4f02 100644 --- a/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/CoberturaReporterTests.cs @@ -11,23 +11,27 @@ public void TestReport() { CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); + Lines lines = new Lines(); lines.Add(1, 1); lines.Add(2, 0); + Branches branches = new Branches(); - branches.Add(1, new List { - new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, - new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } - }); + branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }); + branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 }); + Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Reporters.Tests.CoberturaReporterTests::TestReport()"; methods.Add(methodString, new Method()); methods[methodString].Lines = lines; methods[methodString].Branches = branches; + Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.CoberturaReporterTests", methods); + Documents documents = new Documents(); documents.Add("doc.cs", classes); + result.Modules = new Modules(); result.Modules.Add("module", documents); diff --git a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs index ac0f31428..5a5ecd5fb 100644 --- a/test/coverlet.core.tests/Reporters/JsonReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/JsonReporterTests.cs @@ -10,17 +10,22 @@ public void TestReport() { CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); + Lines lines = new Lines(); lines.Add(1, 1); lines.Add(2, 0); + Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Reporters.Tests.JsonReporterTests.TestReport()"; methods.Add(methodString, new Method()); methods[methodString].Lines = lines; + Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.JsonReporterTests", methods); + Documents documents = new Documents(); documents.Add("doc.cs", classes); + result.Modules = new Modules(); result.Modules.Add("module", documents); diff --git a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs index 588405c61..f5c888bf1 100644 --- a/test/coverlet.core.tests/Reporters/LcovReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/LcovReporterTests.cs @@ -11,21 +11,24 @@ public void TestReport() { CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); + Lines lines = new Lines(); lines.Add(1, 1); lines.Add(2, 0); + Branches branches = new Branches(); - branches.Add(1, new List { - new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, - new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } - }); + branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }); + branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 }); + Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Reporters.Tests.LcovReporterTests.TestReport()"; methods.Add(methodString, new Method()); methods[methodString].Lines = lines; methods[methodString].Branches = branches; + Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.LcovReporterTests", methods); + Documents documents = new Documents(); documents.Add("doc.cs", classes); result.Modules = new Modules(); diff --git a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs index c26fc1a92..f17452b4b 100644 --- a/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs +++ b/test/coverlet.core.tests/Reporters/OpenCoverReporterTests.cs @@ -11,7 +11,7 @@ public void TestReport() { CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); - + result.Modules = new Modules(); result.Modules.Add("Coverlet.Core.Reporters.Tests", CreateFirstDocuments()); @@ -24,7 +24,7 @@ public void TestFilesHaveUniqueIdsOverMultipleModules() { CoverageResult result = new CoverageResult(); result.Identifier = Guid.NewGuid().ToString(); - + result.Modules = new Modules(); result.Modules.Add("Coverlet.Core.Reporters.Tests", CreateFirstDocuments()); result.Modules.Add("Some.Other.Module", CreateSecondDocuments()); @@ -42,25 +42,28 @@ private static Documents CreateFirstDocuments() Lines lines = new Lines(); lines.Add(1, 1); lines.Add(2, 0); + Branches branches = new Branches(); - branches.Add(1, new List { - new BranchInfo{ Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }, - new BranchInfo{ Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 } - }); + branches.Add(new BranchInfo { Line = 1, Hits = 1, Offset = 23, EndOffset = 24, Path = 0, Ordinal = 1 }); + branches.Add(new BranchInfo { Line = 1, Hits = 0, Offset = 23, EndOffset = 27, Path = 1, Ordinal = 2 }); + Methods methods = new Methods(); var methodString = "System.Void Coverlet.Core.Reporters.Tests.OpenCoverReporterTests.TestReport()"; methods.Add(methodString, new Method()); methods[methodString].Lines = lines; methods[methodString].Branches = branches; + Classes classes = new Classes(); classes.Add("Coverlet.Core.Reporters.Tests.OpenCoverReporterTests", methods); + Documents documents = new Documents(); documents.Add("doc.cs", classes); + return documents; } private static Documents CreateSecondDocuments() - { + { Lines lines = new Lines(); lines.Add(1, 1); lines.Add(2, 0); From 807f7b1bd5bea8158ffff343d5511cd16e0da9a0 Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Sun, 24 Jun 2018 13:09:14 +0100 Subject: [PATCH 47/62] move CoverageTracker class to separate assembly --- coverlet.sln | 17 ++++++++++++++++- .../Helpers/InstrumentationHelper.cs | 10 ++++------ .../Instrumentation/Instrumenter.cs | 1 + src/coverlet.core/coverlet.core.csproj | 4 ++++ .../CoverageTracker.cs | 17 +++++++++-------- .../Extensions/DictionaryExtensions.cs | 6 +++--- src/coverlet.tracker/coverlet.tracker.csproj | 7 +++++++ .../Extensions/DictionaryExtensionsTests.cs | 19 ------------------- .../Helpers/InstrumentationHelperTests.cs | 2 +- test/coverlet.core.tests/Samples/Samples.cs | 6 ++---- 10 files changed, 47 insertions(+), 42 deletions(-) rename src/{coverlet.core => coverlet.tracker}/CoverageTracker.cs (87%) rename src/{coverlet.core => coverlet.tracker}/Extensions/DictionaryExtensions.cs (75%) create mode 100644 src/coverlet.tracker/coverlet.tracker.csproj delete mode 100644 test/coverlet.core.tests/Extensions/DictionaryExtensionsTests.cs diff --git a/coverlet.sln b/coverlet.sln index f616006d5..a0efe4e6a 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tracker", "src\coverlet.tracker\coverlet.tracker.csproj", "{F4273009-536D-4999-A126-B0A2E3AA3E70}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +74,18 @@ Global {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x64.Build.0 = Release|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.ActiveCfg = Release|Any CPU {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}.Release|x86.Build.0 = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|x64.ActiveCfg = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|x64.Build.0 = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|x86.ActiveCfg = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Debug|x86.Build.0 = Debug|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|Any CPU.Build.0 = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x64.ActiveCfg = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x64.Build.0 = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x86.ActiveCfg = Release|Any CPU + {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -81,6 +95,7 @@ Global {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} + {F4273009-536D-4999-A126-B0A2E3AA3E70} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index f870ef042..3fc909080 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -41,15 +41,13 @@ public static bool HasPdb(string module) public static void CopyCoverletDependency(string module) { - var directory = Path.GetDirectoryName(module); var moduleFileName = Path.GetFileName(module); - - var assembly = typeof(Coverage).Assembly; - string name = Path.GetFileName(assembly.Location); - if (name == moduleFileName) + if (Path.GetFileName(typeof(Coverage).Assembly.Location) == moduleFileName) return; - File.Copy(assembly.Location, Path.Combine(directory, name), true); + var directory = Path.GetDirectoryName(module); + var assembly = typeof(Coverlet.Tracker.CoverageTracker).Assembly; + File.Copy(assembly.Location, Path.Combine(directory, Path.GetFileName(assembly.Location)), true); } public static void BackupOriginalModule(string module, string identifier) diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index c94a92d32..e1f833d61 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -8,6 +8,7 @@ using Coverlet.Core.Attributes; using Coverlet.Core.Helpers; using Coverlet.Core.Symbols; +using Coverlet.Tracker; using Mono.Cecil; using Mono.Cecil.Cil; diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 9459a519c..58f628ab0 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -12,4 +12,8 @@ + + + + diff --git a/src/coverlet.core/CoverageTracker.cs b/src/coverlet.tracker/CoverageTracker.cs similarity index 87% rename from src/coverlet.core/CoverageTracker.cs rename to src/coverlet.tracker/CoverageTracker.cs index 09836a46e..50f7f11cd 100644 --- a/src/coverlet.core/CoverageTracker.cs +++ b/src/coverlet.tracker/CoverageTracker.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; -using Coverlet.Core.Attributes; -using Coverlet.Core.Extensions; -namespace Coverlet.Core +using Coverlet.Tracker.Extensions; + +namespace Coverlet.Tracker { public static class CoverageTracker { private static Dictionary> _markers; private static Dictionary _markerFileCount; - [ExcludeFromCoverage] + [ExcludeFromCodeCoverage] static CoverageTracker() { _markers = new Dictionary>(); @@ -21,7 +22,7 @@ static CoverageTracker() AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_ProcessExit); } - [ExcludeFromCoverage] + [ExcludeFromCodeCoverage] public static void MarkExecuted(string path, string marker) { lock (_markers) @@ -35,7 +36,7 @@ public static void MarkExecuted(string path, string marker) using (var gz = new GZipStream(fs, CompressionMode.Compress)) using (var sw = new StreamWriter(gz)) { - foreach(var line in _markers[path]) + foreach (var line in _markers[path]) { sw.WriteLine(line); } @@ -46,7 +47,7 @@ public static void MarkExecuted(string path, string marker) } } - [ExcludeFromCoverage] + [ExcludeFromCodeCoverage] public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { lock (_markers) @@ -57,7 +58,7 @@ public static void CurrentDomain_ProcessExit(object sender, EventArgs e) using (var gz = new GZipStream(fs, CompressionMode.Compress)) using (var sw = new StreamWriter(gz)) { - foreach(var line in kvp.Value) + foreach (var line in kvp.Value) { sw.WriteLine(line); } diff --git a/src/coverlet.core/Extensions/DictionaryExtensions.cs b/src/coverlet.tracker/Extensions/DictionaryExtensions.cs similarity index 75% rename from src/coverlet.core/Extensions/DictionaryExtensions.cs rename to src/coverlet.tracker/Extensions/DictionaryExtensions.cs index be1bee0df..2b7164025 100644 --- a/src/coverlet.core/Extensions/DictionaryExtensions.cs +++ b/src/coverlet.tracker/Extensions/DictionaryExtensions.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using Coverlet.Core.Attributes; +using System.Diagnostics.CodeAnalysis; -namespace Coverlet.Core.Extensions +namespace Coverlet.Tracker.Extensions { internal static class DictionaryExtensions { - [ExcludeFromCoverage] + [ExcludeFromCodeCoverage] public static bool TryAdd(this Dictionary dictionary, T key, U value) { if (dictionary.ContainsKey(key)) diff --git a/src/coverlet.tracker/coverlet.tracker.csproj b/src/coverlet.tracker/coverlet.tracker.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/src/coverlet.tracker/coverlet.tracker.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/test/coverlet.core.tests/Extensions/DictionaryExtensionsTests.cs b/test/coverlet.core.tests/Extensions/DictionaryExtensionsTests.cs deleted file mode 100644 index 9f22fbb18..000000000 --- a/test/coverlet.core.tests/Extensions/DictionaryExtensionsTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -using Coverlet.Core.Extensions; -using Xunit; - -namespace Coverlet.Core.Tests.Extensions -{ - public class DictionaryExtensionsTests - { - [Fact] - public void TestTryAdd() - { - var dictionary = new Dictionary(); - Assert.True(DictionaryExtensions.TryAdd(dictionary, "a", "b")); - Assert.Equal("b", dictionary["a"]); - Assert.False(DictionaryExtensions.TryAdd(dictionary, "a", "c")); - } - } -} \ No newline at end of file diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index a41c4646c..f49175ad0 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -46,7 +46,7 @@ public void TestCopyCoverletDependency() var directory = Directory.CreateDirectory(Path.Combine(tempPath, "tempdir")); InstrumentationHelper.CopyCoverletDependency(Path.Combine(directory.FullName, "somemodule.dll")); - Assert.True(File.Exists(Path.Combine(directory.FullName, "coverlet.core.dll"))); + Assert.True(File.Exists(Path.Combine(directory.FullName, "coverlet.tracker.dll"))); Directory.Delete(directory.FullName, true); } diff --git a/test/coverlet.core.tests/Samples/Samples.cs b/test/coverlet.core.tests/Samples/Samples.cs index 31917b11c..4cf007c6d 100644 --- a/test/coverlet.core.tests/Samples/Samples.cs +++ b/test/coverlet.core.tests/Samples/Samples.cs @@ -175,8 +175,8 @@ public string Method(string input) return input; } - } - + } + [ExcludeFromCodeCoverage] public class ClassExcludedByCodeAnalysisCodeCoverageAttr { @@ -189,6 +189,4 @@ public string Method(string input) return input; } } - - } \ No newline at end of file From ab3e5961c864248125c614f1d4b1829ebf20c17e Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Sun, 24 Jun 2018 13:34:40 +0100 Subject: [PATCH 48/62] sign coverlet tracker assembly --- src/coverlet.tracker/Properties/AssemblyInfo.cs | 1 + src/coverlet.tracker/coverlet.tracker.snk | Bin 0 -> 596 bytes 2 files changed, 1 insertion(+) create mode 100644 src/coverlet.tracker/Properties/AssemblyInfo.cs create mode 100644 src/coverlet.tracker/coverlet.tracker.snk diff --git a/src/coverlet.tracker/Properties/AssemblyInfo.cs b/src/coverlet.tracker/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9495ca62a --- /dev/null +++ b/src/coverlet.tracker/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.tracker.snk")] \ No newline at end of file diff --git a/src/coverlet.tracker/coverlet.tracker.snk b/src/coverlet.tracker/coverlet.tracker.snk new file mode 100644 index 0000000000000000000000000000000000000000..172a9e4b14458348e6169b71bd00b040044828c0 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097D@bp2SG3;G^xrXy<+#oAcQ+{X!A_L;q z`R@^2_h`)Gp4}^4bw;QvMO@rwgF{s>{L4OjxOR^?fW*F+6;hDSv`e@a)<;wWU zG6l!c2t-jC+NK`j$N8G}6~JAX()eWkry+785jaGTMOw5`V8T=3@X-94Aj3 zhx$UK0+kE}j{x(Za^d4YZz;d?zu)R9==<{fH2-aa_s^MUkR!5X<9=q>AyVx(D>U7R z4oz5;%w6tH2%Vao0ZOeSG#I7i9XaN&;%sueT+g(_JN~-O5!m!U>fx_Mc!g9z@h{n^ zLeC)LLWR4N0|tx|pt;S`l02OFp05afoccM*QqWW1y^#wJG6ZobD~Stu#nd8 zKS7_6qPl^DScGY7@1R7$xNZo7ruMYG+d~0EveQC+k2mce?2yldsKED3NbtVmZ_me>LMVB0FUxnN779 z!?E}QN4OOz3Di;_jc|cc4OqH$uk;qb8%Iu^Q=kpW9Vx1!eIYfVu&qX_)41QF>u$L% iyx@{`sYrhH$41+THL_SMRdc&b$QYe#%DX^L7^a+Ic`392 literal 0 HcmV?d00001 From 85df12655e7b5758b47771b1f5b7265b78b083a5 Mon Sep 17 00:00:00 2001 From: Toni Solarin-Sodara Date: Sun, 24 Jun 2018 13:38:28 +0100 Subject: [PATCH 49/62] sign coverlet msbuild tasks assembly --- .../Properties/AssemblyInfo.cs | 1 + .../coverlet.msbuild.tasks.snk | Bin 0 -> 596 bytes 2 files changed, 1 insertion(+) create mode 100644 src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs create mode 100644 src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.snk diff --git a/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs b/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..240956ae9 --- /dev/null +++ b/src/coverlet.msbuild.tasks/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: System.Reflection.AssemblyKeyFileAttribute("coverlet.msbuild.tasks.snk")] \ No newline at end of file diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.snk b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.snk new file mode 100644 index 0000000000000000000000000000000000000000..3591ab8d560428472a63ccce18fb031a74e5e317 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098y@l>V&-*L;_ijHZo2XlGA2#L9+m;1`a zHo+8;7D0fX@H;f4J-&PI=<0S;Scw-m7@ZNL4g0@)wqkqVB!?Q{frS*);&EbC*gw?N`JA`MVf@cuNWiBng`n z0zL=+7}J4^#DCzyqMaXI5*^-p4ML@wYbPu+(pVn!x6Xn-6Q^27V|0N^;|NI7zorj< zm5abfM`?!G_}IuN`~Iuxo0ohx{0FqI5|8C9*EN@eO1djj_J{TzbapUA*Ahkq-=wzg zcu#T~4SZ*2VzP5``P-9M=(5}hxtqm<idWejr4SnU)D5NJ{C` ze1_vO)>+l9J3k50zS0&*mCas0G~W1^m6OAB^&!?q@1a<0EMyFl=3k`0TpXWYqqb&L z8bYqp)5}hNaEF5gweEW?PD(=fzFq`%?i5%t)9aJDc9>Y^{$L>embt|e+}12)iBVB0 z$*L!zN|*ks;_Eo&sAY%6&o2f3THoCPnam2k_iMIgK9T#*7lVF)LgwvU8<|%i9OJaY zVAk`UdNw(+37lFSE>azZ>2OX!Dsfsr6x9G4Yr~bO5&Xr^Oy0e`(PR*-jtAl|x&8u- zJU+mqBtC7(y%F|Lor1@dg?^HiP0)LgMmdn#zwfc2lX9zaUeVJ0QmZ literal 0 HcmV?d00001 From b3c080ca369882a770626890d2bac433579ca757 Mon Sep 17 00:00:00 2001 From: Peter Liljenberg Date: Tue, 26 Jun 2018 14:02:47 +0200 Subject: [PATCH 50/62] Add coverlet performance unit test. --- coverlet.sln | 35 +- .../CoverageResultTask.cs | 5 + .../PerformanceTest.cs | 27 + .../coverlet.core.performancetest.csproj | 19 + test/coverlet.testsubject/BigClass.cs | 4906 +++++++++++++++++ .../coverlet.testsubject.csproj | 7 + 6 files changed, 4997 insertions(+), 2 deletions(-) create mode 100644 test/coverlet.core.performancetest/PerformanceTest.cs create mode 100644 test/coverlet.core.performancetest/coverlet.core.performancetest.csproj create mode 100644 test/coverlet.testsubject/BigClass.cs create mode 100644 test/coverlet.testsubject/coverlet.testsubject.csproj diff --git a/coverlet.sln b/coverlet.sln index a0efe4e6a..c14d01a85 100644 --- a/coverlet.sln +++ b/coverlet.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 @@ -14,7 +15,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.core.tests", "test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.console", "src\coverlet.console\coverlet.console.csproj", "{F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.tracker", "src\coverlet.tracker\coverlet.tracker.csproj", "{F4273009-536D-4999-A126-B0A2E3AA3E70}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "coverlet.tracker", "src\coverlet.tracker\coverlet.tracker.csproj", "{F4273009-536D-4999-A126-B0A2E3AA3E70}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.testsubject", "test\coverlet.testsubject\coverlet.testsubject.csproj", "{AE117FAA-C21D-4F23-917E-0C8050614750}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coverlet.core.performancetest", "test\coverlet.core.performancetest\coverlet.core.performancetest.csproj", "{C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -86,6 +91,30 @@ Global {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x64.Build.0 = Release|Any CPU {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x86.ActiveCfg = Release|Any CPU {F4273009-536D-4999-A126-B0A2E3AA3E70}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x64.Build.0 = Debug|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x86.ActiveCfg = Debug|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Debug|x86.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 + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x64.ActiveCfg = Release|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x64.Build.0 = Release|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x86.ActiveCfg = Release|Any CPU + {AE117FAA-C21D-4F23-917E-0C8050614750}.Release|x86.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}.Debug|x64.ActiveCfg = Debug|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x64.Build.0 = Debug|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Debug|x86.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 + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x64.ActiveCfg = Release|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x64.Build.0 = Release|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.ActiveCfg = Release|Any CPU + {C68CF6DE-F86C-4BCF-BAB9-7A60C320E1F9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -96,6 +125,8 @@ Global {E7637CC6-43F7-461A-A0BF-3C14562419BD} = {2FEBDE1B-83E3-445B-B9F8-5644B0E0E134} {F3DBE7C3-ABBB-4B8B-A6CB-A1D3D607163E} = {E877EBA4-E78B-4F7D-A2D3-1E070FED04CD} {F4273009-536D-4999-A126-B0A2E3AA3E70} = {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9CA57C02-97B0-4C38-A027-EA61E8741F10} diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 0bce1363e..93b4129c1 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -50,8 +51,12 @@ public override bool Execute() try { Console.WriteLine("\nCalculating coverage result..."); + var duration = new Stopwatch(); + duration.Start(); var coverage = InstrumentationTask.Coverage; var result = coverage.GetCoverageResult(); + duration.Stop(); + Console.WriteLine($"Results calculated in {duration.Elapsed.TotalSeconds} seconds"); var directory = Path.GetDirectoryName(_filename); if (!Directory.Exists(directory)) diff --git a/test/coverlet.core.performancetest/PerformanceTest.cs b/test/coverlet.core.performancetest/PerformanceTest.cs new file mode 100644 index 000000000..849c14a8a --- /dev/null +++ b/test/coverlet.core.performancetest/PerformanceTest.cs @@ -0,0 +1,27 @@ +using coverlet.testsubject; +using Xunit; + +namespace coverlet.core.performancetest +{ + /// + /// Test the performance of coverlet by running a unit test that calls a reasonably big and complex test class. + /// Enable the test, compile, then run the test in the command line: + /// + /// dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover test/coverlet.core.performa ncetest/ + /// + /// + public class PerformanceTest + { + [Theory(Skip = "Only enabled when explicitly testing performance.")] + [InlineData(150)] + public void TestPerformance(int iterations) + { + var big = new BigClass(); + + for (var i = 0; i < iterations; i++) + { + big.Do(i); + } + } + } +} \ No newline at end of file diff --git a/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj b/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj new file mode 100644 index 000000000..9e471b9bf --- /dev/null +++ b/test/coverlet.core.performancetest/coverlet.core.performancetest.csproj @@ -0,0 +1,19 @@ + + + + + netcoreapp2.0 + false + + + + + + + + + + + + + diff --git a/test/coverlet.testsubject/BigClass.cs b/test/coverlet.testsubject/BigClass.cs new file mode 100644 index 000000000..40f758de8 --- /dev/null +++ b/test/coverlet.testsubject/BigClass.cs @@ -0,0 +1,4906 @@ +namespace coverlet.testsubject { + public class BigClass { + public void Do(int y) { + if (y > 0) { + var c = new SubClass0(); + c.Do(y); + } + if (y > 1) { + var c = new SubClass1(); + c.Do(y); + } + if (y > 2) { + var c = new SubClass2(); + c.Do(y); + } + if (y > 3) { + var c = new SubClass3(); + c.Do(y); + } + if (y > 4) { + var c = new SubClass4(); + c.Do(y); + } + if (y > 5) { + var c = new SubClass5(); + c.Do(y); + } + if (y > 6) { + var c = new SubClass6(); + c.Do(y); + } + if (y > 7) { + var c = new SubClass7(); + c.Do(y); + } + if (y > 8) { + var c = new SubClass8(); + c.Do(y); + } + if (y > 9) { + var c = new SubClass9(); + c.Do(y); + } + if (y > 10) { + var c = new SubClass10(); + c.Do(y); + } + if (y > 11) { + var c = new SubClass11(); + c.Do(y); + } + if (y > 12) { + var c = new SubClass12(); + c.Do(y); + } + if (y > 13) { + var c = new SubClass13(); + c.Do(y); + } + if (y > 14) { + var c = new SubClass14(); + c.Do(y); + } + if (y > 15) { + var c = new SubClass15(); + c.Do(y); + } + if (y > 16) { + var c = new SubClass16(); + c.Do(y); + } + if (y > 17) { + var c = new SubClass17(); + c.Do(y); + } + if (y > 18) { + var c = new SubClass18(); + c.Do(y); + } + if (y > 19) { + var c = new SubClass19(); + c.Do(y); + } + if (y > 20) { + var c = new SubClass20(); + c.Do(y); + } + if (y > 21) { + var c = new SubClass21(); + c.Do(y); + } + if (y > 22) { + var c = new SubClass22(); + c.Do(y); + } + if (y > 23) { + var c = new SubClass23(); + c.Do(y); + } + if (y > 24) { + var c = new SubClass24(); + c.Do(y); + } + if (y > 25) { + var c = new SubClass25(); + c.Do(y); + } + if (y > 26) { + var c = new SubClass26(); + c.Do(y); + } + if (y > 27) { + var c = new SubClass27(); + c.Do(y); + } + if (y > 28) { + var c = new SubClass28(); + c.Do(y); + } + if (y > 29) { + var c = new SubClass29(); + c.Do(y); + } + if (y > 30) { + var c = new SubClass30(); + c.Do(y); + } + if (y > 31) { + var c = new SubClass31(); + c.Do(y); + } + if (y > 32) { + var c = new SubClass32(); + c.Do(y); + } + if (y > 33) { + var c = new SubClass33(); + c.Do(y); + } + if (y > 34) { + var c = new SubClass34(); + c.Do(y); + } + if (y > 35) { + var c = new SubClass35(); + c.Do(y); + } + if (y > 36) { + var c = new SubClass36(); + c.Do(y); + } + if (y > 37) { + var c = new SubClass37(); + c.Do(y); + } + if (y > 38) { + var c = new SubClass38(); + c.Do(y); + } + if (y > 39) { + var c = new SubClass39(); + c.Do(y); + } + if (y > 40) { + var c = new SubClass40(); + c.Do(y); + } + if (y > 41) { + var c = new SubClass41(); + c.Do(y); + } + if (y > 42) { + var c = new SubClass42(); + c.Do(y); + } + if (y > 43) { + var c = new SubClass43(); + c.Do(y); + } + if (y > 44) { + var c = new SubClass44(); + c.Do(y); + } + if (y > 45) { + var c = new SubClass45(); + c.Do(y); + } + if (y > 46) { + var c = new SubClass46(); + c.Do(y); + } + if (y > 47) { + var c = new SubClass47(); + c.Do(y); + } + if (y > 48) { + var c = new SubClass48(); + c.Do(y); + } + if (y > 49) { + var c = new SubClass49(); + c.Do(y); + } + if (y > 50) { + var c = new SubClass50(); + c.Do(y); + } + if (y > 51) { + var c = new SubClass51(); + c.Do(y); + } + if (y > 52) { + var c = new SubClass52(); + c.Do(y); + } + if (y > 53) { + var c = new SubClass53(); + c.Do(y); + } + if (y > 54) { + var c = new SubClass54(); + c.Do(y); + } + if (y > 55) { + var c = new SubClass55(); + c.Do(y); + } + if (y > 56) { + var c = new SubClass56(); + c.Do(y); + } + if (y > 57) { + var c = new SubClass57(); + c.Do(y); + } + if (y > 58) { + var c = new SubClass58(); + c.Do(y); + } + if (y > 59) { + var c = new SubClass59(); + c.Do(y); + } + if (y > 60) { + var c = new SubClass60(); + c.Do(y); + } + if (y > 61) { + var c = new SubClass61(); + c.Do(y); + } + if (y > 62) { + var c = new SubClass62(); + c.Do(y); + } + if (y > 63) { + var c = new SubClass63(); + c.Do(y); + } + if (y > 64) { + var c = new SubClass64(); + c.Do(y); + } + if (y > 65) { + var c = new SubClass65(); + c.Do(y); + } + if (y > 66) { + var c = new SubClass66(); + c.Do(y); + } + if (y > 67) { + var c = new SubClass67(); + c.Do(y); + } + if (y > 68) { + var c = new SubClass68(); + c.Do(y); + } + if (y > 69) { + var c = new SubClass69(); + c.Do(y); + } + if (y > 70) { + var c = new SubClass70(); + c.Do(y); + } + if (y > 71) { + var c = new SubClass71(); + c.Do(y); + } + if (y > 72) { + var c = new SubClass72(); + c.Do(y); + } + if (y > 73) { + var c = new SubClass73(); + c.Do(y); + } + if (y > 74) { + var c = new SubClass74(); + c.Do(y); + } + if (y > 75) { + var c = new SubClass75(); + c.Do(y); + } + if (y > 76) { + var c = new SubClass76(); + c.Do(y); + } + if (y > 77) { + var c = new SubClass77(); + c.Do(y); + } + if (y > 78) { + var c = new SubClass78(); + c.Do(y); + } + if (y > 79) { + var c = new SubClass79(); + c.Do(y); + } + if (y > 80) { + var c = new SubClass80(); + c.Do(y); + } + if (y > 81) { + var c = new SubClass81(); + c.Do(y); + } + if (y > 82) { + var c = new SubClass82(); + c.Do(y); + } + if (y > 83) { + var c = new SubClass83(); + c.Do(y); + } + if (y > 84) { + var c = new SubClass84(); + c.Do(y); + } + if (y > 85) { + var c = new SubClass85(); + c.Do(y); + } + if (y > 86) { + var c = new SubClass86(); + c.Do(y); + } + if (y > 87) { + var c = new SubClass87(); + c.Do(y); + } + if (y > 88) { + var c = new SubClass88(); + c.Do(y); + } + if (y > 89) { + var c = new SubClass89(); + c.Do(y); + } + if (y > 90) { + var c = new SubClass90(); + c.Do(y); + } + if (y > 91) { + var c = new SubClass91(); + c.Do(y); + } + if (y > 92) { + var c = new SubClass92(); + c.Do(y); + } + if (y > 93) { + var c = new SubClass93(); + c.Do(y); + } + if (y > 94) { + var c = new SubClass94(); + c.Do(y); + } + if (y > 95) { + var c = new SubClass95(); + c.Do(y); + } + if (y > 96) { + var c = new SubClass96(); + c.Do(y); + } + if (y > 97) { + var c = new SubClass97(); + c.Do(y); + } + if (y > 98) { + var c = new SubClass98(); + c.Do(y); + } + if (y > 99) { + var c = new SubClass99(); + c.Do(y); + } + } + } + public class SubClass0 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass1 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass2 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass3 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass4 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass5 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass6 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass7 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass8 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass9 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass10 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass11 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass12 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass13 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass14 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass15 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass16 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass17 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass18 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass19 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass20 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass21 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass22 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass23 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass24 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass25 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass26 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass27 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass28 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass29 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass30 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass31 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass32 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass33 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass34 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass35 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass36 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass37 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass38 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass39 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass40 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass41 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass42 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass43 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass44 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass45 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass46 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass47 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass48 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass49 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass50 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass51 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass52 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass53 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass54 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass55 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass56 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass57 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass58 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass59 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass60 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass61 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass62 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass63 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass64 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass65 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass66 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass67 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass68 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass69 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass70 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass71 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass72 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass73 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass74 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass75 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass76 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass77 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass78 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass79 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass80 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass81 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass82 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass83 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass84 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass85 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass86 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass87 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass88 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass89 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass90 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass91 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass92 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass93 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass94 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass95 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass96 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass97 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass98 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } + public class SubClass99 { + private int _x; + public void Do(int y) { + if (y > 0) { Do0(y); } + if (y > 1) { Do1(y); } + if (y > 2) { Do2(y); } + if (y > 3) { Do3(y); } + if (y > 4) { Do4(y); } + if (y > 5) { Do5(y); } + if (y > 6) { Do6(y); } + if (y > 7) { Do7(y); } + if (y > 8) { Do8(y); } + if (y > 9) { Do9(y); } + } + private void Do0(int y) { + if (y > 0) { _x += y; } + } + private void Do1(int y) { + if (y > 1) { _x += y; } + } + private void Do2(int y) { + if (y > 2) { _x += y; } + } + private void Do3(int y) { + if (y > 3) { _x += y; } + } + private void Do4(int y) { + if (y > 4) { _x += y; } + } + private void Do5(int y) { + if (y > 5) { _x += y; } + } + private void Do6(int y) { + if (y > 6) { _x += y; } + } + private void Do7(int y) { + if (y > 7) { _x += y; } + } + private void Do8(int y) { + if (y > 8) { _x += y; } + } + private void Do9(int y) { + if (y > 9) { _x += y; } + } + } +} diff --git a/test/coverlet.testsubject/coverlet.testsubject.csproj b/test/coverlet.testsubject/coverlet.testsubject.csproj new file mode 100644 index 000000000..5766db614 --- /dev/null +++ b/test/coverlet.testsubject/coverlet.testsubject.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp2.0 + + + From 1f4da61d7aaa444ff1857f65ddddaec6f01e1f95 Mon Sep 17 00:00:00 2001 From: Peter Liljenberg Date: Tue, 26 Jun 2018 15:25:59 +0200 Subject: [PATCH 51/62] Reduce coverage analysis run time to almost nothing. This is done by aggregating hits in the CoverateTracker directly, rather than just recording events in files, and by avoiding the former quadratic complexity when translating these event files into hits on lines and branches. --- src/coverlet.core/Coverage.cs | 79 +++++++++---------- .../Instrumentation/Instrumenter.cs | 23 +++--- .../Instrumentation/InstrumenterResult.cs | 17 ++-- src/coverlet.tracker/CoverageTracker.cs | 55 ++++++------- .../Instrumentation/InstrumenterTests.cs | 4 +- 5 files changed, 85 insertions(+), 93 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index f41755fbe..420575d49 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -55,10 +55,10 @@ public CoverageResult GetCoverageResult() foreach (var result in _results) { Documents documents = new Documents(); - foreach (var doc in result.Documents) + foreach (var doc in result.Documents.Values) { // Construct Line Results - foreach (var line in doc.Lines) + foreach (var line in doc.Lines.Values) { if (documents.TryGetValue(doc.Path, out Classes classes)) { @@ -91,7 +91,7 @@ public CoverageResult GetCoverageResult() } // Construct Branch Results - foreach (var branch in doc.Branches) + foreach (var branch in doc.Branches.Values) { if (documents.TryGetValue(doc.Path, out Classes classes)) { @@ -147,55 +147,52 @@ private void CalculateCoverage() { foreach (var result in _results) { - var i = 0; - while (true) + if (!File.Exists(result.HitsFilePath)) { - var file = $"{result.HitsFilePath}_compressed_{i}"; - if(!File.Exists(file)) break; - - using (var fs = new FileStream(file, FileMode.Open)) - using (var gz = new GZipStream(fs, CompressionMode.Decompress)) - using (var sr = new StreamReader(gz)) + // File not instrumented, or nothing in it called. Warn about this? + continue; + } + + using (var fs = new FileStream(result.HitsFilePath, FileMode.Open)) + using (var sr = new StreamReader(fs)) + { + string row; + while ((row = sr.ReadLine()) != null) { - string row; - while ((row = sr.ReadLine()) != null) - { - var info = row.Split(','); - // Ignore malformed lines - if (info.Length != 4) - continue; + var info = row.Split(','); + // Ignore malformed lines + if (info.Length != 5) + continue; - bool isBranch = info[0] == "B"; + bool isBranch = info[0] == "B"; - var document = result.Documents.FirstOrDefault(d => d.Path == info[1]); - if (document == null) - continue; + if (!result.Documents.TryGetValue(info[1], out var document)) + { + continue; + } - int start = int.Parse(info[2]); + int start = int.Parse(info[2]); + int hits = int.Parse(info[4]); - if (isBranch) - { - uint ordinal = uint.Parse(info[3]); - var branch = document.Branches.First(b => b.Number == start && b.Ordinal == ordinal); - if (branch.Hits != int.MaxValue) - branch.Hits += branch.Hits + 1; - } - else + if (isBranch) + { + int ordinal = int.Parse(info[3]); + var branch = document.Branches[(start, ordinal)]; + branch.Hits = hits; + } + else + { + int end = int.Parse(info[3]); + for (int j = start; j <= end; j++) { - int end = int.Parse(info[3]); - for (int j = start; j <= end; j++) - { - var line = document.Lines.First(l => l.Number == j); - if (line.Hits != int.MaxValue) - line.Hits = line.Hits + 1; - } + var line = document.Lines[j]; + line.Hits = hits; } } } - - InstrumentationHelper.DeleteHitsFile(file); - i++; } + + InstrumentationHelper.DeleteHitsFile(result.HitsFilePath); } } } diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index e1f833d61..3b63e761b 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -169,17 +169,16 @@ private void InstrumentIL(MethodDefinition method) private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, SequencePoint sequencePoint) { - var document = _result.Documents.FirstOrDefault(d => d.Path == sequencePoint.Document.Url); - if (document == null) - { + if (!_result.Documents.TryGetValue(sequencePoint.Document.Url, out var document)) + { document = new Document { Path = sequencePoint.Document.Url }; - _result.Documents.Add(document); + _result.Documents.Add(document.Path, document); } for (int i = sequencePoint.StartLine; i <= sequencePoint.EndLine; i++) { - if (!document.Lines.Exists(l => l.Number == i)) - document.Lines.Add(new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName }); + if (!document.Lines.ContainsKey(i)) + document.Lines.Add(i, new Line { Number = i, Class = method.DeclaringType.FullName, Method = method.FullName }); } string marker = $"L,{document.Path},{sequencePoint.StartLine},{sequencePoint.EndLine}"; @@ -197,15 +196,15 @@ private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor private Instruction AddInstrumentationCode(MethodDefinition method, ILProcessor processor, Instruction instruction, BranchPoint branchPoint) { - var document = _result.Documents.FirstOrDefault(d => d.Path == branchPoint.Document); - if (document == null) - { + if (!_result.Documents.TryGetValue(branchPoint.Document, out var document)) + { document = new Document { Path = branchPoint.Document }; - _result.Documents.Add(document); + _result.Documents.Add(document.Path, document); } - if (!document.Branches.Exists(l => l.Number == branchPoint.StartLine && l.Ordinal == branchPoint.Ordinal)) - document.Branches.Add( + var key = (branchPoint.StartLine, (int)branchPoint.Ordinal); + if (!document.Branches.ContainsKey(key)) + document.Branches.Add(key, new Branch { Number = branchPoint.StartLine, diff --git a/src/coverlet.core/Instrumentation/InstrumenterResult.cs b/src/coverlet.core/Instrumentation/InstrumenterResult.cs index a2b92cd36..615e2e316 100644 --- a/src/coverlet.core/Instrumentation/InstrumenterResult.cs +++ b/src/coverlet.core/Instrumentation/InstrumenterResult.cs @@ -22,21 +22,26 @@ internal class Document { public Document() { - Lines = new List(); - Branches = new List(); + Lines = new Dictionary(); + Branches = new Dictionary<(int Line, int Ordinal), Branch>(); } public string Path; - public List Lines { get; private set; } - public List Branches { get; private set; } + + public Dictionary Lines { get; private set; } + public Dictionary<(int Line, int Ordinal), Branch> Branches { get; private set; } } internal class InstrumenterResult { - public InstrumenterResult() => Documents = new List(); + public InstrumenterResult() + { + Documents = new Dictionary(); + } + public string Module; public string HitsFilePath; public string ModulePath; - public List Documents { get; private set; } + public Dictionary Documents { get; private set; } } } \ No newline at end of file diff --git a/src/coverlet.tracker/CoverageTracker.cs b/src/coverlet.tracker/CoverageTracker.cs index 50f7f11cd..ce5960808 100644 --- a/src/coverlet.tracker/CoverageTracker.cs +++ b/src/coverlet.tracker/CoverageTracker.cs @@ -2,47 +2,39 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.Compression; - -using Coverlet.Tracker.Extensions; namespace Coverlet.Tracker { public static class CoverageTracker { - private static Dictionary> _markers; - private static Dictionary _markerFileCount; + private static Dictionary> _events; [ExcludeFromCodeCoverage] static CoverageTracker() { - _markers = new Dictionary>(); - _markerFileCount = new Dictionary(); + _events = new Dictionary>(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_ProcessExit); } [ExcludeFromCodeCoverage] - public static void MarkExecuted(string path, string marker) + public static void MarkExecuted(string file, string evt) { - lock (_markers) + lock (_events) { - _markers.TryAdd(path, new List()); - _markers[path].Add(marker); - _markerFileCount.TryAdd(path, 0); - if (_markers[path].Count >= 100000) + if (!_events.TryGetValue(file, out var fileEvents)) { - using (var fs = new FileStream($"{path}_compressed_{_markerFileCount[path]}", FileMode.OpenOrCreate)) - using (var gz = new GZipStream(fs, CompressionMode.Compress)) - using (var sw = new StreamWriter(gz)) - { - foreach (var line in _markers[path]) - { - sw.WriteLine(line); - } - } - _markers[path].Clear(); - _markerFileCount[path] = _markerFileCount[path] + 1; + fileEvents = new Dictionary(); + _events.Add(file, fileEvents); + } + + if (!fileEvents.TryGetValue(evt, out var count)) + { + fileEvents.Add(evt, 1); + } + else if (count < int.MaxValue) + { + fileEvents[evt] = count + 1; } } } @@ -50,22 +42,21 @@ public static void MarkExecuted(string path, string marker) [ExcludeFromCodeCoverage] public static void CurrentDomain_ProcessExit(object sender, EventArgs e) { - lock (_markers) + lock (_events) { - foreach (var kvp in _markers) + foreach (var files in _events) { - using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) - using (var gz = new GZipStream(fs, CompressionMode.Compress)) - using (var sw = new StreamWriter(gz)) + using (var fs = new FileStream(files.Key, FileMode.Create)) + using (var sw = new StreamWriter(fs)) { - foreach (var line in kvp.Value) + foreach (var evt in files.Value) { - sw.WriteLine(line); + sw.WriteLine($"{evt.Key},{evt.Value}"); } } } - _markers.Clear(); + _events.Clear(); } } } diff --git a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs index 8f3c11b12..196d0e362 100644 --- a/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs +++ b/test/coverlet.core.tests/Instrumentation/InstrumenterTests.cs @@ -30,10 +30,10 @@ public void TestInstrument_ClassesWithExcludeAttributeAreExcluded(Type excludedT var instrumenterTest = CreateInstrumentor(); var result = instrumenterTest.Instrumenter.Instrument(); - var doc = result.Documents.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); + var doc = result.Documents.Values.FirstOrDefault(d => Path.GetFileName(d.Path) == "Samples.cs"); Assert.NotNull(doc); - var found = doc.Lines.Any(l => l.Class == excludedType.FullName); + var found = doc.Lines.Values.Any(l => l.Class == excludedType.FullName); Assert.False(found, "Class decorated with with exclude attribute should be excluded"); instrumenterTest.Directory.Delete(true); From 271a1e64f8da08eb6c59d8023f3178f7c523a5a7 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 27 Jun 2018 09:32:34 +0200 Subject: [PATCH 52/62] address PR feedback --- src/coverlet.core/Helpers/InstrumentationHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index 2d43037e5..025041a47 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -15,7 +15,7 @@ internal static class InstrumentationHelper { public static string[] GetCoverableModules(string module) { - IEnumerable modules = Directory.EnumerateFiles(Path.GetDirectoryName(module)).Where(fileName => Path.HasExtension(fileName) && (Path.GetExtension(fileName) == ".exe" || Path.GetExtension(fileName) == ".dll")); + IEnumerable modules = Directory.EnumerateFiles(Path.GetDirectoryName(module)).Where(f => f.EndsWith(".exe") || f.EndsWith(".dll")); modules = modules.Where(m => IsAssembly(m) && Path.GetFileName(m) != Path.GetFileName(module)); return modules.ToArray(); } From 017b3d9a40ffaa3124177dfe84d90dfb1addb35d Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 30 Jun 2018 15:52:10 +0100 Subject: [PATCH 53/62] remove exe coverable test --- .../Helpers/InstrumentationHelperTests.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs index 0796aa025..52ade8079 100644 --- a/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs +++ b/test/coverlet.core.tests/Helpers/InstrumentationHelperTests.cs @@ -16,21 +16,6 @@ public void TestGetDependencies() Assert.False(Array.Exists(modules, m => m == module)); } - [Fact] - public void TestExeModulesCoverage() - { - string module = typeof(InstrumentationHelperTests).Assembly.Location; - string exeModule = Path.ChangeExtension(module, ".exe"); - string noExtModule = Path.ChangeExtension(module, "").TrimEnd('.'); - File.Delete(exeModule); - File.Copy(module, exeModule); - File.Delete(noExtModule); - File.Copy(module, noExtModule); - var modules = InstrumentationHelper.GetCoverableModules(module); - Assert.True(Array.Exists(modules, m => m == exeModule)); - Assert.False(Array.Exists(modules, m => m == noExtModule)); - } - [Fact] public void TestHasPdb() { From a1f18b4156374f3398d704e898ec58c7c6c64bf8 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Sat, 30 Jun 2018 17:13:06 +0100 Subject: [PATCH 54/62] improve checking of compiler generated types --- src/coverlet.core/Instrumentation/Instrumenter.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/coverlet.core/Instrumentation/Instrumenter.cs b/src/coverlet.core/Instrumentation/Instrumenter.cs index e1f833d61..7d859cc85 100644 --- a/src/coverlet.core/Instrumentation/Instrumenter.cs +++ b/src/coverlet.core/Instrumentation/Instrumenter.cs @@ -66,12 +66,9 @@ private void InstrumentModule() using (var module = ModuleDefinition.ReadModule(stream, parameters)) { var types = module.GetTypes(); - foreach (var type in types) + foreach (TypeDefinition type in types) { - TypeDefinition actualType = type; - if (type.FullName.Contains("/")) - actualType = types.FirstOrDefault(t => t.FullName == type.FullName.Split('/')[0]); - + var actualType = type.DeclaringType ?? type; if (!actualType.CustomAttributes.Any(IsExcludeAttribute) && !InstrumentationHelper.IsTypeExcluded(_module, actualType.FullName, _filters)) InstrumentType(type); From 74fa3a2bf3b1de089252c45806408e831890feac Mon Sep 17 00:00:00 2001 From: Alex McAuliffe Date: Sun, 1 Jul 2018 14:56:42 +0100 Subject: [PATCH 55/62] Update README.md Add Cake.Coverlet addin to the bottom of usage section --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a0d42c82a..fbea45824 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,9 @@ dotnet test /p:CollectCoverage=true /p:Exclude="[coverlet.*]Coverlet.Core.Covera You can specify multiple filter expressions by separting them with a comma (`,`). If you specify multiple filters, then [you'll have to escape the surrounding quotes](https://github.com/Microsoft/msbuild/issues/2999#issuecomment-366078677) like this: `/p:Exclude=\"[coverlet.*]*,[*]Coverlet.Core*\"`. +### Cake Addin +If you're using [Cake Build](https://cakebuild.net) for your build script you can use the [Cake.Coverlet](https://github.com/Romanx/Cake.Coverlet) addin to provide you extensions to dotnet test for passing coverlet arguments in a strongly typed manner. + ## Roadmap * Merging outputs (multiple test projects, one coverage result) From 3f44ad9ea17651d4d762e18ba0ae01a61aa14ce0 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 12:16:03 +0100 Subject: [PATCH 56/62] remove non necessary average coverage table --- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 93b4129c1..86329f55c 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -81,10 +81,6 @@ public override bool Execute() var summary = new CoverageSummary(); var exceptionBuilder = new StringBuilder(); var coverageTable = new ConsoleTable("Module", "Line", "Branch", "Method"); - var averageTable = new ConsoleTable("", "Line", "Branch", "Method"); - var lineAverage = 0d; - var branchAverage = 0d; - var methodAverage = 0d; foreach (var module in result.Modules) { @@ -92,10 +88,6 @@ public override bool Execute() var branchPercent = summary.CalculateBranchCoverage(module.Value).Percent * 100; var methodPercent = summary.CalculateMethodCoverage(module.Value).Percent * 100; - lineAverage += linePercent; - branchAverage += branchPercent; - methodAverage += methodPercent; - coverageTable.AddRow(Path.GetFileNameWithoutExtension(module.Key), $"{linePercent}%", $"{branchPercent}%", $"{methodPercent}%"); if (_threshold > 0) @@ -120,14 +112,8 @@ public override bool Execute() } } - lineAverage = lineAverage / result.Modules.Count; - branchAverage = branchAverage / result.Modules.Count; - methodAverage = methodAverage / result.Modules.Count; - averageTable.AddRow("Average", $"{lineAverage}%", $"{branchAverage}%", $"{methodAverage}%"); - Console.WriteLine(); Console.WriteLine(coverageTable.ToStringAlternative()); - Console.WriteLine(averageTable.ToStringAlternative()); if (thresholdFailed) throw new Exception(exceptionBuilder.ToString().TrimEnd(Environment.NewLine.ToCharArray())); From dcf41fe6155e456ddcdc6d9fe41c952f5689be19 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 12:23:28 +0100 Subject: [PATCH 57/62] remove calculation of coverage result time --- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index 86329f55c..b339b0932 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -51,12 +50,9 @@ public override bool Execute() try { Console.WriteLine("\nCalculating coverage result..."); - var duration = new Stopwatch(); - duration.Start(); + var coverage = InstrumentationTask.Coverage; var result = coverage.GetCoverageResult(); - duration.Stop(); - Console.WriteLine($"Results calculated in {duration.Elapsed.TotalSeconds} seconds"); var directory = Path.GetDirectoryName(_filename); if (!Directory.Exists(directory)) From cb5650998973d17bc60acc370a87b6ed4132e720 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 13:04:05 +0100 Subject: [PATCH 58/62] update streamline options to specify coverage output file --- src/coverlet.msbuild.tasks/CoverageResultTask.cs | 15 ++++++++------- src/coverlet.msbuild/coverlet.msbuild.props | 8 ++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/coverlet.msbuild.tasks/CoverageResultTask.cs b/src/coverlet.msbuild.tasks/CoverageResultTask.cs index b339b0932..e574cb3fa 100644 --- a/src/coverlet.msbuild.tasks/CoverageResultTask.cs +++ b/src/coverlet.msbuild.tasks/CoverageResultTask.cs @@ -12,7 +12,7 @@ namespace Coverlet.MSbuild.Tasks { public class CoverageResultTask : Task { - private string _filename; + private string _output; private string _format; private int _threshold; private string _thresholdType; @@ -20,8 +20,8 @@ public class CoverageResultTask : Task [Required] public string Output { - get { return _filename; } - set { _filename = value; } + get { return _output; } + set { _output = value; } } [Required] @@ -54,11 +54,9 @@ public override bool Execute() var coverage = InstrumentationTask.Coverage; var result = coverage.GetCoverageResult(); - var directory = Path.GetDirectoryName(_filename); + var directory = Path.GetDirectoryName(_output); if (!Directory.Exists(directory)) - { Directory.CreateDirectory(directory); - } var formats = _format.Split(','); foreach (var format in formats) @@ -67,7 +65,10 @@ public override bool Execute() if (reporter == null) throw new Exception($"Specified output format '{format}' is not supported"); - var report = _filename + "." + reporter.Extension; + var filename = Path.GetFileName(_output); + filename = (filename == string.Empty) ? $"coverage.{reporter.Extension}" : filename; + + var report = Path.Combine(directory, filename); Console.WriteLine($" Generating report '{report}'"); File.WriteAllText(report, reporter.Report(result)); } diff --git a/src/coverlet.msbuild/coverlet.msbuild.props b/src/coverlet.msbuild/coverlet.msbuild.props index dafc8382a..3dcb69948 100644 --- a/src/coverlet.msbuild/coverlet.msbuild.props +++ b/src/coverlet.msbuild/coverlet.msbuild.props @@ -1,15 +1,11 @@ - + false json - $(MSBuildProjectDirectory) - coverage - $([MSBuild]::EnsureTrailingSlash('$(CoverletOutputDirectory)'))$(CoverletOutputName) + $([MSBuild]::EnsureTrailingSlash('$(MSBuildProjectDirectory)')) 0 line,branch,method - false - From 5a9f20a8d737668979fe77e4282c727f30b10df9 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 13:16:46 +0100 Subject: [PATCH 59/62] update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbea45824..729e771b8 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Supported Formats: * opencover * cobertura -The output folder of the coverage result file can also be specified using the `CoverletOutputDirectory` property. +The output of the coverage result can also be specified using the `CoverletOutput` property. ### Threshold From c82cebe0f9bdfc753f39035a2a1da63d1ae4c566 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 22:27:52 +0100 Subject: [PATCH 60/62] generate unique identifier within Coverage class --- src/coverlet.core/Coverage.cs | 9 +++++++-- src/coverlet.msbuild.tasks/InstrumentationTask.cs | 2 +- test/coverlet.core.tests/CoverageTests.cs | 5 ++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 420575d49..34ffc5d49 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -17,12 +17,17 @@ public class Coverage private string[] _rules; private List _results; - public Coverage(string module, string identifier, string[] filters, string[] rules) + public string Identifier + { + get { return _identifier; } + } + + public Coverage(string module, string[] filters, string[] rules) { _module = module; - _identifier = identifier; _filters = filters; _rules = rules; + _identifier = Guid.NewGuid().ToString(); _results = new List(); } diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 12c641ce0..680ed204c 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -43,7 +43,7 @@ public override bool Execute() var rules = _excludeByFile?.Split(','); var filters = _exclude?.Split(','); - _coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, rules); + _coverage = new Coverage(_path, filters, rules); _coverage.PrepareModules(); } catch (Exception ex) diff --git a/test/coverlet.core.tests/CoverageTests.cs b/test/coverlet.core.tests/CoverageTests.cs index 5432826d6..52578a50e 100644 --- a/test/coverlet.core.tests/CoverageTests.cs +++ b/test/coverlet.core.tests/CoverageTests.cs @@ -16,9 +16,8 @@ public void TestCoverage() { string module = GetType().Assembly.Location; string pdb = Path.Combine(Path.GetDirectoryName(module), Path.GetFileNameWithoutExtension(module) + ".pdb"); - string identifier = Guid.NewGuid().ToString(); - var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), identifier)); + var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); File.Copy(module, Path.Combine(directory.FullName, Path.GetFileName(module)), true); File.Copy(pdb, Path.Combine(directory.FullName, Path.GetFileName(pdb)), true); @@ -28,7 +27,7 @@ public void TestCoverage() // Since Coverage only instruments dependancies, we need a fake module here var testModule = Path.Combine(directory.FullName, "test.module.dll"); - var coverage = new Coverage(testModule, identifier, Array.Empty(), Array.Empty()); + var coverage = new Coverage(testModule, Array.Empty(), Array.Empty()); coverage.PrepareModules(); var result = coverage.GetCoverageResult(); From 9033bc10bdba9477cdd8f73371b78d44abae6c8c Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Thu, 5 Jul 2018 22:47:28 +0100 Subject: [PATCH 61/62] move unique identifier generation to Coverage class --- src/coverlet.core/Coverage.cs | 10 +++++----- src/coverlet.core/Helpers/InstrumentationHelper.cs | 6 +++--- src/coverlet.msbuild.tasks/InstrumentationTask.cs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/coverlet.core/Coverage.cs b/src/coverlet.core/Coverage.cs index 34ffc5d49..4b56096f6 100644 --- a/src/coverlet.core/Coverage.cs +++ b/src/coverlet.core/Coverage.cs @@ -14,7 +14,7 @@ public class Coverage private string _module; private string _identifier; private string[] _filters; - private string[] _rules; + private string[] _excludes; private List _results; public string Identifier @@ -22,11 +22,11 @@ public string Identifier get { return _identifier; } } - public Coverage(string module, string[] filters, string[] rules) + public Coverage(string module, string[] filters, string[] excludes) { _module = module; _filters = filters; - _rules = rules; + _excludes = excludes; _identifier = Guid.NewGuid().ToString(); _results = new List(); } @@ -34,7 +34,7 @@ public Coverage(string module, string[] filters, string[] rules) public void PrepareModules() { string[] modules = InstrumentationHelper.GetCoverableModules(_module); - string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules); + string[] excludes = InstrumentationHelper.GetExcludedFiles(_excludes); _filters = _filters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray(); foreach (var module in modules) @@ -42,7 +42,7 @@ public void PrepareModules() if (InstrumentationHelper.IsModuleExcluded(module, _filters)) continue; - var instrumenter = new Instrumenter(module, _identifier, _filters, excludedFiles); + var instrumenter = new Instrumenter(module, _identifier, _filters, excludes); if (instrumenter.CanInstrument()) { InstrumentationHelper.BackupOriginalModule(module, _identifier); diff --git a/src/coverlet.core/Helpers/InstrumentationHelper.cs b/src/coverlet.core/Helpers/InstrumentationHelper.cs index bbe5b6ff3..1919c1363 100644 --- a/src/coverlet.core/Helpers/InstrumentationHelper.cs +++ b/src/coverlet.core/Helpers/InstrumentationHelper.cs @@ -166,15 +166,15 @@ public static bool IsTypeExcluded(string module, string type, string[] filters) public static bool IsLocalMethod(string method) => new Regex(WildcardToRegex("<*>*__*|*")).IsMatch(method); - public static string[] GetExcludedFiles(string[] rules) + public static string[] GetExcludedFiles(string[] excludes) { const string RELATIVE_KEY = nameof(RELATIVE_KEY); string parentDir = Directory.GetCurrentDirectory(); - if (rules == null || !rules.Any()) return Array.Empty(); + if (excludes == null || !excludes.Any()) return Array.Empty(); var matcherDict = new Dictionary() { { RELATIVE_KEY, new Matcher() } }; - foreach (var excludeRule in rules) + foreach (var excludeRule in excludes) { if (Path.IsPathRooted(excludeRule)) { diff --git a/src/coverlet.msbuild.tasks/InstrumentationTask.cs b/src/coverlet.msbuild.tasks/InstrumentationTask.cs index 680ed204c..0ddbc2a26 100644 --- a/src/coverlet.msbuild.tasks/InstrumentationTask.cs +++ b/src/coverlet.msbuild.tasks/InstrumentationTask.cs @@ -40,10 +40,10 @@ public override bool Execute() { try { - var rules = _excludeByFile?.Split(','); + var excludes = _excludeByFile?.Split(','); var filters = _exclude?.Split(','); - _coverage = new Coverage(_path, filters, rules); + _coverage = new Coverage(_path, filters, excludes); _coverage.PrepareModules(); } catch (Exception ex) From 9150603b3b34d4f817f08c773fe813c5e7490ce1 Mon Sep 17 00:00:00 2001 From: Oluwatoni Solarin-Sodara Date: Fri, 6 Jul 2018 23:38:49 +0100 Subject: [PATCH 62/62] bump version numbers --- coverlet.msbuild.nuspec | 2 +- src/coverlet.core/coverlet.core.csproj | 2 +- src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coverlet.msbuild.nuspec b/coverlet.msbuild.nuspec index 979fb4737..da87bdfac 100644 --- a/coverlet.msbuild.nuspec +++ b/coverlet.msbuild.nuspec @@ -2,7 +2,7 @@ coverlet.msbuild - 2.0.1 + 2.1.0 coverlet.msbuild tonerdo tonerdo diff --git a/src/coverlet.core/coverlet.core.csproj b/src/coverlet.core/coverlet.core.csproj index 58f628ab0..f14b73dd3 100644 --- a/src/coverlet.core/coverlet.core.csproj +++ b/src/coverlet.core/coverlet.core.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.1 + 3.0.0 diff --git a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj index 7bb9df822..c394fc7fc 100644 --- a/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj +++ b/src/coverlet.msbuild.tasks/coverlet.msbuild.tasks.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 2.0.1 + 2.0.2