edu.umd.cs.marmoset.codeCoverage.CodeCoverageResults.java Source code

Java tutorial

Introduction

Here is the source code for edu.umd.cs.marmoset.codeCoverage.CodeCoverageResults.java

Source

/**
 * Marmoset: a student project snapshot, submission, testing and code review
 * system developed by the Univ. of Maryland, College Park
 * 
 * Developed as part of Jaime Spacco's Ph.D. thesis work, continuing effort led
 * by William Pugh. See http://marmoset.cs.umd.edu/
 * 
 * Copyright 2005 - 2011, Univ. of Maryland
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 */

package edu.umd.cs.marmoset.codeCoverage;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * TODO instantiate with a factory
 * 
 * Right now we only support Clover. In theory someday we might support other
 * code coverage tools like Emma.
 * 
 * @author jspacco
 */
public class CodeCoverageResults implements CodeCoverageConstants, Iterable<FileWithCoverage> {
    /**
     * Map from filenames to FileWithCoverage objects.
     */
    private Map<String, FileWithCoverage> filesCovered = new HashMap<String, FileWithCoverage>();

    private static List<String> filesToSkip = new LinkedList<String>();

    private boolean excludingResult = false;

    public boolean isExcludingResult() {
        return excludingResult;
    }

    static {
        // XXX hack alert: static initializers aren't the best option;
        // would be nice to get this info from web.xml or a -D property
        filesToSkip.add("PublicTests.java");
        filesToSkip.add("ReleaseTests.java");
        filesToSkip.add("SecretTests.java");
        filesToSkip.add("StudentTests.java");
        filesToSkip.add("StudentWrittenTests.java");
        filesToSkip.add("TestingSupport.java");
        filesToSkip.add("SpiderTest.java");
        filesToSkip.add("TextUI.java");
    }

    public static void main(String[] args) throws Exception {
        CodeCoverageResults cloverXMLOutput = parseFile("/tmp/APROJECTS/cloverTest/clover.xml");
        File file = new File("/tmp/APROJECTS/cloverTest/BuildServer16293/build/src/PokerHandEvaluator.java");
        // cloverXMLOutput.equals(false);
        file.exists();
    }

    /*
     * (non-Javadoc)
     * 
     * @see edu.umd.cs.submitServer.codeCoverage.CodeCoverageResults#getFileWithCoverage(java.lang.String)
     */
    public FileWithCoverage getFileWithCoverage(String filename) {
        for (String key : filesCovered.keySet()) {
            // We want to be able to look for either just the filename (Foo.java)
            // or the entire package, possibly with a "src" directory prefix (src/foo/bar/Foo.java)
            // But we don't want searches for "Tree.java" to match against "EmptryTree.java"
            int lastIndexOf = key.lastIndexOf('/');
            if (lastIndexOf > 0 && key.endsWith("/" + filename))
                return filesCovered.get(key);
            else if (key.equals(filename))
                return filesCovered.get(key);
        }
        return filesCovered.get(filename);
    }

    /**
     * Get the overall coverage stats for this set of coverage results.
     * 
     * @return The overall coverageStats for this set of coverage results.
     */
    public CoverageStats getOverallCoverageStats() {
        CoverageStats stats = new CoverageStats();
        stats.setExcludingResult(excludingResult);
        for (FileWithCoverage file : this) {
            // XXX HACK ALERT: I want to skip PublicTests.java,
            // ReleaseTests.java,
            // SecretTests.java and StudentTests.java. I'm guessing based on the
            // name of the file.
            //if (isJUnitTestSuite(file.getFullPathName()) || !file.isAnythingCovered())
            if (isJUnitTestSuite(file.getFullPathName()))
                continue;
            stats.merge(file.getCoverageStats());
        }
        return stats;
    }

    public CodeCoverageResults getCodeCoverageResultsForPackage(String packageName) {
        CodeCoverageResults results = new CodeCoverageResults();
        for (FileWithCoverage file : this) {
            if (file.getFullPathName().contains(packageName)) {
                results.addFileWithCoverage(file);
            }
        }
        return results;
    }

    public CoverageStats getOverallCoverageStatsForPackage(String packageName) {
        CoverageStats stats = new CoverageStats();
        for (FileWithCoverage file : this) {
            if (file.getFullPathName().contains(packageName)) {
                stats.merge(file.getCoverageStats());
            }
        }
        return stats;
    }

    /**
     * Checks if this file appears to be a JUnitTestSuite based on its name.
     * 
     * @param fullPathName
     *            The name of the file.
     * @return True if the file appears to be a JUnitTestSuite; false otherwise.
     */
    public static boolean isJUnitTestSuite(String fullPathName) {
        for (String pattern : filesToSkip) {
            if (fullPathName.contains(pattern))
                return true;
        }
        return false;
    }

    /**
     * Constructs a new CloverXMLOutput object.
     */
    public CodeCoverageResults() {
    }

    /**
     * Copy Constructor. Creates a deep copy.
     * 
     * @param other
     *            The other set of coverage results to copy.
     */
    public CodeCoverageResults(CodeCoverageResults other) {
        if (other != null) {
            for (FileWithCoverage file : other) {
                addFileWithCoverage(new FileWithCoverage(file));
            }
        }
    }

    private void addFileWithCoverage(FileWithCoverage fileWithCoverage) {
        filesCovered.put(fileWithCoverage.getShortFileName(), fileWithCoverage);
    }

    public static CodeCoverageResults parseFile(String filename) throws IOException, DocumentException {
        File file = new File(filename);

        FileInputStream fis = new FileInputStream(file);

        SAXReader reader = new SAXReader();
        Document document = reader.read(fis);
        // should not throw an exception
        fis.close();
        return parseDocument(document);
    }

    public static CodeCoverageResults parseString(String xmlDocumentAsString)
            throws IOException, DocumentException {
        StringReader stringReader = new StringReader(xmlDocumentAsString);
        SAXReader reader = new SAXReader();
        Document document = reader.read(stringReader);
        // Closing a stringReader can't possibly throw an exception
        stringReader.close();
        return parseDocument(document);
    }

    private static CodeCoverageResults parseDocument(Document document) throws DocumentException {
        CodeCoverageResults cloverXMLOutput = new CodeCoverageResults();

        Element root = document.getRootElement();

        for (Iterator<?> ii = root.elementIterator(CodeCoverageConstants.PROJECT); ii.hasNext();) {
            Element projectElement = (Element) ii.next();
            for (Iterator<?> jj = projectElement.elementIterator(CodeCoverageConstants.PACKAGE); jj.hasNext();) {
                Element packageElement = (Element) jj.next();
                for (Iterator<?> kk = packageElement.elementIterator(CodeCoverageConstants.FILE); kk.hasNext();) {
                    Element fileElement = (Element) kk.next();
                    String sourceFileName = fileElement.attributeValue(CodeCoverageConstants.NAME);

                    // Create a new source file that stores coverage
                    // information.
                    FileWithCoverage fileWithCoverage = new FileWithCoverage(sourceFileName);
                    // System.out.println("attributes: "
                    // +fileElement.attributeCount());

                    for (Iterator<?> ll = fileElement.elementIterator(CodeCoverageConstants.LINE); ll.hasNext();) {
                        Element lineElement = (Element) ll.next();
                        // System.out.println("Name: " +node.getName());

                        // Process the coverage information from this line node.
                        fileWithCoverage.processLineNode(lineElement);
                    }
                    // add the file with its coverage information
                    cloverXMLOutput.addFileWithCoverage(fileWithCoverage);
                }
            }
        }
        return cloverXMLOutput;
    }

    public void union(CodeCoverageResults other) {
        if (other.size() == 0) {
            return;
        }
        if (filesCovered.isEmpty()) {
            // If we lack code coverage information, perform a deep copy.
            for (FileWithCoverage file : other) {
                addFileWithCoverage(new FileWithCoverage(file));
            }
        } else {
            // Otherwise compute the union of each file.
            for (FileWithCoverage myFile : filesCovered.values()) {

                FileWithCoverage secondFile = other.getFileWithCoverage(myFile.getShortFileName());
                myFile.union(secondFile);
            }
        }
    }

    public void intersect(CodeCoverageResults other) {
        if (other.size() == 0) {
            filesCovered.clear();
        } else if (filesCovered.isEmpty()) {
            return;
        } else {
            // Otherwise compute the intersection for each file.
            for (FileWithCoverage myFile : filesCovered.values()) {
                FileWithCoverage secondFile = other.getFileWithCoverage(myFile.getShortFileName());
                myFile.intersect(secondFile);
            }
        }
    }

    public void excluding(CodeCoverageResults other) {
        excludingResult = true;
        if (other.size() == 0)
            return;
        if (filesCovered.isEmpty()) {
            return;
        } else {
            // Otherwise compute the intersection for each file.
            for (FileWithCoverage myFile : filesCovered.values()) {
                FileWithCoverage secondFile = other.getFileWithCoverage(myFile.getShortFileName());
                myFile.excluding(secondFile);
            }
        }
    }

    public CoverageLevel coarsestCoverageLevel() {
        CoverageLevel lvl = CoverageLevel.NONE;

        for (FileWithCoverage myFile : filesCovered.values())
            lvl = lvl.min(myFile.coarsestCoverage());
        return lvl;
    }

    @Override
    public Iterator<FileWithCoverage> iterator() {
        return filesCovered.values().iterator();
    }

    public int size() {
        return filesCovered.size();
    }

    /**
     * Does this set of codeCoverage results cover the given file at the given
     * line number?
     * TODO FileNames in CodeCoverageResults are tricky; are they always prepended
     *      with package names, how flexible is the lookup system for detecting the
     *      absence of a package name, etc.
     * @param fileName The name of the file.
     * @param lineNumber The line number of the file.
     * @return True if this set of code coverage results covers the given file at the
     *      given line number; false otherwise.  Returns false if the file is not covered
     *      by these coverage results.
     */
    public boolean coversFileAtLineNumber(String fileName, int lineNumber) {
        FileWithCoverage file = getFileWithCoverage(fileName);
        if (file != null)
            return file.isLineCovered(lineNumber);
        // If for some reason we don't have coverage information for this file,
        // we will simply return false
        return false;
    }
}