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.io.Serializable;
020import java.util.Comparator;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.Set;
025import java.util.stream.Collectors;
026
027import org.conqat.lib.commons.collections.IntList;
028
029import com.fasterxml.jackson.annotation.JsonCreator;
030import com.fasterxml.jackson.annotation.JsonProperty;
031
032/**
033 * Holds line range coverage information for a test covering multiple files.
034 */
035public class MultiFileRangeCoverageInfo implements Serializable {
036
037        private static final long serialVersionUID = 1L;
038
039        /**
040         * Mapping from normalized uniform path to a list of line numbers. The list may
041         * contain duplicates and may be unsorted.
042         */
043        @JsonProperty("uniformPathToRanges")
044        private final Map<String, IntList> uniformPathToRanges = new HashMap<>();
045
046        /**
047         * The timestamp of the code this coverage data refers to. May be -1 if unknown.
048         */
049        @JsonProperty("timestamp")
050        private long timestamp;
051
052        /** Constructor with a default timestamp. */
053        @JsonCreator
054        public MultiFileRangeCoverageInfo() {
055                this(-1);
056        }
057
058        public MultiFileRangeCoverageInfo(long timestamp) {
059                this.timestamp = timestamp;
060        }
061
062        /**
063         * Merges all coverage contained in newCoverage into this coverage. Coverage
064         * from the same test case replaces existing coverage.
065         */
066        public void addLineCoverage(MultiFileRangeCoverageInfo newCoverage) {
067                Set<Entry<String, IntList>> newTestCoverage = newCoverage.uniformPathToRanges.entrySet();
068                for (Entry<String, IntList> testCoverage : newTestCoverage) {
069                        uniformPathToRanges.put(testCoverage.getKey(), testCoverage.getValue());
070                }
071        }
072
073        /** Adds the coverage information for the given lines. */
074        public void addLineCoverage(String uniformPath, IntList lines) {
075                uniformPathToRanges.computeIfAbsent(uniformPath, k -> new IntList()).addAll(lines);
076        }
077
078        /** @see #timestamp */
079        public long getTimestamp() {
080                return timestamp;
081        }
082
083        /** @see #timestamp */
084        public void setTimestamp(long timestamp) {
085                this.timestamp = timestamp;
086        }
087
088        /** Returns the coverage represented by this object in raw form. */
089        public Map<String, IntList> getCoverage() {
090                return uniformPathToRanges;
091        }
092
093        /**
094         * {@inheritDoc}
095         */
096        @Override
097        public String toString() {
098                return "Covered: "
099                                + uniformPathToRanges.entrySet().stream().sorted(Comparator.comparing(Entry::getKey))
100                                                .map((pair) -> pair.getKey() + " " + pair.getValue()).collect(Collectors.joining(", "))
101                                + "; timestamp: " + timestamp;
102        }
103
104        /**
105         * If the test contains coverage from the given file, the uniform path is
106         * adjusted to match the path used inside Teamscale.
107         */
108        public void changeFilePath(String reportPath, String pathInTeamscale) {
109                if (uniformPathToRanges.containsKey(reportPath) && !reportPath.equals(pathInTeamscale)) {
110                        IntList ranges = uniformPathToRanges.remove(reportPath);
111                        uniformPathToRanges.put(pathInTeamscale, ranges);
112                }
113        }
114}