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;
}
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
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);
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;
.setSignature(squidMethod.getName())
.setFromLine(squidMethod.getStartAtLine())
.setToLine(squidMethod.getEndAtLine())
+ .setAccessor(squidMethod.getInt(Metric.ACCESSORS)>0)
.create();
context.index(sonarMethod, sonarClass);
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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());
+ }
+}
+
@Test
public void coverageForFun() {
assertThat(new SquidPlugin().getKey(), not(nullValue()));
- assertThat(new SquidPlugin().getExtensions().size(), is(4));
+ assertThat(new SquidPlugin().getExtensions().size(), is(6));
}
}
+++ /dev/null
-/*\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
+++ /dev/null
-/*\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
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
*/
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;
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;
}
}
--- /dev/null
+/*
+ * 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();
+ }
+}
*/
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;
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));
}
@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) {
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
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);
}
/**
* @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;
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() {
return className;
}
+ public boolean isAccessor() {
+ return isAccessor;
+ }
+
@Override
public String getName() {
return signature;
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;
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);
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);
}
}
}
@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));
@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));
@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"));
}
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));
+++ /dev/null
-/*
- * 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();
- }
-}
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;
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",
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,
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",
*/\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
*/\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
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
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
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
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
--- /dev/null
+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");
+ }
+}
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
--- /dev/null
+<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
--- /dev/null
+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() {
+
+ }
+ }
--- /dev/null
+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");
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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