001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.engine.sourcecode.coverage;
018
019import java.util.ArrayList;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Set;
023
024import org.conqat.engine.commons.findings.location.ILineAdjuster;
025import org.conqat.engine.commons.findings.location.LocationAdjuster;
026import org.conqat.lib.commons.collections.UnmodifiableList;
027import org.conqat.lib.commons.logging.ILogger;
028import org.conqat.lib.commons.region.SimpleRegion;
029
030import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
031import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
032import eu.cqse.check.framework.shallowparser.framework.ShallowEntityTraversalUtils;
033
034/**
035 * Utility methods for dealing with coverage information.
036 */
037public class TestCoverageUtils {
038
039        /**
040         * Adjusts line coverage information for a file to coverage information for a
041         * changed file. The changes to the file are described by a location adjuster.
042         * Due to the nature of coverage, each small change could affect the coverage
043         * globally, hence this is only a rough approximation. This method is robust
044         * w.r.t lines numbers that are out of the range w.r.t. the given
045         * {@link LocationAdjuster}. Such invalid lines are logged as error to the given
046         * logger.
047         * 
048         * @param coverageInfo
049         *            this may be null and then the result is null as well.
050         */
051        public static LineCoverageInfo adjustCoverageInfo(LineCoverageInfo coverageInfo, ILineAdjuster adjuster,
052                        ILogger logger, String appendToErrorMessage) {
053                if (coverageInfo == null) {
054                        return null;
055                }
056
057                LineCoverageInfo adjustedCoverageInfo = new LineCoverageInfo(coverageInfo.getTimestamp(),
058                                coverageInfo.isMethodAccurate());
059
060                for (ELineCoverage lineCoverageType : ELineCoverage.values()) {
061                        List<Integer> relevantLines = lineCoverageType.getRelevantLines(coverageInfo);
062                        adjustLines(relevantLines, lineCoverageType, adjustedCoverageInfo, adjuster, logger, appendToErrorMessage);
063                }
064
065                return adjustedCoverageInfo;
066        }
067
068        /**
069         * Adjusts the given lines using a location adjuster and fills them into the
070         * provided coverage info. Invalid lines, i.e. lines numbers that are out of
071         * range w.r.t the given {@link LocationAdjuster} are logged as error to the
072         * given logger.
073         * 
074         * @param lines
075         *            the line numbers (one-based)
076         */
077        private static void adjustLines(List<Integer> lines, ELineCoverage coverageType,
078                        LineCoverageInfo adjustedCoverageInfo, ILineAdjuster adjuster, ILogger logger,
079                        String appendToErrorMessage) {
080                Set<Integer> invalidLines = new HashSet<>();
081                for (int line : lines) {
082                        SimpleRegion adjustedLines = adjuster.adjustLine(line, invalidLines);
083                        if (adjustedLines == null) {
084                                continue;
085                        }
086                        for (int adjustedLine = adjustedLines.getStart(); adjustedLine <= adjustedLines.getEnd(); ++adjustedLine) {
087                                adjustedCoverageInfo.addLineCoverage(adjustedLine, coverageType);
088                        }
089                }
090                reportInvalidLines(logger, appendToErrorMessage, invalidLines, adjuster);
091        }
092
093        private static void reportInvalidLines(ILogger logger, String appendToErrorMessage, Set<Integer> invalidLines,
094                        ILineAdjuster adjuster) {
095                if (!invalidLines.isEmpty()) {
096                        logger.error("Encountered invalid lines: " + invalidLines + ". Line count: "
097                                        + adjuster.getOriginalLineCount() + ". " + appendToErrorMessage);
098                }
099        }
100
101        /**
102         * Adjusts the line information of the given probole coverage using the
103         * specified location adjuster.
104         */
105        public static void adjustProbeBasedCoverage(ProbeCoverageInfo probeCoverageInfo, LocationAdjuster adjuster,
106                        ILogger logger, String appendToErrorMessage) {
107                if (probeCoverageInfo == null) {
108                        return;
109                }
110                Set<Integer> invalidLines = new HashSet<>();
111                UnmodifiableList<CoverageProbeBase> probes = probeCoverageInfo.getProbes();
112                List<CoverageProbeBase> validProbes = new ArrayList<>();
113                for (CoverageProbeBase probe : probes) {
114                        SimpleRegion adjustedRegion = adjuster.adjustLine(probe.getLine(), invalidLines);
115                        if (adjustedRegion != null) {
116                                validProbes.add(probe);
117                                probe.setLine(adjustedRegion.getStart());
118                        }
119                }
120                reportInvalidLines(logger, appendToErrorMessage, invalidLines, adjuster);
121                probeCoverageInfo.setProbes(validProbes);
122        }
123
124        /**
125         * Adjust the line-coverage to expand to full statements (= shallow entity start
126         * and end)
127         */
128        public static void adjustCoverageToStatements(LineCoverageInfo coverage, List<ShallowEntity> entities) {
129                for (ShallowEntity shallowEntity : ShallowEntityTraversalUtils.listEntitiesOfType(entities,
130                                EShallowEntityType.STATEMENT)) {
131                        int startLine = shallowEntity.getStartLine();
132                        int endLine = shallowEntity.getEndLine();
133                        ELineCoverage lineCoverage = coverage.getLineCoverage(startLine);
134
135                        if (shallowEntity.hasChildren() || startLine == endLine || lineCoverage == null
136                                        || lineCoverage == ELineCoverage.NOT_COVERED) {
137                                continue;
138                        }
139
140                        for (int i = startLine + 1; i <= endLine; i++) {
141                                coverage.addLineCoverage(i, lineCoverage);
142                        }
143                }
144        }
145}