htmlreport.java
来自「java覆盖率测试工具」· Java 代码 · 共 843 行 · 第 1/2 页
JAVA
843 行
{
SourceFileData sourceFileData = (SourceFileData)iter.next();
try
{
generateSourceFile(sourceFileData);
}
catch (IOException e)
{
LOGGER.info("Could not generate HTML file for source file "
+ sourceFileData.getName() + ": "
+ e.getLocalizedMessage());
}
}
}
private void generateSourceFile(SourceFileData sourceFileData)
throws IOException
{
if (!sourceFileData.containsInstrumentationInfo())
{
LOGGER.info("Data file does not contain instrumentation "
+ "information for the file " + sourceFileData.getName()
+ ". Ensure this class was instrumented, and this "
+ "data file contains the instrumentation information.");
}
String filename = sourceFileData.getNormalizedName() + ".html";
File file = new File(destinationDir, filename);
PrintWriter out = null;
try
{
out = IOUtil.getPrintWriter(file);
out
.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"");
out
.println(" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
out.println("<html>");
out.println("<head>");
out
.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>");
out.println("<title>Coverage Report</title>");
out
.println("<link title=\"Style\" type=\"text/css\" rel=\"stylesheet\" href=\"css/main.css\"/>");
out
.println("<script type=\"text/javascript\" src=\"js/popup.js\"></script>");
out.println("</head>");
out.println("<body>");
out.print("<h5>Coverage Report - ");
String classPackageName = sourceFileData.getPackageName();
if ((classPackageName != null) && classPackageName.length() > 0)
{
out.print(classPackageName + ".");
}
out.print(sourceFileData.getBaseName());
out.println("</h5>");
// Output the coverage summary for this class
out.println("<div class=\"separator\"> </div>");
out.println("<table class=\"report\">");
out.println(generateTableHeader("Classes in this File", false));
out.println(generateTableRowsForSourceFile(sourceFileData));
out.println("</table>");
// Output the coverage summary for methods in this class
// TODO
// Output this class's source code with syntax and coverage highlighting
out.println("<div class=\"separator\"> </div>");
out.println(generateHtmlizedJavaSource(sourceFileData));
out.println(generateFooter());
out.println("</body>");
out.println("</html>");
}
finally
{
if (out != null)
{
out.close();
}
}
}
private String generateBranchInfo(LineData lineData, String content) {
boolean hasBranch = (lineData != null) ? lineData.hasBranch() : false;
if (hasBranch)
{
StringBuffer ret = new StringBuffer();
ret.append("<a title=\"Line ").append(lineData.getLineNumber()).append(": Conditional coverage ")
.append(lineData.getConditionCoverage());
if (lineData.getConditionSize() > 1)
{
ret.append(" [each condition: ");
for (int i = 0; i < lineData.getConditionSize(); i++)
{
if (i > 0)
ret.append(", ");
ret.append(lineData.getConditionCoverage(i));
}
ret.append("]");
}
ret.append(".\">").append(content).append("</a>");
return ret.toString();
}
else
{
return content;
}
}
private String generateHtmlizedJavaSource(SourceFileData sourceFileData)
{
File sourceFile = null;
try
{
sourceFile = finder.getFileForSource(sourceFileData.getName());
}
catch (IOException e)
{
return "<p>Unable to locate " + sourceFileData.getName()
+ ". Have you specified the source directory?</p>";
}
BufferedReader br = null;
try
{
br = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile), "UTF-8"));
}
catch (UnsupportedEncodingException e)
{
return "<p>Unable to open " + sourceFile.getAbsolutePath()
+ ": The encoding 'UTF-8' is not supported by your JVM.</p>";
}
catch (FileNotFoundException e)
{
return "<p>Unable to open " + sourceFile.getAbsolutePath() + "</p>";
}
StringBuffer ret = new StringBuffer();
ret
.append("<table cellspacing=\"0\" cellpadding=\"0\" class=\"src\">\n");
try
{
String lineStr;
JavaToHtml javaToHtml = new JavaToHtml();
int lineNumber = 1;
while ((lineStr = br.readLine()) != null)
{
ret.append("<tr>");
if (sourceFileData.isValidSourceLineNumber(lineNumber))
{
LineData lineData = sourceFileData.getLineCoverage(lineNumber);
ret.append(" <td class=\"numLineCover\"> "
+ lineNumber + "</td>");
if ((lineData != null) && (lineData.isCovered()))
{
ret.append(" <td class=\"nbHitsCovered\">"
+ generateBranchInfo(lineData, " " + ((lineData != null) ? lineData.getHits() : 0))
+ "</td>");
ret
.append(" <td class=\"src\"><pre class=\"src\"> "
+ generateBranchInfo(lineData, javaToHtml.process(lineStr))
+ "</pre></td>");
}
else
{
ret.append(" <td class=\"nbHitsUncovered\">"
+ generateBranchInfo(lineData, " " + ((lineData != null) ? lineData.getHits() : 0))
+ "</td>");
ret
.append(" <td class=\"src\"><pre class=\"src\"><span class=\"srcUncovered\"> "
+ generateBranchInfo(lineData, javaToHtml.process(lineStr))
+ "</span></pre></td>");
}
}
else
{
ret.append(" <td class=\"numLine\"> " + lineNumber
+ "</td>");
ret.append(" <td class=\"nbHits\"> </td>\n");
ret.append(" <td class=\"src\"><pre class=\"src\"> "
+ javaToHtml.process(lineStr) + "</pre></td>");
}
ret.append("</tr>\n");
lineNumber++;
}
}
catch (IOException e)
{
ret.append("<tr><td>Error reading from file "
+ sourceFile.getAbsolutePath() + ": "
+ e.getLocalizedMessage() + "</td></tr>\n");
}
finally
{
try
{
br.close();
}
catch (IOException e)
{
}
}
ret.append("</table>\n");
return ret.toString();
}
private static String generateFooter()
{
return "<div class=\"footer\">Report generated by "
+ "<a href=\"http://cobertura.sourceforge.net/\" target=\"_top\">Cobertura</a> "
+ Header.version() + " on "
+ DateFormat.getInstance().format(new Date()) + ".</div>";
}
private static String generateTableHeader(String title,
boolean showColumnForNumberOfClasses)
{
StringBuffer ret = new StringBuffer();
ret.append("<thead>");
ret.append("<tr>");
ret.append(" <td class=\"heading\">" + title + "</td>");
if (showColumnForNumberOfClasses)
{
ret.append(" <td class=\"heading\"># Classes</td>");
}
ret.append(" <td class=\"heading\">"
+ generateHelpURL("Line Coverage",
"The percent of lines executed by this test run.")
+ "</td>");
ret.append(" <td class=\"heading\">"
+ generateHelpURL("Branch Coverage",
"The percent of branches executed by this test run.")
+ "</td>");
ret
.append(" <td class=\"heading\">"
+ generateHelpURL(
"Complexity",
"Average McCabe's cyclomatic code complexity for all methods. This is basically a count of the number of different code paths in a method (incremented by 1 for each if statement, while loop, etc.)")
+ "</td>");
ret.append("</tr>");
ret.append("</thead>");
return ret.toString();
}
private static String generateHelpURL(String text, String description)
{
StringBuffer ret = new StringBuffer();
boolean popupTooltips = false;
if (popupTooltips)
{
ret
.append("<a class=\"hastooltip\" href=\"help.html\" onclick=\"popupwindow('help.html'); return false;\">");
ret.append(text);
ret.append("<span>" + description + "</span>");
ret.append("</a>");
}
else
{
ret
.append("<a class=\"dfn\" href=\"help.html\" onclick=\"popupwindow('help.html'); return false;\">");
ret.append(text);
ret.append("</a>");
}
return ret.toString();
}
private String generateTableRowForTotal()
{
StringBuffer ret = new StringBuffer();
double ccn = complexity.getCCNForProject(projectData);
ret.append(" <tr>");
ret.append("<td><b>All Packages</b></td>");
ret.append("<td class=\"value\">"
+ projectData.getNumberOfSourceFiles() + "</td>");
ret.append(generateTableColumnsFromData(projectData, ccn));
ret.append("</tr>");
return ret.toString();
}
private String generateTableRowForPackage(PackageData packageData)
{
StringBuffer ret = new StringBuffer();
String url1 = "frame-summary-" + packageData.getName() + ".html";
String url2 = "frame-sourcefiles-" + packageData.getName() + ".html";
double ccn = complexity.getCCNForPackage(packageData);
ret.append(" <tr>");
ret.append("<td><a href=\"" + url1
+ "\" onclick='parent.sourceFileList.location.href=\"" + url2
+ "\"'>" + generatePackageName(packageData) + "</a></td>");
ret.append("<td class=\"value\">" + packageData.getNumberOfChildren()
+ "</td>");
ret.append(generateTableColumnsFromData(packageData, ccn));
ret.append("</tr>");
return ret.toString();
}
private String generateTableRowsForSourceFile(SourceFileData sourceFileData)
{
StringBuffer ret = new StringBuffer();
String sourceFileName = sourceFileData.getNormalizedName();
// TODO: ccn should be calculated per-class, not per-file
double ccn = complexity.getCCNForSourceFile(sourceFileData);
Iterator iter = sourceFileData.getClasses().iterator();
while (iter.hasNext())
{
ClassData classData = (ClassData)iter.next();
ret
.append(generateTableRowForClass(classData, sourceFileName,
ccn));
}
return ret.toString();
}
private String generateTableRowForClass(ClassData classData,
String sourceFileName, double ccn)
{
StringBuffer ret = new StringBuffer();
ret.append(" <tr>");
// TODO: URL should jump straight to the class (only for inner classes?)
ret.append("<td><a href=\"" + sourceFileName
+ ".html\">" + classData.getBaseName() + "</a></td>");
ret.append(generateTableColumnsFromData(classData, ccn));
ret.append("</tr>\n");
return ret.toString();
}
/**
* Return a string containing three HTML table cells. The first
* cell contains a graph showing the line coverage, the second
* cell contains a graph showing the branch coverage, and the
* third cell contains the code complexity.
*
* @param ccn The code complexity to display. This should be greater
* than 1.
* @return A string containing the HTML for three table cells.
*/
private static String generateTableColumnsFromData(CoverageData coverageData,
double ccn)
{
int numLinesCovered = coverageData.getNumberOfCoveredLines();
int numLinesValid = coverageData.getNumberOfValidLines();
int numBranchesCovered = coverageData.getNumberOfCoveredBranches();
int numBranchesValid = coverageData.getNumberOfValidBranches();
// The "hidden" CSS class is used below to write the ccn without
// any formatting so that the table column can be sorted correctly
return "<td>" + generatePercentResult(numLinesCovered, numLinesValid)
+"</td><td>"
+ generatePercentResult(numBranchesCovered, numBranchesValid)
+ "</td><td class=\"value\"><span class=\"hidden\">"
+ ccn + ";</span>" + getDoubleValue(ccn) + "</td>";
}
/**
* This is crazy complicated, and took me a while to figure out,
* but it works. It creates a dandy little percentage meter, from
* 0 to 100.
* @param dividend The number of covered lines or branches.
* @param divisor The number of valid lines or branches.
* @return A percentage meter.
*/
private static String generatePercentResult(int dividend, int divisor)
{
StringBuffer sb = new StringBuffer();
sb.append("<table cellpadding=\"0px\" cellspacing=\"0px\" class=\"percentgraph\"><tr class=\"percentgraph\"><td align=\"right\" class=\"percentgraph\" width=\"40\">");
if (divisor > 0)
sb.append(getPercentValue((double)dividend / divisor));
else
sb.append(generateHelpURL(
"N/A",
"Line coverage and branch coverage will appear as \"Not Applicable\" when Cobertura can not find line number information in the .class file. This happens for stub and skeleton classes, interfaces, or when the class was not compiled with \"debug=true.\""));
sb.append("</td><td class=\"percentgraph\"><div class=\"percentgraph\">");
if (divisor > 0)
{
sb.append("<div class=\"greenbar\" style=\"width:"
+ (dividend * 100 / divisor) + "px\">");
sb.append("<span class=\"text\">");
sb.append(dividend);
sb.append("/");
sb.append(divisor);
}
else
{
sb.append("<div class=\"na\" style=\"width:100px\">");
sb.append("<span class=\"text\">");
sb.append(generateHelpURL(
"N/A",
"Line coverage and branch coverage will appear as \"Not Applicable\" when Cobertura can not find line number information in the .class file. This happens for stub and skeleton classes, interfaces, or when the class was not compiled with \"debug=true.\""));
}
sb.append("</span></div></div></td></tr></table>");
return sb.toString();
}
private static String getDoubleValue(double value)
{
NumberFormat formatter;
formatter = new DecimalFormat();
return formatter.format(value);
}
private static String getPercentValue(double value)
{
return StringUtil.getPercentValue(value);
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?