This project has moved. For the latest updates, please go here.

Feedback on beta 5

Jun 27, 2014 at 8:49 AM
Edited Jun 27, 2014 at 9:00 AM
Hello,

I downloaded the beta 5 because I want to use the new custom report feature. That's great :)
I would like to create an Html report that includes only the assembly coverage.
Instead of writing my own report class, I would like to reuse HtmlRenderer, but I cannot do it without modifying ReportGenerator's code a bit, because some classes are not public.
In fact, I had to do the following changes to the source code to be able to write my custom report :
  • Make SummaryResult constructor public
  • Make RenderBase public
  • HtmlRenderer :
    • make it public
    • make constructor public
    • make reportTextWriter protected
    • make public methods virtual
Do you think these changes can be included in next release ?

Thanks !
Coordinator
Jun 27, 2014 at 10:15 AM
Hi pbruneton,

thank you very much for you feedback.

I will give you some explanations why some classes are not public.

I introduced the interface IReportBuilder for custom reports. This interface is very generic and will (most likely) not change in the future.
The HtmlRenderer and the other renderers are not public.
The reason for that is: They contain a lot of methods that are specific for the default reports of ReportGenerator. If I make a little change to one of these methods (which can easily happen if any report changes), your reports may look bad if you override some of the methods.

But I could make RendererBase public, it contains only some helper functions which may be useful for others.

Why do you want the constructor of SummaryResult to be public? This object is constructed by the core of ReportGenerator. Why do you have to construct it yourself?

I have two recommendations for you:
1) Copy HtmlRenderer and adjust it to make the report look like you want.
If I make changes to HtmlRenderer your report will not look bad.

2) In your case you don't really need a custom renderer.
You could just use the regular report and collapse the assemblies. It's a single click on "Collapse all" and you are done:
Image
Jun 27, 2014 at 11:37 AM
Edited Jun 27, 2014 at 12:34 PM
Thanks for your answer.

I use the constructor of SummaryResult, because I want to sort Assemblies on their coverage quota.
So I do the following:
public override void CreateSummaryReport(SummaryResult summaryResult)
        {
            var sortedAssemblies = summaryResult.Assemblies.ToList();
            sortedAssemblies.Sort(new AssemblyComparer());
            summaryResult = new SummaryResult(sortedAssemblies, summaryResult.UsedParser);

            using (var renderer = new ShortHtmlRenderer())
            {
                CreateSummaryReport(renderer, summaryResult);
            }
        }
I think it's not a problem for me that an update from you in HtmlRenderer breaks my code. I will just update my code too then.
I think this is better that copy pasting all code from HtmlRenderer. Plus I would also need to copy the css resources I think.

I tried to use the default renderer as you suggest, but since my report is very big (400MB of open cover xml report, 270 reports), the HTML report is not usable. I have a button "Enable filtering" instead of the links "Collapse/Expand" but the javascript takes forever if I click the button.

My renderer is very simple:
public class ShortHtmlRenderer: HtmlRenderer
    {
        public ShortHtmlRenderer() : base(true)
        {
        }

        public override void SummaryClass(Class @class)
        {
        }

        public override void CustomSummary(IEnumerable<Assembly> assemblies)
        {
        }
    }
Edit: I realize I don't need access to HtmlRenderer.reportTextWriter
Coordinator
Jun 27, 2014 at 8:14 PM
I think it's much easier to create the desired report with minimal effort. I don't have to make any changes to ReportGenerator. You just have to create a single class that inherits from HtmlReportBuilder.

Please try the following code:
using System;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Linq;
using Palmmedia.ReportGenerator.Parser.Analysis;
using Palmmedia.ReportGenerator.Properties;
using Palmmedia.ReportGenerator.Reporting;
using Palmmedia.ReportGenerator.Reporting.Rendering;

namespace Test
{
    [Export(typeof(IReportBuilder))]
    public class CustomHtmlReportBuilder : HtmlReportBuilder
    {
        public override void CreateSummaryReport(IReportRenderer reportRenderer, SummaryResult summaryResult)
        {
            if (reportRenderer == null)
            {
                throw new ArgumentNullException("reportRenderer");
            }

            if (summaryResult == null)
            {
                throw new ArgumentNullException("summaryResult");
            }

            reportRenderer.BeginSummaryReport(this.TargetDirectory, ReportResources.Summary);
            reportRenderer.Header(ReportResources.Summary);

            reportRenderer.BeginKeyValueTable();
            reportRenderer.KeyValueRow(ReportResources.GeneratedOn, DateTime.Now.ToShortDateString() + " - " + DateTime.Now.ToLongTimeString());
            reportRenderer.KeyValueRow(ReportResources.Parser, summaryResult.UsedParser);
            reportRenderer.KeyValueRow(ReportResources.Assemblies2, summaryResult.Assemblies.Count().ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.Classes, summaryResult.Assemblies.SelectMany(a => a.Classes).Count().ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.Files2, summaryResult.Assemblies.SelectMany(a => a.Classes).SelectMany(a => a.Files).Distinct().Count().ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.Coverage2, summaryResult.CoverageQuota.HasValue ? summaryResult.CoverageQuota.Value.ToString(CultureInfo.InvariantCulture) + "%" : string.Empty);
            reportRenderer.KeyValueRow(ReportResources.CoveredLines, summaryResult.CoveredLines.ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.UncoveredLines, (summaryResult.CoverableLines - summaryResult.CoveredLines).ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.CoverableLines, summaryResult.CoverableLines.ToString(CultureInfo.InvariantCulture));
            reportRenderer.KeyValueRow(ReportResources.TotalLines, summaryResult.TotalLines.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
            reportRenderer.FinishTable();

            reportRenderer.Header(ReportResources.Assemblies);

            if (summaryResult.Assemblies.Any())
            {
                reportRenderer.BeginSummaryTable();

                foreach (var assembly in summaryResult.Assemblies.OrderBy(a => a.CoverageQuota))
                {
                    reportRenderer.SummaryAssembly(assembly);
                }

                reportRenderer.FinishTable();
            }
            else
            {
                reportRenderer.Paragraph(ReportResources.NoCoveredAssemblies);
            }

            reportRenderer.SaveSummaryReport(this.TargetDirectory);
        }
    }
}
This class does:
  • Sort the assemblies by coverage
  • Exclude classes from summary report (SummaryClass)
  • Exclude Javascript from summary report (CustomSummary)
Some additional notes:
  • Adjusting to a new HtmlRenderer when I make changes may not be a problem for you, but others may complain if I break their code.
  • The button "Enable filtering" is not perfect. I have to investigate the Javascript code and make it faster for large reports. Thanks for that hint.
Jun 30, 2014 at 8:27 AM
This is working, thanks for the help :)
I alos overrided CreateClassReport to avoid the generation of additional files.
The only missing thing is to be able to sort from the HTML page !
Coordinator
Aug 6, 2014 at 8:04 PM
Hi,
you may want to test beta 6 with your reports. Filtering is now much faster.
It would be great, if you let me know if this works for you.