/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Portions copyright 2012 ForgeRock AS. */ package org.opends.build.tools; import com.vladium.emma.report.*; import com.vladium.emma.report.html.doc.*; import com.vladium.emma.data.*; import com.vladium.util.IntObjectMap; import java.io.*; import java.util.*; import org.apache.tools.ant.Task; import org.apache.tools.ant.BuildException; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNRevision; public class CoverageDiff extends Task { private static SVNClientManager ourClientManager = SVNClientManager.newInstance(); private static final String EOL = System.getProperty("line.separator"); private boolean verbose = false; private boolean enabled = true; private final int COVERED_MOD_EXE_LINES = 0; private final int MOD_EXE_LINES = 1; private final int MOD_LINES = 2; private final int DEL_LINES = 3; private final String ENCODING = "ISO-8859-1"; private final int IO_BUF_SIZE = 32 * 1024; private final LinkedHashMap<String, SrcFileItem> emmaSrcMap = new LinkedHashMap<String, SrcFileItem>(); private final LinkedHashMap<String, Double[]> modCoverageMap = new LinkedHashMap<String, Double[]>(); private final String CSS = "TABLE,TD,TH {border-style:solid; border-color:black;} " + "TD,TH {background:white;margin:0;line-height:100%;padding-left:0.5em;padding-right:0.5em;} " + "TD {border-width:0 1px 0 0;} TH {border-width:1px 1px 1px 0;} " + "TR TD.h {color:red;} " + "TABLE {border-spacing:0; border-collapse:collapse;border-width:0 0 1px 1px;} " + "P,H1,H2,H3,TH {font-family:verdana,arial,sans-serif;font-size:10pt;} " + "TD {font-family:courier,monospace;font-size:10pt;} " + "TABLE.hdft {border-spacing:0;border-collapse:collapse;border-style:none;} " + "TABLE.hdft TH,TABLE.hdft TD {border-style:none;line-height:normal;} " + "TABLE.hdft TH.tl,TABLE.hdft TD.tl {background:#6699CC;color:white;} " + "TABLE.hdft TD.nv {background:#6633DD;color:white;} " + ".nv A:link {color:white;} .nv A:visited {color:white;} " + ".nv A:active {color:yellow;} " + "TABLE.hdft A:link {color:white;} " + "TABLE.hdft A:visited {color:white;} " + "TABLE.hdft A:active {color:yellow;} " + ".in {color:#356085;} " + "TABLE.s TD {padding-left:0.25em;padding-right:0.25em;} " + "TABLE.s TD.ddt {padding-left:0.25em;padding-right:0.25em;color:#AAAAAA;}" + "TABLE.s TD.ds {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#F0F0F0;} " + "TABLE.s TD.dm {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#BCCFF9;} " + "TABLE.s TD.dd {padding-left:0.25em;padding-right:0.25em;text-align:right;background:#AAAAAA;color:#FFFFFF} " + "TABLE.s TH {padding-left:0.25em;padding-right:0.25em;text-align:left;background:#F0F0F0;} " + "TABLE.s TD.cz {background:#FF9999;} " + "TABLE.s TD.cp {background:#FFFF88;} " + "TABLE.s TD.cc {background:#CCFFCC;} " + "A:link {color:#0000EE;text-decoration:none;} " + "A:visited {color:#0000EE;text-decoration:none;} " + "A:hover {color:#0000EE;text-decoration:underline;} " + "TABLE.cn {border-width:0 0 1px 0;} " + "TABLE.s {border-width:1px 0 1px 1px;} " + "TD.h {color:red;border-width:0 1px 0 0;} " + "TD.f {border-width:0 1px 0 1px;} " + "TD.hf {color:red;border-width:0 1px 0 1px;} " + "TH.f {border-width:1px 1px 1px 1px;} " + "TR.cis TD {background:#F0F0F0;} " + "TR.cis TD {border-width:1px 1px 1px 0;} " + "TR.cis TD.h {color:red;border-width:1px 1px 1px 0;} " + "TR.cis TD.f {border-width:1px 1px 1px 1px;} " + "TR.cis TD.hf {color:red;border-width:1px 1px 1px 1px;} " + "TD.b {border-style:none;background:transparent;line-height:50%;} " + "TD.bt {border-width:1px 0 0 0;background:transparent;line-height:50%;} " + "TR.o TD {background:#F0F0F0;}" + "TABLE.it {border-style:none;}" + "TABLE.it TD,TABLE.it TH {border-style:none;}"; private File emmaDataPath; private File outputPath; private String diffPath; // The SVN revision to perform the diff against when calculating // the coverage diff. It can be a revision number, a timestamp, // or a revision keyword (BASE, COMMITTED, and PREV make the // most sense). The primary use case for this setting is to do // a coverage diff against the previous revision when there are // no changes in the working copy. It defaults to BASE. private String fromRevision; public void setEmmaDataPath(String file) { emmaDataPath = new File(file); } public void setOutputPath(String file) { outputPath = new File(file); } public void setDiffPath(String diffArgs) { diffPath = diffArgs; } public void setVerbose(String bol) { verbose = bol.toLowerCase().equals("true"); } public void setEnabled(String bol) { enabled = bol.toLowerCase().equals("true"); } public void setFromRevision(String fromRevision) { this.fromRevision = fromRevision; } public void execute() throws BuildException { try { innerExecute(); } catch (BuildException e) { throw e; } catch (Throwable e) { e.printStackTrace(); } } private void innerExecute() throws BuildException, SVNException { long start = System.currentTimeMillis(); verboseOut("Starting to execute coveragediff."); verboseOut("diffPath='" + diffPath +"'"); if(emmaDataPath == null) { throw new BuildException("emmaDataPath attribute is not set. It must be set to the path of the EMMA data directory"); } if(outputPath == null) { throw new BuildException("outputPath attribute is not set. It must be set to a valid directory where the report will be generated"); } if(fromRevision == null) { throw new BuildException("fromRevision attribute is not set. It must be set to the revision from which the diff is generated (e.g. BASE)."); } if(!enabled) { return; } // So we can go over http:// and https:// when diff'ing against previous versions DAVRepositoryFactory.setup(); IReportDataView emmaDataView = null; try { emmaDataView = loadEmmaData(emmaDataPath); verboseOut("Loaded EMMA data."); } catch(IOException ie) { System.out.println("WARNING: An error occurred while loading EMMA " + "data. Report will not contain any coverage information."); } try { processDiffOutput(getDiffOutputReader(), emmaDataView); } catch(IOException ie) { System.out.println("ERROR: An error occurred while processing diff output: " + ie.toString() + " Quitting..."); ie.printStackTrace(); return; } System.out.println("Coverage diff completed in " + (System.currentTimeMillis() - start) + " ms."); } private IReportDataView loadEmmaData(File emmaCoverageDataDir) throws IOException { if(emmaCoverageDataDir == null) { throw new IOException("Emma Converage Data Directory is null"); } File[] emmaCoverageDataFiles = emmaCoverageDataDir.listFiles(); int emmaCoverageDataFileCount = 0; IReportDataView m_view; IMetaData mdata = null; ICoverageData cdata = null; if(emmaCoverageDataFiles == null || emmaCoverageDataFiles.length <= 0) { throw new IOException("No EMMA data files found"); } verboseOut("processing input files ..."); final long start = System.currentTimeMillis(); // merge all data files: for (final File dataFile : emmaCoverageDataFiles) { verboseOut("processing input file [" + dataFile.getAbsolutePath() + "] ..."); final IMergeable[] fileData = DataFactory.load(dataFile); final IMetaData _mdata = (IMetaData) fileData[DataFactory.TYPE_METADATA]; if (_mdata != null) { verboseOut(" loaded " + _mdata.size() + " metadata entries"); if (mdata == null) mdata = _mdata; else mdata = (IMetaData) mdata.merge(_mdata); // note: later datapath entries override earlier ones } final ICoverageData _cdata = (ICoverageData) fileData[DataFactory.TYPE_COVERAGEDATA]; if (_cdata != null) { verboseOut(" loaded " + _cdata.size() + " coverage data entries"); if (cdata == null) cdata = _cdata; else cdata = (ICoverageData) cdata.merge(_cdata); // note: later datapath entries override earlier ones } ++emmaCoverageDataFileCount; } verboseOut(emmaCoverageDataFileCount + " file(s) read and merged in " + (System.currentTimeMillis() - start) + " ms"); if ((mdata == null) || mdata.isEmpty()) { System.out.println("nothing to do: no metadata found in any of the data files"); return null; } if (cdata == null) { System.out.println("nothing to do: no runtime coverage data found in any of the data files"); return null; } if (cdata.isEmpty()) { System.out.println("no collected coverage data found in any of the data files [Diff output will not include coverage data]"); return null; } if (!mdata.hasLineNumberData() || !mdata.hasSrcFileData()) { System.out.println("no collected line coverage data found in any of the data files [Diff output will not include coverage data]"); return null; } final IReportDataModel model = IReportDataModel.Factory.create (mdata, cdata); m_view = model.getView (IReportDataView.HIER_SRC_VIEW); verboseOut(" merged metadata contains " + mdata.size() + " entries"); verboseOut(" merged coverage data contains " + cdata.size() + " entries"); return m_view; } private BufferedReader getDiffOutputReader() throws IOException, SVNException { File workspaceRoot = getProject().getBaseDir(); File diffFile = new File(outputPath, "svn.diff"); // Most often this will be 'BASE' but it could also be 'PREVIOUS' SVNRevision baseRevision = SVNRevision.parse(fromRevision); System.out.println("Doing coverage diff from revision: " + baseRevision.toString()); ourClientManager.getDiffClient().doDiff(workspaceRoot, baseRevision, workspaceRoot, SVNRevision.WORKING, SVNDepth.INFINITY, false, new FileOutputStream(diffFile), null); return new BufferedReader(new InputStreamReader(new FileInputStream( diffFile))); } private void processDiffOutput(BufferedReader diffOutput, IReportDataView emmaDataView) throws IOException { File file = new File(outputPath, "index.html"); BufferedWriter writer = new BufferedWriter (new OutputStreamWriter ( new FileOutputStream (file), ENCODING), IO_BUF_SIZE); HTMLWriter out = new HTMLWriter(writer); System.out.println("Writing report to [" + file.toString() + "]"); String title = "Coverage Diff Report (generated "; title = title + new Date(System.currentTimeMillis ()); title = title + " )"; HTMLDocument page = new HTMLDocument (title, ENCODING); page.addStyle (CSS); String line = diffOutput.readLine(); ArrayList<String> diffOutputFile = new ArrayList<String>(); while(line != null) { //Diffed file if(line.length() >6 && line.substring(0, 6).equals("Index:")) { processDiffOutputFile(page, diffOutputFile, emmaDataView); diffOutputFile = new ArrayList<String>(); diffOutputFile.add(line); } else { diffOutputFile.add(line); } line = diffOutput.readLine(); } processDiffOutputFile(page, diffOutputFile, emmaDataView); IElementList overallStats = new ElementList(); final IElement statTitle = IElement.Factory.create (Tag.Hs[1]); statTitle.setText("OVERALL STATS SUMMARY", true); overallStats.add(statTitle); final HTMLTable statsTable = new HTMLTable (null, null, null, "0"); statsTable.setClass ("it"); { HTMLTable.IRow row = statsTable.newRow (); row.newCell ().setText ("svn diff arg(s):", true); row.newCell ().setText ("" + diffPath.toString(), true); row = statsTable.newRow (); row.newCell ().setText ("total files modified:", true); row.newCell ().setText ("" + emmaSrcMap.keySet().size(), false); Double[] overallModCoverage = new Double[4]; overallModCoverage[COVERED_MOD_EXE_LINES] = 0.0; overallModCoverage[MOD_EXE_LINES] = 0.0; overallModCoverage[MOD_LINES] = 0.0; overallModCoverage[DEL_LINES] = 0.0; Double[] modCoverage; for (Double[] doubles : modCoverageMap.values()) { modCoverage = doubles; if (modCoverage != null) { overallModCoverage[COVERED_MOD_EXE_LINES] += modCoverage[COVERED_MOD_EXE_LINES]; overallModCoverage[MOD_EXE_LINES] += modCoverage[MOD_EXE_LINES]; overallModCoverage[MOD_LINES] += modCoverage[MOD_LINES]; overallModCoverage[DEL_LINES] += modCoverage[DEL_LINES]; } } String modCoverageStr = ""; if(overallModCoverage[MOD_EXE_LINES] > 0) { modCoverageStr = String.format("%d%% (%.1f/%.1f)", (int)(overallModCoverage[COVERED_MOD_EXE_LINES]/overallModCoverage[MOD_EXE_LINES]*100), overallModCoverage[COVERED_MOD_EXE_LINES], overallModCoverage[MOD_EXE_LINES]); } else { modCoverageStr = String.format("%d%% (%.1f/%.1f)", 100, overallModCoverage[COVERED_MOD_EXE_LINES], overallModCoverage[MOD_EXE_LINES]); } row = statsTable.newRow (); row.newCell ().setText ("total lines modified:", true); row.newCell ().setText ("" + overallModCoverage[MOD_LINES].intValue(), true); row = statsTable.newRow (); row.newCell ().setText ("total lines removed:", true); row.newCell ().setText ("" + overallModCoverage[DEL_LINES].intValue(), true); row = statsTable.newRow (); row.newCell ().setText ("coverage for modified executable lines:", true); row.newCell ().setText ("" + modCoverageStr, true); } overallStats.add(statsTable); final IElement coverageTitle = IElement.Factory.create (Tag.Hs[1]); statTitle.setText("OVERALL DIFF SUMMARY", true); overallStats.add(coverageTitle); HTMLTable summaryTable = new HTMLTable ("100%", null, null, "0"); if(emmaDataView != null) { addHeaderRow(emmaDataView.getRoot(), summaryTable, true); } else { addHeaderRow(null, summaryTable, true); } Set<Map.Entry<String, SrcFileItem>> items = emmaSrcMap.entrySet(); boolean odd = true; int count = 0; for (Map.Entry<String, SrcFileItem> item : items) { if (item != null) { final String fileName = item.getKey(); final SrcFileItem srcFileItem = item.getValue(); final Double[] modCoverage = modCoverageMap.get(fileName); addItemRow(fileName, srcFileItem, modCoverage, odd, summaryTable, "s" + count, true, true); odd = !odd; count++; } } overallStats.add(summaryTable); page.setHeader(overallStats); page.emit(out); out.flush(); } private void processDiffOutputFile(HTMLDocument html, ArrayList<String> diffFile, IReportDataView emmaDataView) throws IOException { if(diffFile.size() <= 0) { return; } Double[] modCoverage = new Double[4]; modCoverage[COVERED_MOD_EXE_LINES] = 0.0; modCoverage[MOD_EXE_LINES] = 0.0; modCoverage[MOD_LINES] = 0.0; modCoverage[DEL_LINES] = 0.0; String fileHeader = diffFile.get(0); verboseOut("fileHeader: " + diffFile); //Try to get the package information if its a Java file File srcFilePath = new File(fileHeader.substring(7)); SrcFileItem emmaSourceItem = null; if(srcFilePath.isFile()) { FileInputStream srcFile = new FileInputStream(srcFilePath); String srcFilePackage = parseJavaPackage(srcFile); if(emmaDataView != null) { emmaSourceItem = getEmmaSrcItem(emmaDataView.getRoot(), srcFilePackage, srcFilePath.getName()); } } //Figure out the flag for the working copy. String workingCopyFlag = null; String otherCopyFlag = null; String firstFileLine = diffFile.get(2); String secondFileLine = diffFile.get(3); verboseOut("firstFileLine=" + firstFileLine); verboseOut("secondFileLine=" + secondFileLine); String revisionStr = "unknown"; // Skip over binary files if (firstFileLine.contains("Cannot display")) { return; } HTMLTable srcTable = null; if(firstFileLine.endsWith("(working copy)")) { workingCopyFlag = firstFileLine.substring(0, 1); } else { otherCopyFlag = firstFileLine.substring(0, 1); revisionStr = firstFileLine.substring(firstFileLine.lastIndexOf("(")); } if(secondFileLine.endsWith("(working copy)")) { workingCopyFlag = secondFileLine.substring(0, 1); } else { otherCopyFlag = secondFileLine.substring(0, 1); revisionStr = secondFileLine.substring(secondFileLine.lastIndexOf("(")); } if(firstFileLine.endsWith("(revision 0)") || secondFileLine.endsWith("(revision 0)")) { workingCopyFlag = "+"; otherCopyFlag = "-"; } if(workingCopyFlag == null || otherCopyFlag == null) { throw new IOException("Error occurred while parsing diff output." + EOL + "firstFileLine= '" + firstFileLine + "'" + EOL + "secondFileLine= '" + secondFileLine + "'"); } else { srcTable = new HTMLTable ("100%", null, null, "0"); srcTable.setClass("s"); ArrayList<String> diffOutputChunk = new ArrayList<String>(); Double[] chunkModCoverage; for(int i = 4; i < diffFile.size(); i++) { //Found a chunk indicator. if(diffFile.get(i).startsWith("@@")) { chunkModCoverage = processDiffOutputFileChunk(srcTable, diffOutputChunk, workingCopyFlag, otherCopyFlag, emmaSourceItem); if(chunkModCoverage != null) { modCoverage[COVERED_MOD_EXE_LINES] += chunkModCoverage[COVERED_MOD_EXE_LINES]; modCoverage[MOD_EXE_LINES] += chunkModCoverage[MOD_EXE_LINES]; modCoverage[MOD_LINES] += chunkModCoverage[MOD_LINES]; modCoverage[DEL_LINES] += chunkModCoverage[DEL_LINES]; } diffOutputChunk = new ArrayList<String>(); diffOutputChunk.add(diffFile.get(i)); } //Not any of the above so this line must be diffed text else { diffOutputChunk.add(diffFile.get(i)); } } //Finishing process whatever we have queued up chunkModCoverage = processDiffOutputFileChunk(srcTable, diffOutputChunk, workingCopyFlag, otherCopyFlag, emmaSourceItem); if(chunkModCoverage != null) { modCoverage[COVERED_MOD_EXE_LINES] += chunkModCoverage[COVERED_MOD_EXE_LINES]; modCoverage[MOD_EXE_LINES] += chunkModCoverage[MOD_EXE_LINES]; modCoverage[MOD_LINES] += chunkModCoverage[MOD_LINES]; modCoverage[DEL_LINES] += chunkModCoverage[DEL_LINES]; } } final IElement a = IElement.Factory.create (Tag.A); a.getAttributes ().set (Attribute.NAME, "s" + emmaSrcMap.keySet().size()); html.add(a); final IElement itemname = IElement.Factory.create (Tag.SPAN); { itemname.setText (srcFilePath.toString(), true); itemname.setClass ("in"); } final IElementList title = new ElementList (); { title.add (new Text ("DIFF SUMMARY FOR SOURCE FILE [", true)); title.add (itemname); title.add (new Text ("] against ", true)); title.add (new Text (revisionStr, true)); } html.addH (1, title, null); if(emmaSourceItem != null) { final HTMLTable coverageTable = new HTMLTable ("100%", null, null, "0"); addHeaderRow(emmaSourceItem, coverageTable, false); addItemRow(srcFilePath.toString(), emmaSourceItem, modCoverage, false, coverageTable, null, false, false); html.add(coverageTable); html.addEmptyP(); } else { html.addH(2, "Coverage Information Not Available (e.g. file is not in src/, is not java, is an interface, or was deleted)", null); } if(srcTable != null) { html.add(srcTable); } emmaSrcMap.put(srcFilePath.toString(), emmaSourceItem); modCoverageMap.put(srcFilePath.toString(), modCoverage); } private Double[] processDiffOutputFileChunk(HTMLTable table, ArrayList<String> diffChunk, String workingCopyFlag, String otherCopyFlag, SrcFileItem emmaSrcItem) { if(diffChunk.size() <= 0) { return null; } int workingCopyBegin; int workingCopyRange; int otherCopyBegin; Double[] modCoverage = new Double[4]; modCoverage[COVERED_MOD_EXE_LINES] = 0.0; modCoverage[MOD_EXE_LINES] = 0.0; modCoverage[MOD_LINES] = 0.0; modCoverage[DEL_LINES] = 0.0; IntObjectMap lineCoverageMap = null; if(emmaSrcItem != null) { lineCoverageMap = emmaSrcItem.getLineCoverage (); } String chunkHeader = diffChunk.get(0); int workingCopyBeginIdx = chunkHeader.indexOf(workingCopyFlag); int workingCopyCommaIdx = chunkHeader.indexOf(",", workingCopyBeginIdx); int workingCopyEndIdx = chunkHeader.indexOf(" ", workingCopyCommaIdx); int otherCopyBeginIdx = chunkHeader.indexOf(otherCopyFlag); int otherCopyCommaIdx = chunkHeader.indexOf(",", otherCopyBeginIdx); workingCopyBegin = Integer.parseInt( chunkHeader.substring(workingCopyBeginIdx + 1, workingCopyCommaIdx)); workingCopyRange = Integer.parseInt( chunkHeader.substring(workingCopyCommaIdx + 1, workingCopyEndIdx)); otherCopyBegin = Integer.parseInt( chunkHeader.substring(otherCopyBeginIdx + 1, otherCopyCommaIdx)); String chunkLine; SrcFileItem.LineCoverageData lCoverageData = null; int workingCopyLine = workingCopyBegin; int otherCopyLine = otherCopyBegin; final HTMLTable.IRow chunkRow = table.newTitleRow(); final HTMLTable.ICell chunkCell = chunkRow.newCell(); chunkCell.setColspan(2); chunkCell.setText("Lines " + workingCopyBegin + " - " + String.valueOf(workingCopyLine + workingCopyRange), true); for(int i = 1; i < diffChunk.size(); i++) { chunkLine = diffChunk.get(i); if(lineCoverageMap != null) { lCoverageData = (SrcFileItem.LineCoverageData) lineCoverageMap.get (workingCopyLine); } final HTMLTable.IRow srcRow = table.newRow(); final HTMLTable.ICell lineNumCell = srcRow.newCell(); final HTMLTable.ICell lineTxtCell = srcRow.newCell(); if (chunkLine.length() == 0) { lineTxtCell.setText(" ", true); } else { lineTxtCell.setText(chunkLine.substring(1), true); } //This line is either a modified line or a unchanged line if(!chunkLine.startsWith(otherCopyFlag)) { lineNumCell.setText(String.valueOf(workingCopyLine), true); //Determine if this line is a modified line or a unchange line if(chunkLine.startsWith(workingCopyFlag)) { lineNumCell.setClass("dm"); modCoverage[MOD_LINES] ++; if(lCoverageData != null) { modCoverage[MOD_EXE_LINES] ++; switch(lCoverageData.m_coverageStatus) { case SrcFileItem.LineCoverageData.LINE_COVERAGE_ZERO: lineTxtCell.setClass ("cz"); break; case SrcFileItem.LineCoverageData.LINE_COVERAGE_PARTIAL: lineTxtCell.setClass ("cp"); modCoverage[COVERED_MOD_EXE_LINES] += 0.5; break; case SrcFileItem.LineCoverageData.LINE_COVERAGE_COMPLETE: lineTxtCell.setClass ("cc"); modCoverage[COVERED_MOD_EXE_LINES] ++; break; default: } } } else { lineNumCell.setClass("ds"); } } else { lineNumCell.setClass("dd"); lineNumCell.setText(String.valueOf(otherCopyLine), true); lineTxtCell.setClass("ddt"); modCoverage[DEL_LINES] ++; } if(!chunkLine.startsWith(otherCopyFlag)) { workingCopyLine++; } if(!chunkLine.startsWith(workingCopyFlag)) { otherCopyLine++; } } return modCoverage; } private String parseJavaPackage(FileInputStream srcFile) throws IOException { BufferedReader srcFileReader = new BufferedReader( new InputStreamReader(srcFile)); String line = srcFileReader.readLine(); while(line != null) { int beginIdx = line.indexOf("package"); if(beginIdx > -1) { int endIdx = line.indexOf(";", beginIdx); if(endIdx > -1) { return line.substring(beginIdx + 7, endIdx).trim(); } } line = srcFileReader.readLine(); } return null; } private SrcFileItem getEmmaSrcItem(IItem rootItem, String srcPackageName, String srcFileName) { if(rootItem == null || srcPackageName == null || srcFileName == null) { return null; } for(Iterator<?> packages = rootItem.getChildren(); packages.hasNext();) { IItem packageItem = (IItem)packages.next(); if(packageItem.getName().equals(srcPackageName)) { for(Iterator<?> sources = packageItem.getChildren(); sources.hasNext();) { SrcFileItem sourceItem = (SrcFileItem)sources.next(); if(sourceItem.getName().equals(srcFileName)) { return sourceItem; } } } } return null; } private void addHeaderRow (final IItem item, final HTMLTable table, boolean includeName) { // header row: final HTMLTable.IRow header = table.newTitleRow (); if(includeName) { final HTMLTable.ICell nameCell = header.newCell(); nameCell.setText("File", true); } for (int c = 1; c <= 4; ++ c) { IItemAttribute attr = null; if(item != null) { attr = item.getAttribute (c, 0); } if (attr != null) { final HTMLTable.ICell cell = header.newCell (); cell.setText (attr.getName (), true); } else { final HTMLTable.ICell cell = header.newCell (); cell.setText (" ", true); } } if(item != null) { final HTMLTable.ICell cell = header.newCell(); cell.setText("mod lines, %", true); } else { final HTMLTable.ICell cell = header.newCell (); cell.setText (" ", true); } } /* * No header row, just data rows. */ private void addItemRow (final String fileName, final IItem item, final Double[] modCoverage, final boolean odd, final HTMLTable table, final String nameHREF, final boolean anchor, final boolean includeName) { final HTMLTable.IRow row = table.newRow (); if (odd) row.setClass ("o"); if(includeName) { final HTMLTable.ICell nameCell = row.newCell(); if(nameHREF != null) { final String fullHREFName = anchor ? "#".concat (nameHREF) : nameHREF; nameCell.add(new HyperRef(fullHREFName, fileName, true)); } else { nameCell.setText(fileName, true); } } final StringBuffer buf = new StringBuffer (11); for (int c = 1; c <=4; ++ c) { IItemAttribute attr = null; if(item != null) { attr = item.getAttribute (c, 0); } if (attr != null) { final HTMLTable.ICell cell = row.newCell (); //final boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); buf.setLength (0); attr.format (item, buf); cell.setText (buf.toString (), true); //if (fail) cell.setClass (CSS_DATA_HIGHLIGHT); } else { final HTMLTable.ICell cell = row.newCell (); cell.setText (" ", true); } } if(item != null && modCoverage != null) { String modCoverageStr = ""; if(modCoverage[1] > 0) { modCoverageStr = String.format("%d%% (%.1f/%.1f)", (int)(modCoverage[COVERED_MOD_EXE_LINES]/modCoverage[MOD_EXE_LINES]*100), modCoverage[COVERED_MOD_EXE_LINES], modCoverage[MOD_EXE_LINES]); } else { modCoverageStr = String.format("%d%% (%.1f/%.1f)", 100, modCoverage[COVERED_MOD_EXE_LINES], modCoverage[MOD_EXE_LINES]); } final HTMLTable.ICell cell = row.newCell(); cell.setText(modCoverageStr, true); } else { final HTMLTable.ICell cell = row.newCell (); cell.setText (" ", true); } } // Enable this with -Dtest.diff.verbose=true from the commandline private void verboseOut(Object msg) { if (verbose) { System.out.println(msg.toString()); } } }