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