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}