private static XElement CreateLineElement(int line, IEnumerable <InstrumentedSequence> instructions, HitsInfo hitsInfo) { var conditions = instructions .SelectMany(i => i.Conditions) .ToArray(); var allBranches = conditions .SelectMany(c => c.Branches) .Count(); var coveredBranches = conditions .SelectMany(c => c.Branches) .Where(b => hitsInfo.WasHit(b.HitId)) .Count(); var hits = instructions.Sum(i => hitsInfo.GetHitCount(i.HitId)); var conditionCoverage = allBranches == 0 ? 0 : coveredBranches * 100 / allBranches; return(new XElement( XName.Get("line"), new XAttribute(XName.Get("number"), line), new XAttribute(XName.Get("hits"), hits), new XAttribute(XName.Get("branch"), allBranches > 0 ? "true" : "false"), new XAttribute(XName.Get("condition-coverage"), $"{conditionCoverage}% ({coveredBranches}/{allBranches})"), allBranches > 0 ? CreateConditionsElements(conditions, hitsInfo) : null )); }
public void ShouldMergeTests() { var contexts = new[] { new HitContext( "Sample.UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Sample.UnitTests.UnitTest1", "XUnitTest2", new Dictionary <int, int> { { 8, 1 }, }), new HitContext( "Sample.UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Sample.UnitTests.UnitTest1", "XUnitTest2", new Dictionary <int, int> { { 8, 1 }, }) }; var hits = new HitsInfo(contexts); hits.GetHitCount(8).Should().Be(2); hits.GetHitContexts(8).Should().HaveCount(1); hits.GetHitContexts(8).First().GetHitCount(8).Should().Be(2); }
private static IEnumerable <XElement> CreateLinesElement(IEnumerable <InstrumentedSequence> instructions, HitsInfo hits) { return(instructions .SelectMany(t => t.GetLines(), (i, l) => new { instructionId = i.HitId, line = l }) .Select(instruction => new XElement( XName.Get("line"), new XAttribute(XName.Get("num"), instruction.line), new XAttribute(XName.Get("count"), hits.GetHitCount(instruction.instructionId)), new XAttribute(XName.Get("type"), "stmt") ))); }
public void ShouldMergeTestsCorrectly() { var contexts = new[] { new HitContext( "Sample.UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Sample.UnitTests.UnitTest1", "XUnitTest2", new Dictionary <int, int> { { 17, 2500000 }, { 19, 2500000 }, { 20, 50 }, { 21, 2500000 }, { 22, 2500000 }, { 23, 2500050 }, { 24, 50 }, { 33, 1 }, { 34, 1 }, { 35, 1 }, { 37, 1 }, { 38, 50 }, { 39, 50 }, { 40, 51 } }), new HitContext( "Sample.UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Sample.UnitTests.UnitTest1", "NUnitTest2", new Dictionary <int, int> { { 9, 1 }, { 10, 1 }, { 11, 1 }, { 13, 1 }, { 14, 50 }, { 15, 50 }, { 16, 51 }, { 17, 2500000 }, { 19, 2500000 }, { 20, 50 }, { 21, 2500000 }, { 22, 2500000 }, { 23, 2500050 }, { 24, 50 } }) }; var hits = new HitsInfo(contexts); hits.GetHitCount(17).Should().Be(5000000); hits.GetHitContexts(17).Count().Should().Be(2); hits.GetHitContexts(17).First().GetHitCount(17).Should().Be(2500000); }
public void Generate( InstrumentationResult result, SourceFile sourceFile, HitsInfo hitsInfo, float threshold, string outputFile) { var lines = File.ReadAllLines(Path.Combine(result.SourcePath, sourceFile.Path)); _fileSystem.Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); var summary = _summaryFactory.CalculateFilesSummary(new[] { sourceFile }, hitsInfo, threshold); var lineCoverageClass = summary.LinesCoveragePass ? "green" : "red"; var statementsCoverageClass = summary.StatementsCoveragePass ? "green" : "red"; var branchCoverageClass = summary.BranchesCoveragePass ? "green" : "red"; using (var htmlWriter = (TextWriter)File.CreateText(outputFile)) { htmlWriter.WriteLine("<html>"); htmlWriter.WriteLine("<style>"); htmlWriter.WriteLine(ResourceUtils.GetContent("MiniCover.Reports.Html.Shared.css")); htmlWriter.WriteLine(ResourceUtils.GetContent("MiniCover.Reports.Html.SourceFile.css")); htmlWriter.WriteLine("</style>"); htmlWriter.WriteLine("<script>"); htmlWriter.WriteLine(ResourceUtils.GetContent("MiniCover.Reports.Html.Shared.js")); htmlWriter.WriteLine("</script>"); htmlWriter.WriteLine("<body>"); htmlWriter.WriteLine("<h2>Summary</h2>"); htmlWriter.WriteLine("<table>"); htmlWriter.WriteLine($"<tr><th>Generated on</th><td>{DateTime.Now}</td></tr>"); htmlWriter.WriteLine($"<tr><th>Line Coverage</th><td class=\"{lineCoverageClass}\">{summary.LinesPercentage:P} ({summary.CoveredLines}/{summary.Lines})</td></tr>"); htmlWriter.WriteLine($"<tr><th>Statements Coverage</th><td class=\"{branchCoverageClass}\">{summary.StatementsPercentage:P} ({summary.CoveredStatements}/{summary.Statements})</td></tr>"); htmlWriter.WriteLine($"<tr><th>Branch Coverage</th><td class=\"{branchCoverageClass}\">{summary.BranchesPercentage:P} ({summary.CoveredBranches}/{summary.Branches})</td></tr>"); htmlWriter.WriteLine($"<tr><th>Threshold</th><td>{threshold:P}</td></tr>"); htmlWriter.WriteLine("</table>"); htmlWriter.WriteLine("<h2>Code</h2>"); htmlWriter.WriteLine("<div class=\"legend\">"); htmlWriter.Write("<label>Legend:</label>"); htmlWriter.Write("<div class=\"hit\">Covered</div>"); htmlWriter.Write("<div class=\"partial\">Partially covered</div>"); htmlWriter.Write("<div class=\"not-hit\">Not covered</div>"); htmlWriter.WriteLine("</div>"); htmlWriter.WriteLine("<div class=\"code\">"); for (var l = 1; l <= lines.Length; l++) { var line = lines[l - 1]; var instructions = sourceFile.Sequences .Where(i => i.GetLines().Contains(l)) .ToArray(); var lineHitCount = instructions.Sum(a => hitsInfo.GetHitCount(a.HitId)); var lineClasses = new List <string> { "line" }; if (lineHitCount > 0) { if (instructions.Any(i => !hitsInfo.WasHit(i.HitId) || i.Conditions.SelectMany(x => x.Branches).Any(b => !hitsInfo.WasHit(b.HitId)))) { lineClasses.Add("partial"); } else { lineClasses.Add("hit"); } } else if (instructions.Length > 0) { lineClasses.Add("not-hit"); } htmlWriter.Write($"<div class=\"{string.Join(" ", lineClasses)}\">"); htmlWriter.Write($"<div class=\"line-number\">{l}</div>"); htmlWriter.Write("<div class=\"line-content\">"); if (line.Length > 0) { for (var c = 1; c <= line.Length; c++) { var character = line[c - 1].ToString(); foreach (var instruction in instructions) { if (instruction.StartLine == l && instruction.StartColumn == c || instruction.StartLine < l && c == 1) { var statementIdClass = $"s-{instruction.HitId}"; var statementClasses = new List <string> { "statement", statementIdClass }; if (hitsInfo.WasHit(instruction.HitId)) { statementClasses.Add("hit"); if (instruction.Conditions.SelectMany(x => x.Branches).Any(b => !hitsInfo.WasHit(b.HitId))) { statementClasses.Add("partial"); } } else { statementClasses.Add("not-hit"); } htmlWriter.Write($"<div data-hover-target=\".{statementIdClass}\" data-activate-target=\".{statementIdClass}\" class=\"{string.Join(" ", statementClasses)}\">"); if (instruction.EndLine == l) { var hitCount = hitsInfo.GetHitCount(instruction.HitId); var contexts = hitsInfo.GetHitContexts(instruction.HitId) .Distinct() .ToArray(); htmlWriter.Write($"<div class=\"statement-info {statementIdClass}\">"); htmlWriter.Write($"<div>Id: {instruction.HitId}</div>"); htmlWriter.Write($"<div>Hits: {hitCount}</div>"); if (instruction.Conditions.Length > 0) { var conditionIndex = 0; foreach (var condition in instruction.Conditions) { htmlWriter.Write($"<div>Condition {++conditionIndex}:"); htmlWriter.Write("<ul>"); var branchIndex = 0; foreach (var branch in condition.Branches) { var branchHitCount = hitsInfo.GetHitCount(branch.HitId); htmlWriter.Write($"<li>Branch {++branchIndex}: {FormatHits(branchHitCount)}</li>"); } htmlWriter.Write("</ul>"); htmlWriter.Write("</div>"); } } if (contexts.Length > 0) { htmlWriter.Write("<div>Contexts:"); htmlWriter.Write("<ul>"); foreach (var context in contexts) { var contextHitCount = context.GetHitCount(instruction.HitId); var description = $"{context.ClassName}.{context.MethodName}"; htmlWriter.Write($"<li>{WebUtility.HtmlEncode(description)}: {FormatHits(contextHitCount)}</li>"); } htmlWriter.Write("</ul></div>"); } htmlWriter.Write("</div>"); } } } htmlWriter.Write(WebUtility.HtmlEncode(character)); foreach (var instruction in instructions) { if (instruction.EndLine == l && instruction.EndColumn == c + 1 || instruction.EndLine > l && c == line.Length) { htmlWriter.Write("</div>"); } } } } else { htmlWriter.WriteLine(" "); } htmlWriter.Write("</div>"); htmlWriter.WriteLine("</div>"); } htmlWriter.WriteLine("</div>"); htmlWriter.WriteLine("</body>"); htmlWriter.WriteLine("</html>"); } }
protected override void WriteDetailedReport(InstrumentationResult result, IDictionary <string, SourceFile> files, HitsInfo hitsInfo) { foreach (var kvFile in files) { var lines = File.ReadAllLines(Path.Combine(result.SourcePath, kvFile.Key)); var fileName = GetHtmlFileName(kvFile.Key); Directory.CreateDirectory(Path.GetDirectoryName(fileName)); using (var htmlWriter = (TextWriter)File.CreateText(fileName)) { htmlWriter.WriteLine("<html>"); htmlWriter.WriteLine("<style>"); htmlWriter.WriteLine("details summary::-webkit-details-marker {"); htmlWriter.WriteLine("display: none;"); htmlWriter.WriteLine("}"); htmlWriter.WriteLine("</style>"); htmlWriter.WriteLine("<body style=\"font-family: monospace;\">"); var uncoveredLineNumbers = new HashSet <int>(); var coveredLineNumbers = new HashSet <int>(); foreach (var i in kvFile.Value.Sequences) { if (hitsInfo.WasHit(i.HitId)) { coveredLineNumbers.UnionWith(i.GetLines()); } else { uncoveredLineNumbers.UnionWith(i.GetLines()); } } var l = 0; foreach (var line in lines) { l++; var style = "white-space: pre;"; if (coveredLineNumbers.Contains(l)) { style += BgColorGreen; style += CursorPointer; } else if (uncoveredLineNumbers.Contains(l)) { style += BgColorRed; } else { style += BgColorNeutral; } var instructions = kvFile.Value.Sequences .Where(i => i.GetLines().Contains(l)) .ToArray(); var counter = instructions.Sum(a => hitsInfo.GetHitCount(a.HitId)); var codeContent = !string.IsNullOrEmpty(line) ? WebUtility.HtmlEncode(line) : " "; var contexts = instructions .SelectMany(i => hitsInfo.GetHitContexts(i.HitId)) .Distinct() .ToArray(); var hitCountHtml = coveredLineNumbers.Contains(l) || uncoveredLineNumbers.Contains(l) ? $"<span style=\"display: inline-block; width: 30px; font-size: 10px;\">{counter}x</span>" : "<span style=\"display: inline-block; width: 30px; font-size: 10px;\"></span>"; if (coveredLineNumbers.Contains(l)) { htmlWriter.WriteLine($"<details>"); htmlWriter.WriteLine($"<summary style=\"{style}\">{hitCountHtml}{codeContent}</summary>"); htmlWriter.WriteLine("<ul>"); foreach (var context in contexts) { var count = instructions.Sum(i => context.GetHitCount(i.HitId)); var description = $"{context.ClassName}.{context.MethodName}"; htmlWriter.WriteLine($"<li>{WebUtility.HtmlEncode(description)}: {count}x</li>"); } htmlWriter.WriteLine("</ul>"); htmlWriter.WriteLine($"</details>"); } else { htmlWriter.WriteLine($"<div style=\"{style}\">{hitCountHtml}{codeContent}</div>"); } } htmlWriter.WriteLine("</body>"); htmlWriter.WriteLine("</html>"); } } }