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);
        }
        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("&nbsp;");
                    }

                    htmlWriter.Write("</div>");
                    htmlWriter.WriteLine("</div>");
                }

                htmlWriter.WriteLine("</div>");
                htmlWriter.WriteLine("</body>");
                htmlWriter.WriteLine("</html>");
            }
        }
Beispiel #4
0
        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)
                            : "&nbsp;";

                        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>");
                }
            }
        }