From d4beaec49895945d23d509d1502790794b008d56 Mon Sep 17 00:00:00 2001
From: simonbrandhof
Date: Fri, 25 Feb 2011 00:16:09 +0100
Subject: [PATCH] SONAR-2218 Add CoverageMeasuresBuilder to API + add
Metric.Builder, first step to get an immutable Metric class
---
.../api/AbstractCoberturaParser.java | 122 ++++---------
.../cobertura/CoberturaSensorTest.java | 53 ++----
.../sonar/batch/index/MeasurePersister.java | 5 +-
.../org/sonar/api/measures/CoreMetrics.java | 107 ++++++++++--
.../api/measures/CoverageMeasuresBuilder.java | 161 ++++++++++++++++++
.../java/org/sonar/api/measures/Measure.java | 72 ++++----
.../java/org/sonar/api/measures/Metric.java | 97 ++++++++++-
.../measures/CoverageMeasuresBuilderTest.java | 128 ++++++++++++++
.../org/sonar/api/measures/MetricTest.java | 59 +++++++
9 files changed, 622 insertions(+), 182 deletions(-)
create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/measures/CoverageMeasuresBuilder.java
create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/measures/CoverageMeasuresBuilderTest.java
create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/measures/MetricTest.java
diff --git a/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/api/AbstractCoberturaParser.java b/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/api/AbstractCoberturaParser.java
index bd095688fb0..3507795e693 100644
--- a/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/api/AbstractCoberturaParser.java
+++ b/plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/api/AbstractCoberturaParser.java
@@ -19,36 +19,31 @@
*/
package org.sonar.plugins.cobertura.api;
-import static java.util.Locale.ENGLISH;
-import static org.sonar.api.utils.ParsingUtils.parseNumber;
-import static org.sonar.api.utils.ParsingUtils.scaleValue;
-
+import com.google.common.collect.Maps;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
import org.sonar.api.batch.SensorContext;
-import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.CoverageMeasuresBuilder;
import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
-import org.sonar.api.measures.PropertiesBuilder;
import org.sonar.api.resources.Resource;
import org.sonar.api.utils.StaxParser;
import org.sonar.api.utils.XmlParserException;
+import javax.xml.stream.XMLStreamException;
import java.io.File;
import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import javax.xml.stream.XMLStreamException;
+import static java.util.Locale.ENGLISH;
+import static org.sonar.api.utils.ParsingUtils.parseNumber;
/**
* @since 2.4
*/
public abstract class AbstractCoberturaParser {
+
public void parseReport(File xmlFile, final SensorContext context) {
try {
StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
@@ -70,110 +65,57 @@ public abstract class AbstractCoberturaParser {
private void collectPackageMeasures(SMInputCursor pack, SensorContext context) throws ParseException, XMLStreamException {
while (pack.getNext() != null) {
- Map fileDataPerFilename = new HashMap();
- collectFileMeasures(pack.descendantElementCursor("class"), fileDataPerFilename);
- for (FileData cci : fileDataPerFilename.values()) {
- if (isFileExist(context, cci.getFile())) {
- for (Measure measure : cci.getMeasures()) {
- context.saveMeasure(cci.getFile(), measure);
+ Map builderByFilename = Maps.newHashMap();
+ collectFileMeasures(pack.descendantElementCursor("class"), builderByFilename);
+ for (Map.Entry entry : builderByFilename.entrySet()) {
+ String filename = sanitizeFilename(entry.getKey());
+ Resource file = getResource(filename);
+ if (fileExists(context, file)) {
+ for (Measure measure : entry.getValue().createMeasures()) {
+ System.out.println(file + " -> " + measure);
+ context.saveMeasure(file, measure);
}
}
}
}
}
- private boolean isFileExist(SensorContext context, Resource> file) {
+ private boolean fileExists(SensorContext context, Resource file) {
return context.getResource(file) != null;
}
- private void collectFileMeasures(SMInputCursor clazz, Map dataPerFilename) throws ParseException, XMLStreamException {
+ private void collectFileMeasures(SMInputCursor clazz, Map builderByFilename) throws ParseException, XMLStreamException {
while (clazz.getNext() != null) {
- String fileName = FilenameUtils.removeExtension(clazz.getAttrValue("filename"));
- fileName = fileName.replace('/', '.').replace('\\', '.');
- FileData data = dataPerFilename.get(fileName);
- if (data == null) {
- data = new FileData(getResource(fileName));
- dataPerFilename.put(fileName, data);
+ String fileName = clazz.getAttrValue("filename");
+ CoverageMeasuresBuilder builder = builderByFilename.get(fileName);
+ if (builder==null) {
+ builder = CoverageMeasuresBuilder.create();
+ builderByFilename.put(fileName, builder);
}
- collectFileData(clazz, data);
+ collectFileData(clazz, builder);
}
}
- private void collectFileData(SMInputCursor clazz, FileData data) throws ParseException, XMLStreamException {
+ private void collectFileData(SMInputCursor clazz, CoverageMeasuresBuilder builder) throws ParseException, XMLStreamException {
SMInputCursor line = clazz.childElementCursor("lines").advance().childElementCursor("line");
while (line.getNext() != null) {
- String lineId = line.getAttrValue("number");
- data.addLine(lineId, (int) parseNumber(line.getAttrValue("hits"), ENGLISH));
+ int lineId = Integer.parseInt(line.getAttrValue("number"));
+ builder.setHits(lineId, (int) parseNumber(line.getAttrValue("hits"), ENGLISH));
String isBranch = line.getAttrValue("branch");
String text = line.getAttrValue("condition-coverage");
if (StringUtils.equals(isBranch, "true") && StringUtils.isNotBlank(text)) {
String[] conditions = StringUtils.split(StringUtils.substringBetween(text, "(", ")"), "/");
- data.addConditionLine(lineId, Integer.parseInt(conditions[0]), Integer.parseInt(conditions[1]), StringUtils.substringBefore(text, " "));
- }
- }
- }
-
- private class FileData {
-
- private int lines = 0;
- private int conditions = 0;
- private int coveredLines = 0;
- private int coveredConditions = 0;
-
- private Resource> file;
- private PropertiesBuilder lineHitsBuilder = new PropertiesBuilder(CoreMetrics.COVERAGE_LINE_HITS_DATA);
- private PropertiesBuilder branchHitsBuilder = new PropertiesBuilder(CoreMetrics.BRANCH_COVERAGE_HITS_DATA);
-
- public void addLine(String lineId, int lineHits) {
- lines++;
- if (lineHits > 0) {
- coveredLines++;
- }
- lineHitsBuilder.add(lineId, lineHits);
- }
-
- public void addConditionLine(String lineId, int coveredConditions, int conditions, String label) {
- this.conditions += conditions;
- this.coveredConditions += coveredConditions;
- branchHitsBuilder.add(lineId, label);
- }
-
- public FileData(Resource> file) {
- this.file = file;
- }
-
- public List getMeasures() {
- List measures = new ArrayList();
- if (lines > 0) {
- measures.add(new Measure(CoreMetrics.COVERAGE, calculateCoverage(coveredLines + coveredConditions, lines + conditions)));
-
- measures.add(new Measure(CoreMetrics.LINE_COVERAGE, calculateCoverage(coveredLines, lines)));
- measures.add(new Measure(CoreMetrics.LINES_TO_COVER, (double) lines));
- measures.add(new Measure(CoreMetrics.UNCOVERED_LINES, (double) lines - coveredLines));
- measures.add(lineHitsBuilder.build().setPersistenceMode(PersistenceMode.DATABASE));
-
- if (conditions > 0) {
- measures.add(new Measure(CoreMetrics.BRANCH_COVERAGE, calculateCoverage(coveredConditions, conditions)));
- measures.add(new Measure(CoreMetrics.CONDITIONS_TO_COVER, (double) conditions));
- measures.add(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, (double) conditions - coveredConditions));
- measures.add(branchHitsBuilder.build().setPersistenceMode(PersistenceMode.DATABASE));
- }
+ builder.setConditions(lineId, Integer.parseInt(conditions[1]), Integer.parseInt(conditions[0]));
}
- return measures;
- }
-
- public Resource> getFile() {
- return file;
}
}
- private double calculateCoverage(int coveredElements, int elements) {
- if (elements > 0) {
- return scaleValue(100.0 * ((double) coveredElements / (double) elements));
- }
- return 0.0;
+ private String sanitizeFilename(String s) {
+ String fileName = FilenameUtils.removeExtension(s);
+ fileName = fileName.replace('/', '.').replace('\\', '.');
+ return fileName;
}
- protected abstract Resource> getResource(String fileName);
+ protected abstract Resource getResource(String fileName);
}
diff --git a/plugins/sonar-cobertura-plugin/src/test/java/org/sonar/plugins/cobertura/CoberturaSensorTest.java b/plugins/sonar-cobertura-plugin/src/test/java/org/sonar/plugins/cobertura/CoberturaSensorTest.java
index 0b57c57523c..8b5c57f7e3f 100644
--- a/plugins/sonar-cobertura-plugin/src/test/java/org/sonar/plugins/cobertura/CoberturaSensorTest.java
+++ b/plugins/sonar-cobertura-plugin/src/test/java/org/sonar/plugins/cobertura/CoberturaSensorTest.java
@@ -23,9 +23,7 @@ import org.junit.Test;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
-import org.sonar.api.resources.JavaFile;
-import org.sonar.api.resources.JavaPackage;
-import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.*;
import org.sonar.api.test.IsMeasure;
import org.sonar.api.test.IsResource;
@@ -37,10 +35,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyDouble;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
public class CoberturaSensorTest {
@@ -103,7 +98,6 @@ public class CoberturaSensorTest {
new CoberturaSensor().parseReport(getCoverageReport(), context);
final JavaFile file = new JavaFile("org.apache.commons.chain.config.ConfigParser");
- // verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.LINE_COVERAGE, 83.3)));
verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.LINES_TO_COVER, 30.0)));
verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.UNCOVERED_LINES, 5.0)));
}
@@ -115,7 +109,6 @@ public class CoberturaSensorTest {
new CoberturaSensor().parseReport(getCoverageReport(), context);
final JavaFile file = new JavaFile("org.apache.commons.chain.config.ConfigParser");
- verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.BRANCH_COVERAGE, 66.7)));
verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.CONDITIONS_TO_COVER, 6.0)));
verify(context).saveMeasure(eq(file), argThat(new IsMeasure(CoreMetrics.UNCOVERED_CONDITIONS, 2.0)));
}
@@ -153,56 +146,40 @@ public class CoberturaSensorTest {
when(context.getResource(any(Resource.class))).thenReturn(new JavaFile("org.sonar.MyClass"));
new CoberturaSensor().parseReport(coverage, context);
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
- argThat(new IsMeasure(CoreMetrics.LINE_COVERAGE, 37.1)));
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
+ verify(context).saveMeasure(argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass")),
argThat(new IsMeasure(CoreMetrics.LINES_TO_COVER, 35.0)));
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
+ verify(context).saveMeasure(argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_LINES, 22.0)));
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
- argThat(new IsMeasure(CoreMetrics.BRANCH_COVERAGE, 25.0)));
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
+ verify(context).saveMeasure(argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass")),
argThat(new IsMeasure(CoreMetrics.CONDITIONS_TO_COVER, 4.0)));
- verify(context).saveMeasure(argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass")),
+ verify(context).saveMeasure(argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_CONDITIONS, 3.0)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
- argThat(new IsMeasure(CoreMetrics.LINE_COVERAGE)));
- verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
- argThat(new IsMeasure(CoreMetrics.BRANCH_COVERAGE)));
- verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
argThat(new IsMeasure(CoreMetrics.LINES_TO_COVER)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
argThat(new IsMeasure(CoreMetrics.CONDITIONS_TO_COVER)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_CONDITIONS)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.InnerClass$InnerClassInside")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_LINES)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
- argThat(new IsMeasure(CoreMetrics.LINE_COVERAGE)));
- verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
- argThat(new IsMeasure(CoreMetrics.BRANCH_COVERAGE)));
- verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.PrivateClass")),
argThat(new IsMeasure(CoreMetrics.LINES_TO_COVER)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.PrivateClass")),
argThat(new IsMeasure(CoreMetrics.CONDITIONS_TO_COVER)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.PrivateClass")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_CONDITIONS)));
verify(context, never()).saveMeasure(
- argThat(new IsResource(Resource.SCOPE_ENTITY, Resource.QUALIFIER_CLASS, "org.sonar.samples.PrivateClass")),
+ argThat(new IsResource(Scopes.FILE, Qualifiers.CLASS, "org.sonar.samples.PrivateClass")),
argThat(new IsMeasure(CoreMetrics.UNCOVERED_LINES)));
verify(context)
@@ -221,7 +198,7 @@ public class CoberturaSensorTest {
verify(context).saveMeasure(
eq(new JavaFile("org.apache.commons.chain.impl.CatalogBase")),
argThat(new IsMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA,
- "111=18;121=0;122=0;125=0;126=0;127=0;128=0;131=0;133=0;48=117;56=234;66=0;67=0;68=0;84=999;86=999;98=318")));
+ "48=117;56=234;66=0;67=0;68=0;84=999;86=999;98=318;111=18;121=0;122=0;125=0;126=0;127=0;128=0;131=0;133=0")));
}
@Test
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
index f80e487d868..97942ef8f30 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
@@ -29,7 +29,6 @@ import org.sonar.api.database.model.Snapshot;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.RuleMeasure;
-import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.api.rules.Rule;
@@ -90,12 +89,12 @@ public final class MeasurePersister {
return measure.getId() == null &&
metric.isOptimizedBestValue() == Boolean.TRUE &&
metric.getBestValue() != null &&
- (measure.getValue()==null || NumberUtils.compare(metric.getBestValue(), measure.getValue()) == 0) &&
+ (measure.getValue() == null || NumberUtils.compare(metric.getBestValue(), measure.getValue()) == 0) &&
measure.getAlertStatus() == null &&
measure.getDescription() == null &&
measure.getTendency() == null &&
measure.getUrl() == null &&
- measure.getData() == null &&
+ !measure.hasData() &&
(measure.getVariation1() == null || NumberUtils.compare(measure.getVariation1().doubleValue(), 0.0) == 0) &&
(measure.getVariation2() == null || NumberUtils.compare(measure.getVariation2().doubleValue(), 0.0) == 0) &&
(measure.getVariation3() == null || NumberUtils.compare(measure.getVariation3().doubleValue(), 0.0) == 0) &&
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
index 0b5c68efb46..d7c94b4853b 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -19,15 +19,13 @@
*/
package org.sonar.api.measures;
+import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.SonarException;
import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* @since 1.10
@@ -43,6 +41,7 @@ public final class CoreMetrics {
public static final String DOMAIN_COMPLEXITY = "Complexity";
public static final String DOMAIN_DOCUMENTATION = "Documentation";
public static final String DOMAIN_RULES = "Rules";
+ public static final String DOMAIN_SCM = "SCM";
/**
* @deprecated since 2.5 See http://jira.codehaus.org/browse/SONAR-2007
@@ -180,7 +179,12 @@ public final class CoreMetrics {
"Commented lines of code", Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_DOCUMENTATION).setFormula(
new SumChildValuesFormula(false)).setBestValue(0.0).setOptimizedBestValue(true);
- /* unit tests */
+
+
+
+
+ // UNIT TESTS
+
public static final String TESTS_KEY = "tests";
public static final Metric TESTS = new Metric(TESTS_KEY, "Unit tests", "Number of unit tests", Metric.ValueType.INT,
Metric.DIRECTION_WORST, false, DOMAIN_TESTS);
@@ -214,33 +218,83 @@ public final class CoreMetrics {
public static final Metric LINES_TO_COVER = new Metric(LINES_TO_COVER_KEY, "Lines to cover", "Lines to cover", Metric.ValueType.INT,
Metric.DIRECTION_BETTER, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false)).setHidden(true);
+
public static final String UNCOVERED_LINES_KEY = "uncovered_lines";
- public static final Metric UNCOVERED_LINES = new Metric(UNCOVERED_LINES_KEY, "Uncovered lines", "Uncovered lines", Metric.ValueType.INT,
- Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false));
+ public static final Metric UNCOVERED_LINES = new Metric.Builder(UNCOVERED_LINES_KEY, Metric.ValueType.INT)
+ .setName("Uncovered lines")
+ .setDescription("Uncovered lines")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_TESTS)
+ .setFormula(new SumChildValuesFormula(false))
+ .create();
+
+ public static final String NEW_UNCOVERED_LINES_KEY = "new_uncovered_lines";
+ public static final Metric NEW_UNCOVERED_LINES = new Metric.Builder(NEW_UNCOVERED_LINES_KEY, Metric.ValueType.INT)
+ .setName("New uncovered lines")
+ .setDescription("New uncovered lines")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_TESTS)
+ .setFormula(new SumChildValuesFormula(false))
+ .create();
public static final String LINE_COVERAGE_KEY = "line_coverage";
public static final Metric LINE_COVERAGE = new Metric(LINE_COVERAGE_KEY, "Line coverage", "Line coverage", Metric.ValueType.PERCENT,
Metric.DIRECTION_BETTER, true, DOMAIN_TESTS);
public static final String COVERAGE_LINE_HITS_DATA_KEY = "coverage_line_hits_data";
- public static final Metric COVERAGE_LINE_HITS_DATA = new Metric(COVERAGE_LINE_HITS_DATA_KEY, "Coverage hits data",
- "Code coverage line hits data", Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_TESTS);
+ public static final Metric COVERAGE_LINE_HITS_DATA = new Metric.Builder(COVERAGE_LINE_HITS_DATA_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_TESTS)
+ .create();
public static final String CONDITIONS_TO_COVER_KEY = "conditions_to_cover";
public static final Metric CONDITIONS_TO_COVER = new Metric(CONDITIONS_TO_COVER_KEY, "Conditions to cover", "Conditions to cover",
Metric.ValueType.INT, Metric.DIRECTION_BETTER, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false)).setHidden(true);
public static final String UNCOVERED_CONDITIONS_KEY = "uncovered_conditions";
- public static final Metric UNCOVERED_CONDITIONS = new Metric(UNCOVERED_CONDITIONS_KEY, "Uncovered conditions", "Uncovered conditions",
- Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false));
+ public static final Metric UNCOVERED_CONDITIONS = new Metric.Builder(UNCOVERED_CONDITIONS_KEY, Metric.ValueType.INT)
+ .setName("Uncovered conditions")
+ .setDescription("Uncovered conditions")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_TESTS)
+ .setFormula(new SumChildValuesFormula(false))
+ .create();
+
+ public static final String NEW_UNCOVERED_CONDITIONS_KEY = "new_uncovered_conditions";
+ public static final Metric NEW_UNCOVERED_CONDITIONS = new Metric.Builder(NEW_UNCOVERED_CONDITIONS_KEY, Metric.ValueType.INT)
+ .setName("New uncovered conditions")
+ .setDescription("New uncovered conditions")
+ .setDirection(Metric.DIRECTION_WORST)
+ .setDomain(DOMAIN_TESTS)
+ .setFormula(new SumChildValuesFormula(false))
+ .create();
public static final String BRANCH_COVERAGE_KEY = "branch_coverage";
public static final Metric BRANCH_COVERAGE = new Metric(BRANCH_COVERAGE_KEY, "Branch coverage", "Branch coverage",
Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_TESTS).setWorstValue(0.0).setBestValue(100.0);
public static final String BRANCH_COVERAGE_HITS_DATA_KEY = "branch_coverage_hits_data";
- public static final Metric BRANCH_COVERAGE_HITS_DATA = new Metric(BRANCH_COVERAGE_HITS_DATA_KEY, "Branch coverage hits",
- "Branch coverage hits", Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_TESTS);
+ public static final Metric BRANCH_COVERAGE_HITS_DATA = new Metric.Builder(BRANCH_COVERAGE_HITS_DATA_KEY, Metric.ValueType.DATA)
+ .setName("Branch coverage hits")
+ .setDomain(DOMAIN_TESTS)
+ .create();
+
+ public static final String CONDITIONS_BY_LINE_DATA_KEY = "conditions_by_line_data";
+
+ /**
+ * @since 2.7
+ */
+ public static final Metric CONDITIONS_BY_LINE_DATA = new Metric.Builder(CONDITIONS_BY_LINE_DATA_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_TESTS)
+ .create();
+
+ public static final String COVERED_CONDITIONS_BY_LINE_DATA_KEY = "covered_conditions_by_line_data";
+
+ /**
+ * @since 2.7
+ */
+ public static final Metric COVERED_CONDITIONS_BY_LINE_DATA = new Metric.Builder(COVERED_CONDITIONS_BY_LINE_DATA_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_TESTS)
+ .create();
/**
* @deprecated replaced since 1.11 by UNCOVERED_LINES and UNCOVERED_CONDITIONS
@@ -398,7 +452,11 @@ public final class CoreMetrics {
public static final Metric NEW_INFO_VIOLATIONS = new Metric(NEW_INFO_VIOLATIONS_KEY, "New Info violations", "New Info violations",
Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_RULES).setHidden(true).setBestValue(0.0).setOptimizedBestValue(true);
- /* Design */
+
+
+
+
+ // DESIGN
public static final String ABSTRACTNESS_KEY = "abstractness";
public static final Metric ABSTRACTNESS = new Metric(ABSTRACTNESS_KEY, "Abstractness", "Abstractness", Metric.ValueType.PERCENT,
@@ -506,9 +564,28 @@ public final class CoreMetrics {
public static final Metric PROFILE = new Metric(PROFILE_KEY, "Profile", "Selected quality profile", Metric.ValueType.DATA,
Metric.DIRECTION_NONE, false, DOMAIN_GENERAL);
- public static List metrics = new ArrayList();
- public static Set metricKeys = new HashSet();
+
+
+
+ // SCM
+
+ public static final String SCM_AUTHORS_BY_LINE_KEY = "blame_authors_data";//"scm_authors_by_line";
+ public static final Metric SCM_AUTHORS_BY_LINE = new Metric.Builder(SCM_AUTHORS_BY_LINE_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_SCM)
+ .create();
+
+ public static final String SCM_REVISIONS_BY_LINE_KEY = "blame_revision_data";//"scm_revisions_by_line";
+ public static final Metric SCM_REVISIONS_BY_LINE = new Metric.Builder(SCM_REVISIONS_BY_LINE_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_SCM)
+ .create();
+
+ public static final String SCM_LAST_UPDATE_DATETIME_BY_LINE_KEY = "blame_date_data";//"scm_last_update_datetime_by_line";
+ public static final Metric SCM_LAST_UPDATE_DATETIME_BY_LINE = new Metric.Builder(SCM_LAST_UPDATE_DATETIME_BY_LINE_KEY, Metric.ValueType.DATA)
+ .setDomain(DOMAIN_SCM)
+ .create();
+
+ public static List metrics = Lists.newLinkedList();
public static List getMetrics() {
if (metrics.isEmpty()) {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoverageMeasuresBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoverageMeasuresBuilder.java
new file mode 100644
index 00000000000..dfc37496a54
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoverageMeasuresBuilder.java
@@ -0,0 +1,161 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.measures;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.sonar.api.utils.KeyValueFormat;
+
+import java.util.*;
+
+/**
+ * @since 2.7
+ */
+public final class CoverageMeasuresBuilder {
+
+ /**
+ * Metrics of generated measures
+ */
+ public static final List METRICS = Arrays.asList(
+ CoreMetrics.LINES_TO_COVER, CoreMetrics.UNCOVERED_LINES, CoreMetrics.COVERAGE_LINE_HITS_DATA,
+ CoreMetrics.CONDITIONS_TO_COVER, CoreMetrics.UNCOVERED_CONDITIONS, CoreMetrics.CONDITIONS_BY_LINE_DATA,
+ CoreMetrics.COVERED_CONDITIONS_BY_LINE_DATA, CoreMetrics.BRANCH_COVERAGE_HITS_DATA);
+
+
+ private int totalCoveredLines = 0, totalConditions = 0, totalCoveredConditions = 0;
+ private SortedMap hitsByLine = Maps.newTreeMap();
+ private SortedMap conditionsByLine = Maps.newTreeMap();
+ private SortedMap coveredConditionsByLine = Maps.newTreeMap();
+
+ private CoverageMeasuresBuilder() {
+ // use the factory
+ }
+
+ public CoverageMeasuresBuilder reset() {
+ totalCoveredLines = 0;
+ totalConditions = 0;
+ totalCoveredConditions = 0;
+ hitsByLine.clear();
+ conditionsByLine.clear();
+ coveredConditionsByLine.clear();
+ return this;
+ }
+
+ public CoverageMeasuresBuilder setHits(int lineId, int hits) {
+ if (hitsByLine.containsKey(lineId)) {
+ throw new IllegalArgumentException("Line " + lineId + " is count twice (hits=" + hits + ")");
+ }
+ hitsByLine.put(lineId, hits);
+ if (hits > 0) {
+ totalCoveredLines += 1;
+ }
+ return this;
+ }
+
+ public CoverageMeasuresBuilder setConditions(int lineId, int conditions, int coveredConditions) {
+ if (conditionsByLine.containsKey(lineId)) {
+ throw new IllegalArgumentException("Line " + lineId + " is count twice (conditions=" + conditions + ")");
+ }
+ if (conditions > 0) {
+ totalConditions += conditions;
+ totalCoveredConditions += coveredConditions;
+ conditionsByLine.put(lineId, conditions);
+ coveredConditionsByLine.put(lineId, coveredConditions);
+ }
+ return this;
+ }
+
+ public int getCoveredLines() {
+ return totalCoveredLines;
+ }
+
+ public int getLinesToCover() {
+ return hitsByLine.size();
+ }
+
+ public int getConditions() {
+ return totalConditions;
+ }
+
+ public int getCoveredConditions() {
+ return totalCoveredConditions;
+ }
+
+ public SortedMap getHitsByLine() {
+ return Collections.unmodifiableSortedMap(hitsByLine);
+ }
+
+ public SortedMap getConditionsByLine() {
+ return Collections.unmodifiableSortedMap(conditionsByLine);
+ }
+
+ public SortedMap getCoveredConditionsByLine() {
+ return Collections.unmodifiableSortedMap(coveredConditionsByLine);
+ }
+
+ public Collection createMeasures() {
+ Collection measures = Lists.newArrayList();
+ if (getLinesToCover() > 0) {
+ measures.add(new Measure(CoreMetrics.LINES_TO_COVER, (double) getLinesToCover()));
+ measures.add(new Measure(CoreMetrics.UNCOVERED_LINES, (double) (getLinesToCover() - getCoveredLines())));
+ measures.add(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA).setData(KeyValueFormat.format(hitsByLine)).setPersistenceMode(PersistenceMode.DATABASE));
+ }
+ if (getConditions() > 0) {
+ measures.add(new Measure(CoreMetrics.CONDITIONS_TO_COVER, (double) getConditions()));
+ measures.add(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, (double) (getConditions() - getCoveredConditions())));
+ measures.add(createConditionsByLineData());
+ measures.add(createCoveredConditionsByLineData());
+ measures.add(createBranchCoverageByLine());
+ }
+ return measures;
+ }
+
+ private Measure createCoveredConditionsByLineData() {
+ return new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE_DATA)
+ .setData(KeyValueFormat.format(coveredConditionsByLine))
+ .setPersistenceMode(PersistenceMode.DATABASE);
+ }
+
+ private Measure createConditionsByLineData() {
+ return new Measure(CoreMetrics.CONDITIONS_BY_LINE_DATA)
+ .setData(KeyValueFormat.format(conditionsByLine))
+ .setPersistenceMode(PersistenceMode.DATABASE);
+ }
+
+ private Measure createBranchCoverageByLine() {
+ PropertiesBuilder builder = new PropertiesBuilder(CoreMetrics.BRANCH_COVERAGE_HITS_DATA);
+ for (Map.Entry entry : conditionsByLine.entrySet()) {
+ Integer lineId = entry.getKey();
+ int conditions = entry.getValue();
+ int coveredConditions = coveredConditionsByLine.get(lineId);
+ builder.add(lineId, formatBranchCoverage(conditions, coveredConditions));
+ }
+ return builder.build().setPersistenceMode(PersistenceMode.DATABASE);
+ }
+
+ static String formatBranchCoverage(int conditions, int coveredConditions) {
+ long branchCoverage = Math.round(100.0 * coveredConditions / conditions);
+ return branchCoverage + "%";
+ }
+
+ public static CoverageMeasuresBuilder create() {
+ return new CoverageMeasuresBuilder();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
index e27d13cf69e..3b64b89da3f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
@@ -28,7 +28,7 @@ import java.util.Date;
/**
* A class to handle measures.
- *
+ *
* @since 1.10
*/
public class Measure {
@@ -60,7 +60,7 @@ public class Measure {
/**
* Creates a measure with a metric
- *
+ *
* @param metric the metric
*/
public Measure(Metric metric) {
@@ -70,9 +70,9 @@ public class Measure {
/**
* Creates a measure with a metric and a value
- *
+ *
* @param metric the metric
- * @param value its value
+ * @param value its value
*/
public Measure(Metric metric, Double value) {
this.metric = metric;
@@ -82,9 +82,9 @@ public class Measure {
/**
* Creates a measure with a metric, a value and a precision for the value
- *
- * @param metric the metric
- * @param value its value
+ *
+ * @param metric the metric
+ * @param value its value
* @param precision the value precision
*/
public Measure(Metric metric, Double value, int precision) {
@@ -95,10 +95,10 @@ public class Measure {
/**
* Creates a measure with a metric, a value and a data field
- *
+ *
* @param metric the metric
- * @param value the value
- * @param data the data field
+ * @param value the value
+ * @param data the data field
*/
public Measure(Metric metric, Double value, String data) {
this.metric = metric;
@@ -109,9 +109,9 @@ public class Measure {
/**
* * Creates a measure with a metric and a data field
- *
+ *
* @param metric the metric
- * @param data the data field
+ * @param data the data field
*/
public Measure(Metric metric, String data) {
this.metric = metric;
@@ -121,9 +121,9 @@ public class Measure {
/**
* Creates a measure with a metric and an alert level
- *
+ *
* @param metric the metric
- * @param level the alert level
+ * @param level the alert level
*/
public Measure(Metric metric, Metric.Level level) {
this.metric = metric;
@@ -156,7 +156,7 @@ public class Measure {
* example, a measure save in memory at the module level will not be accessible by the root project. In that case, database should be
* used.
*
- *
+ *
* @param mode the mode
* @return the measure object instance
*/
@@ -178,7 +178,7 @@ public class Measure {
/**
* Set the underlying metric
- *
+ *
* @param metric the metric
* @return the measure object instance
*/
@@ -198,6 +198,10 @@ public class Measure {
return null;
}
+ public boolean hasData() {
+ return data != null;
+ }
+
/**
* @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries
*/
@@ -207,7 +211,7 @@ public class Measure {
/**
* Sets the date of the measure - Used only in TimeMachine queries
- *
+ *
* @param date the date
* @return the measure object instance
*/
@@ -235,7 +239,7 @@ public class Measure {
/**
* Sets the measure value with the default precision of 1
- *
+ *
* @param v the measure value
* @return the measure object instance
*/
@@ -245,7 +249,7 @@ public class Measure {
/**
* Sets the measure value as an int
- *
+ *
* @param i the value
* @return the measure object instance
*/
@@ -260,8 +264,8 @@ public class Measure {
/**
* Sets the measure value with a given precision
- *
- * @param v the measure value
+ *
+ * @param v the measure value
* @param precision the measure value precision
* @return the measure object instance
*/
@@ -291,7 +295,7 @@ public class Measure {
/**
* Sets the data field of the measure.
- *
+ *
* @param s the data
* @return the measure object instance
*/
@@ -305,7 +309,7 @@ public class Measure {
/**
* Sets an alert level as the data field
- *
+ *
* @param level the alert level
* @return the measure object instance
*/
@@ -327,7 +331,7 @@ public class Measure {
/**
* Sets the measure description
- *
+ *
* @param description the description
* @return the measure object instance
*/
@@ -345,7 +349,7 @@ public class Measure {
/**
* Set the alert status of the measure
- *
+ *
* @param status the status
* @return the measure object instance
*/
@@ -363,7 +367,7 @@ public class Measure {
/**
* Sets the text associated to the alert on the measure
- *
+ *
* @param alertText the text
* @return the measure object instance
*/
@@ -374,7 +378,7 @@ public class Measure {
/**
* Gets the measure tendency
- *
+ *
* @return the tendency
*/
public Integer getTendency() {
@@ -383,7 +387,7 @@ public class Measure {
/**
* Sets the tendency for the measure - Internal use only
- *
+ *
* @param tendency the tendency
* @return the measure object instance
*/
@@ -401,7 +405,7 @@ public class Measure {
/**
* Sets the measure id - Internal use only
- *
+ *
* @param id the id
* @return the measure object instance
*/
@@ -420,7 +424,7 @@ public class Measure {
/**
* Internal use only
- *
+ *
* @since 2.5
*/
public Measure setVariation1(Double d) {
@@ -438,7 +442,7 @@ public class Measure {
/**
* Internal use only
- *
+ *
* @since 2.5
*/
public Measure setVariation2(Double d) {
@@ -456,7 +460,7 @@ public class Measure {
/**
* Internal use only
- *
+ *
* @since 2.5
*/
public Measure setVariation3(Double d) {
@@ -522,7 +526,7 @@ public class Measure {
/**
* Internal use only
- *
+ *
* @since 2.5
*/
public Measure setVariation(int index, Double d) {
@@ -557,7 +561,7 @@ public class Measure {
/**
* Sets the URL of the measure
- *
+ *
* @param url the url
* @return the measure object instance
*/
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
index 781b63eef13..92050c92d81 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
@@ -105,7 +105,7 @@ public class Metric implements ServerExtension, BatchExtension {
@Column(name = "origin", updatable = true, nullable = true, length = 3)
@Enumerated(EnumType.STRING)
- private Origin origin;
+ private Origin origin = Origin.JAV;
@Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20)
private Double worstValue;
@@ -119,7 +119,6 @@ public class Metric implements ServerExtension, BatchExtension {
@Column(name = "hidden", updatable = true, nullable = true)
private Boolean hidden = Boolean.FALSE;
-
/**
* Creates an empty metric
*/
@@ -216,6 +215,23 @@ public class Metric implements ServerExtension, BatchExtension {
}
}
+ private Metric(String key, String name, ValueType type, String description, Integer direction, String domain, Boolean qualitative, Double worstValue, Double bestValue, Boolean optimizedBestValue, Boolean hidden, Formula formula) {
+ this.key = key;
+ this.name = name;
+ this.description = description;
+ this.type = type;
+ this.direction = direction;
+ this.domain = domain;
+ this.qualitative = qualitative;
+ this.userManaged = Boolean.FALSE;
+ this.enabled = Boolean.TRUE;
+ this.worstValue = worstValue;
+ this.optimizedBestValue = optimizedBestValue;
+ this.bestValue = bestValue;
+ this.hidden = hidden;
+ this.formula = formula;
+ }
+
/**
* For internal use only
*/
@@ -554,4 +570,81 @@ public class Metric implements ServerExtension, BatchExtension {
this.hidden = with.hidden;
return this;
}
+
+ public static final class Builder {
+ private String key;
+ private Metric.ValueType type;
+ private String name;
+ private String description;
+ private Integer direction = DIRECTION_NONE;
+ private Boolean qualitative = Boolean.FALSE;
+ private String domain = null;
+ private Formula formula;
+ private Double worstValue;
+ private Double bestValue;
+ private boolean optimizedBestValue = false;
+ private boolean hidden = false;
+
+ public Builder(String key, ValueType type) {
+ this.key = key;
+ this.type = type;
+ }
+
+ public Builder setName(String s) {
+ this.name = s;
+ return this;
+ }
+
+ public Builder setDescription(String s) {
+ this.description = s;
+ return this;
+ }
+
+ /**
+ * Used for numeric values only
+ */
+ public Builder setDirection(Integer i) {
+ this.direction = i;
+ return this;
+ }
+
+ public Builder setQualitative(Boolean b) {
+ this.qualitative = b;
+ return this;
+ }
+
+ public Builder setDomain(String s) {
+ this.domain = s;
+ return this;
+ }
+
+ public Builder setFormula(Formula f) {
+ this.formula = f;
+ return this;
+ }
+
+ public Builder setWorstValue(Double d) {
+ this.worstValue = d;
+ return this;
+ }
+
+ public Builder setBestValue(Double d) {
+ this.bestValue = d;
+ return this;
+ }
+
+ public Builder setOptimizedBestValue(boolean b) {
+ this.optimizedBestValue = b;
+ return this;
+ }
+
+ public Builder setHidden(boolean b) {
+ this.hidden = b;
+ return this;
+ }
+
+ public Metric create() {
+ return new Metric(key, name, type, description, direction, domain, qualitative, worstValue, bestValue, optimizedBestValue, hidden, formula);
+ }
+ }
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/measures/CoverageMeasuresBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/measures/CoverageMeasuresBuilderTest.java
new file mode 100644
index 00000000000..12c43709c37
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/measures/CoverageMeasuresBuilderTest.java
@@ -0,0 +1,128 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.measures;
+
+import org.junit.Test;
+
+import java.util.Collection;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class CoverageMeasuresBuilderTest {
+
+ @Test
+ public void shouldNotCreateIfNoValues() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ assertThat(builder.createMeasures().size(), is(0));
+ }
+
+ @Test
+ public void shouldCreateHitsByLineData() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setHits(1, 0);
+ builder.setHits(2, 3);
+ builder.setHits(4, 2);
+ assertThat(find(builder.createMeasures(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY).getData(), is("1=0;2=3;4=2"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldFailIfDuplicatedLineHits() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setHits(1, 0);
+ builder.setHits(1, 3);
+ }
+
+ @Test
+ public void shouldCreateUncoveredLines() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setHits(1, 0);
+ builder.setHits(2, 3);
+ builder.setHits(3, 0);
+ assertThat(find(builder.createMeasures(), CoreMetrics.UNCOVERED_LINES_KEY).getIntValue(), is(2));
+ }
+
+ @Test
+ public void shouldCreateConditionsByLineData() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setConditions(1, 2, 2);
+ builder.setConditions(2, 1, 0);
+ assertThat(find(builder.createMeasures(), CoreMetrics.CONDITIONS_BY_LINE_DATA_KEY).getData(), is("1=2;2=1"));
+ assertThat(find(builder.createMeasures(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_DATA_KEY).getData(), is("1=2;2=0"));
+ assertThat(find(builder.createMeasures(), CoreMetrics.BRANCH_COVERAGE_HITS_DATA_KEY).getData(), is("1=100%;2=0%"));
+ }
+
+ @Test
+ public void shouldCreateNumberOfConditionsToCover() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setConditions(1, 2, 2);
+ builder.setConditions(2, 1, 0);
+ assertThat(find(builder.createMeasures(), CoreMetrics.CONDITIONS_TO_COVER_KEY).getIntValue(), is(3));
+ }
+
+ @Test
+ public void shouldCreateNumberOfUncoveredConditions() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setConditions(1, 2, 2);
+ builder.setConditions(2, 1, 0);
+ builder.setConditions(3, 3, 1);
+ assertThat(find(builder.createMeasures(), CoreMetrics.UNCOVERED_CONDITIONS_KEY).getIntValue(), is(3));
+ }
+
+ @Test
+ public void shouldSetOnlyPositiveConditions() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setConditions(1, 0, 0);
+ builder.setConditions(2, 1, 0);
+ assertThat(find(builder.createMeasures(), CoreMetrics.CONDITIONS_BY_LINE_DATA_KEY).getData(), is("2=1"));
+ assertThat(find(builder.createMeasures(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_DATA_KEY).getData(), is("2=0"));
+ assertThat(find(builder.createMeasures(), CoreMetrics.BRANCH_COVERAGE_HITS_DATA_KEY).getData(), is("2=0%"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void shouldFailIfDuplicatedLineConditions() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setConditions(1, 3, 0);
+ builder.setConditions(1, 3, 1);
+ }
+
+ @Test
+ public void shouldResetFields() {
+ CoverageMeasuresBuilder builder = CoverageMeasuresBuilder.create();
+ builder.setHits(1, 4);
+ builder.setConditions(1, 3, 1);
+ builder.reset();
+ assertThat(builder.getConditions(), is(0));
+ assertThat(builder.getCoveredConditions(), is(0));
+ assertThat(builder.getCoveredLines(), is(0));
+ assertThat(builder.getHitsByLine().size(), is(0));
+ assertThat(builder.getConditionsByLine().size(), is(0));
+ assertThat(builder.getCoveredConditionsByLine().size(), is(0));
+ }
+
+ private Measure find(Collection measures, String metricKey) {
+ for (Measure measure : measures) {
+ if (metricKey.equals(measure.getMetricKey())) {
+ return measure;
+ }
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/measures/MetricTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MetricTest.java
new file mode 100644
index 00000000000..84706bcc658
--- /dev/null
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MetricTest.java
@@ -0,0 +1,59 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.measures;
+
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class MetricTest {
+
+ @Test
+ public void shouldCreateMetric() {
+ Metric metric = new Metric.Builder("foo", Metric.ValueType.INT)
+ .setDomain("my domain")
+ .setName("Foo")
+ .create();
+
+ assertThat(metric.getKey(), is("foo"));
+ assertThat(metric.getName(), is("Foo"));
+ assertThat(metric.getDomain(), is("my domain"));
+ }
+
+ @Test
+ public void shouldCreateMetricWithDefaultValues() {
+ Metric metric = new Metric.Builder("foo", Metric.ValueType.INT)
+ .create();
+
+ assertThat(metric.getBestValue(), nullValue());
+ assertThat(metric.getDescription(), nullValue());
+ assertThat(metric.getWorstValue(), nullValue());
+ assertThat(metric.getDirection(), is(Metric.DIRECTION_NONE));
+ assertThat(metric.getEnabled(), is(true));
+ assertThat(metric.getOrigin(), is(Metric.Origin.JAV));
+ assertThat(metric.getFormula(), nullValue());
+ assertThat(metric.getId(), nullValue());
+ assertThat(metric.getUserManaged(), is(false));
+ assertThat(metric.isHidden(), is(false));
+ assertThat(metric.isOptimizedBestValue(), is(false));
+ }
+}
--
2.39.5