diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2016-10-14 17:36:53 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2016-10-18 16:00:26 +0200 |
commit | f21abcbb8250053703ce6edc36bfea03bb3665e7 (patch) | |
tree | 32522db31a2464c91898fec6eac03eca17effd31 /sonar-scanner-engine | |
parent | 6bec40f7b12d952e8fb1a1d61b1b5978593be96b (diff) | |
download | sonarqube-f21abcbb8250053703ce6edc36bfea03bb3665e7.tar.gz sonarqube-f21abcbb8250053703ce6edc36bfea03bb3665e7.zip |
SONAR-8281 Rework Sensor storage to merge multiple coverage reports
Diffstat (limited to 'sonar-scanner-engine')
20 files changed, 566 insertions, 804 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java index ca5dc73c1c9..2a22d573b1e 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java @@ -28,9 +28,9 @@ import java.util.Map; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; -import org.sonar.api.measures.Measure; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; import org.sonar.scanner.scan.measure.MeasureCache; @@ -152,8 +152,8 @@ public class DefaultFileLinesContext implements FileLinesContext { } private Map loadData(String metricKey, Converter converter) { - Measure measure = measureCache.byMetric(inputFile.key(), metricKey); - String data = measure != null ? measure.getData() : null; + DefaultMeasure<?> measure = measureCache.byMetric(inputFile.key(), metricKey); + String data = measure != null ? (String) measure.value() : null; if (data != null) { return ImmutableMap.copyOf(KeyValueFormat.parse(data, KeyValueFormat.newIntegerConverter(), converter)); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java index bd97a527353..8a5df01a559 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/DeprecatedSensorContext.java @@ -42,20 +42,17 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.scanner.index.DefaultIndex; import org.sonar.scanner.sensor.DefaultSensorContext; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; public class DeprecatedSensorContext extends DefaultSensorContext implements SensorContext { private final DefaultIndex index; private final Project project; - private final CoverageExclusions coverageFilter; public DeprecatedSensorContext(InputModule module, DefaultIndex index, Project project, Settings settings, FileSystem fs, ActiveRules activeRules, - AnalysisMode analysisMode, CoverageExclusions coverageFilter, SensorStorage sensorStorage, SonarRuntime sonarRuntime) { + AnalysisMode analysisMode, SensorStorage sensorStorage, SonarRuntime sonarRuntime) { super(module, settings, fs, activeRules, analysisMode, sensorStorage, sonarRuntime); this.index = index; this.project = project; - this.coverageFilter = coverageFilter; } @@ -124,19 +121,13 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen @Override public Measure saveMeasure(Resource resource, Metric metric, Double value) { Measure<?> measure = new Measure(metric, value); - coverageFilter.validate(measure, resource.getPath()); return saveMeasure(resource, measure); } @Override public Measure saveMeasure(Resource resource, Measure measure) { Resource resourceOrProject = resourceOrProject(resource); - - if (coverageFilter.accept(resourceOrProject, measure)) { - return index.addMeasure(resourceOrProject, measure); - } else { - return measure; - } + return index.addMeasure(resourceOrProject, measure); } @Override @@ -155,13 +146,11 @@ public class DeprecatedSensorContext extends DefaultSensorContext implements Sen @Override public Measure saveMeasure(InputFile inputFile, Metric metric, Double value) { Measure<?> measure = new Measure(metric, value); - coverageFilter.validate(measure, inputFile); return saveMeasure(getResource(inputFile), measure); } @Override public Measure saveMeasure(InputFile inputFile, Measure measure) { - coverageFilter.validate(measure, inputFile); return saveMeasure(getResource(inputFile), measure); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Cache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Cache.java index 1b6028d41f8..40ae68cfe7f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Cache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/Cache.java @@ -24,13 +24,11 @@ import com.persistit.Exchange; import com.persistit.Key; import com.persistit.KeyFilter; import com.persistit.exception.PersistitException; -import org.apache.commons.lang.builder.ToStringBuilder; - -import javax.annotation.CheckForNull; - import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; +import javax.annotation.CheckForNull; +import org.apache.commons.lang.builder.ToStringBuilder; /** * <p> @@ -73,7 +71,7 @@ public class Cache<V> { exchange.store(); return this; } catch (Exception e) { - throw new IllegalStateException("Fail to put element in the cache " + name, e); + throw new IllegalStateException("Fail to put element in the storage '" + name + "'", e); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java index cf929bc3271..23d1c4b8a66 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/index/DefaultIndex.java @@ -37,16 +37,21 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metric.ValueType; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.core.component.ComponentKeys; +import org.sonar.core.util.stream.Collectors; import org.sonar.scanner.DefaultProjectTree; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.sensor.DefaultSensorStorage; @@ -58,15 +63,17 @@ public class DefaultIndex { private final BatchComponentCache componentCache; private final MeasureCache measureCache; private final DefaultProjectTree projectTree; + private final MetricFinder metricFinder; // caches private DefaultSensorStorage sensorStorage; private Project currentProject; private Map<Resource, Bucket> buckets = Maps.newLinkedHashMap(); - public DefaultIndex(BatchComponentCache componentCache, DefaultProjectTree projectTree, MeasureCache measureCache) { + public DefaultIndex(BatchComponentCache componentCache, DefaultProjectTree projectTree, MeasureCache measureCache, MetricFinder metricFinder) { this.componentCache = componentCache; this.projectTree = projectTree; this.measureCache = measureCache; + this.metricFinder = metricFinder; } public void start() { @@ -143,25 +150,83 @@ public class DefaultIndex { if (indexedResource == null) { return null; } - Collection<Measure> unfiltered = new ArrayList<>(); + Collection<DefaultMeasure<?>> unfiltered = new ArrayList<>(); if (filter instanceof MeasuresFilters.MetricFilter) { // optimization - Measure byMetric = measureCache.byMetric(indexedResource, ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey()); + DefaultMeasure<?> byMetric = measureCache.byMetric(indexedResource.getEffectiveKey(), ((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey()); if (byMetric != null) { unfiltered.add(byMetric); } } else { - for (Measure measure : measureCache.byResource(indexedResource)) { + for (DefaultMeasure<?> measure : measureCache.byComponentKey(indexedResource.getEffectiveKey())) { unfiltered.add(measure); } } - return filter.filter(unfiltered); + return filter.filter(unfiltered.stream().map(DefaultIndex::toDeprecated).collect(Collectors.toList())); + } + + private static Measure toDeprecated(org.sonar.api.batch.sensor.measure.Measure<?> measure) { + org.sonar.api.measures.Measure deprecatedMeasure = new org.sonar.api.measures.Measure((Metric<?>) measure.metric()); + setValueAccordingToMetricType(measure, deprecatedMeasure); + return deprecatedMeasure; + } + + private static void setValueAccordingToMetricType(org.sonar.api.batch.sensor.measure.Measure<?> measure, Measure measureToSave) { + ValueType deprecatedType = ((Metric<?>) measure.metric()).getType(); + switch (deprecatedType) { + case BOOL: + measureToSave.setValue(Boolean.TRUE.equals(measure.value()) ? 1.0 : 0.0); + break; + case INT: + case MILLISEC: + case WORK_DUR: + case FLOAT: + case PERCENT: + case RATING: + measureToSave.setValue(((Number) measure.value()).doubleValue()); + break; + case STRING: + case LEVEL: + case DATA: + case DISTRIB: + measureToSave.setData((String) measure.value()); + break; + default: + throw new UnsupportedOperationException("Unsupported type :" + deprecatedType); + } } public Measure addMeasure(Resource resource, Measure measure) { Bucket bucket = getBucket(resource); if (bucket != null) { - sensorStorage.saveMeasure(resource, measure); + if (sensorStorage.isDeprecatedMetric(measure.getMetricKey())) { + // Ignore deprecated metrics + return measure; + } + org.sonar.api.batch.measure.Metric<?> metric = metricFinder.findByKey(measure.getMetricKey()); + if (metric == null) { + throw new UnsupportedOperationException("Unknown metric: " + measure.getMetricKey()); + } + DefaultMeasure<?> newMeasure; + if (Boolean.class.equals(metric.valueType())) { + newMeasure = new DefaultMeasure<Boolean>().forMetric((Metric<Boolean>) metric) + .withValue(measure.getValue() != 0.0); + } else if (Integer.class.equals(metric.valueType())) { + newMeasure = new DefaultMeasure<Integer>().forMetric((Metric<Integer>) metric) + .withValue(measure.getValue().intValue()); + } else if (Double.class.equals(metric.valueType())) { + newMeasure = new DefaultMeasure<Double>().forMetric((Metric<Double>) metric) + .withValue(measure.getValue().doubleValue()); + } else if (String.class.equals(metric.valueType())) { + newMeasure = new DefaultMeasure<String>().forMetric((Metric<String>) metric) + .withValue(measure.getData()); + } else if (Long.class.equals(metric.valueType())) { + newMeasure = new DefaultMeasure<Long>().forMetric((Metric<Long>) metric) + .withValue(measure.getValue().longValue()); + } else { + throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); + } + sensorStorage.saveMeasure(componentCache.get(resource).inputComponent(), newMeasure); } return measure; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java index 49f12b154c8..cf17785dab8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java @@ -26,15 +26,15 @@ import java.util.Map; import javax.annotation.Nonnull; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.Builder; -import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; +import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.Builder; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.measure.MeasureCache; public class CoveragePublisher implements ReportPublisherStep { @@ -112,7 +112,7 @@ public class CoveragePublisher implements ReportPublisherStep { } void applyLineMeasure(String inputFileKey, int lineCount, String metricKey, Map<Integer, LineCoverage.Builder> coveragePerLine, MeasureOperation op) { - Measure measure = measureCache.byMetric(inputFileKey, metricKey); + DefaultMeasure<?> measure = measureCache.byMetric(inputFileKey, metricKey); if (measure != null) { Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) measure.value()); for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java index 74e2d43c0ff..e00f9793cb1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java @@ -20,12 +20,10 @@ package org.sonar.scanner.report; import com.google.common.base.Function; -import com.google.common.base.Predicate; import java.io.Serializable; -import java.util.Set; import javax.annotation.Nonnull; import org.sonar.api.batch.measure.Metric; -import org.sonar.api.measures.Measure; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.core.metric.ScannerMetrics; import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.index.BatchComponentCache; @@ -35,16 +33,14 @@ import org.sonar.scanner.protocol.output.ScannerReport.Measure.DoubleValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.IntValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.LongValue; import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; -import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.measure.MeasureCache; -import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Sets.newHashSet; public class MeasuresPublisher implements ReportPublisherStep { - private static final class MeasureToReportMeasure implements Function<Measure, ScannerReport.Measure> { + private static final class MeasureToReportMeasure implements Function<DefaultMeasure, ScannerReport.Measure> { private final BatchComponent resource; private final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); @@ -53,69 +49,44 @@ public class MeasuresPublisher implements ReportPublisherStep { } @Override - public ScannerReport.Measure apply(@Nonnull Measure input) { + public ScannerReport.Measure apply(@Nonnull DefaultMeasure input) { validateMeasure(input, resource.key()); return toReportMeasure(builder, input); } - private static void validateMeasure(Measure measure, String componentKey) { - if (measure.getValue() == null && measure.getData() == null) { - throw new IllegalArgumentException(String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", measure.getMetricKey(), componentKey)); + private static void validateMeasure(DefaultMeasure measure, String componentKey) { + if (measure.value() == null) { + throw new IllegalArgumentException(String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", measure.metric().key(), componentKey)); } } - private ScannerReport.Measure toReportMeasure(ScannerReport.Measure.Builder builder, Measure measure) { + private static ScannerReport.Measure toReportMeasure(ScannerReport.Measure.Builder builder, DefaultMeasure measure) { builder.clear(); - builder.setMetricKey(measure.getMetricKey()); + builder.setMetricKey(measure.metric().key()); setValueAccordingToType(builder, measure); return builder.build(); } - private void setValueAccordingToType(ScannerReport.Measure.Builder builder, Measure measure) { + private static void setValueAccordingToType(ScannerReport.Measure.Builder builder, DefaultMeasure measure) { Serializable value = measure.value(); - String data = measure.getData() != null ? measure.getData() : ""; - switch (measure.getMetric().getType()) { - case INT: - case RATING: - builder.setIntValue(IntValue.newBuilder().setValue(((Number) value).intValue()).setData(data)); - break; - case FLOAT: - case PERCENT: - builder.setDoubleValue(DoubleValue.newBuilder().setValue(((Number) value).doubleValue()).setData(data)); - break; - case BOOL: - builder.setBooleanValue(BoolValue.newBuilder().setValue(((Boolean) value).booleanValue()).setData(data)); - break; - case WORK_DUR: - case MILLISEC: - builder.setLongValue(LongValue.newBuilder().setValue(((Number) value).longValue()).setData(data)); - break; - case STRING: - case DATA: - case LEVEL: - case DISTRIB: - builder.setStringValue(StringValue.newBuilder().setValue((String) value)); - break; - default: - throw new IllegalStateException("Unknown metric type: " + measure.getMetric().getType()); + Metric<?> metric = measure.metric(); + if (Boolean.class.equals(metric.valueType())) { + builder.setBooleanValue(BoolValue.newBuilder().setValue(((Boolean) value).booleanValue())); + } else if (Integer.class.equals(metric.valueType())) { + builder.setIntValue(IntValue.newBuilder().setValue(((Number) value).intValue())); + } else if (Double.class.equals(metric.valueType())) { + builder.setDoubleValue(DoubleValue.newBuilder().setValue(((Number) value).doubleValue())); + } else if (String.class.equals(metric.valueType())) { + builder.setStringValue(StringValue.newBuilder().setValue((String) value)); + } else if (Long.class.equals(metric.valueType())) { + builder.setLongValue(LongValue.newBuilder().setValue(((Number) value).longValue())); + } else { + throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); } } } - private static final class IsMetricAllowed implements Predicate<Measure> { - private final Set<String> allowedMetricKeys; - - private IsMetricAllowed(Set<String> allowedMetricKeys) { - this.allowedMetricKeys = allowedMetricKeys; - } - - @Override - public boolean apply(Measure input) { - return allowedMetricKeys.contains(input.getMetricKey()); - } - } - private static final class MetricToKey implements Function<Metric, String> { @Override public String apply(Metric input) { @@ -135,12 +106,9 @@ public class MeasuresPublisher implements ReportPublisherStep { @Override public void publish(ScannerReportWriter writer) { - final Set<String> allowedMetricKeys = newHashSet(transform(scannerMetrics.getMetrics(), new MetricToKey())); for (final BatchComponent resource : resourceCache.all()) { - Iterable<Measure> batchMeasures = measureCache.byResource(resource.resource()); - Iterable<org.sonar.scanner.protocol.output.ScannerReport.Measure> reportMeasures = transform( - filter(batchMeasures, new IsMetricAllowed(allowedMetricKeys)), - new MeasureToReportMeasure(resource)); + Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(resource.key()); + Iterable<ScannerReport.Measure> reportMeasures = transform(scannerMeasures, new MeasureToReportMeasure(resource)); writer.writeComponentMeasures(resource.batchId(), reportMeasures); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java index 8792e32d9b9..b0e9b87ceeb 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java @@ -23,11 +23,10 @@ import com.google.common.base.Preconditions; import javax.annotation.CheckForNull; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.measures.Measure; -import org.sonar.api.resources.Resource; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.scanner.index.Cache; -import org.sonar.scanner.index.Caches; import org.sonar.scanner.index.Cache.Entry; +import org.sonar.scanner.index.Caches; /** * Cache of all measures. This cache is shared amongst all project modules. @@ -35,50 +34,41 @@ import org.sonar.scanner.index.Cache.Entry; @ScannerSide public class MeasureCache { - private final Cache<Measure> cache; + private final Cache<DefaultMeasure<?>> cache; public MeasureCache(Caches caches, MetricFinder metricFinder) { - caches.registerValueCoder(Measure.class, new MeasureValueCoder(metricFinder)); + caches.registerValueCoder(DefaultMeasure.class, new MeasureValueCoder(metricFinder)); cache = caches.createCache("measures"); } - public Iterable<Entry<Measure>> entries() { + public Iterable<Entry<DefaultMeasure<?>>> entries() { return cache.entries(); } - public Iterable<Measure> all() { + public Iterable<DefaultMeasure<?>> all() { return cache.values(); } - public Iterable<Measure> byResource(Resource r) { - return byComponentKey(r.getEffectiveKey()); - } - - public Iterable<Measure> byComponentKey(String effectiveKey) { + public Iterable<DefaultMeasure<?>> byComponentKey(String effectiveKey) { return cache.values(effectiveKey); } @CheckForNull - public Measure byMetric(Resource r, String metricKey) { - return byMetric(r.getEffectiveKey(), metricKey); - } - - @CheckForNull - public Measure byMetric(String componentKey, String metricKey) { + public DefaultMeasure<?> byMetric(String componentKey, String metricKey) { return cache.get(componentKey, metricKey); } - public MeasureCache put(Resource resource, Measure measure) { - Preconditions.checkNotNull(resource.getEffectiveKey()); - Preconditions.checkNotNull(measure.getMetricKey()); - cache.put(resource.getEffectiveKey(), measure.getMetricKey(), measure); + public MeasureCache put(String componentKey, String metricKey, DefaultMeasure<?> measure) { + Preconditions.checkNotNull(componentKey); + Preconditions.checkNotNull(metricKey); + cache.put(componentKey, metricKey, measure); return this; } - public boolean contains(Resource resource, Measure measure) { - Preconditions.checkNotNull(resource.getEffectiveKey()); - Preconditions.checkNotNull(measure.getMetricKey()); - return cache.containsKey(resource.getEffectiveKey(), measure.getMetricKey()); + public boolean contains(String componentKey, String metricKey) { + Preconditions.checkNotNull(componentKey); + Preconditions.checkNotNull(metricKey); + return cache.containsKey(componentKey, metricKey); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java index f13151a5902..15655a1f46c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java @@ -22,11 +22,9 @@ package org.sonar.scanner.scan.measure; import com.persistit.Value; import com.persistit.encoding.CoderContext; import com.persistit.encoding.ValueCoder; -import javax.annotation.Nullable; +import java.io.Serializable; import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.PersistenceMode; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; class MeasureValueCoder implements ValueCoder { @@ -38,57 +36,22 @@ class MeasureValueCoder implements ValueCoder { @Override public void put(Value value, Object object, CoderContext context) { - Measure<?> m = (Measure) object; - value.putUTF(m.getMetricKey()); - value.put(m.getValue()); - putUTFOrNull(value, m.getData()); - putUTFOrNull(value, m.getDescription()); - value.putString(m.getAlertStatus() != null ? m.getAlertStatus().name() : null); - putUTFOrNull(value, m.getAlertText()); - value.putDate(m.getDate()); - value.put(m.getVariation1()); - value.put(m.getVariation2()); - value.put(m.getVariation3()); - value.put(m.getVariation4()); - value.put(m.getVariation5()); - putUTFOrNull(value, m.getUrl()); - Integer personId = m.getPersonId(); - value.put(personId != null ? personId.intValue() : null); - PersistenceMode persistenceMode = m.getPersistenceMode(); - value.putString(persistenceMode != null ? persistenceMode.name() : null); - } - - private static void putUTFOrNull(Value value, @Nullable String utfOrNull) { - if (utfOrNull != null) { - value.putUTF(utfOrNull); - } else { - value.putNull(); - } + DefaultMeasure<?> m = (DefaultMeasure<?>) object; + org.sonar.api.batch.measure.Metric<?> metric = m.metric(); + value.putString(metric.key()); + value.put(m.value()); } @Override public Object get(Value value, Class clazz, CoderContext context) { - Measure<?> m = new Measure(); String metricKey = value.getString(); - org.sonar.api.batch.measure.Metric metric = metricFinder.findByKey(metricKey); + org.sonar.api.batch.measure.Metric<?> metric = metricFinder.findByKey(metricKey); if (metric == null) { throw new IllegalStateException("Unknow metric with key " + metricKey); } - m.setMetric((org.sonar.api.measures.Metric) metric); - m.setRawValue(value.isNull(true) ? null : value.getDouble()); - m.setData(value.getString()); - m.setDescription(value.getString()); - m.setAlertStatus(value.isNull(true) ? null : Metric.Level.valueOf(value.getString())); - m.setAlertText(value.getString()); - m.setDate(value.getDate()); - m.setVariation1(value.isNull(true) ? null : value.getDouble()); - m.setVariation2(value.isNull(true) ? null : value.getDouble()); - m.setVariation3(value.isNull(true) ? null : value.getDouble()); - m.setVariation4(value.isNull(true) ? null : value.getDouble()); - m.setVariation5(value.isNull(true) ? null : value.getDouble()); - m.setUrl(value.getString()); - m.setPersonId(value.isNull(true) ? null : value.getInt()); - m.setPersistenceMode(value.isNull(true) ? null : PersistenceMode.valueOf(value.getString())); + DefaultMeasure<?> m = new DefaultMeasure() + .forMetric(metric) + .withValue((Serializable) value.get()); return m; } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index 1c2f90b686f..41478c716c1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -23,9 +23,14 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.Iterables; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,8 +38,8 @@ import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; import org.sonar.api.batch.sensor.error.AnalysisError; @@ -46,17 +51,12 @@ import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; import org.sonar.api.config.Settings; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Metric; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Resource; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.SonarException; +import org.sonar.core.metric.ScannerMetrics; import org.sonar.duplications.block.Block; import org.sonar.duplications.internal.pmd.PmdBlockChunker; import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.index.BatchComponent; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.issue.ModuleIssues; import org.sonar.scanner.protocol.output.FileStructure; @@ -68,26 +68,77 @@ import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.sensor.coverage.CoverageExclusions; +import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY; +import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.COVERAGE; +import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA; +import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.DEPENDENCY_MATRIX_KEY; +import static org.sonar.api.measures.CoreMetrics.DIRECTORY_CYCLES_KEY; +import static org.sonar.api.measures.CoreMetrics.DIRECTORY_EDGES_WEIGHT_KEY; +import static org.sonar.api.measures.CoreMetrics.DIRECTORY_FEEDBACK_EDGES_KEY; +import static org.sonar.api.measures.CoreMetrics.DIRECTORY_TANGLES_KEY; +import static org.sonar.api.measures.CoreMetrics.DIRECTORY_TANGLE_INDEX_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_CYCLES_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_EDGES_WEIGHT_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_FEEDBACK_EDGES_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_TANGLES_KEY; +import static org.sonar.api.measures.CoreMetrics.FILE_TANGLE_INDEX_KEY; +import static org.sonar.api.measures.CoreMetrics.IT_BRANCH_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE_LINE_HITS_DATA; +import static org.sonar.api.measures.CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.IT_LINES_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.IT_LINE_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_CONDITIONS; +import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_LINES; +import static org.sonar.api.measures.CoreMetrics.LINES; +import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_BRANCH_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA; +import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_LINES_TO_COVER; +import static org.sonar.api.measures.CoreMetrics.OVERALL_LINE_COVERAGE; +import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_CONDITIONS; +import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_LINES; +import static org.sonar.api.measures.CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY; +import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY; +import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; +import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; + public class DefaultSensorStorage implements SensorStorage { private static final Logger LOG = LoggerFactory.getLogger(DefaultSensorStorage.class); - private static final List<Metric> INTERNAL_METRICS = Arrays.<Metric>asList( + private static final List<Metric<?>> INTERNAL_METRICS = Arrays.<Metric<?>>asList( // Computed by LinesSensor - CoreMetrics.LINES); + LINES); private static final List<String> DEPRECATED_METRICS_KEYS = Arrays.asList( - CoreMetrics.DEPENDENCY_MATRIX_KEY, - CoreMetrics.DIRECTORY_CYCLES_KEY, - CoreMetrics.DIRECTORY_EDGES_WEIGHT_KEY, - CoreMetrics.DIRECTORY_FEEDBACK_EDGES_KEY, - CoreMetrics.DIRECTORY_TANGLE_INDEX_KEY, - CoreMetrics.DIRECTORY_TANGLES_KEY, - CoreMetrics.FILE_CYCLES_KEY, - CoreMetrics.FILE_EDGES_WEIGHT_KEY, - CoreMetrics.FILE_FEEDBACK_EDGES_KEY, - CoreMetrics.FILE_TANGLE_INDEX_KEY, - CoreMetrics.FILE_TANGLES_KEY); + DEPENDENCY_MATRIX_KEY, + DIRECTORY_CYCLES_KEY, + DIRECTORY_EDGES_WEIGHT_KEY, + DIRECTORY_FEEDBACK_EDGES_KEY, + DIRECTORY_TANGLE_INDEX_KEY, + DIRECTORY_TANGLES_KEY, + FILE_CYCLES_KEY, + FILE_EDGES_WEIGHT_KEY, + FILE_FEEDBACK_EDGES_KEY, + FILE_TANGLE_INDEX_KEY, + FILE_TANGLES_KEY, + COMMENTED_OUT_CODE_LINES_KEY); + + private static final List<String> COMPUTED_ON_CE_SIDE_METRICS_KEYS = Arrays.asList( + TEST_SUCCESS_DENSITY_KEY, + PUBLIC_DOCUMENTED_API_DENSITY_KEY); private final MetricFinder metricFinder; private final ModuleIssues moduleIssues; @@ -98,12 +149,16 @@ public class DefaultSensorStorage implements SensorStorage { private final SonarCpdBlockIndex index; private final ContextPropertiesCache contextPropertiesCache; private final Settings settings; + private final ScannerMetrics scannerMetrics; + private final Map<Metric<?>, Metric<?>> deprecatedCoverageMetricMapping = new IdentityHashMap<>(); + private final Set<Metric<?>> coverageMetrics = new HashSet<>(); + private final Set<Metric<?>> byLineMetrics = new HashSet<>(); public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Settings settings, CoverageExclusions coverageExclusions, BatchComponentCache componentCache, ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, - ContextPropertiesCache contextPropertiesCache) { + ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; this.settings = settings; @@ -113,83 +168,163 @@ public class DefaultSensorStorage implements SensorStorage { this.measureCache = measureCache; this.index = index; this.contextPropertiesCache = contextPropertiesCache; - } + this.scannerMetrics = scannerMetrics; - private Metric findMetricOrFail(String metricKey) { - Metric m = (Metric) metricFinder.findByKey(metricKey); - if (m == null) { - throw new IllegalStateException("Unknow metric with key: " + metricKey); - } - return m; + coverageMetrics.add(UNCOVERED_LINES); + coverageMetrics.add(LINES_TO_COVER); + coverageMetrics.add(UNCOVERED_CONDITIONS); + coverageMetrics.add(CONDITIONS_TO_COVER); + coverageMetrics.add(CONDITIONS_BY_LINE); + coverageMetrics.add(COVERED_CONDITIONS_BY_LINE); + coverageMetrics.add(COVERAGE_LINE_HITS_DATA); + + byLineMetrics.add(COVERAGE_LINE_HITS_DATA); + byLineMetrics.add(COVERED_CONDITIONS_BY_LINE); + byLineMetrics.add(CONDITIONS_BY_LINE); + + deprecatedCoverageMetricMapping.put(IT_COVERAGE, COVERAGE); + deprecatedCoverageMetricMapping.put(IT_LINE_COVERAGE, LINE_COVERAGE); + deprecatedCoverageMetricMapping.put(IT_BRANCH_COVERAGE, BRANCH_COVERAGE); + deprecatedCoverageMetricMapping.put(IT_UNCOVERED_LINES, UNCOVERED_LINES); + deprecatedCoverageMetricMapping.put(IT_LINES_TO_COVER, LINES_TO_COVER); + deprecatedCoverageMetricMapping.put(IT_UNCOVERED_CONDITIONS, UNCOVERED_CONDITIONS); + deprecatedCoverageMetricMapping.put(IT_CONDITIONS_TO_COVER, CONDITIONS_TO_COVER); + deprecatedCoverageMetricMapping.put(IT_CONDITIONS_BY_LINE, CONDITIONS_BY_LINE); + deprecatedCoverageMetricMapping.put(IT_COVERED_CONDITIONS_BY_LINE, COVERED_CONDITIONS_BY_LINE); + deprecatedCoverageMetricMapping.put(IT_COVERAGE_LINE_HITS_DATA, COVERAGE_LINE_HITS_DATA); + deprecatedCoverageMetricMapping.put(OVERALL_COVERAGE, COVERAGE); + deprecatedCoverageMetricMapping.put(OVERALL_LINE_COVERAGE, LINE_COVERAGE); + deprecatedCoverageMetricMapping.put(OVERALL_BRANCH_COVERAGE, BRANCH_COVERAGE); + deprecatedCoverageMetricMapping.put(OVERALL_UNCOVERED_LINES, UNCOVERED_LINES); + deprecatedCoverageMetricMapping.put(OVERALL_LINES_TO_COVER, LINES_TO_COVER); + deprecatedCoverageMetricMapping.put(OVERALL_UNCOVERED_CONDITIONS, UNCOVERED_CONDITIONS); + deprecatedCoverageMetricMapping.put(OVERALL_CONDITIONS_TO_COVER, CONDITIONS_TO_COVER); + deprecatedCoverageMetricMapping.put(OVERALL_CONDITIONS_BY_LINE, CONDITIONS_BY_LINE); + deprecatedCoverageMetricMapping.put(OVERALL_COVERED_CONDITIONS_BY_LINE, COVERED_CONDITIONS_BY_LINE); + deprecatedCoverageMetricMapping.put(OVERALL_COVERAGE_LINE_HITS_DATA, COVERAGE_LINE_HITS_DATA); } @Override public void store(Measure newMeasure) { - DefaultMeasure<?> measure = (DefaultMeasure<?>) newMeasure; - org.sonar.api.measures.Metric m = findMetricOrFail(measure.metric().key()); - org.sonar.api.measures.Measure measureToSave = new org.sonar.api.measures.Measure(m); - setValueAccordingToMetricType(newMeasure, m, measureToSave); - measureToSave.setFromCore(measure.isFromCore()); - InputComponent inputComponent = newMeasure.inputComponent(); - Resource resource = componentCache.get(inputComponent).resource(); - if (coverageExclusions.accept(resource, measureToSave)) { - saveMeasure(resource, measureToSave); - } + saveMeasure(newMeasure.inputComponent(), (DefaultMeasure<?>) newMeasure); } - public void saveMeasure(Resource resource, org.sonar.api.measures.Measure measure) { - if (DEPRECATED_METRICS_KEYS.contains(measure.getMetricKey())) { - // Ignore deprecated metrics + public void saveMeasure(InputComponent component, DefaultMeasure<?> measure) { + if (isDeprecatedMetric(measure.metric().key()) || isComputedOnCeMetric(measure.metric().key())) { + LOG.debug("Metric '{}' should not be saved by Sensors. It will be ignored.", measure.metric().key()); + // Ignore deprecated and CE side metrics return; } - org.sonar.api.batch.measure.Metric metric = metricFinder.findByKey(measure.getMetricKey()); + Metric<?> metric = metricFinder.findByKey(measure.metric().key()); if (metric == null) { - throw new SonarException("Unknown metric: " + measure.getMetricKey()); + throw new UnsupportedOperationException("Unknown metric: " + measure.metric().key()); + } + if (!scannerMetrics.getMetrics().contains(metric)) { + throw new UnsupportedOperationException("Metric '" + metric.key() + "' should not be computed by a Sensor"); } if (!measure.isFromCore() && INTERNAL_METRICS.contains(metric)) { LOG.debug("Metric " + metric.key() + " is an internal metric computed by SonarQube. Provided value is ignored."); + return; + } + if (deprecatedCoverageMetricMapping.containsKey(metric)) { + metric = deprecatedCoverageMetricMapping.get(metric); } - if (measureCache.contains(resource, measure)) { - throw new SonarException("Can not add the same measure twice on " + resource + ": " + measure); + + if (coverageMetrics.contains(metric)) { + if (!component.isFile()) { + throw new UnsupportedOperationException("Saving coverage metric is only allowed on files. Attempt to save '" + metric.key() + "' on '" + component.key() + "'"); + } + if (coverageExclusions.isExcluded((InputFile) component)) { + return; + } + saveCoverageMetricInternal((InputFile) component, metric, measure); + } else { + if (measureCache.contains(component.key(), metric.key())) { + throw new UnsupportedOperationException("Can not add the same measure twice on " + component + ": " + measure); + } + measureCache.put(component.key(), metric.key(), measure); } - measureCache.put(resource, measure); } - private void setValueAccordingToMetricType(Measure<?> measure, org.sonar.api.measures.Metric<?> m, org.sonar.api.measures.Measure measureToSave) { - switch (m.getType()) { - case BOOL: - measureToSave.setValue(Boolean.TRUE.equals(measure.value()) ? 1.0 : 0.0); - break; - case INT: - case MILLISEC: - case WORK_DUR: - case FLOAT: - case PERCENT: - case RATING: - measureToSave.setValue(((Number) measure.value()).doubleValue()); - break; - case STRING: - case LEVEL: - case DATA: - case DISTRIB: - measureToSave.setData((String) measure.value()); - break; - default: - throw new UnsupportedOperationException("Unsupported type :" + m.getType()); + private void saveCoverageMetricInternal(InputFile file, Metric<?> metric, DefaultMeasure<?> measure) { + if (isLineMetrics(metric)) { + validateCoverageMeasure((String) measure.value(), file); + DefaultMeasure<?> previousMeasure = measureCache.byMetric(file.key(), metric.key()); + if (previousMeasure != null) { + measureCache.put(file.key(), metric.key(), new DefaultMeasure<String>() + .forMetric((Metric<String>) metric) + .withValue(KeyValueFormat.format(mergeLineMetric((String) previousMeasure.value(), (String) measure.value())))); + } else { + measureCache.put(file.key(), metric.key(), measure); + } + } else { + // Other coverage metrics are all integer values + DefaultMeasure<?> previousMeasure = measureCache.byMetric(file.key(), metric.key()); + if (previousMeasure != null) { + measureCache.put(file.key(), metric.key(), new DefaultMeasure<Integer>() + .forMetric((Metric<Integer>) metric) + .withValue(Math.max((Integer) previousMeasure.value(), (Integer) measure.value()))); + } else { + measureCache.put(file.key(), metric.key(), measure); + } } } - @Override - public void store(Issue issue) { - moduleIssues.initAndAddIssue(issue); + /** + * Merge the two line data measures, keeping max value in case they both contains a value for the same line. + */ + private Map<Integer, Integer> mergeLineMetric(String value1, String value2) { + Map<Integer, Integer> data1 = KeyValueFormat.parseIntInt(value1); + Map<Integer, Integer> data2 = KeyValueFormat.parseIntInt(value2); + return Stream.of(data1, data2) + .map(Map::entrySet) + .flatMap(Collection::stream) + .collect( + Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + Integer::max)); + } + + public boolean isDeprecatedMetric(String metricKey) { + return DEPRECATED_METRICS_KEYS.contains(metricKey); } - private File getFile(InputFile file) { - BatchComponent r = componentCache.get(file); - if (r == null) { - throw new IllegalStateException("Provided input file is not indexed"); + public boolean isComputedOnCeMetric(String metricKey) { + return COMPUTED_ON_CE_SIDE_METRICS_KEYS.contains(metricKey); + } + + private boolean isLineMetrics(Metric<?> metric) { + return this.byLineMetrics.contains(metric); + } + + public void validateCoverageMeasure(String value, InputFile inputFile) { + Map<Integer, Integer> m = KeyValueFormat.parseIntInt(value); + validatePositiveLine(m, inputFile.absolutePath()); + validateMaxLine(m, inputFile); + } + + private static void validateMaxLine(Map<Integer, Integer> m, InputFile inputFile) { + int maxLine = inputFile.lines(); + + for (int line : m.keySet()) { + if (line > maxLine) { + throw new IllegalStateException(String.format("Can't create measure for line %d for file '%s' with %d lines", line, inputFile.absolutePath(), maxLine)); + } } - return (File) r.resource(); + } + + private static void validatePositiveLine(Map<Integer, Integer> m, String filePath) { + for (int l : m.keySet()) { + if (l <= 0) { + throw new IllegalStateException(String.format("Measure with line %d for file '%s' must be > 0", l, filePath)); + } + } + } + + @Override + public void store(Issue issue) { + moduleIssues.initAndAddIssue(issue); } @Override @@ -241,21 +376,25 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultCoverage defaultCoverage) { - File file = getFile(defaultCoverage.inputFile()); - if (coverageExclusions.hasMatchingPattern(file)) { + if (coverageExclusions.isExcluded(defaultCoverage.inputFile())) { return; } - CoverageType type = defaultCoverage.type(); if (defaultCoverage.linesToCover() > 0) { - saveMeasure(file, new org.sonar.api.measures.Measure(type.linesToCover(), (double) defaultCoverage.linesToCover())); - saveMeasure(file, new org.sonar.api.measures.Measure(type.uncoveredLines(), (double) (defaultCoverage.linesToCover() - defaultCoverage.coveredLines()))); - saveMeasure(file, new org.sonar.api.measures.Measure(type.lineHitsData()).setData(KeyValueFormat.format(defaultCoverage.hitsByLine()))); + saveCoverageMetricInternal(defaultCoverage.inputFile(), LINES_TO_COVER, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(defaultCoverage.linesToCover())); + saveCoverageMetricInternal(defaultCoverage.inputFile(), UNCOVERED_LINES, + new DefaultMeasure<Integer>().forMetric(UNCOVERED_LINES).withValue(defaultCoverage.linesToCover() - defaultCoverage.coveredLines())); + saveCoverageMetricInternal(defaultCoverage.inputFile(), COVERAGE_LINE_HITS_DATA, + new DefaultMeasure<String>().forMetric(COVERAGE_LINE_HITS_DATA).withValue(KeyValueFormat.format(defaultCoverage.hitsByLine()))); } if (defaultCoverage.conditions() > 0) { - saveMeasure(file, new org.sonar.api.measures.Measure(type.conditionsToCover(), (double) defaultCoverage.conditions())); - saveMeasure(file, new org.sonar.api.measures.Measure(type.uncoveredConditions(), (double) (defaultCoverage.conditions() - defaultCoverage.coveredConditions()))); - saveMeasure(file, new org.sonar.api.measures.Measure(type.coveredConditionsByLine()).setData(KeyValueFormat.format(defaultCoverage.coveredConditionsByLine()))); - saveMeasure(file, new org.sonar.api.measures.Measure(type.conditionsByLine()).setData(KeyValueFormat.format(defaultCoverage.conditionsByLine()))); + saveCoverageMetricInternal(defaultCoverage.inputFile(), CONDITIONS_TO_COVER, + new DefaultMeasure<Integer>().forMetric(CONDITIONS_TO_COVER).withValue(defaultCoverage.conditions())); + saveCoverageMetricInternal(defaultCoverage.inputFile(), UNCOVERED_CONDITIONS, + new DefaultMeasure<Integer>().forMetric(UNCOVERED_CONDITIONS).withValue(defaultCoverage.conditions() - defaultCoverage.coveredConditions())); + saveCoverageMetricInternal(defaultCoverage.inputFile(), COVERED_CONDITIONS_BY_LINE, + new DefaultMeasure<String>().forMetric(COVERED_CONDITIONS_BY_LINE).withValue(KeyValueFormat.format(defaultCoverage.coveredConditionsByLine()))); + saveCoverageMetricInternal(defaultCoverage.inputFile(), CONDITIONS_BY_LINE, + new DefaultMeasure<String>().forMetric(CONDITIONS_BY_LINE).withValue(KeyValueFormat.format(defaultCoverage.conditionsByLine()))); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageConstants.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageConstants.java deleted file mode 100644 index 1bf08673dee..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageConstants.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.scanner.sensor.coverage; - -import com.google.common.collect.ImmutableList; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Metric; - -import java.util.Collection; - -public class CoverageConstants { - - public static final Collection<Metric> COVERAGE_METRICS = ImmutableList.<Metric>of(CoreMetrics.LINES_TO_COVER, CoreMetrics.UNCOVERED_LINES, CoreMetrics.NEW_LINES_TO_COVER, - CoreMetrics.NEW_UNCOVERED_LINES, CoreMetrics.CONDITIONS_TO_COVER, CoreMetrics.UNCOVERED_CONDITIONS, - CoreMetrics.NEW_CONDITIONS_TO_COVER, CoreMetrics.NEW_UNCOVERED_CONDITIONS); - - public static final Collection<Metric> LINE_COVERAGE_METRICS = ImmutableList.<Metric>of(CoreMetrics.UNCOVERED_LINES, CoreMetrics.LINES_TO_COVER, CoreMetrics.NEW_UNCOVERED_LINES, - CoreMetrics.NEW_LINES_TO_COVER); - - public static final Collection<Metric> BRANCH_COVERAGE_METRICS = ImmutableList.<Metric>of(CoreMetrics.UNCOVERED_CONDITIONS, CoreMetrics.CONDITIONS_TO_COVER, - CoreMetrics.NEW_UNCOVERED_CONDITIONS, CoreMetrics.NEW_CONDITIONS_TO_COVER); - - private CoverageConstants() { - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java index 58e17f9251b..00ee9421845 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java @@ -23,168 +23,41 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import javax.annotation.CheckForNull; +import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Settings; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.resources.Resource; -import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.WildcardPattern; -public class CoverageExclusions { +public class CoverageExclusions implements Startable { private static final Logger LOG = LoggerFactory.getLogger(CoverageExclusions.class); private final Settings settings; - private final Set<Metric> coverageMetrics; - private final Set<Metric> byLineMetrics; - private Collection<WildcardPattern> resourcePatterns; + private Collection<WildcardPattern> exclusionPatterns; - private final FileSystem fs; - - public CoverageExclusions(Settings settings, FileSystem fs) { + public CoverageExclusions(Settings settings) { this.settings = settings; - this.fs = fs; - this.coverageMetrics = new HashSet<>(); - this.byLineMetrics = new HashSet<>(); - // UT - coverageMetrics.add(CoreMetrics.COVERAGE); - coverageMetrics.add(CoreMetrics.LINE_COVERAGE); - coverageMetrics.add(CoreMetrics.BRANCH_COVERAGE); - coverageMetrics.add(CoreMetrics.UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.UNCOVERED_CONDITIONS); - coverageMetrics.add(CoreMetrics.CONDITIONS_TO_COVER); - coverageMetrics.add(CoreMetrics.CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.COVERED_CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.COVERAGE_LINE_HITS_DATA); - coverageMetrics.add(CoreMetrics.NEW_LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.NEW_UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.NEW_UNCOVERED_CONDITIONS); - // IT - coverageMetrics.add(CoreMetrics.IT_COVERAGE); - coverageMetrics.add(CoreMetrics.IT_LINE_COVERAGE); - coverageMetrics.add(CoreMetrics.IT_BRANCH_COVERAGE); - coverageMetrics.add(CoreMetrics.IT_UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.IT_LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.IT_UNCOVERED_CONDITIONS); - coverageMetrics.add(CoreMetrics.IT_CONDITIONS_TO_COVER); - coverageMetrics.add(CoreMetrics.IT_CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA); - coverageMetrics.add(CoreMetrics.NEW_IT_LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.NEW_IT_UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.NEW_IT_UNCOVERED_CONDITIONS); - // OVERALL - coverageMetrics.add(CoreMetrics.OVERALL_COVERAGE); - coverageMetrics.add(CoreMetrics.OVERALL_LINE_COVERAGE); - coverageMetrics.add(CoreMetrics.OVERALL_BRANCH_COVERAGE); - coverageMetrics.add(CoreMetrics.OVERALL_UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.OVERALL_LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.OVERALL_UNCOVERED_CONDITIONS); - coverageMetrics.add(CoreMetrics.OVERALL_CONDITIONS_TO_COVER); - coverageMetrics.add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE); - coverageMetrics.add(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA); - coverageMetrics.add(CoreMetrics.NEW_OVERALL_LINES_TO_COVER); - coverageMetrics.add(CoreMetrics.NEW_OVERALL_UNCOVERED_LINES); - coverageMetrics.add(CoreMetrics.NEW_OVERALL_UNCOVERED_CONDITIONS); - - byLineMetrics.add(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA); - byLineMetrics.add(CoreMetrics.OVERALL_CONDITIONS_BY_LINE); - byLineMetrics.add(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE); - byLineMetrics.add(CoreMetrics.COVERAGE_LINE_HITS_DATA); - byLineMetrics.add(CoreMetrics.COVERED_CONDITIONS_BY_LINE); - byLineMetrics.add(CoreMetrics.CONDITIONS_BY_LINE); - byLineMetrics.add(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA); - byLineMetrics.add(CoreMetrics.IT_CONDITIONS_BY_LINE); - byLineMetrics.add(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE); - - initPatterns(); - } - - private boolean isLineMetrics(Metric<?> metric) { - return this.byLineMetrics.contains(metric); - } - - public void validate(Measure<?> measure, InputFile inputFile) { - Metric<?> metric = measure.getMetric(); - - if (!isLineMetrics(metric)) { - return; - } - - Map<Integer, Integer> m = KeyValueFormat.parseIntInt(measure.getData()); - validatePositiveLine(m, inputFile.absolutePath()); - validateMaxLine(m, inputFile); - } - - @CheckForNull - private InputFile getInputFile(String filePath) { - return fs.inputFile(fs.predicates().hasRelativePath(filePath)); - } - - public void validate(Measure<?> measure, String filePath) { - Metric<?> metric = measure.getMetric(); - - if (!isLineMetrics(metric)) { - return; - } - - InputFile inputFile = getInputFile(filePath); - - if (inputFile == null) { - throw new IllegalStateException(String.format("Can't create measure for resource '%s': resource is not indexed as a file", filePath)); - } - - validate(measure, inputFile); - } - - private static void validateMaxLine(Map<Integer, Integer> m, InputFile inputFile) { - int maxLine = inputFile.lines(); - - for (int l : m.keySet()) { - if (l > maxLine) { - throw new IllegalStateException(String.format("Can't create measure for line %d for file '%s' with %d lines", l, inputFile.absolutePath(), maxLine)); - } - } - } - - private static void validatePositiveLine(Map<Integer, Integer> m, String filePath) { - for (int l : m.keySet()) { - if (l <= 0) { - throw new IllegalStateException(String.format("Measure with line %d for file '%s' must be > 0", l, filePath)); - } - } } - public boolean accept(Resource resource, Measure<?> measure) { - if (isCoverageMetric(measure.getMetric())) { - return !hasMatchingPattern(resource); - } else { - return true; - } + @Override + public void start() { + initPatterns(); } - private boolean isCoverageMetric(Metric<?> metric) { - return this.coverageMetrics.contains(metric); + @Override + public void stop() { + // Nothing to do } - public boolean hasMatchingPattern(Resource resource) { + public boolean isExcluded(InputFile file) { boolean found = false; - Iterator<WildcardPattern> iterator = resourcePatterns.iterator(); + Iterator<WildcardPattern> iterator = exclusionPatterns.iterator(); while (!found && iterator.hasNext()) { - found = resource.matchFilePattern(iterator.next().toString()); + found = iterator.next().match(file.relativePath()); } return found; } @@ -195,8 +68,8 @@ public class CoverageExclusions { for (String pattern : settings.getStringArray(CoreProperties.PROJECT_COVERAGE_EXCLUSIONS_PROPERTY)) { builder.add(WildcardPattern.create(pattern)); } - resourcePatterns = builder.build(); - log("Excluded sources for coverage: ", resourcePatterns); + exclusionPatterns = builder.build(); + log("Excluded sources for coverage: ", exclusionPatterns); } private static void log(String title, Collection<WildcardPattern> patterns) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java index 432ba9ba932..782a7976db8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java @@ -34,8 +34,8 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.coverage.NewCoverage; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; import org.sonar.api.utils.KeyValueFormat; import org.sonar.scanner.scan.measure.MeasureCache; @@ -46,10 +46,10 @@ import static com.google.common.collect.Sets.newHashSet; @Phase(name = Phase.Name.POST) public final class ZeroCoverageSensor implements Sensor { - private static final class MeasureToMetricKey implements Function<Measure, String> { + private static final class MeasureToMetricKey implements Function<DefaultMeasure<?>, String> { @Override - public String apply(Measure input) { - return input.getMetricKey(); + public String apply(DefaultMeasure<?> input) { + return input.metric().key(); } } @@ -76,7 +76,7 @@ public final class ZeroCoverageSensor implements Sensor { FileSystem fs = context.fileSystem(); for (InputFile f : fs.inputFiles(fs.predicates().hasType(Type.MAIN))) { if (!isCoverageMeasuresAlreadyDefined(f)) { - Measure execLines = measureCache.byMetric(f.key(), CoreMetrics.EXECUTABLE_LINES_DATA_KEY); + DefaultMeasure<String> execLines = (DefaultMeasure<String>) measureCache.byMetric(f.key(), CoreMetrics.EXECUTABLE_LINES_DATA_KEY); if (execLines != null) { storeZeroCoverageForEachExecutableLine(context, f, execLines); } @@ -85,8 +85,8 @@ public final class ZeroCoverageSensor implements Sensor { } } - private static void storeZeroCoverageForEachExecutableLine(final SensorContext context, InputFile f, Measure execLines) { - NewCoverage newCoverage = context.newCoverage().ofType(CoverageType.UNIT).onFile(f); + private static void storeZeroCoverageForEachExecutableLine(final SensorContext context, InputFile f, DefaultMeasure<String> execLines) { + NewCoverage newCoverage = context.newCoverage().onFile(f); Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) execLines.value()); for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) { int lineIdx = lineMeasure.getKey(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/DefaultFileLinesContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/DefaultFileLinesContextTest.java index 647b67e110d..37d74deeb24 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/DefaultFileLinesContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/DefaultFileLinesContextTest.java @@ -27,9 +27,8 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.scanner.DefaultFileLinesContext; import org.sonar.scanner.scan.measure.MeasureCache; import static org.assertj.core.api.Assertions.assertThat; @@ -146,7 +145,7 @@ public class DefaultFileLinesContextTest { @Test public void shouldLoadIntValues() { - when(measureCache.byMetric("foo:src/foo.php", HITS_METRIC_KEY)).thenReturn(new Measure(HITS_METRIC_KEY).setData("1=2;3=4")); + when(measureCache.byMetric("foo:src/foo.php", HITS_METRIC_KEY)).thenReturn(new DefaultMeasure().withValue("1=2;3=4")); assertThat(fileLineMeasures.getIntValue(HITS_METRIC_KEY, 1), is(2)); assertThat(fileLineMeasures.getIntValue(HITS_METRIC_KEY, 3), is(4)); @@ -155,7 +154,7 @@ public class DefaultFileLinesContextTest { @Test public void shouldLoadStringValues() { - when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new Measure(AUTHOR_METRIC_KEY).setData("1=simon;3=evgeny")); + when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new DefaultMeasure().withValue("1=simon;3=evgeny")); assertThat(fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 1), is("simon")); assertThat(fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 3), is("evgeny")); @@ -164,7 +163,7 @@ public class DefaultFileLinesContextTest { @Test(expected = UnsupportedOperationException.class) public void shouldNotModifyAfterLoad() { - when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new Measure(AUTHOR_METRIC_KEY).setData("1=simon;3=evgeny")); + when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new DefaultMeasure().withValue("1=simon;3=evgeny")); fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 1); fileLineMeasures.setStringValue(AUTHOR_METRIC_KEY, 1, "evgeny"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java index 9d52431cd4f..4c0565b2e09 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/index/DefaultIndexTest.java @@ -25,6 +25,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilters; import org.sonar.api.profiles.RulesProfile; @@ -64,7 +65,7 @@ public class DefaultIndexTest { DefaultProjectTree projectTree = mock(DefaultProjectTree.class); BatchComponentCache resourceCache = new BatchComponentCache(); - index = new DefaultIndex(resourceCache, projectTree, mock(MeasureCache.class)); + index = new DefaultIndex(resourceCache, projectTree, mock(MeasureCache.class), mock(MetricFinder.class)); baseDir = temp.newFolder(); project = new Project("project"); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java index e5c5f0e99db..ccc5440ae86 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java @@ -59,7 +59,7 @@ public class CoverageMediumTest { } @Test - public void unitTests() throws IOException { + public void singleReport() throws IOException { File baseDir = temp.getRoot(); File srcDir = new File(baseDir, "src"); @@ -99,6 +99,46 @@ public class CoverageMediumTest { } @Test + public void twoReports() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + File xooUtCoverageFile = new File(srcDir, "sample.xoo.coverage"); + FileUtils.write(xooUtCoverageFile, "2:2:2:2"); + File xooItCoverageFile = new File(srcDir, "sample.xoo.itcoverage"); + FileUtils.write(xooItCoverageFile, "2:2:2:1\n3:1"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + InputFile file = result.inputFile("src/sample.xoo"); + assertThat(result.coverageFor(file, 2).getUtHits()).isTrue(); + assertThat(result.coverageFor(file, 2).getConditions()).isEqualTo(2); + assertThat(result.coverageFor(file, 2).getUtCoveredConditions()).isEqualTo(2); + assertThat(result.coverageFor(file, 3).getUtHits()).isTrue(); + + Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures(); + assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey", "intValue.value") + .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 0), + tuple(CoreMetrics.CONDITIONS_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_CONDITIONS_KEY, 1)); + } + + @Test public void exclusions() throws IOException { File baseDir = temp.getRoot(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java index d465419e59b..6ce6b181fc9 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/CoveragePublisherTest.java @@ -27,16 +27,15 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; import org.sonar.core.util.CloseableIterator; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; -import org.sonar.scanner.report.CoveragePublisher; -import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.index.BatchComponentCache; +import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.measure.MeasureCache; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyString; @@ -68,67 +67,14 @@ public class CoveragePublisherTest { @Test public void publishCoverage() throws Exception { - Measure utLineHits = new Measure<>(CoreMetrics.COVERAGE_LINE_HITS_DATA).setData("2=1;3=1;5=0;6=3"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(utLineHits); - - Measure itsConditionsByLine = new Measure<>(CoreMetrics.IT_CONDITIONS_BY_LINE).setData("3=4"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_CONDITIONS_BY_LINE_KEY)).thenReturn(itsConditionsByLine); - - Measure conditionsByLine = new Measure<>(CoreMetrics.CONDITIONS_BY_LINE).setData("3=4"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.CONDITIONS_BY_LINE_KEY)).thenReturn(conditionsByLine); - - Measure coveredConditionsByUts = new Measure<>(CoreMetrics.COVERED_CONDITIONS_BY_LINE).setData("3=2"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByUts); - - Measure itLineHits = new Measure<>(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA).setData("2=0;3=0;5=1"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(itLineHits); - - Measure coveredConditionsByIts = new Measure<>(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE).setData("3=1"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByIts); - - Measure overallCoveredConditions = new Measure<>(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE).setData("3=2"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(overallCoveredConditions); - - File outputDir = temp.newFolder(); - ScannerReportWriter writer = new ScannerReportWriter(outputDir); - - publisher.publish(writer); - - try (CloseableIterator<LineCoverage> it = new ScannerReportReader(outputDir).readComponentCoverage(2)) { - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(2) - .setUtHits(true) - .setItHits(false) - .build()); - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(3) - .setUtHits(true) - .setItHits(false) - .setConditions(4) - .setUtCoveredConditions(2) - .setItCoveredConditions(1) - .setOverallCoveredConditions(2) - .build()); - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(5) - .setUtHits(false) - .setItHits(true) - .build()); - } - - } - - @Test - public void publishCoverageOnlyUts() throws Exception { - - Measure utLineHits = new Measure<>(CoreMetrics.COVERAGE_LINE_HITS_DATA).setData("2=1;3=1;5=0;6=3"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(utLineHits); + DefaultMeasure<String> utLineHits = new DefaultMeasure<String>().forMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA).withValue("2=1;3=1;5=0;6=3"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn((DefaultMeasure) utLineHits); - Measure conditionsByLine = new Measure<>(CoreMetrics.CONDITIONS_BY_LINE).setData("3=4"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.CONDITIONS_BY_LINE_KEY)).thenReturn(conditionsByLine); + DefaultMeasure<String> conditionsByLine = new DefaultMeasure<String>().forMetric(CoreMetrics.CONDITIONS_BY_LINE).withValue("3=4"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.CONDITIONS_BY_LINE_KEY)).thenReturn((DefaultMeasure) conditionsByLine); - Measure coveredConditionsByUts = new Measure<>(CoreMetrics.COVERED_CONDITIONS_BY_LINE).setData("3=2"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByUts); + DefaultMeasure<String> coveredConditionsByUts = new DefaultMeasure<String>().forMetric(CoreMetrics.COVERED_CONDITIONS_BY_LINE).withValue("3=2"); + when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn((DefaultMeasure) coveredConditionsByUts); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); @@ -153,40 +99,4 @@ public class CoveragePublisherTest { } } - - @Test - public void publishCoverageOnlyIts() throws Exception { - - Measure itsConditionsByLine = new Measure<>(CoreMetrics.IT_CONDITIONS_BY_LINE).setData("3=4"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_CONDITIONS_BY_LINE_KEY)).thenReturn(itsConditionsByLine); - - Measure itLineHits = new Measure<>(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA).setData("2=0;3=0;5=1"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(itLineHits); - - Measure coveredConditionsByIts = new Measure<>(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE).setData("3=1"); - when(measureCache.byMetric("foo:src/Foo.php", CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY)).thenReturn(coveredConditionsByIts); - - File outputDir = temp.newFolder(); - ScannerReportWriter writer = new ScannerReportWriter(outputDir); - - publisher.publish(writer); - - try (CloseableIterator<LineCoverage> it = new ScannerReportReader(outputDir).readComponentCoverage(2)) { - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(2) - .setItHits(false) - .build()); - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(3) - .setItHits(false) - .setConditions(4) - .setItCoveredConditions(1) - .build()); - assertThat(it.next()).isEqualTo(LineCoverage.newBuilder() - .setLine(5) - .setItHits(true) - .build()); - } - - } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java index b11d262f7eb..7f264b74152 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MeasuresPublisherTest.java @@ -28,28 +28,28 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.core.metric.ScannerMetrics; import org.sonar.core.util.CloseableIterator; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.report.MeasuresPublisher; import org.sonar.scanner.scan.measure.MeasureCache; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MeasuresPublisherTest { + private static final String FILE_KEY = "foo:src/Foo.php"; + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -65,22 +65,22 @@ public class MeasuresPublisherTest { public void prepare() { Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); BatchComponentCache resourceCache = new BatchComponentCache(); - sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey(FILE_KEY); resourceCache.add(p, null); resourceCache.add(sampleFile, null); measureCache = mock(MeasureCache.class); - when(measureCache.byResource(any(Resource.class))).thenReturn(Collections.<Measure>emptyList()); + when(measureCache.byComponentKey(anyString())).thenReturn(Collections.<DefaultMeasure<?>>emptyList()); publisher = new MeasuresPublisher(resourceCache, measureCache, new ScannerMetrics()); } @Test public void publishMeasures() throws Exception { - Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER) - .setValue(2.0); + DefaultMeasure<Integer> measure = new DefaultMeasure<Integer>().forMetric(CoreMetrics.LINES_TO_COVER) + .withValue(2); // String value - Measure stringMeasure = new Measure<>(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION) - .setData("foo bar"); - when(measureCache.byResource(sampleFile)).thenReturn(asList(measure, stringMeasure)); + DefaultMeasure<String> stringMeasure = new DefaultMeasure<String>().forMetric(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION) + .withValue("foo bar"); + when(measureCache.byComponentKey(FILE_KEY)).thenReturn(asList(measure, stringMeasure)); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); @@ -97,8 +97,8 @@ public class MeasuresPublisherTest { @Test public void fail_with_IAE_when_measure_has_no_value() throws Exception { - Measure measure = new Measure<>(CoreMetrics.LINES_TO_COVER); - when(measureCache.byResource(sampleFile)).thenReturn(Collections.singletonList(measure)); + DefaultMeasure<Integer> measure = new DefaultMeasure<Integer>().forMetric(CoreMetrics.LINES_TO_COVER); + when(measureCache.byComponentKey(FILE_KEY)).thenReturn(Collections.singletonList(measure)); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/measure/MeasureCacheTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/measure/MeasureCacheTest.java index 555baa35173..702fd8a31d5 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/measure/MeasureCacheTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/measure/MeasureCacheTest.java @@ -19,21 +19,14 @@ */ package org.sonar.scanner.scan.measure; -import java.util.Date; import java.util.Iterator; -import org.apache.commons.lang.builder.EqualsBuilder; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric.Level; -import org.sonar.api.resources.Directory; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.scanner.index.AbstractCachesTest; import org.sonar.scanner.index.Cache.Entry; @@ -42,6 +35,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class MeasureCacheTest extends AbstractCachesTest { + + private static final String COMPONENT_KEY = "struts"; + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -54,60 +50,28 @@ public class MeasureCacheTest extends AbstractCachesTest { super.start(); metricFinder = mock(MetricFinder.class); when(metricFinder.<Integer>findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); + when(metricFinder.<String>findByKey(CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).thenReturn(CoreMetrics.COVERAGE_LINE_HITS_DATA); measureCache = new MeasureCache(caches, metricFinder); } @Test public void should_add_measure() { - Project p = new Project("struts"); - assertThat(measureCache.entries()).hasSize(0); - assertThat(measureCache.byResource(p)).hasSize(0); + assertThat(measureCache.byComponentKey(COMPONENT_KEY)).hasSize(0); - Measure m = new Measure(CoreMetrics.NCLOC, 1.0); - measureCache.put(p, m); + DefaultMeasure<?> m = new DefaultMeasure().forMetric(CoreMetrics.NCLOC).withValue(1.0); + measureCache.put(COMPONENT_KEY, CoreMetrics.NCLOC_KEY, m); - assertThat(measureCache.contains(p, m)).isTrue(); + assertThat(measureCache.contains(COMPONENT_KEY, CoreMetrics.NCLOC_KEY)).isTrue(); assertThat(measureCache.entries()).hasSize(1); - Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); + Iterator<Entry<DefaultMeasure<?>>> iterator = measureCache.entries().iterator(); iterator.hasNext(); - Entry<Measure> next = iterator.next(); + Entry<DefaultMeasure<?>> next = iterator.next(); assertThat(next.value()).isEqualTo(m); - assertThat(next.key()[0]).isEqualTo("struts"); + assertThat(next.key()[0]).isEqualTo(COMPONENT_KEY); - assertThat(measureCache.byResource(p)).hasSize(1); - assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); - } - - @Test - public void should_add_measure_with_big_data() { - Project p = new Project("struts"); - - assertThat(measureCache.entries()).hasSize(0); - - assertThat(measureCache.byResource(p)).hasSize(0); - - Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); - m.setAlertText("foooooooooooooooooooooooooooooooooooo"); - StringBuilder data = new StringBuilder(); - for (int i = 0; i < 1_048_575; i++) { - data.append("a"); - } - - m.setData(data.toString()); - - measureCache.put(p, m); - - assertThat(measureCache.contains(p, m)).isTrue(); - assertThat(measureCache.entries()).hasSize(1); - Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); - iterator.hasNext(); - Entry<Measure> next = iterator.next(); - assertThat(next.value()).isEqualTo(m); - assertThat(next.key()[0]).isEqualTo("struts"); - - assertThat(measureCache.byResource(p)).hasSize(1); - assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); + assertThat(measureCache.byComponentKey(COMPONENT_KEY)).hasSize(1); + assertThat(measureCache.byComponentKey(COMPONENT_KEY).iterator().next()).isEqualTo(m); } /** @@ -115,117 +79,84 @@ public class MeasureCacheTest extends AbstractCachesTest { */ @Test public void should_add_measure_with_too_big_data_for_persistit_pre_patch() { - Project p = new Project("struts"); - assertThat(measureCache.entries()).hasSize(0); + assertThat(measureCache.byComponentKey(COMPONENT_KEY)).hasSize(0); - assertThat(measureCache.byResource(p)).hasSize(0); - - Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); - StringBuilder data = new StringBuilder(); - for (int i = 0; i < 500000; i++) { - data.append("some data"); + StringBuilder data = new StringBuilder(4_500_000); + for (int i = 0; i < 4_500_000; i++) { + data.append('a'); } - m.setData(data.toString()); - - measureCache.put(p, m); + DefaultMeasure<?> m = new DefaultMeasure().forMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA).withValue(data.toString()); + measureCache.put(COMPONENT_KEY, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, m); - assertThat(measureCache.contains(p, m)).isTrue(); + assertThat(measureCache.contains(COMPONENT_KEY, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)).isTrue(); assertThat(measureCache.entries()).hasSize(1); - Iterator<Entry<Measure>> iterator = measureCache.entries().iterator(); + Iterator<Entry<DefaultMeasure<?>>> iterator = measureCache.entries().iterator(); iterator.hasNext(); - Entry<Measure> next = iterator.next(); + Entry<DefaultMeasure<?>> next = iterator.next(); assertThat(next.value()).isEqualTo(m); - assertThat(next.key()[0]).isEqualTo("struts"); + assertThat(next.key()[0]).isEqualTo(COMPONENT_KEY); - assertThat(measureCache.byResource(p)).hasSize(1); - assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); + assertThat(measureCache.byComponentKey(COMPONENT_KEY)).hasSize(1); + assertThat(measureCache.byComponentKey(COMPONENT_KEY).iterator().next()).isEqualTo(m); } @Test public void should_add_measure_with_too_big_data_for_persistit() { - Project p = new Project("struts"); - assertThat(measureCache.entries()).hasSize(0); + assertThat(measureCache.byComponentKey(COMPONENT_KEY)).hasSize(0); - assertThat(measureCache.byResource(p)).hasSize(0); - - Measure m = new Measure(CoreMetrics.NCLOC, 1.0).setDate(new Date()); - StringBuilder data = new StringBuilder(64 * 1024 * 1024 + 1); // Limit is 64Mo - for (int i = 0; i < (64 * 1024 * 1024 + 1); i++) { + StringBuilder data = new StringBuilder(64 * 1024 * 1024 + 1); + for (int i = 0; i < 64 * 1024 * 1024 + 1; i++) { data.append('a'); } - m.setData(data.toString()); + DefaultMeasure<?> m = new DefaultMeasure().forMetric(CoreMetrics.COVERAGE_LINE_HITS_DATA).withValue(data.toString()); thrown.expect(IllegalStateException.class); - thrown.expectMessage("Fail to put element in the cache measures"); + thrown.expectMessage("Fail to put element in the storage 'measures'"); - measureCache.put(p, m); + measureCache.put(COMPONENT_KEY, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, m); } @Test public void should_get_measures() { - Project p = new Project("struts"); - Resource dir = Directory.create("foo/bar").setEffectiveKey("struts:foo/bar"); - Resource file1 = Directory.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt"); - Resource file2 = Directory.create("foo/bar/File2.txt").setEffectiveKey("struts:foo/bar/File2.txt"); + String projectKey = "struts"; + String dirKey = "struts:foo/bar"; + String file1Key = "struts:foo/bar/File1.txt"; + String file2Key = "struts:foo/bar/File2.txt"; assertThat(measureCache.entries()).hasSize(0); - assertThat(measureCache.byResource(p)).hasSize(0); - assertThat(measureCache.byResource(dir)).hasSize(0); + assertThat(measureCache.byComponentKey(projectKey)).hasSize(0); + assertThat(measureCache.byComponentKey(dirKey)).hasSize(0); - Measure mFile1 = new Measure(CoreMetrics.NCLOC, 1.0); - measureCache.put(file1, mFile1); - Measure mFile2 = new Measure(CoreMetrics.NCLOC, 3.0); - measureCache.put(file2, mFile2); + DefaultMeasure<?> mFile1 = new DefaultMeasure().forMetric(CoreMetrics.NCLOC).withValue(1.0); + measureCache.put(file1Key, CoreMetrics.NCLOC_DATA_KEY, mFile1); + DefaultMeasure<?> mFile2 = new DefaultMeasure().forMetric(CoreMetrics.NCLOC).withValue(3.0); + measureCache.put(file2Key, CoreMetrics.NCLOC_DATA_KEY, mFile2); assertThat(measureCache.entries()).hasSize(2); - assertThat(measureCache.byResource(p)).hasSize(0); - assertThat(measureCache.byResource(dir)).hasSize(0); + assertThat(measureCache.byComponentKey(projectKey)).hasSize(0); + assertThat(measureCache.byComponentKey(dirKey)).hasSize(0); - Measure mDir = new Measure(CoreMetrics.NCLOC, 4.0); - measureCache.put(dir, mDir); + DefaultMeasure<?> mDir = new DefaultMeasure().forMetric(CoreMetrics.NCLOC).withValue(4.0); + measureCache.put(dirKey, CoreMetrics.NCLOC_DATA_KEY, mDir); assertThat(measureCache.entries()).hasSize(3); - assertThat(measureCache.byResource(p)).hasSize(0); - assertThat(measureCache.byResource(dir)).hasSize(1); - assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir); + assertThat(measureCache.byComponentKey(projectKey)).hasSize(0); + assertThat(measureCache.byComponentKey(dirKey)).hasSize(1); + assertThat(measureCache.byComponentKey(dirKey).iterator().next()).isEqualTo(mDir); - Measure mProj = new Measure(CoreMetrics.NCLOC, 4.0); - measureCache.put(p, mProj); + DefaultMeasure<?> mProj = new DefaultMeasure().forMetric(CoreMetrics.NCLOC).withValue(4.0); + measureCache.put(projectKey, CoreMetrics.NCLOC_DATA_KEY, mProj); assertThat(measureCache.entries()).hasSize(4); - assertThat(measureCache.byResource(p)).hasSize(1); - assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(mProj); - assertThat(measureCache.byResource(dir)).hasSize(1); - assertThat(measureCache.byResource(dir).iterator().next()).isEqualTo(mDir); + assertThat(measureCache.byComponentKey(projectKey)).hasSize(1); + assertThat(measureCache.byComponentKey(projectKey).iterator().next()).isEqualTo(mProj); + assertThat(measureCache.byComponentKey(dirKey)).hasSize(1); + assertThat(measureCache.byComponentKey(dirKey).iterator().next()).isEqualTo(mDir); } - @Test - public void test_measure_coder() throws Exception { - Resource file1 = File.create("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt"); - - Measure measure = new Measure(CoreMetrics.NCLOC, 3.14); - measure.setData("data"); - measure.setAlertStatus(Level.ERROR); - measure.setAlertText("alert"); - measure.setDate(new Date()); - measure.setDescription("description"); - measure.setPersistenceMode(null); - measure.setPersonId(3); - measure.setUrl("http://foo"); - measure.setVariation1(11.0); - measure.setVariation2(12.0); - measure.setVariation3(13.0); - measure.setVariation4(14.0); - measure.setVariation5(15.0); - measureCache.put(file1, measure); - - Measure savedMeasure = measureCache.byResource(file1).iterator().next(); - assertThat(EqualsBuilder.reflectionEquals(measure, savedMeasure)).isTrue(); - - } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java index e9ae12fd688..c78affbaa9b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java @@ -19,6 +19,8 @@ */ package org.sonar.scanner.sensor; +import com.google.common.collect.ImmutableMap; +import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,13 +34,14 @@ import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; -import org.sonar.api.config.Settings; import org.sonar.api.config.MapSettings; +import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.core.metric.ScannerMetrics; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; import org.sonar.scanner.index.BatchComponentCache; import org.sonar.scanner.issue.ModuleIssues; @@ -66,10 +69,9 @@ public class DefaultSensorStorageTest { private DefaultSensorStorage underTest; private Settings settings; private ModuleIssues moduleIssues; - private Project project; private MeasureCache measureCache; private ContextPropertiesCache contextPropertiesCache = new ContextPropertiesCache(); - private BatchComponentCache resourceCache; + private BatchComponentCache componentCache; @Before public void prepare() throws Exception { @@ -78,24 +80,23 @@ public class DefaultSensorStorageTest { when(metricFinder.<String>findByKey(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)).thenReturn(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION); settings = new MapSettings(); moduleIssues = mock(ModuleIssues.class); - project = new Project("myProject"); measureCache = mock(MeasureCache.class); CoverageExclusions coverageExclusions = mock(CoverageExclusions.class); - when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true); - resourceCache = new BatchComponentCache(); + when(coverageExclusions.isExcluded(any(InputFile.class))).thenReturn(false); + componentCache = new BatchComponentCache(); ReportPublisher reportPublisher = mock(ReportPublisher.class); when(reportPublisher.getWriter()).thenReturn(new ScannerReportWriter(temp.newFolder())); underTest = new DefaultSensorStorage(metricFinder, - moduleIssues, settings, coverageExclusions, resourceCache, reportPublisher, measureCache, - mock(SonarCpdBlockIndex.class), contextPropertiesCache); + moduleIssues, settings, coverageExclusions, componentCache, reportPublisher, measureCache, + mock(SonarCpdBlockIndex.class), contextPropertiesCache, new ScannerMetrics()); } @Test public void shouldFailIfUnknownMetric() { InputFile file = new DefaultInputFile("foo", "src/Foo.php"); - thrown.expect(IllegalStateException.class); - thrown.expectMessage("Unknow metric with key: lines"); + thrown.expect(UnsupportedOperationException.class); + thrown.expectMessage("Unknown metric: lines"); underTest.store(new DefaultMeasure() .on(file) @@ -107,36 +108,37 @@ public class DefaultSensorStorageTest { public void shouldSaveFileMeasureToSensorContext() { InputFile file = new DefaultInputFile("foo", "src/Foo.php"); - ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); + ArgumentCaptor<DefaultMeasure> argumentCaptor = ArgumentCaptor.forClass(DefaultMeasure.class); Resource sonarFile = File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); - resourceCache.add(sonarFile, null).setInputComponent(file); - when(measureCache.put(eq(sonarFile), argumentCaptor.capture())).thenReturn(null); + componentCache.add(sonarFile, null).setInputComponent(file); + when(measureCache.put(eq(file.key()), eq(CoreMetrics.NCLOC_KEY), argumentCaptor.capture())).thenReturn(null); underTest.store(new DefaultMeasure() .on(file) .forMetric(CoreMetrics.NCLOC) .withValue(10)); - org.sonar.api.measures.Measure m = argumentCaptor.getValue(); - assertThat(m.getValue()).isEqualTo(10.0); - assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC); + DefaultMeasure m = argumentCaptor.getValue(); + assertThat(m.value()).isEqualTo(10); + assertThat(m.metric()).isEqualTo(CoreMetrics.NCLOC); } @Test public void shouldSaveProjectMeasureToSensorContext() { - DefaultInputModule module = new DefaultInputModule(project.getEffectiveKey()); - resourceCache.add(project, null).setInputComponent(module); + String projectKey = "myProject"; + DefaultInputModule module = new DefaultInputModule(projectKey); + componentCache.add(new Project(projectKey), null).setInputComponent(module); - ArgumentCaptor<org.sonar.api.measures.Measure> argumentCaptor = ArgumentCaptor.forClass(org.sonar.api.measures.Measure.class); - when(measureCache.put(eq(project), argumentCaptor.capture())).thenReturn(null); + ArgumentCaptor<DefaultMeasure> argumentCaptor = ArgumentCaptor.forClass(DefaultMeasure.class); + when(measureCache.put(eq(module.key()), eq(CoreMetrics.NCLOC_KEY), argumentCaptor.capture())).thenReturn(null); underTest.store(new DefaultMeasure() .on(module) .forMetric(CoreMetrics.NCLOC) .withValue(10)); - org.sonar.api.measures.Measure m = argumentCaptor.getValue(); - assertThat(m.getValue()).isEqualTo(10.0); - assertThat(m.getMetric()).isEqualTo(CoreMetrics.NCLOC); + DefaultMeasure m = argumentCaptor.getValue(); + assertThat(m.value()).isEqualTo(10); + assertThat(m.metric()).isEqualTo(CoreMetrics.NCLOC); } @Test(expected = UnsupportedOperationException.class) @@ -144,7 +146,7 @@ public class DefaultSensorStorageTest { Resource sonarFile = File.create("src/Foo.java").setEffectiveKey("foo:src/Foo.java"); DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.java") .setModuleBaseDir(temp.newFolder().toPath()); - resourceCache.add(sonarFile, null).setInputComponent(inputFile); + componentCache.add(sonarFile, null).setInputComponent(inputFile); DefaultHighlighting h = new DefaultHighlighting(null) .onFile(inputFile); underTest.store(h); @@ -156,7 +158,7 @@ public class DefaultSensorStorageTest { Resource sonarFile = File.create("src/Foo.java").setEffectiveKey("foo:src/Foo.java"); DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.java") .setModuleBaseDir(temp.newFolder().toPath()); - resourceCache.add(sonarFile, null).setInputComponent(inputFile); + componentCache.add(sonarFile, null).setInputComponent(inputFile); DefaultSymbolTable st = new DefaultSymbolTable(null) .onFile(inputFile); underTest.store(st); @@ -166,9 +168,28 @@ public class DefaultSensorStorageTest { @Test public void shouldStoreContextProperty() { underTest.storeProperty("foo", "bar"); - assertThat(contextPropertiesCache.getAll()).containsOnly(entry("foo", "bar")); + } + + @Test + public void shouldValidateStrictlyPositiveLine() throws Exception { + DefaultInputFile file = new DefaultInputFile("module", "testfile").setModuleBaseDir(temp.newFolder().toPath()); + Map<Integer, Integer> map = ImmutableMap.of(0, 3); + String data = KeyValueFormat.format(map); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("must be > 0"); + underTest.validateCoverageMeasure(data, file); + } + + @Test + public void shouldValidateMaxLine() throws Exception { + DefaultInputFile file = new DefaultInputFile("module", "testfile").setModuleBaseDir(temp.newFolder().toPath()); + Map<Integer, Integer> map = ImmutableMap.of(11, 3); + String data = KeyValueFormat.format(map); + + thrown.expect(IllegalStateException.class); + underTest.validateCoverageMeasure(data, file); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java index 3a59eecdce3..68d4080cb9b 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java @@ -19,124 +19,41 @@ */ package org.sonar.scanner.sensor.coverage; -import com.google.common.collect.ImmutableMap; -import java.util.Map; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.MapSettings; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; -import org.sonar.api.config.MapSettings; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Resource; -import org.sonar.api.utils.KeyValueFormat; import org.sonar.core.config.ExclusionProperties; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; public class CoverageExclusionsTest { - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - private Settings settings; - private DefaultFileSystem fs; - - private CoverageExclusions filter; + private CoverageExclusions coverageExclusions; @Before - public void createFilter() { + public void prepare() { settings = new MapSettings(new PropertyDefinitions(ExclusionProperties.all())); - fs = new DefaultFileSystem(temp.getRoot()); - filter = new CoverageExclusions(settings, fs); - } - - @Test - public void shouldValidateStrictlyPositiveLine() { - DefaultInputFile file = new DefaultInputFile("module", "testfile"); - Measure measure = mock(Measure.class); - Map<Integer, Integer> map = ImmutableMap.of(0, 3); - - String data = KeyValueFormat.format(map); - when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE); - when(measure.getData()).thenReturn(data); - - fs.add(file); - - exception.expect(IllegalStateException.class); - exception.expectMessage("must be > 0"); - filter.validate(measure, "testfile"); + coverageExclusions = new CoverageExclusions(settings); } @Test - public void shouldValidateFileExists() { - DefaultInputFile file = new DefaultInputFile("module", "testfile"); - Measure measure = mock(Measure.class); - Map<Integer, Integer> map = ImmutableMap.of(0, 3); - - String data = KeyValueFormat.format(map); - when(measure.getMetric()).thenReturn(CoreMetrics.IT_CONDITIONS_BY_LINE); - when(measure.getData()).thenReturn(data); - - fs.add(file); - - exception.expect(IllegalStateException.class); - exception.expectMessage("resource is not indexed as a file"); - filter.validate(measure, "dummy"); - } - - @Test - public void shouldValidateMaxLine() { - DefaultInputFile file = new DefaultInputFile("module", "testfile"); - file.setLines(10); - Measure measure = mock(Measure.class); - Map<Integer, Integer> map = ImmutableMap.of(11, 3); - - String data = KeyValueFormat.format(map); - when(measure.getMetric()).thenReturn(CoreMetrics.COVERED_CONDITIONS_BY_LINE); - when(measure.getData()).thenReturn(data); - - exception.expect(IllegalStateException.class); - filter.validate(measure, file); - } - - @Test - public void shouldNotFilterNonCoverageMetrics() { - Measure otherMeasure = mock(Measure.class); - when(otherMeasure.getMetric()).thenReturn(CoreMetrics.LINES); - assertThat(filter.accept(mock(Resource.class), otherMeasure)).isTrue(); - } - - @Test - public void shouldFilterFileBasedOnPattern() { - Resource resource = File.create("src/org/polop/File.php", null, false); - Measure coverageMeasure = mock(Measure.class); - when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER); - + public void shouldExcludeFileBasedOnPattern() { + InputFile file = new DefaultInputFile("foo", "src/org/polop/File.php"); settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*"); - filter.initPatterns(); - assertThat(filter.accept(resource, coverageMeasure)).isFalse(); + coverageExclusions.initPatterns(); + assertThat(coverageExclusions.isExcluded(file)).isTrue(); } @Test - public void shouldNotFilterFileBasedOnPattern() { - Resource resource = File.create("src/org/polop/File.php", null, false); - Measure coverageMeasure = mock(Measure.class); - when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.COVERAGE); - + public void shouldNotExcludeFileBasedOnPattern() { + InputFile file = new DefaultInputFile("foo", "src/org/polop/File.php"); settings.setProperty("sonar.coverage.exclusions", "src/org/other/*"); - filter.initPatterns(); - assertThat(filter.accept(resource, coverageMeasure)).isTrue(); + coverageExclusions.initPatterns(); + assertThat(coverageExclusions.isExcluded(file)).isFalse(); } } |