]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2218 Add CoverageMeasuresBuilder to API + add Metric.Builder, first step to...
authorsimonbrandhof <simon.brandhof@gmail.com>
Thu, 24 Feb 2011 23:16:09 +0000 (00:16 +0100)
committersimonbrandhof <simon.brandhof@gmail.com>
Fri, 25 Feb 2011 06:44:16 +0000 (07:44 +0100)
plugins/sonar-cobertura-plugin/src/main/java/org/sonar/plugins/cobertura/api/AbstractCoberturaParser.java
plugins/sonar-cobertura-plugin/src/test/java/org/sonar/plugins/cobertura/CoberturaSensorTest.java
sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoverageMeasuresBuilder.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
sonar-plugin-api/src/test/java/org/sonar/api/measures/CoverageMeasuresBuilderTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/measures/MetricTest.java [new file with mode: 0644]

index bd095688fb05cabbc26c59913d8d7470007b3ccb..3507795e69343b5d949b1effee64f839a063d22f 100644 (file)
  */
 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<String, FileData> fileDataPerFilename = new HashMap<String, FileData>();
-      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<String, CoverageMeasuresBuilder> builderByFilename = Maps.newHashMap();
+      collectFileMeasures(pack.descendantElementCursor("class"), builderByFilename);
+      for (Map.Entry<String, CoverageMeasuresBuilder> 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<String, FileData> dataPerFilename) throws ParseException, XMLStreamException {
+  private void collectFileMeasures(SMInputCursor clazz, Map<String, CoverageMeasuresBuilder> 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<String, Integer> lineHitsBuilder = new PropertiesBuilder<String, Integer>(CoreMetrics.COVERAGE_LINE_HITS_DATA);
-    private PropertiesBuilder<String, String> branchHitsBuilder = new PropertiesBuilder<String, String>(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<Measure> getMeasures() {
-      List<Measure> measures = new ArrayList<Measure>();
-      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);
 }
index 0b57c57523cd2d3395f505ff5a3f869d7ee4c17c..8b5c57f7e3f34db25a383374a5deba7a779f3d0b 100644 (file)
@@ -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
index f80e487d8686e3cc868b7342dc34adfe1f943391..97942ef8f30546c424ff980c80acc93b8994c43b 100644 (file)
@@ -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) &&
index 0b5c68efb46dbb87ca37ebb8c229f227aafe118b..d7c94b4853b1ed7e450a197daa6a84c9830fccf8 100644 (file)
  */
 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<Metric> metrics = new ArrayList<Metric>();
 
-  public static Set<String> metricKeys = new HashSet<String>();
+
+
+
+  // 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<Metric> metrics = Lists.newLinkedList();
 
   public static List<Metric> 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 (file)
index 0000000..dfc3749
--- /dev/null
@@ -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<Metric> 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<Integer, Integer> hitsByLine = Maps.newTreeMap();
+  private SortedMap<Integer, Integer> conditionsByLine = Maps.newTreeMap();
+  private SortedMap<Integer, Integer> 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<Integer, Integer> getHitsByLine() {
+    return Collections.unmodifiableSortedMap(hitsByLine);
+  }
+
+  public SortedMap<Integer, Integer> getConditionsByLine() {
+    return Collections.unmodifiableSortedMap(conditionsByLine);
+  }
+
+  public SortedMap<Integer, Integer> getCoveredConditionsByLine() {
+    return Collections.unmodifiableSortedMap(coveredConditionsByLine);
+  }
+
+  public Collection<Measure> createMeasures() {
+    Collection<Measure> 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<Integer, String> builder = new PropertiesBuilder<Integer, String>(CoreMetrics.BRANCH_COVERAGE_HITS_DATA);
+    for (Map.Entry<Integer, Integer> 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();
+  }
+}
index e27d13cf69ec795a311273f3706f458d0bf7c8ea..3b64b89da3fe9ac12184b61fc624efa9d45a0317 100644 (file)
@@ -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.
    * </p>
-   * 
+   *
    * @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
    */
index 781b63eef1329ddb8c2a0bbbfea5118f23a38cbc..92050c92d814d6e9051e0ac14a1c58594e4ea21d 100644 (file)
@@ -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 (file)
index 0000000..12c4370
--- /dev/null
@@ -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<Measure> 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 (file)
index 0000000..84706bc
--- /dev/null
@@ -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));
+  }
+}