com.googlecode.jgenhtml.CoverageReport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jgenhtml Show documentation
Show all versions of jgenhtml Show documentation
This tool is a java implementation of lcov's genhtml tool,
primarily intended for use with the output fileproduced by JsTestDriver's coverage plugin
but could be used anywhere genhtml is used.
/*
Copyright (C) 2012 Rick Brown
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
package com.googlecode.jgenhtml;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
/**
* Represents the entire coverage report.
* Knows about the generation lifecycle of the whole report.
* @author Rick Brown
*/
public final class CoverageReport
{
private static final Logger LOGGER = Logger.getLogger(CoverageReport.class.getName());
private static Config config = null;
public static final String DEFAULT_TEST_NAME = "";
private String testTitle;
private String[] traceFiles;
private ParsedFiles parsedFiles;
private DescriptionsPage descriptionsPage;
private Collection indexPages;
private Set runTestNames;
/**
* Create a new report based off these tracefiles.
* @param traceFiles
* @throws IOException
* @throws ParserConfigurationException
*/
public CoverageReport(final String[] traceFiles) throws IOException, ParserConfigurationException
{
if(config == null)
{
config = new Config();
}
this.traceFiles = traceFiles;
this.descriptionsPage = null;
this.indexPages = null;
this.runTestNames = null;
this.testTitle = null;
this.parsedFiles = new ParsedFiles();
processTraceFiles();
checkProcessBaselineFile(config.getBaseFile());
checkGenerateDescriptions(config.getDescFile());
removePrefix();//check if there is a common prefix and strip it
}
/**
* Stores information about all the source code files parsed from the tracefile.
* Basically a registry to manage parsed source file data.
* Should be a singleton.
*/
private class ParsedFiles
{
private Map parsedFiles;
public ParsedFiles()
{
this.parsedFiles = new HashMap();
}
public TestCaseSourceFile get(final String filePath)
{
TestCaseSourceFile result = null;
if(parsedFiles.containsKey(filePath))
{
result = parsedFiles.get(filePath);
}
return result;
}
public TestCaseSourceFile put(final String filePath, final TestCaseSourceFile parsedFile)
{
return parsedFiles.put(filePath, parsedFile);
}
public Collection getAll()
{
return this.parsedFiles.values();
}
public int getCount()
{
return this.parsedFiles.size();
}
}
public int getPageCount()
{
return this.parsedFiles.getCount();
}
public void setDescriptionsPage(final DescriptionsPage descriptionsPage)
{
this.descriptionsPage = descriptionsPage;
}
/**
* Process LCOV tracefiles.
*/
public void processTraceFiles() throws IOException, ParserConfigurationException
{
for(int i=0, len=traceFiles.length; i= 0 || (tokenIdx = line.indexOf("KF:")) >= 0)
{
String fullPath = line.substring(line.indexOf(tokenIdx) + 4);
File sourceFile = new File(fullPath);
fullPath = sourceFile.getCanonicalPath();
testCaseSourceFile = parsedFiles.get(fullPath);
if(!isBaseFile && testCaseSourceFile == null)
{
testCaseSourceFile = new TestCaseSourceFile(testTitle, sourceFile.getName());
testCaseSourceFile.setSourceFile(sourceFile);
parsedFiles.put(fullPath, testCaseSourceFile);
}
}
else if(line.indexOf("end_of_record") >= 0)
{
if(testCaseSourceFile != null)
{
testCaseName = DEFAULT_TEST_NAME;
testCaseSourceFile = null;
}
else
{
LOGGER.log(Level.FINE, "Unexpected end of record");
}
}
else if(testCaseSourceFile != null)
{
testCaseSourceFile.processLine(testCaseName, line, isBaseFile);
}
else
{
if(isDescFile)
{
descriptionsPage.addLine(line);
}
else if(line.startsWith("TN:"))
{
String[] data = JGenHtmlUtils.extractLineValues(line);
testCaseName = data[0].trim();
if(testCaseName.length() > 0)
{
if(runTestNames == null)
{
runTestNames = new HashSet();
}
runTestNames.add(testCaseName);
}
}
else
{
LOGGER.log(Level.FINE, "Unexpected line: {0}", line);
}
}
}
}
finally
{
LineIterator.closeQuietly(iterator);
}
}
/**
* Write the coverage reports to the file system.
* @param testCaseSourceFiles The source files we are processing.
*/
public void generateReports() throws IOException, ParserConfigurationException
{
try
{
LOGGER.log(Level.INFO, "Generating output at {0}", config.getOutRootDir().getAbsolutePath());
Line.setTabExpand(config.getNumSpaces());
generateCoverageReports();
generateIndexFiles();
generateResources();
generateDescriptionPage();
TopLevelIndexPage index = new TopLevelIndexPage(testTitle, indexPages);
LOGGER.log(Level.INFO, "Writing directory view page.");
try
{
LOGGER.log(Level.INFO, "Overall coverage rate:");
logSummary("lines", index.getLineRate(), index.getLineHit(), index.getLineCount());
logSummary("functions", index.getFunctionRate(), index.getFuncHit(), index.getFuncCount());
logSummary("branches", index.getBranchRate(), index.getBranchHit(), index.getBranchCount());
}
catch(Throwable t)
{
//don't die if there is an exception in logging
LOGGER.log(Level.WARNING, t.getLocalizedMessage());
}
index.writeToFileSystem();
}
catch (TransformerConfigurationException ex)
{
LOGGER.log(Level.SEVERE, ex.getLocalizedMessage());
}
catch (TransformerException ex)
{
LOGGER.log(Level.SEVERE, ex.getLocalizedMessage());
}
}
/**
* Log a summary of the coverage information.
* @param prefix The type of coverage: "lines", "functions" or "branches".
* @param rate The coverage rate.
* @param hit The execution count.
* @param count The executable count.
*/
private static void logSummary(final String type, final float rate, final int hit, final int count)
{
String prefix = String.format("%1$-11s", type);
prefix = prefix.replace(" ", ".");
if(count > 0)
{
String[] info = new String[]{prefix, String.valueOf(rate * 100), String.valueOf(hit), String.valueOf(count), type};
LOGGER.log(Level.INFO, "\t{0}: {1}% ({2} of {3} {4})", info);
}
else
{
LOGGER.log(Level.INFO, "\t{0}: no data found", type);
}
}
/**
* Generates required resources in the output directory (CSS etc).
*/
private static void generateResources() throws IOException
{
File outRootDir = config.getOutRootDir();
File docsRootDir;
if(config.isHtmlOnly())
{
generateResourcesInDocRoot(outRootDir, false);
}
else
{
docsRootDir = JGenHtmlUtils.getTargetDir(outRootDir, false);
generateResourcesInDocRoot(docsRootDir, false);
docsRootDir = JGenHtmlUtils.getTargetDir(outRootDir, true);
generateResourcesInDocRoot(docsRootDir, true);
JGenHtmlUtils.writeResource("index.html", outRootDir);
}
}
private static void generateResourcesInDocRoot(final File docRootDir, final boolean asXml) throws IOException
{
File cssFile = config.getCssFile();
JGenHtmlUtils.writeResource(JGenHtmlUtils.JS_NAME, docRootDir);
if(cssFile != null)
{
JGenHtmlUtils.writeResource(cssFile, docRootDir);
}
else
{
JGenHtmlUtils.writeResource(JGenHtmlUtils.CSS_NAME, docRootDir);
}
if(asXml)
{
JGenHtmlUtils.writeResource(JGenHtmlUtils.XSLT_NAME, docRootDir);
}
else if(config.isGzip())
{
FileUtils.writeStringToFile(new File(docRootDir,".htaccess"), "AddEncoding x-gzip .html");
}
}
private void generateDescriptionPage() throws TransformerConfigurationException, IOException, TransformerException
{
if(this.descriptionsPage != null)
{
LOGGER.log(Level.INFO, "Writing test case description file.");
this.descriptionsPage.writeToFileSystem(config.getOutRootDir(), false);
if(!config.isHtmlOnly())
{
this.descriptionsPage.writeToFileSystem(config.getOutRootDir(), true);
}
}
}
/**
* Generates index pages in output directory.
* @param indexPages The index pages to generate.
* @throws TransformerConfigurationException
* @throws TransformerException
*/
private void generateIndexFiles() throws TransformerConfigurationException, TransformerException, IOException
{
for(TestCaseIndexPage index : indexPages)
{
index.writeToFileSystem();
}
}
/**
* Generate line coverage report pages in the output directory.
* @param testCaseSourceFiles The test case source files to generate.
* @return Index pages required to reference the coverage reports.
* @throws TransformerConfigurationException
* @throws TransformerException
*/
private void generateCoverageReports() throws TransformerConfigurationException, TransformerException, IOException, ParserConfigurationException
{
Map indeces = new HashMap();
for(TestCaseSourceFile testCaseSourceFile : parsedFiles.getAll())
{
LOGGER.log(Level.INFO, "Writing report for {0}", testCaseSourceFile.getPageName());
String path = testCaseSourceFile.getPath();
if(!indeces.containsKey(path))
{
String testName = testCaseSourceFile.getTestName();
TestCaseIndexPage indexPage = new TestCaseIndexPage(testName, path);
String prefix = testCaseSourceFile.getPrefix();
if(prefix != null)
{
indexPage.setPrefix(prefix);
}
indeces.put(path, indexPage);
}
TestCaseIndexPage indexPage = indeces.get(path);
indexPage.addSourceFile(testCaseSourceFile);
testCaseSourceFile.writeToFileSystem();
}
indexPages = indeces.values();
}
/**
* For the list of source files, iterate over each of them and remove the prefix if appropriate.
* @param testCaseSourceFiles The list of source files.
*/
public void removePrefix()
{
Collection testCaseSourceFiles = parsedFiles.getAll();
String prefix = getPrefix(testCaseSourceFiles);
if(prefix != null)
{
for(TestCaseSourceFile sourceFile : testCaseSourceFiles)
{
String path = sourceFile.getPath();
int prefixLen = prefix.length();
if(path.startsWith(prefix) && path.length() > prefixLen)
{
sourceFile.setPath(path.substring(prefixLen));
sourceFile.setPrefix(prefix);
}
}
}
}
/**
* Get the prefix to remove from paths to shorten them in the index pages.
* @param testCaseSourceFiles The source files we are processing.
* @return The prefix to remove or null (if the user specified not to remove prefixes).
*/
private static String getPrefix(final Collection testCaseSourceFiles)
{
String result;
if(config.isNoPrefix())
{
LOGGER.log(Level.INFO, "User asked not to remove filename prefix");
result = null;
}
else if((result = config.getPrefix()) == null)
{
result = JGenHtmlUtils.getPrefix(testCaseSourceFiles);
if(result != null)
{
LOGGER.log(Level.INFO, "Found common filename prefix {0}", result);
}
else
{
LOGGER.log(Level.INFO, "No common filename prefix found!", result);
}
}
else
{
LOGGER.log(Level.INFO, "Using user-specified filename prefix \"{0}\'", result);
}
return result;
}
public static void setConfig(Config config)
{
CoverageReport.config = config;
}
public static Config getConfig()
{
return config;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy