http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CodeCoverageReporter.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CodeCoverageReporter.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CodeCoverageReporter.java new file mode 100755 index 0000000..9a4bb0e --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CodeCoverageReporter.java @@ -0,0 +1,549 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.flex.tools.codecoverage.reporter.reports.IReportFactory; +import org.apache.flex.tools.codecoverage.reporter.reports.IReportWriter; +import org.apache.flex.tools.codecoverage.reporter.reports.ReportOptions; +import org.apache.flex.tools.codecoverage.reporter.reports.XMLReportFactory; +import org.apache.flex.tools.codecoverage.reporter.swf.SWFLineReporter; + +/** + * Analyzes code coverage data files and produces a report. + * + * This class can be run from the command line and accept arguments to control + * the format of the report. + * + */ +public class CodeCoverageReporter +{ + /** + * Extension for code coverage reports. + */ + private static final String DEFAULT_REPORT_NAME = "ccreport.xml"; + private static final String DEFAULT_DATA_DIRECTORY = "ccdata"; + + private static final String CCREPORTER_VERSION = "0.9"; + + /** + * CodeCovergeReporter will read code code coverage result files and + * generate reports. + * + */ + public static void main(String[] args) throws IOException + { + // This message should not be localized. + System.out.println("Apache Flex Code Coverage Reporter"); + System.out.println("Version " + CCREPORTER_VERSION); + System.out.println(""); + + if (args.length == 1 && "-help".equals(args[0])) + { + displayUsage(); + System.exit(1); + } + + CodeCoverageReporter reporter = new CodeCoverageReporter(); + reporter.processArgs(args); + + System.out.println("Analyzing " + reporter.files.size() + " files"); + + try + { + final CoverageData coverageData = new CoverageData(); + + // analyzes the files and then report the results. + for (File file : reporter.files) + { + reporter.analyze(file, coverageData); + } + + final CoverageSummary coverageSummary = reporter.createCoverageSummary(coverageData); + reporter.createReport(coverageSummary); + + // output high level coverage to the console + reporter.outputCoverageSummaryToConsole(coverageSummary); + + System.out.println("Report output to " + reporter.reportFile.getAbsolutePath()); + } + catch (Error e) + { + e.printStackTrace(); + + System.err.println(""); + System.err.println("An unrecoverable error occurred."); + } + } + + /** + * Create a regex string from a given user input string that can have + * wild cards but is not a regex pattern. + * Supports simple wild cards: '*', '?' + * + * @param userFilter user supplied filter that may only contain the '*' and '?' + * wild card characters. All other cards will have no special meaning. + */ + private static String createRegexStringFromUserArg(String userFilter) + { + // substitute regex for user wild cards. + StringBuilder regexString = new StringBuilder("^"); + String[] results = userFilter.split("[\\?\\*]"); + int currentIndex = 0; + + for (String filter : results) + { + currentIndex += filter.length(); + + if (filter.length() > 0) + { + regexString.append("\\Q"); + regexString.append(filter); + regexString.append("\\E"); + } + + if (currentIndex < userFilter.length()) + { + if (userFilter.charAt(currentIndex) == '*') + { + regexString.append(".*"); + } + else if (userFilter.charAt(currentIndex) == '?') + { + regexString.append(".?"); + } + } + + currentIndex++; // skip past wild card + } + + regexString.append("$"); + return regexString.toString(); + } + + public static Collection<File> listFiles(final File dir) + { + final String[] fileNames = dir.list(); + + if (fileNames == null) + { + return Collections.emptyList(); + } + + final Collection<File> fileList = new ArrayList<File>(fileNames.length); + for (int i = 0; i < fileNames.length; i++) + { + fileList.add(new File(dir.getPath(), fileNames[i])); + } + + return fileList; + } + + private void outputCoverageSummaryToConsole(final CoverageSummary coverageSummary) + { + System.out.println("Results:"); + + final SummaryInfo summaryInfo = coverageSummary.getOverallSummaryInfo(); + int executed = summaryInfo.getExecutedLineCount(); + int total = summaryInfo.getTotalLineCount(); + int percent = total == 0 ? 0 : executed * 100 / total; + System.out.println("Line Coverage: " + percent + "%"); + + executed = summaryInfo.getExecutedMethodCount(); + total = summaryInfo.getTotalMethodCount(); + percent = total == 0 ? 0 : executed * 100 / total; + System.out.println("Method Coverage: " + percent + "%"); + } + + private static void displayUsage() + { + System.out.println("Usage: ccreporter"); + System.out.println("[-exclude filter]"); + System.out.println("[-help]"); + System.out.println("[-hide-unexecuted]"); + System.out.println("[-hide-files]"); + System.out.println("[-hide-methods]"); + System.out.println("[-hide-packages]"); + System.out.println("[-include filter]"); + System.out.println("[-output filename]"); + System.out.println("[-report-factory"); + System.out.println("[filename | directory name]*"); + } + + /** + * The report file. + */ + private File reportFile; + + /** + * Collection of data files to analyze. + */ + private Collection<File> files; + + /** + * User filters converted to regex. + */ + private Collection<String> includeFilters; + private Collection<String> excludeFilters; + + /** + * Raw user filters as input. + */ + private Collection<String> userIncludeFilters; + private Collection<String> userExcludeFilters; + private boolean showPackages = true; + private boolean showFiles = true; + private boolean showMethods = true; + private boolean showUnexecutedDetails = false; + + /** + * User can specify their own report factory. + */ + private String reportFactory; + + /** + * Create a code coverage reporter. + * + */ + public CodeCoverageReporter() + { + } + + /** + * Parse the command line arguments and set the instance variables of + * the reporter. + * + * @param args User specified arguments. + */ + public void processArgs(final String[] args) + { + int index = 0; + File reportFile = null; + List<String> includeFilters = new ArrayList<String>(); + List<String> excludeFilters = new ArrayList<String>(); + List<String> userIncludeFilters = new ArrayList<String>(); + List<String> userExcludeFilters = new ArrayList<String>(); + boolean showPackages = true; + boolean showFiles = true; + boolean showMethods = true; + boolean showUnexecutedDetails = true; + String reportFactory = null; + + while ((index < args.length) && (args[index].startsWith("-"))) + { + switch (args[index]) + { + case "-include": + { + userIncludeFilters.add(args[++index]); + includeFilters.add(createRegexStringFromUserArg(args[index])); + index++; + break; + } + case "-exclude": + { + userExcludeFilters.add(args[++index]); + excludeFilters.add(createRegexStringFromUserArg(args[index])); + index++; + break; + } + case "-include-regex": + { + includeFilters.add(args[++index]); + index++; + break; + } + case "-exclude-regex": + { + excludeFilters.add(args[++index]); + index++; + break; + } + case "-output": + { + reportFile = new File(args[++index]); + index++; + break; + } + case "-hide-unexecuted": + { + showUnexecutedDetails = false; + index++; + break; + } + case "-hide-methods": + { + showMethods = false; + index++; + break; + } + case "-hide-files": + { + showFiles = false; + index++; + break; + } + case "-hide-packages": + { + showPackages = false; + index++; + break; + } + case "-report-factory": + { + reportFactory = args[++index]; + index++; + break; + } + default: + { + System.err.println("unknown argument " + args[index]); + index++; + System.exit(1); + } + } + } + + if (reportFile == null) + reportFile = new File(DEFAULT_REPORT_NAME); + + Collection<String>filenames = new ArrayList<String>(); + Collection<File> files = new ArrayList<File>(); + + // Collect filenames/directories from user args. + // If no files specified look in the default data directory. + if (index >= args.length) + { + filenames.add(new File(System.getProperty("user.home"), DEFAULT_DATA_DIRECTORY).getAbsolutePath()); + } + else + { + for (int i = index; i < args.length; i++) + { + filenames.add(args[i]); + } + } + + // validate files + for (String filename : filenames) + { + File file = new File(filename); + + if (!file.exists()) + { + System.err.println("File not found: " + file.getAbsolutePath()); + System.exit(1); + } + else + { + if (file.isDirectory()) + { + Collection<File> newFiles = listFiles(file); + files.addAll(newFiles); + System.out.println("Found " + newFiles.size() + " files in " + file.getAbsolutePath()); + } + else + { + files.add(file); + System.out.println("Found " + file.getAbsolutePath()); + } + } + + } + + this.includeFilters = includeFilters; + this.excludeFilters = excludeFilters; + this.userIncludeFilters = userIncludeFilters; + this.userExcludeFilters = userExcludeFilters; + this.showUnexecutedDetails = showUnexecutedDetails; + this.showMethods = showMethods; + this.showFiles = showFiles; + this.showPackages = showPackages; + this.files = files; + this.reportFile = reportFile; + this.reportFactory = reportFactory; + } + + /** + * Read in a code coverage raw file and output raw code coverage data. + * + * @param inFile + * Code coverage execution data. + * @throws IOException + * @throws FileNotFoundException + */ + public void analyze(final File inFile, final CoverageData coverageData) + throws FileNotFoundException, IOException + { + try (BufferedReader reader = new BufferedReader(new FileReader(inFile))) + { + String inLine; + final ArrayList<String> stringPool = new ArrayList<String>(); + int inLineNumber = 1; + + while ((inLine = reader.readLine()) != null) + { + // test first char: + // @{path} the SWF's path + // #{id,string} defines an id string + char firstChar = inLine.charAt(0); + + switch (firstChar) + { + case '@': // @swfURL + { + // read in SWF and analyze + String swfURL = inLine.substring(1); + SWFLineReporter swfLineReporter = new SWFLineReporter(swfURL); + swfLineReporter.readLines(coverageData); + break; + } + case '#': // instruction: packageFileID,packageFileString + { + // add id to pool + String[] results = inLine.substring(1).split(","); + + assert results.length == 2 : "line " + inLineNumber + + System.lineSeparator() + + "improperly formatted line" + + System.lineSeparator() + inLine; + + assert !stringPool.contains(results[1]) : "line " + + inLineNumber + System.lineSeparator() + + "string is already in the string pool: " + + results[1]; + + assert Integer.valueOf(results[0]) == stringPool.size() : "line " + + inLineNumber + + System.lineSeparator() + + "string is already in the string pool: " + + results[1]; + + stringPool.add(results[1]); + break; + } + default: + { + // "id,linenum" + // Split line and record linenum as a hit. + if (firstChar >= '0' && firstChar <= '9') + { + String[] results = inLine.split(","); + String file = stringPool.get(Integer.valueOf(results[0])); + int hitLineNumber = Integer.valueOf(results[1]); + coverageData.setLineExecuted(file, hitLineNumber); + break; + } + else + { + System.err.println("Warning: line " + inLineNumber + ": unrecognized data, " + inLine); + } + } + } + + inLineNumber++; + } + } + catch (NumberFormatException e) + { + System.err.println(inFile.getAbsolutePath() + " may not be a code coverage data file"); + throw new IOException(e); + } + } + + /** + * Create a code coverage report. The report summarizes the code coverage by + * package, file, and method. The report is output to the file specified + * by the user. + * + * @param coverageSummary A summary of the coverage data. + * @throws IOException + */ + public void createReport(final CoverageSummary coverageSummary) throws IOException + { + createXMLReport(coverageSummary); + } + + /** + * Create a summary of the raw code coverage data. + * + * @param coverageData + * @return + */ + protected CoverageSummary createCoverageSummary(final CoverageData coverageData) + { + final CoverageSummary coverageSummary = new CoverageSummary(coverageData, + includeFilters, excludeFilters); + + coverageSummary.processCoverageData(); + return coverageSummary; + } + + /** + * Output an XML report to the reportFile. + * + * @param coverageSummary A summary of the coverage data. + * @throws IOException + */ + protected void createXMLReport(final CoverageSummary coverageSummary) throws IOException + { + Class<?> classFactory = null; + IReportFactory factory = null; + + if (reportFactory != null) + { + try + { + classFactory = Class.forName(reportFactory); + factory = (IReportFactory) classFactory.newInstance(); + } + catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) + { + // fallback to default report writer + e.printStackTrace(); + System.err.println("Report factory not found," + reportFactory); + } + } + + if (factory == null) + factory = new XMLReportFactory(); + + final ReportOptions reportOptions = new ReportOptions(userIncludeFilters, userExcludeFilters, + showPackages, showFiles, showMethods, showUnexecutedDetails); + final IReportWriter reportWriter = factory.createReport(reportOptions); + try (final FileWriter fileWriter = new FileWriter(reportFile); + final BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) + { + reportWriter.writeTo(bufferedWriter, coverageSummary); + } + } + +}
http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageData.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageData.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageData.java new file mode 100755 index 0000000..4df2632 --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageData.java @@ -0,0 +1,113 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +/** + * Class to record the execution data as well the all the debug lines that exist + * in SWF file. + * Each line number is associated with method. + */ +public class CoverageData +{ + /** + * key = package;file + * value = Map<line number, LineInfo> + */ + final Map<String, Map<Integer, LineInfo>> lineMap = + new HashMap<String, Map<Integer, LineInfo>>(); + Iterator<Map<String, Map<Integer, LineInfo>>> lineMapIterator; + + /** + * Add the existence of a line to the coverage data. + * + * @param file The file the line number is in. + * @param lineNumber The line number of the line + * @param methodName The name of the method the line is in. + */ + public void addLine(final String file, final int lineNumber, + final String methodName) + { + doAddLine(file, lineNumber, methodName); + } + + /** + * Mark a line number as being executed. + * @param file The file. + * @param lineNumber The line number that was executed. + */ + public void setLineExecuted(final String file, final int lineNumber) + { + LineInfo lineInfo = doAddLine(file, lineNumber, null); + lineInfo.setExecuted(true); + } + + /** + * Get a list of entries in the coverage data. + * + * @return list of entries in the coverage data. + */ + public Set<Entry<String, Map<Integer, LineInfo>>> entrySet() + { + return lineMap.entrySet(); + } + + /** + * Add a line to the coverage data. + * This line is recorded as unexecuted. + * + * @param file The file the line is found in. + * @param lineNumber The line number. + * @param methodName The method name. + * @return new LineInfo object + */ + private LineInfo doAddLine(final String file, final int lineNumber, + final String methodName) + { + Map<Integer, LineInfo> lineInfoMap = lineMap.get(file); + + if (lineInfoMap == null) + { + lineInfoMap = new TreeMap<Integer, LineInfo>(); + lineMap.put(file, lineInfoMap); + } + + LineInfo lineInfo = lineInfoMap.get(lineNumber); + + if (lineInfo == null) + { + lineInfo = new LineInfo(methodName, lineNumber); + lineInfoMap.put(lineNumber, lineInfo); + } + else if (methodName != null && lineInfo.getMethodName() == null) + { + lineInfo.setMethodName(methodName); + } + + return lineInfo; + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageSummary.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageSummary.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageSummary.java new file mode 100755 index 0000000..ddf01a6 --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/CoverageSummary.java @@ -0,0 +1,423 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Summary of code coverage at the package, file, and method level. + * + */ +public class CoverageSummary +{ + /** + * Used to return the results of splitting the debugfile string. + */ + private static class FilePathComponents + { + public String packageName; + public String filename; + } + + private static final TreeSet<String> emptyTreeSet = new TreeSet<String>(); + + /** + * The coverage data being summarized. + */ + private final CoverageData coverageData; + + /** + * Compiled regex patterns to include only specific files. + */ + private final Collection<Pattern> includeFilterPatterns = new ArrayList<Pattern>(); + + /** + * Compiled regex patterns to exclude only specific files. + */ + private final Collection<Pattern> excludeFilterPatterns = new ArrayList<Pattern>(); + + /** + * Coverage summary rolled up to the package level. + * + * Key: package name + * Value: PackageSummaryInfo + */ + private final Map<String, PackageSummaryInfo> packageSummaryMap = + new TreeMap<String, PackageSummaryInfo>(); + + /** + * Coverage summary rolled up to the file level. + * + * key: package;file + * value: SummaryInfo. + */ + private final Map<String, SummaryInfo> fileSummaryMap = + new TreeMap<String, SummaryInfo>(); + + /** + * Coverage summary for methods. + * + * Key: method name + * Value: SummaryInfo + */ + private final Map<String, SummaryInfo> methodSummaryMap = + new TreeMap<String, SummaryInfo>(); + + /** + * Summary information for all packages being reported. + */ + private final SummaryInfo overall = new SummaryInfo(); + + /** + * Association of methods in a file. + * + * Key: package;filename + * Value: Ordered set of method names. + */ + private final Map<String, TreeSet<String>> methodsInFile = + new HashMap<String, TreeSet<String>>(); + + /** + * Create a coverage summmary object. The coverage summary object + * can process the coverage data and provide coverage data by + * package, file, and method. + * + * @param coverageData the coverage data to generate the summary from. + * @param includeFilters Regex filter patterns. Controls which files + * are included in the coverage summary. + * @param excludeFilters Regex filter patterns. Controls which files + * are excluded in the coverage summary. + * + */ + public CoverageSummary(final CoverageData coverageData, + final Collection<String> includeFilters, + final Collection<String> excludeFilters) + { + this.coverageData = coverageData; + + initializePatterns(includeFilters, excludeFilters); + } + + /** + * Get the coverage data the summary is generated from. + * @return the coverage data. + */ + public CoverageData getCoverageData() + { + return coverageData; + } + + /** + * Get the aggregate summary info of all the included packages. + * + * @return aggregate summary info. + */ + public SummaryInfo getOverallSummaryInfo() + { + return overall; + } + + /** + * Entry set of all included packages. + * + * @return Set of packages included in summary data. + */ + public Set<Map.Entry<String, PackageSummaryInfo>> getPackageSummaryEntrySet() + { + return packageSummaryMap.entrySet(); + } + + /** + * Entry set of all files included in the summary data. + * + * @return Entry set + */ + public Set<Map.Entry<String, SummaryInfo>> getFileSummaryEntrySet() + { + return fileSummaryMap.entrySet(); + } + + /** + * Get the coverage summmary info for a specific file. + * + * @param packageFileKey key formed by calling createPackageFileKey(). + * @return SummaryInfo for the specified file or null the file is + * not included in the summary data. + */ + public SummaryInfo getFileSummaryInfo(final String packageFileKey) + { + return fileSummaryMap.get(packageFileKey); + } + + /** + * Get the summary info for a specific method. + * + * @param methodName The name of the method to get the summary info for. + * @return SummyInfo for the specified method or null if the method is + * not included in the summary data. + */ + public SummaryInfo getMethodSummaryInfo(String methodName) + { + return methodSummaryMap.get(methodName); + } + + /** + * A set of methods declared in the specified file. + * + * @param filename The name of the AS or MXML file. + * @return Set of method names. + */ + public Set<String> getMethodsInFile(String filename) + { + TreeSet<String> results = methodsInFile.get(filename); + return results != null ? results : emptyTreeSet; + } + + /** + * Roll up the coverage data at a method, file, and package level. + * If include filters and/or exclude filters are specified then + * they are applied that this time. + */ + public void processCoverageData() + { + // for all packages;files + for (Map.Entry<String, Map<Integer, LineInfo>> fileEntry : coverageData.entrySet()) + { + final String packageFile = fileEntry.getKey(); + final FilePathComponents fileComponents = splitPackageFile(packageFile); + final SummaryInfo summaryInfo = new SummaryInfo(); + summaryInfo.setName(fileComponents.filename); + + // for all line numbers; + // 1. summary at the file level + // 2. record execution status for each method. + for (final Map.Entry<Integer, LineInfo> lineEntry : fileEntry.getValue().entrySet()) + { + final LineInfo lineInfo = lineEntry.getValue(); + final String methodName = lineInfo.getMethodName(); + + if (!shouldIncludeFile(fileComponents)) + continue; + + summaryInfo.setLineExecutionStatus(lineInfo.getLineNumber(), + methodName, lineInfo.isExecuted()); + + addMethodSummaryInfo(fileComponents.packageName, + fileComponents.filename, methodName, lineInfo); + } + + addFileSummaryInfo(fileComponents.packageName, + fileComponents.filename, summaryInfo); + } + } + + /** + * Determine if a file should be included in the summary. + * If no include filters or exclude filters are specified then + * all files are included. + * + * @param pathComponents The package and file name to test. + * @return true if the package should be included, false otherwise. + */ + private boolean shouldIncludeFile(final FilePathComponents pathComponents) + { + String className = pathComponents.packageName + "." + pathComponents.filename; + final int index = className.lastIndexOf('.'); + + assert index != -1 : "no suffix found on filename"; + + className = className.substring(0, index); + + // a class name is included if it is included by the pattern + // and not exclude by the exclude filters. + if (!(includeFilterPatterns.size() == 0) && + !matches(includeFilterPatterns, className)) + { + return false; + } + + if (!(excludeFilterPatterns.size() == 0) && + matches(excludeFilterPatterns, className)) + { + return false; + } + + return true; + } + + /** + * Utility to test is a string matches one of a set of patterns. + * + * @param patterns Collection of patterns + * @param value The value to match + * @return true if the pattern matches, false otherwise. + */ + private boolean matches(final Collection<Pattern> patterns, final String value) + { + for (Pattern pattern : patterns) + { + Matcher matcher = pattern.matcher(value); + if (matcher.matches()) + return true; + } + + return false; + } + + /** + * Create a combination package/file key. + * + * @param packageName The package name part of the key. + * @param filename THe filename part of the key. + * @return Key for the package/file. + */ + public String createPackageFileKey(final String packageName, final String filename) + { + return packageName + ";" + filename; + } + + /** + * Split a sourcePath;package/file string. These string come from the + * AS debug_file opcodes and are in the code coverage data files. + * + * @param packageFile The string to split into components. + * @return FilePathComponents + */ + private FilePathComponents splitPackageFile(final String packageFile) + { + final String[] splitResults = packageFile.split(";"); + String packageName = splitResults.length > 1 ? + splitResults[splitResults.length - 2] : ""; + final String filename = splitResults[splitResults.length - 1]; + + // convert '\' and '/' path separators to '.' package separators. + if (packageName.length() > 0) + packageName = packageName.replaceAll("[\\\\/]", "."); + + final FilePathComponents results = new FilePathComponents(); + results.packageName = packageName; + results.filename = filename; + + return results; + } + + /** + * Aggregate summary information for a file. + * + * This method takes summary information for a file + * and aggregates them at the file, package, and overall coverage. + * + * @param packageName Package name the summary info is for. + * @param filename The file name the summary info is for. + * @param summaryInfo The summary info to aggregate. + */ + private void addFileSummaryInfo(final String packageName, final String filename, + final SummaryInfo summaryInfo) + { + if (summaryInfo.getTotalLineCount() == 0) + return; + + // summarize at the package level + fileSummaryMap.put(createPackageFileKey(packageName, filename), summaryInfo); + PackageSummaryInfo packageSummaryInfo = packageSummaryMap.get(packageName); + if (packageSummaryInfo == null) + { + packageSummaryInfo = new PackageSummaryInfo(); + packageSummaryInfo.getSummaryInfo().setName(packageName); + packageSummaryMap.put(packageName, packageSummaryInfo); + } + + packageSummaryInfo.addFile(packageName, filename); + packageSummaryInfo.add(summaryInfo); + + // summary of all of the packages + overall.add(summaryInfo); + } + + /** + * Add line coverage information about a method. + * + * This method aggregates the line coverage information for a method. + * + * @param packageName The name of the package the line info is from. + * @param filename The name of the file the line info is from. + * @param methodName The name of the method the line info is from. + * @param lineInfo The line info. + */ + private void addMethodSummaryInfo(final String packageName, final String filename, + final String methodName, final LineInfo lineInfo) + { + // ignore cinit and script init methods. + if (methodName == null || methodName.isEmpty()) + return; + + // method summary info + SummaryInfo methodSummaryInfo = methodSummaryMap.get(methodName); + if (methodSummaryInfo == null) + { + methodSummaryInfo = new SummaryInfo(); + methodSummaryInfo.setName(methodName); + methodSummaryMap.put(methodName, methodSummaryInfo); + + final String key = createPackageFileKey(packageName, filename); + TreeSet<String> treeSet = methodsInFile.get(key); + if (treeSet == null) + { + treeSet = new TreeSet<String>(); + treeSet.add(methodName); + methodsInFile.put(key, treeSet); + } + + treeSet.add(methodName); + } + + methodSummaryInfo.setLineExecutionStatus(lineInfo.getLineNumber(), + methodName, lineInfo.isExecuted()); + } + + /** + * Initialize the patterns for the include and exclude filters. + * + * @param includeFilters regex strings of classes to include. + * @param excludeFilters regex strings of classes to exclude. + */ + private void initializePatterns(final Collection<String> includeFilters, + final Collection<String>excludeFilters) + { + for (final String includeFilter : includeFilters) + { + includeFilterPatterns.add(Pattern.compile(includeFilter)); + } + + for (final String excludeFilter : excludeFilters) + { + excludeFilterPatterns.add(Pattern.compile(excludeFilter)); + } + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/LineInfo.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/LineInfo.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/LineInfo.java new file mode 100755 index 0000000..1c22e93 --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/LineInfo.java @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +/** + * Simple value class that contains information about a line number in a source + * file. + * + * Tracks: + * 1. the line number of this line + * 2. if the line was executed or not + * 3. the name of the method the line is in. + */ +public class LineInfo +{ + private int lineNumber; + private boolean executed; + private String methodName; + + /** + * Create an instance of LineInfo. + * + * @param methodName The name of the method. + * @param lineNumber The source line number. + */ + public LineInfo(final String methodName, final int lineNumber) + { + this.methodName = methodName; + this.lineNumber = lineNumber; + } + + /** + * The source line number of this line. + * @return the line number + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * Set the source line number of this line. + * + * @param lineNumber + */ + public void setLineNumber(final int lineNumber) + { + this.lineNumber = lineNumber; + } + + /** + * Has the line been executed? + * + * @return true if the line has been executed, false otherwise. + */ + public boolean isExecuted() + { + return executed; + } + + /** + * Set the line number as being executed. + * + * @param executed true if the line has been hit, false otherwise. + */ + public void setExecuted(final boolean executed) + { + this.executed = executed; + } + + /** + * The method name containing this line. + * + * @return The method name or an empty string if no method name is set. + */ + public String getMethodName() + { + return methodName != null ? methodName : ""; + } + + /** + * Set the method name this line is contained in. + * + * @param methodName + */ + public void setMethodName(final String methodName) + { + this.methodName = methodName; + } +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/PackageSummaryInfo.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/PackageSummaryInfo.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/PackageSummaryInfo.java new file mode 100755 index 0000000..6e1fcdc --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/PackageSummaryInfo.java @@ -0,0 +1,77 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +import java.util.Set; +import java.util.TreeSet; + +/** + * Summary of a package's coverage information. + * Contains coverage summary of all of the files in the package. + */ +public class PackageSummaryInfo +{ + private Set<String> filesInPackage = new TreeSet<String>(); + private SummaryInfo summaryInfo = new SummaryInfo(); + + /** + * Set of files in a package. + * + * @return The set files in the package. Each item in the + * set is a combined package name/filename in the following + * format: "packageName;filename". + */ + public Set<String> getFilesInPackage() + { + return filesInPackage; + } + + /** + * Add a file to this package summary info. + * + * @param packageName The package name to add the summary info to. + * @param filename The name of file to add to the package. + */ + public void addFile(final String packageName, final String filename) + { + filesInPackage.add(packageName + ";" + filename); + } + + /** + * The rolled up summary info for a package. + * + * @return SummaryInfo + */ + public SummaryInfo getSummaryInfo() + { + return summaryInfo; + } + + /** + * Aggregate summary info to this package. + * + * @param info The summary info. + */ + public void add(final SummaryInfo info) + { + summaryInfo.add(info); + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryInfo.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryInfo.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryInfo.java new file mode 100755 index 0000000..08e490a --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryInfo.java @@ -0,0 +1,187 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; + +/** + * Simple value class to record summary information on the lines executed vs the + * total number of lines. + * + * This information can be used for reporting at the package, file, and method + * level. + * + * 1. total executed lines + * 2. total number of lines + * 3. total executed methods + * 4. total number of methods + * + */ +public class SummaryInfo +{ + // name of object the summary info is for. + private String name; + + /** + * The set of executed lines in sorted order. + */ + private final Set<Integer> executedLineSet = new TreeSet<Integer>(); + + /** + * The set of unexecuted lines in sorted order. + */ + private final Set<Integer> unexecutedLineSet = new TreeSet<Integer>(); + + /** + * The set of executed lines in sorted order. + */ + private final Set<String> executedMethodSet = new TreeSet<String>(); + private final Set<String> unexecutedMethodSet = new TreeSet<String>(); + + /** + * Name of the object the summary information applies to. + * @return name of the object. + */ + public String getName() + { + return name != null ? name : ""; + } + + /** + * Set the name of the object the summary information applies to. + * @param name Name of the object. + */ + public void setName(final String name) + { + this.name = name; + } + + /** + * @return Number of executed lines. + */ + public int getExecutedLineCount() + { + return executedLineSet.size(); + } + + /** + * + * @return total number of lines. + */ + public int getTotalLineCount() + { + return executedLineSet.size() + unexecutedLineSet.size(); + } + + /** + * + * @return Number of methods executed. + */ + public int getExecutedMethodCount() + { + return executedMethodSet.size(); + } + + /** + * + * @return Total number of methods. + */ + public int getTotalMethodCount() + { + return executedMethodSet.size() + unexecutedMethodSet.size(); + } + + /** + * The set of unexecuted lines. + * + * @return Set of executed lines, sorted in ascending order. + */ + public Set<Integer> getUnexecutedLines() + { + return Collections.unmodifiableSet(unexecutedLineSet); + } + + /** + * The set of unexecuted methods. + * + * @return Set of executed methods, sorted in ascending order. + */ + public Set<String> getUnexecutedMethods() + { + return Collections.unmodifiableSet(unexecutedMethodSet); + } + + /** + * Set the execution status of a given line and method. + * + * @param lineNumber The line number of the line. + * @param methodName The name of the method containing the line. + * @param executed true if the line is executed, false otherwise. + */ + public void setLineExecutionStatus(final int lineNumber, String methodName, + final Boolean executed) + { + if (executedLineSet.contains(lineNumber)) + return; + + // fixup method name that were not executed. + if (methodName == null) + methodName = ""; + + if (executed) + { + unexecutedLineSet.remove(lineNumber); + executedLineSet.add(lineNumber); + + // ignore empty method names. + // these are cinit and script init methods. + if (!methodName.isEmpty()) + { + unexecutedMethodSet.remove(methodName); + executedMethodSet.add(methodName); + } + } + else + { + unexecutedLineSet.add(lineNumber); + + // ignore empty method names. + // these are cinit and script init methods. + if (!methodName.isEmpty()) + unexecutedMethodSet.add(methodName); + } + } + + /** + * Add the passed summary info to the current set of summary info. + * + * @param summaryInfo + * summary information to add in, may not be null. + */ + public void add(final SummaryInfo summaryInfo) + { + unexecutedLineSet.addAll(summaryInfo.unexecutedLineSet); + unexecutedMethodSet.addAll(summaryInfo.unexecutedMethodSet); + executedLineSet.addAll(summaryInfo.executedLineSet); + executedMethodSet.addAll(summaryInfo.executedMethodSet); + } +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryType.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryType.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryType.java new file mode 100755 index 0000000..cd6be2f --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/SummaryType.java @@ -0,0 +1,29 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter; + +/** + * The type of summary info, either line or method. + */ +public enum SummaryType +{ + LINE, + METHOD +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportFactory.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportFactory.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportFactory.java new file mode 100755 index 0000000..7603726 --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportFactory.java @@ -0,0 +1,37 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.reports; + +/** + * Interface for code coverage report factories. + * + * The default report factory can overridden by specifying the fully qualified + * class name of a report factory with the -report-factory command line option. + * + */ +public interface IReportFactory +{ + /** + * Create a report writer that will output the code coverage report. + * + * @param reportOptions Options to control the contents of the report. + */ + IReportWriter createReport(ReportOptions reportOptions); +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportWriter.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportWriter.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportWriter.java new file mode 100755 index 0000000..353cd0f --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/IReportWriter.java @@ -0,0 +1,40 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.reports; + +import java.io.BufferedWriter; + +import org.apache.flex.tools.codecoverage.reporter.CoverageSummary; + +/** + * Interface to a report writer. + * + * Allow for different reports to be created. + */ +public interface IReportWriter +{ + /** + * Write a report to the specified writer. + * + * @param writer The writer. + * @param coverageSummary A summary of the coverage data. + */ + void writeTo(BufferedWriter writer, CoverageSummary coverageSummary); +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/ReportOptions.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/ReportOptions.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/ReportOptions.java new file mode 100644 index 0000000..ac129ff --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/ReportOptions.java @@ -0,0 +1,204 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.reports; + +import java.util.Collection; + +/** + * Code Coverage Report options. + * + */ +public class ReportOptions +{ + private Collection<String> includeFilters; + private Collection<String> excludeFilters; + private boolean showPackages = true; + private boolean showFiles = true; + private boolean showMethods = true; + private boolean showUnexecutedDetails = false; + + /** + * Constructor. + * + * @param includeFilters User specified include filters. + * Equivalent to the -includes command line argument. + * @param excludeFilters User specified exclude filters. + * Equivalent to the -excludes command line argument. + * @param showPackages Determines if packages are listed in the report. + * Equivalent to the -showPackages command line argument. + * @param showFiles Determines if files are listed in the report. Files + * will not be show if packages are not shown. + * Equivalent to the -showFiles command line argument. + * @param showMethods Determines if method are listed in the report. + * Equivalent to the -showMethods command line argument. + * @param showUnexecutedDetails Determines if unexecuted lines and methods are + * listed in the report. + * Equivalent to the -showUnexecutedDetails command line argument. + */ + public ReportOptions(Collection<String> includeFilters, + Collection<String> excludeFilters, boolean showPackages, + boolean showFiles, boolean showMethods, + boolean showUnexecutedDetails) + { + super(); + this.includeFilters = includeFilters; + this.excludeFilters = excludeFilters; + this.showPackages = showPackages; + this.showFiles = showFiles; + this.showMethods = showMethods; + this.showUnexecutedDetails = showUnexecutedDetails; + } + + /** + * User specified include filters. + * Equivalent to the -includes command line argument. + * + * @return Collection of user filters. + */ + public Collection<String> getIncludeFilters() + { + return includeFilters; + } + + /** + * Equivalent to the -includes command line argument. + * + * @param includeFilters Collection of filters to include classes in the + * report. + */ + public void setIncludeFilters(Collection<String> includeFilters) + { + this.includeFilters = includeFilters; + } + + /** + * User specified exclude filters. + * Equivalent to the -excludes command line argument. + * + * @return Collection of filters to exclude classes from the report. + */ + public Collection<String> getExcludeFilters() + { + return excludeFilters; + } + + /** + * Equivalent to the -excludes command line argument. + * + * @param excludeFilters Collection of filters to exclude classes from + * the report. + */ + public void setExcludeFilters(Collection<String> excludeFilters) + { + this.excludeFilters = excludeFilters; + } + + /** + * Determines if packages are listed in the report. + * Equivalent to the -showPackages command line argument. + * + * @return true if packages are listed in the report, false otherwise. + */ + public boolean isShowPackages() + { + return showPackages; + } + + /** + * Determines if packages are listed in the report. + * Equivalent to the -showPackages command line argument. + * + * @param showPackages Set to true to show packages in the report. + */ + public void setShowPackages(boolean showPackages) + { + this.showPackages = showPackages; + } + + /** + * Determines if files are listed in the report. + * Equivalent to the -showFiles command line argument. + * + * @return true if files are listed in the report, false otherwise. + */ + public boolean isShowFiles() + { + return showFiles; + } + + /** + * Determines if files are listed in the report. + * Equivalent to the -showFiles command line argument. + * + * @param showFiles Set to true to show files in the report. + */ + public void setShowFiles(boolean showFiles) + { + this.showFiles = showFiles; + } + + /** + * Determines if methods are listed in the report. + * Equivalent to the -showMethods command line argument. + * + * @return true if methods are listed in the report, false otherwise. + */ + public boolean isShowMethods() + { + return showMethods; + } + + /** + * Determines if methods are listed in the report. + * Equivalent to the -showMethods command line argument. + * + * @param showFiles Set to true to show methods in the report. + */ + public void setShowMethods(boolean showMethods) + { + this.showMethods = showMethods; + } + + /** + * Determines if unexecuted lines and unexecuted methods are listed in the + * report. + * Equivalent to the -showUnexecutedDetails command line argument. + * + * @return true if unexecuted lines and unexecuted are listed in the + * report, false otherwise. + */ + public boolean isShowUnexecutedDetails() + { + return showUnexecutedDetails; + } + + /** + * Determines if unexecuted lines and unexecuted methods are listed in the + * report. + * Equivalent to the -showUnexecutedDetails command line argument. + * + * @param showUnexecutedDetails Set to true to show methods in the report. + */ + public void setShowUnexecutedDetails(boolean showUnexecutedDetails) + { + this.showUnexecutedDetails = showUnexecutedDetails; + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportFactory.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportFactory.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportFactory.java new file mode 100755 index 0000000..d5e405f --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportFactory.java @@ -0,0 +1,36 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.reports; + +/** + * Factory for an XML report of code coverage. + * This is the default factory. + * + */ +public class XMLReportFactory implements IReportFactory +{ + + @Override + public IReportWriter createReport(ReportOptions reportOptions) + { + return new XMLReportWriter(reportOptions); + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportWriter.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportWriter.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportWriter.java new file mode 100755 index 0000000..aeeec72 --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/reports/XMLReportWriter.java @@ -0,0 +1,371 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.reports; + +import java.io.BufferedWriter; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.apache.flex.tools.codecoverage.reporter.CoverageSummary; +import org.apache.flex.tools.codecoverage.reporter.PackageSummaryInfo; +import org.apache.flex.tools.codecoverage.reporter.SummaryInfo; +import org.apache.flex.tools.codecoverage.reporter.SummaryType; + +/** + * XML formatted code coverage report. + * + */ +public class XMLReportWriter implements IReportWriter +{ + private static final String REPORT_VERSION = "1.0"; + + private Collection<String> includeFilters; + private Collection<String> excludeFilters; + private boolean showPackages; + private boolean showFiles; + private boolean showMethods; + private boolean showUnexecutedDetails; + + /** + * Constructor. + * + * @param reportOptions Options to control the contents of the report. + */ + public XMLReportWriter(final ReportOptions reportOptions) + { + this.includeFilters = reportOptions.getIncludeFilters(); + this.excludeFilters = reportOptions.getExcludeFilters(); + this.showPackages = reportOptions.isShowPackages(); + this.showFiles = reportOptions.isShowFiles(); + this.showMethods = reportOptions.isShowMethods(); + this.showUnexecutedDetails = reportOptions.isShowUnexecutedDetails(); + } + + @Override + public void writeTo(final BufferedWriter writer, final CoverageSummary coverageSummary) + { + final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); + assert xmlOutputFactory != null : "Expect XMLOutputFactory implementation."; + + try + { + final XMLStreamWriter xmlWriter = xmlOutputFactory. + createXMLStreamWriter(writer); + xmlWriter.writeStartDocument("utf-8", "1.0"); + + // write top-level report + outputTopLevelStart(xmlWriter, coverageSummary.getOverallSummaryInfo()); + + if (showPackages) + { + // start package element + xmlWriter.writeStartElement("packages"); + + for (Map.Entry<String, PackageSummaryInfo> packageEntry : + coverageSummary.getPackageSummaryEntrySet()) + { + PackageSummaryInfo packageSummary = packageEntry.getValue(); + outputPackageCoverageStart(xmlWriter, packageSummary.getSummaryInfo()); + + if (showFiles) + { + outputFiles(xmlWriter, packageSummary, coverageSummary); + } + + xmlWriter.writeEndElement(); // package + } + + xmlWriter.writeEndElement(); // packages + } + + xmlWriter.writeEndElement(); // code-coverage (top-level) + xmlWriter.writeEndDocument(); + xmlWriter.close(); + } + catch (XMLStreamException e) + { + e.printStackTrace(); + } + + } + + /** + * Output the files xml and its children. + * + * @param xmlWriter + * @param packageSummary + * @param coverageSummary + * @throws XMLStreamException + */ + private void outputFiles(final XMLStreamWriter xmlWriter, + final PackageSummaryInfo packageSummary, + final CoverageSummary coverageSummary) throws XMLStreamException + { + // start file element + xmlWriter.writeStartElement("files"); + + for (final String packageFileKey : packageSummary.getFilesInPackage()) + { + final SummaryInfo fileSummary = coverageSummary.getFileSummaryInfo(packageFileKey); + outputFileCoverageStart(xmlWriter, fileSummary); + + final Set<String> methodNames = coverageSummary.getMethodsInFile(packageFileKey); + if (showMethods && !methodNames.isEmpty()) + { + // start method element + xmlWriter.writeStartElement("methods"); + + for (final String methodName : methodNames) + { + SummaryInfo methodSummary = coverageSummary. + getMethodSummaryInfo(methodName); + outputMethodNameCoverage(xmlWriter, methodSummary); + } + + xmlWriter.writeEndElement(); // methods + } + + xmlWriter.writeEndElement(); // file + } + + xmlWriter.writeEndElement(); // files + } + + /** + * Output the coverage information for a method. + * + * @param xmlWriter + * @param summaryInfo + * @throws XMLStreamException + */ + private void outputMethodNameCoverage(final XMLStreamWriter xmlWriter, + final SummaryInfo summaryInfo) throws XMLStreamException + { + xmlWriter.writeStartElement("method"); + xmlWriter.writeAttribute("name", summaryInfo.getName()); + + outputLineCoverage(xmlWriter, summaryInfo, false); + outputMethodCoverage(xmlWriter, summaryInfo, false); + xmlWriter.writeEndElement(); + } + + /** + * Output the start element for file coverage information. + * + * @param xmlWriter + * @param summaryInfo + * @throws XMLStreamException + */ + private void outputFileCoverageStart(final XMLStreamWriter xmlWriter, + final SummaryInfo summaryInfo) throws XMLStreamException + { + xmlWriter.writeStartElement("file"); + xmlWriter.writeAttribute("name", summaryInfo.getName()); + + outputLineCoverage(xmlWriter, summaryInfo, showUnexecutedDetails); + outputMethodCoverage(xmlWriter, summaryInfo, showUnexecutedDetails); + } + + /** + * Output code coverage for a package. + * @param xmlWriter + * @param summaryInfo + * @throws XMLStreamException + */ + private void outputPackageCoverageStart(final XMLStreamWriter xmlWriter, + final SummaryInfo summaryInfo) throws XMLStreamException + { + xmlWriter.writeStartElement("package"); + xmlWriter.writeAttribute("name", summaryInfo.getName()); + + outputLineCoverage(xmlWriter, summaryInfo, false); + outputMethodCoverage(xmlWriter, summaryInfo, false); + } + + /** + * Output top level summary information + * + * @param summaryInfo overall coverage info. + * @throws XMLStreamException + */ + private void outputTopLevelStart(final XMLStreamWriter xmlWriter, + final SummaryInfo summaryInfo) throws XMLStreamException + { + xmlWriter.writeStartElement("code-coverage"); + xmlWriter.writeAttribute("version", REPORT_VERSION); + + outputLineCoverage(xmlWriter, summaryInfo, false); + outputMethodCoverage(xmlWriter, summaryInfo, false); + outputFilter(xmlWriter, "include-filter"); + outputFilter(xmlWriter, "exclude-filter"); + } + + /** + * Output filters as part of the report. + * + * @param xmlWriter + * @param filterName The name of the filter to output. + * @throws XMLStreamException + */ + private void outputFilter(final XMLStreamWriter xmlWriter, + final String filterName) throws XMLStreamException + { + Collection<String> filters = null; + String startElementName = null; + + if ("include-filter".equals(filterName)) + { + startElementName = "include-filters"; + filters = includeFilters; + } + else if ("exclude-filter".equals(filterName)) + { + startElementName = "exclude-filters"; + filters = excludeFilters; + } + else + { + throw new IllegalArgumentException("unexpected filter name:" + filterName); + } + + if (filters.isEmpty()) + return; + + xmlWriter.writeStartElement(startElementName); + + for (final String filter : filters) + { + xmlWriter.writeStartElement(filterName); + xmlWriter.writeCharacters(filter); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeEndElement(); + } + + /** + * Common method to output line coverage information. + * + * @param xmlWriter + * @param overall + * @throws XMLStreamException + */ + private void outputLineCoverage(final XMLStreamWriter xmlWriter, + final SummaryInfo overall, boolean showDetails) throws XMLStreamException + { + outputSummaryInfo(xmlWriter, "line-coverage", overall, SummaryType.LINE, showDetails); + } + + /** + * Common method to output method coverage information. + * + * @param xmlWriter + * @param overall + * @throws XMLStreamException + */ + private void outputMethodCoverage(final XMLStreamWriter xmlWriter, + final SummaryInfo overall, final boolean showDetails) + throws XMLStreamException + { + outputSummaryInfo(xmlWriter, "method-coverage", overall, + SummaryType.METHOD, showDetails); + } + + /** + * Common method to output summary information. + * + * @param xmlWriter The writer. + * @param elementName The name of the element. + * @param summaryInfo The summary info to output. + * @param summaryType Choose either LINE or METHOD. + * @param showDetails Show the lines/methods that were not executed. + * @throws XMLStreamException + */ + private void outputSummaryInfo(final XMLStreamWriter xmlWriter, + final String elementName, + final SummaryInfo summaryInfo, + final SummaryType summaryType, + final boolean showDetails) throws XMLStreamException + { + xmlWriter.writeStartElement(elementName); + + final int executed; + final int total; + final int percent; + Set<?> items = null; + + if (summaryType == SummaryType.LINE) + { + executed = summaryInfo.getExecutedLineCount(); + total = summaryInfo.getTotalLineCount(); + percent = total == 0 ? 0 : executed * 100 / total; + if (showDetails) + items = summaryInfo.getUnexecutedLines(); + } + else + { + executed = summaryInfo.getExecutedMethodCount(); + total = summaryInfo.getTotalMethodCount(); + percent = total == 0 ? 0 : executed * 100 / total; + if (showDetails) + items = summaryInfo.getUnexecutedMethods(); + } + + xmlWriter.writeAttribute("executed", Integer.toString(executed)); + xmlWriter.writeAttribute("total", Integer.toString(total)); + xmlWriter.writeAttribute("percent", Integer.toString(percent)); + + if (showDetails) + outputUnexecutedSet(xmlWriter, items); + + xmlWriter.writeEndElement(); + } + + /** + * Output unexecuted lines or methods. + * + * @param xmlWriter + * @param items + * @throws XMLStreamException + */ + private void outputUnexecutedSet(final XMLStreamWriter xmlWriter, + final Set<?> items) throws XMLStreamException + { + if (items == null || items.isEmpty()) + return; + + xmlWriter.writeStartElement("unexecuted-set"); + + for (Object item : items) + { + xmlWriter.writeStartElement("unexecuted"); + xmlWriter.writeCharacters(item.toString()); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeEndElement(); + } + +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/5077c457/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/swf/DebugLineVisitor.java ---------------------------------------------------------------------- diff --git a/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/swf/DebugLineVisitor.java b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/swf/DebugLineVisitor.java new file mode 100755 index 0000000..648e19b --- /dev/null +++ b/CodeCoverage/JavaServer/java/src/org/apache/flex/tools/codecoverage/reporter/swf/DebugLineVisitor.java @@ -0,0 +1,239 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.flex.tools.codecoverage.reporter.swf; + +import org.apache.flex.abc.ABCConstants; +import org.apache.flex.abc.Pool; +import org.apache.flex.abc.instructionlist.InstructionList; +import org.apache.flex.abc.semantics.ExceptionInfo; +import org.apache.flex.abc.semantics.Instruction; +import org.apache.flex.abc.semantics.Label; +import org.apache.flex.abc.semantics.MethodBodyInfo; +import org.apache.flex.abc.semantics.MethodInfo; +import org.apache.flex.abc.semantics.Name; +import org.apache.flex.abc.visitors.IMethodBodyVisitor; +import org.apache.flex.abc.visitors.IMethodVisitor; +import org.apache.flex.abc.visitors.ITraitsVisitor; +import org.apache.flex.abc.visitors.NilABCVisitor; +import org.apache.flex.abc.visitors.NilVisitors; +import org.apache.flex.tools.codecoverage.reporter.CoverageData; + +/** + * Gather all the debug lines and method in a SWF file. + * Look for debug_file and debug_line op codes and record them so we know all + * of possible lines in the SWF that can be executed. + * + */ +public class DebugLineVisitor extends NilABCVisitor +{ + + private final Pool<String> stringPool = new Pool<String>(Pool.DefaultType.HasDefaultZero); + private final CoverageData coverageData; + + /** + * Constructor + * + * @param coverageData The coverage data where debug lines will be added + * as unexecuted lines. If a line is already in the coverage data and is + * marked as executed, then it will remain an executed line. + */ + public DebugLineVisitor(final CoverageData coverageData) + { + this.coverageData = coverageData; + } + + @Override + public IMethodVisitor visitMethod(MethodInfo minfo) + { + return new MethodVisitor(); + } + + @Override + public void visitPooledString(String s) + { + stringPool.add(s); + } + + /** + * Visit all the methods in the ABC. + */ + private class MethodVisitor implements IMethodVisitor + { + @Override + public void visitEnd() + { + } + + @Override + public void visit() + { + } + + @Override + public IMethodBodyVisitor visitBody(MethodBodyInfo mbInfo) + { + return new MethodBodyVisitor(mbInfo); + } + } + + /** + * Visit all the method bodies in the ABC. + */ + private class MethodBodyVisitor implements IMethodBodyVisitor + { + private final MethodBodyInfo mbInfo; + private String currentFilename; + + public MethodBodyVisitor(MethodBodyInfo mbInfo) + { + this.mbInfo = mbInfo; + } + + @Override + public void visitEnd() + { + } + + @Override + public void labelCurrent(Label l) + { + } + + @Override + public void labelNext(Label l) + { + } + + @Override + public void visit() + { + } + + @Override + public int visitException(Label from, Label to, Label target, + Name exception_type, Name catch_var) + { + return mbInfo.addExceptionInfo(new ExceptionInfo(from, to, target, + exception_type, catch_var)); + } + + @Override + public void visitInstructionList(InstructionList new_list) + { + for (Instruction inst : new_list.getInstructions()) + { + int opcode = inst.getOpcode(); + assert opcode != ABCConstants.OP_debugfile && + opcode != ABCConstants.OP_debugline : + "unexpected debug opcode"; + } + } + + @Override + public void visitInstruction(int opcode) + { + assert opcode != ABCConstants.OP_debugfile && + opcode != ABCConstants.OP_debugline : + "unexpected debug opcode"; + } + + @Override + public void visitInstruction(int opcode, int immediate_operand) + { + if (opcode == ABCConstants.OP_debugfile) + { + recordDebugFile(immediate_operand); + } + else if (opcode == ABCConstants.OP_debugline) + { + recordDebugLine(immediate_operand); + } + } + + @Override + public void visitInstruction(int opcode, Object[] operands) + { + assert opcode != ABCConstants.OP_debugfile && + opcode != ABCConstants.OP_debugline : + "unexpected debug opcode"; + } + + @Override + public void visitInstruction(int opcode, Object single_operand) + { + assert opcode != ABCConstants.OP_debugline : + "unexpected debug opcode"; + + if (opcode == ABCConstants.OP_debugfile) + recordDebugFile((String)single_operand); + } + + @Override + public void visitInstruction(Instruction instruction) + { + int opcode = instruction.getOpcode(); + assert opcode != ABCConstants.OP_debugfile && + opcode != ABCConstants.OP_debugline : + "unexpected debug opcode"; + } + + @Override + public ITraitsVisitor visitTraits() + { + return NilVisitors.NIL_TRAITS_VISITOR; + } + + /** + * Record a debugfile string + * + * @param filenameIndex Index of filename in the string pool. + */ + private void recordDebugFile(final int filenameIndex) + { + recordDebugFile(stringPool.getValues().get(filenameIndex)); + } + + /** + * Set the filename in the context for debug lines that + * come after it. + * + * @param filename The debugfile filename. + */ + private void recordDebugFile(final String filename) + { + currentFilename = filename; + } + + /** + * Record a debug line into the coverage data. + * + * @param lineNumber The line number of the debug line in + * the file that is set by the last debugfile opcode. + */ + private void recordDebugLine(final int lineNumber) + { + assert currentFilename != null : + "currentFilename must have been set by a debugFile instruction"; + + String methodName = mbInfo.getMethodInfo().getMethodName(); + coverageData.addLine(currentFilename, lineNumber, methodName); + } + } +}