]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-2149 Resource filters are ignored in complexity distributions of Java projects
authorsimonbrandhof <simon.brandhof@gmail.com>
Fri, 28 Jan 2011 12:17:57 +0000 (13:17 +0100)
committersimonbrandhof <simon.brandhof@gmail.com>
Fri, 28 Jan 2011 12:18:46 +0000 (13:18 +0100)
This issue implies SONAR-2153 : API: A decorator should override formulas

32 files changed:
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/BridgeFactory.java
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/bridges/ResourceIndex.java
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilder.java [new file with mode: 0644]
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilder.java [new file with mode: 0644]
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/SquidPluginTest.java
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/ClassComplexityDistributionBridgeTest.java [deleted file]
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/FunctionComplexityDistributionBridgeTest.java [deleted file]
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilderTest.java [new file with mode: 0644]
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilderTest.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java
sonar-batch/src/main/java/org/sonar/batch/FormulaDecorator.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/DecoratorsSelectorTest.java
sonar-batch/src/test/java/org/sonar/batch/FormulaDecoratorTest.java [new file with mode: 0644]
sonar-java-api/src/main/java/org/sonar/java/api/JavaClass.java
sonar-java-api/src/main/java/org/sonar/java/api/JavaMethod.java
sonar-java-api/src/test/java/org/sonar/java/api/JavaClassTest.java
sonar-java-api/src/test/java/org/sonar/java/api/JavaMethodTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildDistributionFormula.java
sonar-plugin-api/src/main/java/org/sonar/api/resources/BlockUnit.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/batch/FormulaDecoratorTest.java [deleted file]
sonar-plugin-api/src/test/java/org/sonar/api/measures/SumChildDistributionFormulaTest.java
tests/integration/sonar-it-reference-plugin/src/main/java/itests/ExcludedResourceFilter.java [new file with mode: 0644]
tests/integration/sonar-it-reference-plugin/src/main/java/itests/ITestsPlugin.java
tests/integration/tests/maven-projects/java-complexity/pom.xml [new file with mode: 0644]
tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ContainsInnerClasses.java [new file with mode: 0644]
tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ExcludedByFilter.java [new file with mode: 0644]
tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/Helloworld.java [new file with mode: 0644]
tests/integration/tests/src/test/java/org/sonar/tests/integration/JavaComplexityIT.java [new file with mode: 0644]
tests/integration/tests/src/test/java/org/sonar/tests/integration/Struts139IT.java

index 856c3438feae9707586ce96069c209710eaf73db..ea18236faef7c548f0a71a16d0f6c1c8ad491d1b 100644 (file)
@@ -23,6 +23,8 @@ import org.sonar.api.CoreProperties;
 import org.sonar.api.Plugin;
 import org.sonar.api.Properties;
 import org.sonar.api.Property;
+import org.sonar.plugins.squid.decorators.ClassComplexityDistributionBuilder;
+import org.sonar.plugins.squid.decorators.FunctionComplexityDistributionBuilder;
 
 import java.util.Arrays;
 import java.util.List;
@@ -58,7 +60,8 @@ public class SquidPlugin implements Plugin {
   }
 
   public List getExtensions() {
-    return Arrays.asList(SquidSearchProxy.class, SquidSensor.class, SquidRuleRepository.class, JavaSourceImporter.class);
+    return Arrays.asList(SquidSearchProxy.class, SquidSensor.class, SquidRuleRepository.class, JavaSourceImporter.class,
+        ClassComplexityDistributionBuilder.class, FunctionComplexityDistributionBuilder.class);
   }
 
   @Override
index 05b8726e1b1f35177c4976c7e059c08638a36d48..b621e87b2307c7ca22eecf5f43c119220f6fba33 100644 (file)
@@ -36,17 +36,17 @@ public final class BridgeFactory {
 
   private static List<Bridge> create(NoSonarFilter noSonarFilter) {
     return Arrays.asList(new CopyBasicMeasuresBridge(), new PackagesBridge(), new PublicUndocumentedApiBridge(),
-        new ClassComplexityDistributionBridge(), new FunctionComplexityDistributionBridge(), new NoSonarFilterLoader(noSonarFilter),
+        new NoSonarFilterLoader(noSonarFilter),
         new ChidamberKemererBridge(), new RobertCMartinBridge(), new ChidamberKemererDistributionBridge(), new DesignBridge(),
         new Lcom4BlocksBridge(), new ChecksBridge());
   }
 
   public static List<Bridge> create(boolean bytecodeScanned, SensorContext context, CheckFactory checkFactory,
-      ResourceIndex resourceIndex, Squid squid, NoSonarFilter noSonarFilter) {
+                                    ResourceIndex resourceIndex, Squid squid, NoSonarFilter noSonarFilter) {
     List<Bridge> result = new ArrayList<Bridge>();
     for (Bridge bridge : create(noSonarFilter)) {
       bridge.setCheckFactory(checkFactory);
-      if ( !bridge.needsBytecode() || bytecodeScanned) {
+      if (!bridge.needsBytecode() || bytecodeScanned) {
         bridge.setContext(context);
         bridge.setSquid(squid);
         bridge.setResourceIndex(resourceIndex);
index f0720cdb7f83bde5e2d50fa757bad2a964b96f3f..105a33e6b2d9285a92f0c0e608e64825cf51fecd 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.java.api.JavaMethod;
 import org.sonar.squid.Squid;
 import org.sonar.squid.api.*;
 import org.sonar.squid.indexer.QueryByType;
+import org.sonar.squid.measures.Metric;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -93,6 +94,7 @@ public final class ResourceIndex extends HashMap<SourceCode, Resource> {
           .setSignature(squidMethod.getName())
           .setFromLine(squidMethod.getStartAtLine())
           .setToLine(squidMethod.getEndAtLine())
+          .setAccessor(squidMethod.getInt(Metric.ACCESSORS)>0)
           .create();
 
       context.index(sonarMethod, sonarClass);
diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilder.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilder.java
new file mode 100644 (file)
index 0000000..f0a0636
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.squid.decorators;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+/**
+ * @since 2.6
+ */
+public final class ClassComplexityDistributionBuilder implements Decorator {
+
+  public static final Number[] LIMITS = {0, 5, 10, 20, 30, 60, 90};
+
+  @DependsUpon
+  public Metric dependOnComplexity() {
+    return CoreMetrics.COMPLEXITY;
+  }
+
+  @DependedUpon
+  public Metric generatesFunctionComplexityDistribution() {
+    return CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION;
+  }
+
+  public void decorate(Resource resource, DecoratorContext context) {
+    if (shouldExecuteOn(resource, context)) {
+      RangeDistributionBuilder builder = new RangeDistributionBuilder(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, LIMITS);
+      for (DecoratorContext childContext : context.getChildren()) {
+        if (Scopes.isType(childContext.getResource())) {
+          Measure complexity = childContext.getMeasure(CoreMetrics.COMPLEXITY);
+          if (complexity != null) {
+            builder.add(complexity.getValue());
+          }
+        }
+      }
+      Measure measure = builder.build(true);
+      measure.setPersistenceMode(PersistenceMode.MEMORY);
+      context.saveMeasure(measure);
+    }
+  }
+
+  boolean shouldExecuteOn(Resource resource, DecoratorContext context) {
+    return Scopes.isFile(resource) && context.getMeasure(CoreMetrics.COMPLEXITY) != null;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return Java.KEY.equals(project.getLanguageKey());
+  }
+}
diff --git a/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilder.java b/plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilder.java
new file mode 100644 (file)
index 0000000..75c5c28
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.squid.decorators;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+import org.sonar.java.api.JavaMethod;
+
+/**
+ * @since 2.6
+ */
+public final class FunctionComplexityDistributionBuilder implements Decorator {
+
+  public static final Number[] LIMITS = {1, 2, 4, 6, 8, 10, 12};
+
+  @DependsUpon
+  public Metric dependOnComplexity() {
+    return CoreMetrics.COMPLEXITY;
+  }
+
+  @DependedUpon
+  public Metric generatesFunctionComplexityDistribution() {
+    return CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
+  }
+
+  public void decorate(Resource resource, DecoratorContext context) {
+    if (shouldExecuteOn(resource, context)) {
+      RangeDistributionBuilder builder = new RangeDistributionBuilder(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, LIMITS);
+      for (DecoratorContext childContext : context.getChildren()) {
+        if (childContext.getResource() instanceof JavaMethod) {
+          JavaMethod javaMethod = (JavaMethod)childContext.getResource();
+          Measure complexity = childContext.getMeasure(CoreMetrics.COMPLEXITY);
+          if (!javaMethod.isAccessor() && complexity != null) {
+            builder.add(complexity.getValue());
+          }
+        }
+      }
+      Measure measure = builder.build(true);
+      measure.setPersistenceMode(PersistenceMode.MEMORY);
+      context.saveMeasure(measure);
+    }
+  }
+
+  boolean shouldExecuteOn(Resource resource, DecoratorContext context) {
+    return Scopes.isType(resource) && context.getMeasure(CoreMetrics.COMPLEXITY) != null;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return Java.KEY.equals(project.getLanguageKey());
+  }
+}
+
index 12a6cbed18281c970fe80de99f112c168f325a3b..0387b5010951aef725212d649dd109dfa03d69d2 100644 (file)
@@ -31,6 +31,6 @@ public class SquidPluginTest {
   @Test
   public void coverageForFun() {
     assertThat(new SquidPlugin().getKey(), not(nullValue()));
-    assertThat(new SquidPlugin().getExtensions().size(), is(4));
+    assertThat(new SquidPlugin().getExtensions().size(), is(6));
   }
 }
diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/ClassComplexityDistributionBridgeTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/ClassComplexityDistributionBridgeTest.java
deleted file mode 100644 (file)
index 9e132df..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*\r
- * Sonar, open source software quality management tool.\r
- * Copyright (C) 2009 SonarSource SA\r
- * mailto:contact AT sonarsource DOT com\r
- *\r
- * Sonar is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 3 of the License, or (at your option) any later version.\r
- *\r
- * Sonar is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with Sonar; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
- */\r
-\r
-package org.sonar.plugins.squid.bridges;\r
-\r
-import org.junit.Test;\r
-import static org.mockito.Mockito.verify;\r
-import static org.mockito.Mockito.never;\r
-import static org.mockito.Matchers.*;\r
-import org.sonar.api.resources.JavaFile;\r
-import org.sonar.api.resources.JavaPackage;\r
-import org.sonar.api.measures.CoreMetrics;\r
-import org.sonar.api.measures.Measure;\r
-import org.sonar.api.test.IsMeasure;\r
-\r
-public class ClassComplexityDistributionBridgeTest extends BridgeTestCase {\r
-  \r
-  @Test\r
-  public void classComplexityDistribution() {\r
-    verify(context).saveMeasure(eq(new JavaPackage("org.apache.struts.config")), argThat(new IsMeasure(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, "0=10;5=3;10=2;20=1;30=4;60=4;90=1")));\r
-    verify(context, never()).saveMeasure(eq(new JavaFile("org.apache.struts.config.ConfigRuleSet")), eq(new Measure(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, "equals() on measure only uses the metric")));\r
-    verify(context, never()).saveMeasure(eq(project), eq(new Measure(CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION, "equals() on measure only uses the metric")));\r
-  }\r
-}\r
diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/FunctionComplexityDistributionBridgeTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/bridges/FunctionComplexityDistributionBridgeTest.java
deleted file mode 100644 (file)
index d520377..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*\r
- * Sonar, open source software quality management tool.\r
- * Copyright (C) 2009 SonarSource SA\r
- * mailto:contact AT sonarsource DOT com\r
- *\r
- * Sonar is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 3 of the License, or (at your option) any later version.\r
- *\r
- * Sonar is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with Sonar; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02\r
- */\r
-\r
-package org.sonar.plugins.squid.bridges;\r
-\r
-import org.junit.Test;\r
-import org.sonar.api.measures.CoreMetrics;\r
-import org.sonar.api.measures.Measure;\r
-import org.sonar.api.resources.JavaFile;\r
-import org.sonar.api.resources.JavaPackage;\r
-import org.sonar.api.test.IsMeasure;\r
-\r
-import static org.mockito.Matchers.argThat;\r
-import static org.mockito.Matchers.eq;\r
-import static org.mockito.Mockito.never;\r
-import static org.mockito.Mockito.verify;\r
-\r
-public class FunctionComplexityDistributionBridgeTest extends BridgeTestCase {\r
-\r
-  @Test\r
-  public void functionComplexityDistribution() {\r
-    verify(context).saveMeasure(eq(new JavaPackage("org.apache.struts.config")), argThat(new IsMeasure(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, "1=186;2=88;4=11;6=12;8=7;10=2;12=8")));\r
-    verify(context, never()).saveMeasure(eq(new JavaFile("org.apache.struts.config.ConfigRuleSet")), eq(new Measure(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, "equals() on measure only uses the metric")));\r
-    verify(context, never()).saveMeasure(eq(project), eq(new Measure(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, "equals() on measure only uses the metric")));\r
-  }\r
-}\r
diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilderTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/ClassComplexityDistributionBuilderTest.java
new file mode 100644 (file)
index 0000000..4776430
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.squid.decorators;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.JavaPackage;
+import org.sonar.api.resources.Project;
+import org.sonar.java.api.JavaClass;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ClassComplexityDistributionBuilderTest {
+
+  @Test
+  public void shouldExecuteOnJavaProjectsOnly() throws Exception {
+    ClassComplexityDistributionBuilder builder = new ClassComplexityDistributionBuilder();
+    assertThat(builder.shouldExecuteOnProject(new Project("java").setLanguageKey(Java.KEY)), is(true));
+    assertThat(builder.shouldExecuteOnProject(new Project("php").setLanguageKey("php")), is(false));
+  }
+
+  @Test
+  public void shouldExecuteOnFilesOnly() throws Exception {
+    ClassComplexityDistributionBuilder builder = new ClassComplexityDistributionBuilder();
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(new Measure(CoreMetrics.COMPLEXITY, 20.0));
+
+    assertThat(builder.shouldExecuteOn(new JavaPackage("org.foo"), context), is(false));
+    assertThat(builder.shouldExecuteOn(new JavaFile("org.foo.Bar"), context), is(true));
+    assertThat(builder.shouldExecuteOn(JavaClass.create("org.foo.Bar"), context), is(false));
+  }
+
+}
diff --git a/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilderTest.java b/plugins/sonar-squid-java-plugin/src/test/java/org/sonar/plugins/squid/decorators/FunctionComplexityDistributionBuilderTest.java
new file mode 100644 (file)
index 0000000..aa0efe3
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.plugins.squid.decorators;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.JavaPackage;
+import org.sonar.api.resources.Project;
+import org.sonar.java.api.JavaClass;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FunctionComplexityDistributionBuilderTest {
+  @Test
+  public void shouldExecuteOnJavaProjectsOnly() throws Exception {
+    FunctionComplexityDistributionBuilder builder = new FunctionComplexityDistributionBuilder();
+    assertThat(builder.shouldExecuteOnProject(new Project("java").setLanguageKey(Java.KEY)), is(true));
+    assertThat(builder.shouldExecuteOnProject(new Project("php").setLanguageKey("php")), is(false));
+  }
+
+  @Test
+  public void shouldExecuteOnClassesOnly() throws Exception {
+    FunctionComplexityDistributionBuilder builder = new FunctionComplexityDistributionBuilder();
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(new Measure(CoreMetrics.COMPLEXITY, 20.0));
+
+    assertThat(builder.shouldExecuteOn(new JavaPackage("org.foo"), context), is(false));
+    assertThat(builder.shouldExecuteOn(new JavaFile("org.foo.Bar"), context), is(false));
+    assertThat(builder.shouldExecuteOn(JavaClass.create("org.foo.Bar"), context), is(true));
+  }
+}
index 8f8c000099e5d33e7f2155f296422772f71c9542..5aa54d0cfc4754929b96d4659eefa56b28afa637 100644 (file)
  */
 package org.sonar.batch;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
 import org.sonar.api.batch.BatchExtensionDictionnary;
 import org.sonar.api.batch.Decorator;
-import org.sonar.api.batch.FormulaDecorator;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.Project;
 
 import java.util.*;
 
-public class DecoratorsSelector {
+public final class DecoratorsSelector {
 
   private BatchExtensionDictionnary dictionnary;
 
@@ -37,26 +38,26 @@ public class DecoratorsSelector {
 
   public Collection<Decorator> select(Project project) {
     List<Decorator> decorators = new ArrayList<Decorator>(dictionnary.select(Decorator.class, project, false));
-    Set<Metric> coveredMetrics = getMetricsCoveredByPlugins(decorators);
+    SetMultimap<Metric, Decorator> decoratorsByGeneratedMetric = getDecoratorsByMetric(decorators);
     for (Metric metric : dictionnary.select(Metric.class)) {
-      if (metric.getFormula() != null && !coveredMetrics.contains(metric)) {
-        decorators.add(new FormulaDecorator(metric));
+      if (metric.getFormula() != null) {
+        decorators.add(new FormulaDecorator(metric, decoratorsByGeneratedMetric.get(metric)));
       }
     }
 
     return dictionnary.sort(decorators);
   }
 
-  private Set<Metric> getMetricsCoveredByPlugins(Collection<Decorator> pluginDecorators) {
-    Set<Metric> coveredMetrics = new HashSet<Metric>();
-    for (Decorator pluginDecorator : pluginDecorators) {
-      List dependents = dictionnary.getDependents(pluginDecorator);
+  private SetMultimap<Metric, Decorator> getDecoratorsByMetric(Collection<Decorator> pluginDecorators) {
+    SetMultimap<Metric, Decorator> decoratorsByGeneratedMetric = HashMultimap.create();
+    for (Decorator decorator : pluginDecorators) {
+      List dependents = dictionnary.getDependents(decorator);
       for (Object dependent : dependents) {
         if (dependent instanceof Metric) {
-          coveredMetrics.add((Metric) dependent);
+          decoratorsByGeneratedMetric.put((Metric) dependent, decorator);
         }
       }
     }
-    return coveredMetrics;
+    return decoratorsByGeneratedMetric;
   }
 }
diff --git a/sonar-batch/src/main/java/org/sonar/batch/FormulaDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/FormulaDecorator.java
new file mode 100644 (file)
index 0000000..0c80290
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.batch;
+
+import org.sonar.api.batch.*;
+import org.sonar.api.measures.FormulaData;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A pre-implementation of a decorator using a simple calculation formula
+ * @since 1.11
+ */
+public final class FormulaDecorator implements Decorator {
+
+  private Metric metric;
+  private DefaultFormulaContext formulaContext;
+  private Set<Decorator> executeAfterDecorators;
+
+  /**
+   * Creates a FormulaDecorator
+   *
+   * @param metric the metric should have an associated formula
+   * 
+   * @throws IllegalArgumentException if no formula is associated to the metric
+   */
+  public FormulaDecorator(Metric metric, Set<Decorator> executeAfterDecorators) {
+    if (metric.getFormula() == null) {
+      throw new IllegalArgumentException("No formula defined on metric");
+    }
+    this.metric = metric;
+    this.formulaContext = new DefaultFormulaContext(metric);
+    this.executeAfterDecorators = executeAfterDecorators;
+  }
+
+  public FormulaDecorator(Metric metric) {
+    this(metric, Collections.<Decorator>emptySet());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean shouldExecuteOnProject(Project project) {
+    return true;
+  }
+
+  /**
+   * @return metric generated by the decorator
+   */
+  @DependedUpon
+  public Metric generatesMetric() {
+    return metric;
+  }
+
+  /**
+   * @return metric the decorator depends upon
+   */
+  @DependsUpon
+  public List<Metric> dependsUponMetrics() {
+    return metric.getFormula().dependsUponMetrics();
+  }
+
+  @DependsUpon
+  public Collection<Decorator> dependsUponDecorators() {
+    return executeAfterDecorators;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void decorate(Resource resource, DecoratorContext context) {
+    if (context.getMeasure(metric) != null) {
+      return;
+    }
+
+    formulaContext.setDecoratorContext(context);
+    FormulaData data = new DefaultFormulaData(context);
+    Measure measure = metric.getFormula().calculate(data, formulaContext);
+    if (measure != null) {
+      context.saveMeasure(measure);
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    FormulaDecorator that = (FormulaDecorator) o;
+    return !(metric != null ? !metric.equals(that.metric) : that.metric != null);
+  }
+
+  @Override
+  public int hashCode() {
+    return metric != null ? metric.hashCode() : 0;
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder().append("f(").append(metric.getKey()).append(")").toString();
+  }
+}
index 90edc924f105b70b0b101bee1f3b80fe9dc37add..5439d9b12c466e0bc74725dcb9437c87a73a52e7 100644 (file)
  */
 package org.sonar.batch;
 
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import org.apache.commons.collections.CollectionUtils;
 import org.junit.Test;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
-import static org.mockito.Mockito.mock;
 import org.picocontainer.containers.TransientPicoContainer;
-import org.sonar.api.batch.*;
+import org.sonar.api.batch.BatchExtensionDictionnary;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
 import org.sonar.api.measures.*;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
@@ -34,16 +34,20 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
+
 public class DecoratorsSelectorTest {
 
   private Metric withFormula1 = new Metric("metric1").setFormula(new FakeFormula());
   private Metric withFormula2 = new Metric("metric2").setFormula(new FakeFormula());
-  private Metric withoutFormula = new Metric("metric3");
+  private Metric withoutFormula3 = new Metric("metric3");
 
   @Test
   public void selectAndSortFormulas() {
     Project project = new Project("key");
-    BatchExtensionDictionnary dictionnary = newDictionnary(withFormula1, withoutFormula, withFormula2);
+    BatchExtensionDictionnary dictionnary = newDictionnary(withFormula1, withoutFormula3, withFormula2);
 
     Collection<Decorator> decorators = new DecoratorsSelector(dictionnary).select(project);
     assertThat(decorators.size(), is(2));
@@ -52,18 +56,22 @@ public class DecoratorsSelectorTest {
   }
 
   @Test
-  public void pluginDecoratorsCanOverrideFormulas() {
+  public void decoratorsShouldBeExecutedBeforeFormulas() {
     Project project = new Project("key");
-    Decorator fakeDecorator = new FakeDecorator();
     Decorator metric1Decorator = new Metric1Decorator();
-    BatchExtensionDictionnary dictionnary = newDictionnary(fakeDecorator, metric1Decorator, withFormula1, withoutFormula, withFormula2);
+    BatchExtensionDictionnary dictionnary = newDictionnary(metric1Decorator, withFormula1);
 
     Collection<Decorator> decorators = new DecoratorsSelector(dictionnary).select(project);
 
-    assertThat(decorators.size(), is(3));
-    assertThat(decorators, hasItem(fakeDecorator));
-    assertThat(decorators, hasItem(metric1Decorator));
-    assertThat(decorators, hasItem((Decorator) new FormulaDecorator(withFormula2)));
+    Decorator firstDecorator = (Decorator)CollectionUtils.get(decorators, 0);
+    Decorator secondDecorator = (Decorator)CollectionUtils.get(decorators, 1);
+
+    assertThat(firstDecorator, is(Metric1Decorator.class));
+    assertThat(secondDecorator, is(FormulaDecorator.class));
+
+    FormulaDecorator formulaDecorator = (FormulaDecorator) secondDecorator;
+    assertThat(formulaDecorator.dependsUponDecorators().size(), is(1));
+    assertThat(CollectionUtils.get(formulaDecorator.dependsUponDecorators(), 0), is((Object)firstDecorator));
   }
 
   private BatchExtensionDictionnary newDictionnary(Object... extensions) {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/FormulaDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/FormulaDecoratorTest.java
new file mode 100644 (file)
index 0000000..b9e25d6
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.batch;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.*;
+import org.sonar.api.test.IsMeasure;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+public class FormulaDecoratorTest {
+
+  @Test
+  public void doAlwaysExecute() {
+    assertThat(new FormulaDecorator(CoreMetrics.LINES).shouldExecuteOnProject(null), is(true));
+  }
+
+  @Test
+  public void declareDependencies() {
+    Formula formula = new Formula() {
+      public List<Metric> dependsUponMetrics() {
+        return Arrays.asList(CoreMetrics.COMPLEXITY, CoreMetrics.COVERAGE);
+      }
+
+      public Measure calculate(FormulaData data, FormulaContext context) {
+        return null;
+      }
+    };
+    Metric metric = new Metric("ncloc").setFormula(formula);
+    List<Metric> dependencies = new FormulaDecorator(metric).dependsUponMetrics();
+    assertThat(dependencies, hasItem(CoreMetrics.COMPLEXITY));
+    assertThat(dependencies, hasItem(CoreMetrics.COVERAGE));
+  }
+
+  @Test
+  public void saveMeasure() {
+    FormulaDecorator decorator = new FormulaDecorator(new Metric("fake").setFormula(new FakeFormula()));
+
+    DecoratorContext context = mock(DecoratorContext.class);
+    decorator.decorate(null, context);
+
+    verify(context).saveMeasure(argThat(new IsMeasure(new Metric("fake"), 50.0)));
+  }
+
+  @Test
+  public void doNotExecuteIfExistingResult() {
+    Metric fake = new Metric("fake");
+    FormulaDecorator decorator = new FormulaDecorator(fake.setFormula(new FakeFormula()));
+
+    DecoratorContext context = mock(DecoratorContext.class);
+    when(context.getMeasure(fake)).thenReturn(new Measure(fake, 10.0));
+    decorator.decorate(null, context);
+
+    verify(context, never()).saveMeasure((Measure) anyObject());
+  }
+
+  class FakeFormula implements Formula {
+
+    public List<Metric> dependsUponMetrics() {
+      return Collections.emptyList();
+    }
+
+    public Measure calculate(FormulaData data, FormulaContext context) {
+      return new Measure(new Metric("fake")).setValue(50.0);
+    }
+  }
+}
index 563879e23bb2f6b548ef3be9a5a741cee81c0e83..d482c3d8bb23abdba923884655e754583149a35f 100644 (file)
@@ -109,11 +109,11 @@ public final class JavaClass extends Resource {
     return getName();
   }
 
-  public static JavaClass createRef(String name) {
+  public static JavaClass create(String name) {
     return new JavaClass(name);
   }
 
-  public static JavaClass createRef(String packageName, String className) {
+  public static JavaClass create(String packageName, String className) {
     if (StringUtils.isBlank(packageName)) {
       return new JavaClass(className);
     }
index 743b3fa40695423592c91dd0f6af22c689aed272..ce7eb14fe8b49ebf5bf5837ca7692a93e3725567 100644 (file)
@@ -25,9 +25,8 @@ import org.sonar.api.resources.*;
 /**
  * @since 2.6
  */
-public final class JavaMethod extends Resource {
+public final class JavaMethod extends BlockUnit {
 
-  public static final String SCOPE = Scopes.BLOCK_UNIT;
   public static final String QUALIFIER = Qualifiers.METHOD;
 
   public static final int UNKNOWN_LINE = -1;
@@ -37,17 +36,19 @@ public final class JavaMethod extends Resource {
   private String className;
   private int fromLine;
   private int toLine;
+  private boolean isAccessor = false;
 
   private JavaMethod(String className, String signature) {
-    setKey(toKey(className, signature));
+    super(toKey(className, signature), QUALIFIER, Java.INSTANCE);
     this.className = className;
     this.signature = signature;
   }
 
-  private JavaMethod(String className, String signature, int fromLine, int toLine) {
+  private JavaMethod(String className, String signature, int fromLine, int toLine, boolean isAccessor) {
     this(className, signature);
     this.fromLine = fromLine;
     this.toLine = toLine;
+    this.isAccessor = isAccessor;
   }
 
   public int getFromLine() {
@@ -66,6 +67,10 @@ public final class JavaMethod extends Resource {
     return className;
   }
 
+  public boolean isAccessor() {
+    return isAccessor;
+  }
+
   @Override
   public String getName() {
     return signature;
@@ -81,31 +86,11 @@ public final class JavaMethod extends Resource {
     return null;
   }
 
-  @Override
-  public Language getLanguage() {
-    return Java.INSTANCE;
-  }
-
-  @Override
-  public String getScope() {
-    return SCOPE;
-  }
-
-  @Override
-  public String getQualifier() {
-    return QUALIFIER;
-  }
-
   @Override
   public Resource getParent() {
     return null;
   }
 
-  @Override
-  public boolean matchFilePattern(String antPattern) {
-    return false;
-  }
-
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -155,6 +140,7 @@ public final class JavaMethod extends Resource {
     private String signature;
     private int fromLine = UNKNOWN_LINE;
     private int toLine = UNKNOWN_LINE;
+    private boolean isAccessor = false;
 
     public Builder setKey(String key) {
       String[] parts = splitClassAndMethodFromKey(key);
@@ -188,8 +174,13 @@ public final class JavaMethod extends Resource {
       return this;
     }
 
+    public Builder setAccessor(boolean accessor) {
+      isAccessor = accessor;
+      return this;
+    }
+
     public JavaMethod create() {
-      return new JavaMethod(className, signature, fromLine, toLine);
+      return new JavaMethod(className, signature, fromLine, toLine, isAccessor);
     }
   }
 }
index 869f60b3be5e4bfcf139b393336b983660b1d09a..92eba48740ab1a763d7a4518e9ed42e59b1e910b 100644 (file)
@@ -30,7 +30,7 @@ public class JavaClassTest {
 
   @Test
   public void shouldCreateReferenceFromName() {
-    JavaClass javaClass = JavaClass.createRef("org.foo.Bar");
+    JavaClass javaClass = JavaClass.create("org.foo.Bar");
     assertThat(javaClass.getClassName(), is("Bar"));
     assertThat(javaClass.getKey(), is("org.foo.Bar"));
     assertThat(javaClass.getLanguage(), is((Language)Java.INSTANCE));
@@ -40,7 +40,7 @@ public class JavaClassTest {
 
   @Test
   public void shouldCreateReferenceFromPackageAndClassname() {
-    JavaClass javaClass = JavaClass.createRef("org.foo", "Bar");
+    JavaClass javaClass = JavaClass.create("org.foo", "Bar");
     assertThat(javaClass.getClassName(), is("Bar"));
     assertThat(javaClass.getKey(), is("org.foo.Bar"));
     assertThat(javaClass.getLanguage(), is((Language)Java.INSTANCE));
@@ -50,25 +50,25 @@ public class JavaClassTest {
 
   @Test
   public void shouldGetPackageName() {
-    JavaClass javaClass = JavaClass.createRef("org.foo.Bar");
+    JavaClass javaClass = JavaClass.create("org.foo.Bar");
     assertThat(javaClass.getPackageName(), is("org.foo"));
 
-    javaClass = JavaClass.createRef("Bar");
+    javaClass = JavaClass.create("Bar");
     assertThat(javaClass.getPackageName(), is(""));
   }
 
   @Test
   public void shouldGetClassName() {
-    JavaClass javaClass = JavaClass.createRef("org.foo.Bar");
+    JavaClass javaClass = JavaClass.create("org.foo.Bar");
     assertThat(javaClass.getClassName(), is("Bar"));
 
-    javaClass = JavaClass.createRef("Bar");
+    javaClass = JavaClass.create("Bar");
     assertThat(javaClass.getClassName(), is("Bar"));
   }
 
   @Test
   public void shouldOverrideToString() {
-    JavaClass javaClass = JavaClass.createRef("org.foo.Bar");
+    JavaClass javaClass = JavaClass.create("org.foo.Bar");
     assertThat(javaClass.toString(), is("org.foo.Bar"));
   }
 
index bc6976aca257a44f77f7cc4a800551c91e9a76da..fad1e992dfec9264c5fee7fbd0fef6203c6bf974 100644 (file)
@@ -40,7 +40,7 @@ public class JavaMethodTest {
   public void shouldCreateReferenceFromClassAndSignature() {
     String className = "org.foo.Bar";
     String signature = "hello(LString;)V";
-    JavaMethod method = JavaMethod.createRef(JavaClass.createRef(className), signature);
+    JavaMethod method = JavaMethod.createRef(JavaClass.create(className), signature);
     assertThat(method.getKey(), is(className + "#" + signature));
     assertThat(method.getClassName(), is(className));
     assertThat(method.getName(), is(signature));
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java
deleted file mode 100644 (file)
index a082bff..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2009 SonarSource SA
- * 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.batch;
-
-import org.sonar.api.measures.FormulaData;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-
-import java.util.List;
-
-/**
- * A pre-implementation of a decorator using a simple calculation formula
- * @since 1.11
- */
-public class FormulaDecorator implements Decorator {
-
-  private Metric metric;
-  private DefaultFormulaContext formulaContext;
-
-  /**
-   * Creates a FormulaDecorator
-   *
-   * @param metric the metric should have an associated formula
-   * 
-   * @throws IllegalArgumentException if no formula is associated to the metric
-   */
-  public FormulaDecorator(Metric metric) {
-    if (metric.getFormula() == null) {
-      throw new IllegalArgumentException("No formula defined on metric");
-    }
-    this.metric = metric;
-    this.formulaContext = new DefaultFormulaContext(metric);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean shouldExecuteOnProject(Project project) {
-    return true;
-  }
-
-  /**
-   * @return metric generated by the decorator
-   */
-  @DependedUpon
-  public Metric generatesMetric() {
-    return metric;
-  }
-
-  /**
-   * @return metric the decorator depends upon
-   */
-  @DependsUpon
-  public List<Metric> dependsUponMetrics() {
-    return metric.getFormula().dependsUponMetrics();
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  public void decorate(Resource resource, DecoratorContext context) {
-    if (context.getMeasure(metric) != null) {
-      return;
-    }
-
-    formulaContext.setDecoratorContext(context);
-    FormulaData data = new DefaultFormulaData(context);
-    Measure measure = metric.getFormula().calculate(data, formulaContext);
-    if (measure != null) {
-      context.saveMeasure(measure);
-    }
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
-    }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
-    }
-
-    FormulaDecorator that = (FormulaDecorator) o;
-
-    if (metric != null ? !metric.equals(that.metric) : that.metric != null) {
-      return false;
-    }
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return metric != null ? metric.hashCode() : 0;
-  }
-
-  @Override
-  public String toString() {
-    return new StringBuilder().append("f(").append(metric.getKey()).append(")").toString();
-  }
-}
index 35f270d3c4df6a697956dcb9c56075f5dc7c144a..ab1c36d8c0a8e08211f2175ac00255948729b5fc 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.api.measures;
 
 import org.apache.commons.lang.StringUtils;
+import org.sonar.api.resources.Scopes;
 import org.sonar.api.utils.SonarException;
 
 import java.lang.reflect.Field;
@@ -133,22 +134,22 @@ public final class CoreMetrics {
   public static final String CLASS_COMPLEXITY_DISTRIBUTION_KEY = "class_complexity_distribution";
   public static final Metric CLASS_COMPLEXITY_DISTRIBUTION = new Metric(CLASS_COMPLEXITY_DISTRIBUTION_KEY,
       "Classes distribution /complexity", "Classes distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
-      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String FUNCTION_COMPLEXITY_DISTRIBUTION_KEY = "function_complexity_distribution";
   public static final Metric FUNCTION_COMPLEXITY_DISTRIBUTION = new Metric(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY,
       "Functions distribution /complexity", "Functions distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
-      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String FILE_COMPLEXITY_DISTRIBUTION_KEY = "file_complexity_distribution";
   public static final Metric FILE_COMPLEXITY_DISTRIBUTION = new Metric(FILE_COMPLEXITY_DISTRIBUTION_KEY, "Files distribution /complexity",
       "Files distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_COMPLEXITY)
-      .setFormula(new SumChildDistributionFormula());
+      .setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String PARAGRAPH_COMPLEXITY_DISTRIBUTION_KEY = "paragraph_complexity_distribution";
   public static final Metric PARAGRAPH_COMPLEXITY_DISTRIBUTION = new Metric(PARAGRAPH_COMPLEXITY_DISTRIBUTION_KEY,
       "Paragraph distribution /complexity", "Paragraph distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
-      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+      DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String COMMENT_LINES_KEY = "comment_lines";
   public static final Metric COMMENT_LINES = new Metric(COMMENT_LINES_KEY, "Comment lines", "Number of comment lines",
@@ -423,7 +424,8 @@ public final class CoreMetrics {
 
   public static final String RFC_DISTRIBUTION_KEY = "rfc_distribution";
   public static final Metric RFC_DISTRIBUTION = new Metric(RFC_DISTRIBUTION_KEY, "Class distribution /RFC", "Class distribution /RFC",
-      Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_DESIGN).setFormula(new SumChildDistributionFormula());
+      Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_DESIGN)
+      .setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String LCOM4_KEY = "lcom4";
   public static final Metric LCOM4 = new Metric(LCOM4_KEY, "LCOM4", "Lack of Cohesion of Methods", Metric.ValueType.FLOAT,
@@ -436,7 +438,7 @@ public final class CoreMetrics {
   public static final String LCOM4_DISTRIBUTION_KEY = "lcom4_distribution";
   public static final Metric LCOM4_DISTRIBUTION = new Metric(LCOM4_DISTRIBUTION_KEY, "Class distribution /LCOM4",
       "Class distribution /LCOM4", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_DESIGN)
-      .setFormula(new SumChildDistributionFormula());
+      .setFormula(new SumChildDistributionFormula().setMinimumScopeToPersist(Scopes.DIRECTORY));
 
   public static final String SUSPECT_LCOM4_DENSITY_KEY = "suspect_lcom4_density";
   public static final Metric SUSPECT_LCOM4_DENSITY = new Metric(SUSPECT_LCOM4_DENSITY_KEY, "Suspect LCOM4 density",
index 423d2df83e9ad3abd225d9dc5119c34227fb9d6f..d80b78ddc1219ef5218632605b4262dfd0eaad24 100644 (file)
  */\r
 package org.sonar.api.measures;\r
 \r
-import java.util.List;\r
-import java.util.Collections;\r
+import org.sonar.api.resources.Scopes;\r
+\r
 import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.List;\r
 \r
 /**\r
  * @since 2.0\r
@@ -30,10 +32,21 @@ import java.util.Collection;
  */\r
 public class SumChildDistributionFormula implements Formula {\r
 \r
+  private String minimumScopeToPersist= Scopes.FILE;\r
+\r
   public List<Metric> dependsUponMetrics() {\r
     return Collections.emptyList();\r
   }\r
 \r
+  public String getMinimumScopeToPersist() {\r
+    return minimumScopeToPersist;\r
+  }\r
+\r
+  public SumChildDistributionFormula setMinimumScopeToPersist(String s) {\r
+    this.minimumScopeToPersist = s;\r
+    return this;\r
+  }\r
+\r
   public Measure calculate(FormulaData data, FormulaContext context) {\r
     Collection<Measure> measures = data.getChildrenMeasures(context.getTargetMetric());\r
     if (measures == null || measures.isEmpty()) {\r
@@ -44,7 +57,11 @@ public class SumChildDistributionFormula implements Formula {
       for (Measure measure : measures) {\r
         distribution.add(measure);\r
       }\r
-      return distribution.build();\r
+      Measure measure = distribution.build();\r
+      if (!Scopes.isHigherThanOrEquals(context.getResource().getScope(), minimumScopeToPersist)) {\r
+        measure.setPersistenceMode(PersistenceMode.MEMORY);\r
+      }\r
+      return measure;\r
     }\r
   }\r
 }\r
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/BlockUnit.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/BlockUnit.java
new file mode 100644 (file)
index 0000000..ee6bbe1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.resources;
+
+public class BlockUnit extends Resource {
+
+  public static final String SCOPE = Scopes.BLOCK_UNIT;
+
+  protected String qualifier;
+  protected Language language;
+
+  protected BlockUnit(String key, String qualifier, Language language) {
+    setKey(key);
+    this.qualifier = qualifier;
+    this.language = language;
+  }
+
+  @Override
+  public String getName() {
+    return getKey();
+  }
+
+  @Override
+  public String getLongName() {
+    return getKey();
+  }
+
+  @Override
+  public String getDescription() {
+    return null;
+  }
+
+  @Override
+  public final Language getLanguage() {
+    return language;
+  }
+
+  @Override
+  public final String getScope() {
+    return SCOPE;
+  }
+
+  @Override
+  public final String getQualifier() {
+    return qualifier;
+  }
+
+  @Override
+  public Resource getParent() {
+    return null;
+  }
+
+  @Override
+  public final boolean matchFilePattern(String antPattern) {
+    return false;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    BlockUnit blockUnit = (BlockUnit) o;
+    if (!qualifier.equals(blockUnit.qualifier)) {
+      return false;
+    }
+    return getKey().equals(blockUnit.getKey());
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + getKey().hashCode();
+    result = 31 * result + qualifier.hashCode();
+    return result;
+  }
+
+  public static BlockUnit createMethod(String key, Language language) {
+    return new BlockUnit(key, Qualifiers.METHOD, language);
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/FormulaDecoratorTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/FormulaDecoratorTest.java
deleted file mode 100644 (file)
index 50865b2..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Sonar, open source software quality management tool.
- * Copyright (C) 2009 SonarSource SA
- * 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.batch;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
-import org.junit.Test;
-import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
-import org.sonar.api.measures.*;
-import org.sonar.api.test.IsMeasure;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-public class FormulaDecoratorTest {
-
-  @Test
-  public void doAlwaysExecute() {
-    assertThat(new FormulaDecorator(CoreMetrics.LINES).shouldExecuteOnProject(null), is(true));
-  }
-
-  @Test
-  public void declareDependencies() {
-    Formula formula = new Formula() {
-      public List<Metric> dependsUponMetrics() {
-        return Arrays.asList(CoreMetrics.COMPLEXITY, CoreMetrics.COVERAGE);
-      }
-
-      public Measure calculate(FormulaData data, FormulaContext context) {
-        return null;
-      }
-    };
-    Metric metric = new Metric().setFormula(formula);
-    List<Metric> dependencies = new FormulaDecorator(metric).dependsUponMetrics();
-    assertThat(dependencies, hasItem(CoreMetrics.COMPLEXITY));
-    assertThat(dependencies, hasItem(CoreMetrics.COVERAGE));
-  }
-
-  @Test
-  public void saveMeasure() {
-    FormulaDecorator decorator = new FormulaDecorator(new Metric("fake").setFormula(new FakeFormula()));
-
-    DecoratorContext context = mock(DecoratorContext.class);
-    decorator.decorate(null, context);
-
-    verify(context).saveMeasure(argThat(new IsMeasure(new Metric("fake"), 50.0)));
-  }
-
-  @Test
-  public void doNotExecuteIfExistingResult() {
-    Metric fake = new Metric("fake");
-    FormulaDecorator decorator = new FormulaDecorator(fake.setFormula(new FakeFormula()));
-
-    DecoratorContext context = mock(DecoratorContext.class);
-    when(context.getMeasure(fake)).thenReturn(new Measure(fake, 10.0));
-    decorator.decorate(null, context);
-
-    verify(context, never()).saveMeasure((Measure) anyObject());
-  }
-
-  class FakeFormula implements Formula {
-
-    public List<Metric> dependsUponMetrics() {
-      return Collections.emptyList();
-    }
-
-    public Measure calculate(FormulaData data, FormulaContext context) {
-      return new Measure(new Metric("fake")).setValue(50.0);
-    }
-  }
-}
index 9849c28d035e79b1dfa690537c500fdc752c9cd0..93d89b98abbd8b2fc5d1b45fbdbfd8ce14425eb1 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.measures;
 import com.google.common.collect.Lists;\r
 import org.junit.Before;\r
 import org.junit.Test;\r
+import org.sonar.api.resources.File;\r
 \r
 import java.util.Collections;\r
 import java.util.List;\r
@@ -42,6 +43,7 @@ public class SumChildDistributionFormulaTest {
   public void init() {\r
     formula = new SumChildDistributionFormula();\r
     context = mock(FormulaContext.class);\r
+    when(context.getResource()).thenReturn(new File("foo"));\r
     data = mock(FormulaData.class);\r
   }\r
 \r
diff --git a/tests/integration/sonar-it-reference-plugin/src/main/java/itests/ExcludedResourceFilter.java b/tests/integration/sonar-it-reference-plugin/src/main/java/itests/ExcludedResourceFilter.java
new file mode 100644 (file)
index 0000000..4c045cd
--- /dev/null
@@ -0,0 +1,13 @@
+package itests;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.ResourceFilter;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Scopes;
+
+public final class ExcludedResourceFilter implements ResourceFilter {
+
+  public boolean isIgnored(Resource resource) {
+    return Scopes.isFile(resource) && StringUtils.contains(resource.getName(), "ExcludedByFilter");
+  }
+}
index 6df7c15befbb820fee3080c52814634266492801..5fd5809f3b36b689a5e5b0f6fbdb4a2ae2a4e182 100644 (file)
@@ -50,6 +50,7 @@ public class ITestsPlugin implements Plugin {
     extensions.add(SampleSensor.class);\r
     extensions.add(LanguageWithoutRulesEngine.class);\r
     extensions.add(ServerSideExtensionUsingExternalDependency.class);\r
+    extensions.add(ExcludedResourceFilter.class);\r
 \r
     // web\r
     extensions.add(SampleResourceTab.class);\r
diff --git a/tests/integration/tests/maven-projects/java-complexity/pom.xml b/tests/integration/tests/maven-projects/java-complexity/pom.xml
new file mode 100644 (file)
index 0000000..63afab1
--- /dev/null
@@ -0,0 +1,8 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.sonar.tests</groupId>
+  <artifactId>java-complexity</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>java-complexity</name>
+</project>
\ No newline at end of file
diff --git a/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ContainsInnerClasses.java b/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ContainsInnerClasses.java
new file mode 100644 (file)
index 0000000..9ed05ab
--- /dev/null
@@ -0,0 +1,37 @@
+package foo;
+
+
+public class ContainsInnerClasses {
+
+  // complexity: 1
+  public ContainsInnerClasses() {
+
+  }
+
+  // complexity: 3
+  public static class InnerClass {
+    private String field;
+
+    // complexity: 1
+    public InnerClass() {
+
+    }
+
+    // complexity: 2
+    public InnerClass(String s) {
+      if (s != null) {
+        field = s;
+      }
+    }
+  }
+}
+
+// complexity: 1
+class PackageClass {
+    private String field;
+
+    // complexity: 1
+    public PackageClass() {
+
+    }
+  }
diff --git a/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ExcludedByFilter.java b/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/ExcludedByFilter.java
new file mode 100644 (file)
index 0000000..b519344
--- /dev/null
@@ -0,0 +1,19 @@
+package foo;
+
+// this class is excluded by the resource filter defined in sonar-it-reference-plugin
+public class ExcludedByFilter {
+
+  public void say() {
+    int i=0;
+    if(i>5) {
+      System.out.println("say something");
+    }
+  }
+
+  public void cry() {
+    int i=0;
+    if(i<5) {
+      System.out.println("cry");
+    }
+  }
+}
diff --git a/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/Helloworld.java b/tests/integration/tests/maven-projects/java-complexity/src/main/java/foo/Helloworld.java
new file mode 100644 (file)
index 0000000..37fae84
--- /dev/null
@@ -0,0 +1,42 @@
+package foo;
+
+// complexity: 6
+public class Helloworld {
+
+  private String field = null;
+
+  // this is considered as a method
+  // complexity: 2
+  static {
+    int i = 0;
+    if (i > 5) {
+      System.out.println("hello from static block");
+    }
+  }
+
+  // complexity: 1
+  public Helloworld(String s) {
+    this.field = s;
+  }
+
+  // accessor
+  // complexity: 0
+  public String getField() {
+    return field;
+  }
+
+  // accessor
+  // complexity: 0
+  public void setField(String s) {
+    this.field = s;
+  }
+
+  // complexity: 3
+  public void sayHello() {
+    for (int i = 0; i < 5; i++) {
+      if (field != null) {
+        System.out.println(field);
+      }
+    }
+  }
+}
diff --git a/tests/integration/tests/src/test/java/org/sonar/tests/integration/JavaComplexityIT.java b/tests/integration/tests/src/test/java/org/sonar/tests/integration/JavaComplexityIT.java
new file mode 100644 (file)
index 0000000..e3e3ad1
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * 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.tests.integration;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.wsclient.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class JavaComplexityIT {
+  private static Sonar sonar;
+
+  @BeforeClass
+  public static void buildServer() {
+    sonar = ITUtils.createSonarWsClient();
+  }
+
+  @Test
+  public void testFileComplexity() {
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.Helloworld", CoreMetrics.COMPLEXITY_KEY).getIntValue(), is(6));
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.ContainsInnerClasses", CoreMetrics.COMPLEXITY_KEY).getIntValue(), is(5));
+  }
+
+  @Test
+  public void testPackageComplexity() {
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo", CoreMetrics.COMPLEXITY_KEY).getIntValue(), is(11));
+  }
+
+  @Test
+  public void testProjectComplexity() {
+    assertThat(getMeasure("org.sonar.tests:java-complexity", CoreMetrics.COMPLEXITY_KEY).getIntValue(), is(11));
+  }
+
+  @Test
+  public void testAverageMethodComplexity() {
+    // complexity 6 / 2 methods
+    // BUG http://jira.codehaus.org/browse/SONAR-2152
+    // => the complexity of the static block should not be included. Good value should be 4 / 2 = 2
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.Helloworld", CoreMetrics.FUNCTION_COMPLEXITY_KEY).getValue(), is(3.0));
+
+    // complexity 5 / 4 methods. Real value is 1.25 but round up to 1.3
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.ContainsInnerClasses", CoreMetrics.FUNCTION_COMPLEXITY_KEY).getValue(), is(1.3));
+
+    // 3.0 * 2 + 1.25 * 4 = 11 for 6 methods
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo", CoreMetrics.FUNCTION_COMPLEXITY_KEY).getValue(), is(1.8));
+    assertThat(getMeasure("org.sonar.tests:java-complexity", CoreMetrics.FUNCTION_COMPLEXITY_KEY).getValue(), is(1.8));
+  }
+
+  @Test
+  public void testAverageClassComplexity() {
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.Helloworld", CoreMetrics.CLASS_COMPLEXITY_KEY).getValue(), is(6.0));
+
+    // 1 + 1 + 3 => complexity 5/3
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.ContainsInnerClasses", CoreMetrics.CLASS_COMPLEXITY_KEY).getValue(), is(1.7));
+
+    // 1 + 1 + 3 + 6 => 11/4 = 2.75
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo", CoreMetrics.CLASS_COMPLEXITY_KEY).getValue(), is(2.8));
+  }
+
+  @Test
+  public void testDistributionOfClassComplexity() {
+    // 1 + 1 + 3 + 6 => 3 in range [0,5[ and 1 in range [5,10[
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo", CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY).getData(), is("0=3;5=1;10=0;20=0;30=0;60=0;90=0"));
+    assertThat(getMeasure("org.sonar.tests:java-complexity", CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY).getData(), is("0=3;5=1;10=0;20=0;30=0;60=0;90=0"));
+  }
+
+  @Test
+  public void testDistributionOfMethodComplexity() {
+    // ContainsInnerClasses: 1+ 1 + 2 + 1
+    // Helloworld: 1 + 3 (static block is not a method)
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo", CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY).getData(), is("1=4;2=2;4=0;6=0;8=0;10=0;12=0"));
+    assertThat(getMeasure("org.sonar.tests:java-complexity", CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY).getData(), is("1=4;2=2;4=0;6=0;8=0;10=0;12=0"));
+  }
+
+  @Test
+  public void shouldNotPersistDistributionOnFiles() {
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.Helloworld", CoreMetrics.CLASS_COMPLEXITY_DISTRIBUTION_KEY), nullValue());
+    assertThat(getMeasure("org.sonar.tests:java-complexity:foo.Helloworld", CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY), nullValue());
+  }
+
+  private Measure getMeasure(String resourceKey, String metricKey) {
+    return sonar.find(ResourceQuery.createForMetrics(resourceKey, metricKey)).getMeasure(metricKey);
+  }
+}
index bcffec6f59a6315adf4831c128357cd0d136140e..2724b24db7f84e169c6749d5353551ed13905101 100644 (file)
@@ -129,7 +129,23 @@ public class Struts139IT {
     assertThat(getProjectMeasure("statements").getIntValue(), is(21896));
     assertThat(getProjectMeasure("class_complexity").getValue(), is(21.5));
     assertThat(getProjectMeasure("function_complexity").getValue(), is(2.6));
-    assertThat(getProjectMeasure("class_complexity_distribution").getData(), is("0=172;5=90;10=86;20=55;30=69;60=34;90=17"));
+  }
+
+  @Test
+  public void classComplexityDistribution() throws Exception {
+    assertThat(sonar.find(ResourceQuery.createForMetrics("org.apache.struts:struts-core:org.apache.struts.config", "class_complexity_distribution")).getMeasure("class_complexity_distribution").getData(), is("0=10;5=3;10=2;20=1;30=4;60=4;90=1"));
+    assertThat(getCoreModuleMeasure("class_complexity_distribution").getData(), is("0=49;5=26;10=24;20=14;30=18;60=9;90=10"));
+    assertThat(getProjectMeasure("class_complexity_distribution").getData(), is("0=173;5=90;10=86;20=55;30=69;60=34;90=17"));
+  }
+
+  @Test
+  public void functionComplexityDistribution() throws Exception {
+    assertThat(sonar.find(ResourceQuery.createForMetrics("org.apache.struts:struts-core:org.apache.struts.config", "function_complexity_distribution")).getMeasure("function_complexity_distribution").getData(), is("1=186;2=88;4=11;6=12;8=7;10=2;12=8"));
+  }
+
+  @Test
+  public void shouldNotPersistComplexityDistributionsOnFiles() {
+    assertThat(sonar.find(ResourceQuery.createForMetrics("org.apache.struts:struts-core:org.apache.struts.config.ConfigRuleSet", "function_complexity_distribution", "class_complexity_distribution")).getMeasures().size(), is(0));
   }
 
   @Test