package org.sonar.plugins.core;
import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.Properties;
-import org.sonar.api.Property;
-import org.sonar.api.PropertyType;
-import org.sonar.api.SonarPlugin;
+import org.sonar.api.*;
import org.sonar.api.checks.NoSonarFilter;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Java;
import org.sonar.plugins.core.charts.DistributionBarChart;
import org.sonar.plugins.core.charts.XradarChart;
import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
-import org.sonar.plugins.core.dashboards.GlobalDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectDefaultDashboard;
-import org.sonar.plugins.core.dashboards.ProjectHotspotDashboard;
-import org.sonar.plugins.core.dashboards.ProjectIssuesDashboard;
-import org.sonar.plugins.core.dashboards.ProjectTimeMachineDashboard;
-import org.sonar.plugins.core.issue.CountFalsePositivesDecorator;
-import org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator;
-import org.sonar.plugins.core.issue.InitialOpenIssuesSensor;
-import org.sonar.plugins.core.issue.InitialOpenIssuesStack;
-import org.sonar.plugins.core.issue.IssueHandlers;
-import org.sonar.plugins.core.issue.IssueTracking;
-import org.sonar.plugins.core.issue.IssueTrackingDecorator;
-import org.sonar.plugins.core.issue.IssuesDensityDecorator;
-import org.sonar.plugins.core.issue.WeightedIssuesDecorator;
+import org.sonar.plugins.core.dashboards.*;
+import org.sonar.plugins.core.issue.*;
import org.sonar.plugins.core.issue.ignore.IgnoreIssuesPlugin;
-import org.sonar.plugins.core.issue.notification.ChangesOnMyIssueNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.IssueChangesEmailTemplate;
-import org.sonar.plugins.core.issue.notification.NewFalsePositiveNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.NewIssuesEmailTemplate;
-import org.sonar.plugins.core.issue.notification.NewIssuesNotificationDispatcher;
-import org.sonar.plugins.core.issue.notification.SendIssueNotificationsPostJob;
+import org.sonar.plugins.core.issue.notification.*;
import org.sonar.plugins.core.measurefilters.MyFavouritesFilter;
import org.sonar.plugins.core.measurefilters.ProjectFilter;
import org.sonar.plugins.core.notifications.alerts.NewAlerts;
import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
-import org.sonar.plugins.core.sensors.BranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.CheckAlertThresholds;
-import org.sonar.plugins.core.sensors.CommentDensityDecorator;
-import org.sonar.plugins.core.sensors.CoverageDecorator;
-import org.sonar.plugins.core.sensors.CoverageMeasurementFilter;
-import org.sonar.plugins.core.sensors.DirectoriesDecorator;
-import org.sonar.plugins.core.sensors.FileHashSensor;
-import org.sonar.plugins.core.sensors.FilesDecorator;
-import org.sonar.plugins.core.sensors.GenerateAlertEvents;
-import org.sonar.plugins.core.sensors.ItBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItCoverageDecorator;
-import org.sonar.plugins.core.sensors.ItLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.LineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ManualMeasureDecorator;
-import org.sonar.plugins.core.sensors.OverallBranchCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallCoverageDecorator;
-import org.sonar.plugins.core.sensors.OverallLineCoverageDecorator;
-import org.sonar.plugins.core.sensors.ProfileEventsSensor;
-import org.sonar.plugins.core.sensors.ProfileSensor;
-import org.sonar.plugins.core.sensors.ProjectLinksSensor;
-import org.sonar.plugins.core.sensors.UnitTestDecorator;
-import org.sonar.plugins.core.sensors.VersionEventsSensor;
+import org.sonar.plugins.core.sensors.*;
import org.sonar.plugins.core.technicaldebt.TechnicalDebtDecorator;
-import org.sonar.plugins.core.timemachine.NewCoverageAggregator;
-import org.sonar.plugins.core.timemachine.NewCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewItCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.NewOverallCoverageFileAnalyzer;
-import org.sonar.plugins.core.timemachine.TendencyDecorator;
-import org.sonar.plugins.core.timemachine.TimeMachineConfigurationPersister;
-import org.sonar.plugins.core.timemachine.VariationDecorator;
+import org.sonar.plugins.core.technicaldebt.TechnicalDebtDensityDecorator;
+import org.sonar.plugins.core.timemachine.*;
import org.sonar.plugins.core.web.Lcom4Viewer;
import org.sonar.plugins.core.web.TestsViewer;
-import org.sonar.plugins.core.widgets.AlertsWidget;
-import org.sonar.plugins.core.widgets.ComplexityWidget;
-import org.sonar.plugins.core.widgets.CoverageWidget;
-import org.sonar.plugins.core.widgets.CustomMeasuresWidget;
-import org.sonar.plugins.core.widgets.DescriptionWidget;
-import org.sonar.plugins.core.widgets.DocumentationCommentsWidget;
-import org.sonar.plugins.core.widgets.DuplicationsWidget;
-import org.sonar.plugins.core.widgets.EventsWidget;
-import org.sonar.plugins.core.widgets.HotspotMetricWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget;
-import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget;
-import org.sonar.plugins.core.widgets.ItCoverageWidget;
-import org.sonar.plugins.core.widgets.MeasureFilterListWidget;
-import org.sonar.plugins.core.widgets.MeasureFilterTreemapWidget;
-import org.sonar.plugins.core.widgets.RulesWidget;
-import org.sonar.plugins.core.widgets.SizeWidget;
-import org.sonar.plugins.core.widgets.TechnicalDebtPyramidWidget;
-import org.sonar.plugins.core.widgets.TechnicalDebtWidget;
-import org.sonar.plugins.core.widgets.TimeMachineWidget;
-import org.sonar.plugins.core.widgets.TimelineWidget;
-import org.sonar.plugins.core.widgets.TreemapWidget;
-import org.sonar.plugins.core.widgets.WelcomeWidget;
-import org.sonar.plugins.core.widgets.issues.ActionPlansWidget;
-import org.sonar.plugins.core.widgets.issues.FalsePositiveIssuesWidget;
-import org.sonar.plugins.core.widgets.issues.IssueFilterWidget;
-import org.sonar.plugins.core.widgets.issues.MyUnresolvedIssuesWidget;
-import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesPerAssigneeWidget;
-import org.sonar.plugins.core.widgets.issues.UnresolvedIssuesStatusesWidget;
+import org.sonar.plugins.core.widgets.*;
+import org.sonar.plugins.core.widgets.issues.*;
import java.util.Arrays;
import java.util.List;
NewOverallCoverageFileAnalyzer.class,
NewCoverageAggregator.class,
+ // technical debt
+ TechnicalDebtDensityDecorator.class,
+
// Notify alerts on my favourite projects
NewAlerts.class,
NewAlerts.newMetadata());
import org.sonar.core.technicaldebt.TechnicalDebtConverter;
import org.sonar.core.technicaldebt.TechnicalDebtRequirement;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import static com.google.common.collect.Lists.newArrayList;
+
/**
* Decorator that computes the technical debt metric
*/
@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
public final class TechnicalDebtDecorator implements Decorator {
- public static final int DECIMALS_PRECISION = 5;
+ private static final int DECIMALS_PRECISION = 5;
private TechnicalDebtCalculator costCalculator;
public TechnicalDebtDecorator(TechnicalDebtCalculator costCalculator) {
@DependedUpon
public List<Metric> generatesMetrics() {
- return Arrays.asList(CoreMetrics.TECHNICAL_DEBT);
+ return newArrayList(CoreMetrics.TECHNICAL_DEBT);
}
public void decorate(Resource resource, DecoratorContext context) {
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.core.technicaldebt;
+
+import org.sonar.api.batch.*;
+import org.sonar.api.measures.CoreMetrics;
+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;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Decorator that computes the technical debt density metric
+ */
+@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
+public final class TechnicalDebtDensityDecorator implements Decorator {
+
+ private static final int DECIMALS_PRECISION = 5;
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesMetrics() {
+ return newArrayList(CoreMetrics.TECHNICAL_DEBT_DENSITY);
+ }
+
+ @DependsUpon
+ public Metric dependsUponTechnicalDebt() {
+ return CoreMetrics.TECHNICAL_DEBT;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (context.getMeasure(CoreMetrics.TECHNICAL_DEBT_DENSITY) != null) {
+ return;
+ }
+
+ Measure technicalDebt = context.getMeasure(CoreMetrics.TECHNICAL_DEBT);
+ Measure ncloc = context.getMeasure(CoreMetrics.NCLOC);
+
+ if (technicalDebt != null && ncloc != null && ncloc.getValue() > 0d) {
+ double value = technicalDebt.getValue() / ncloc.getValue();
+ context.saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT_DENSITY, value, DECIMALS_PRECISION));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+
+}
metric.sqale_index.name=Technical Debt
metric.sqale_index.description=The technical debt is the total effort required to fully adhere all quality requirements defined by a user.
+metric.technical_debt_density.name=Technical Debt Density
+metric.technical_debt_density.description=Density of technical debt based on the number of non commenting lines of code.
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.core.technicaldebt;
+
+import org.junit.Before;
+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.Resource;
+import org.sonar.api.test.IsMeasure;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+public class TechnicalDebtDensityDecoratorTest {
+
+ private TechnicalDebtDensityDecorator decorator;
+
+ private Resource resource;
+ private DecoratorContext context;
+
+ @Before
+ public void before() {
+ decorator = new TechnicalDebtDensityDecorator();
+ resource = mock(Resource.class);
+ context = mock(DecoratorContext.class);
+ }
+
+ @Test
+ public void generates_metrics() throws Exception {
+ assertThat(decorator.generatesMetrics()).hasSize(1);
+ }
+
+ @Test
+ public void depends_upon_technical_debt_metric() throws Exception {
+ assertThat(decorator.dependsUponTechnicalDebt()).isEqualTo(CoreMetrics.TECHNICAL_DEBT);
+ }
+
+ @Test
+ public void execute_on_project() throws Exception {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+ }
+
+ @Test
+ public void do_nothing_if_measure_already_computed() throws Exception {
+ when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT_DENSITY)).thenReturn(new Measure().setValue(5d).setMetric(CoreMetrics.TECHNICAL_DEBT_DENSITY));
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void do_nothing_if_no_ncloc() throws Exception {
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(null);
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void do_nothing_if_ncloc_is_zero() throws Exception {
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure().setValue(0d).setMetric(CoreMetrics.NCLOC));
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void do_nothing_if_no_technical_debt() throws Exception {
+ when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(null);
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void compute_technical_debt_density() throws Exception {
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure().setValue(48d).setMetric(CoreMetrics.NCLOC));
+ when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure().setValue(1.48d).setMetric(CoreMetrics.TECHNICAL_DEBT));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT_DENSITY, 0.03125d)));
+ }
+
+}
.setQualitative(true)
.create();
+ /**
+ * @since 4.0
+ */
+ public static final String TECHNICAL_DEBT_DENSITY_KEY = "technical_debt_density";
+
+ /**
+ * @since 4.0
+ */
+ public static final Metric TECHNICAL_DEBT_DENSITY = new Metric.Builder(TECHNICAL_DEBT_DENSITY_KEY, "Technical Debt Density", Metric.ValueType.FLOAT)
+ .setDomain(DOMAIN_TECHNICAL_DEBT)
+ .setDirection(Metric.DIRECTION_WORST)
+ .setOptimizedBestValue(true)
+ .setBestValue(0.0)
+ .setQualitative(true)
+ .create();
+
// --------------------------------------------------------------------------------------------------------------------
//
import static org.fest.assertions.Assertions.assertThat;
public class CoreMetricsTest {
+
@Test
- public void shouldReadMetricsFromClassReflection() {
+ public void read_metrics_from_class_reflection() {
List<Metric> metrics = CoreMetrics.getMetrics();
- assertThat(metrics).hasSize(149);
+ assertThat(metrics).hasSize(150);
assertThat(metrics).contains(CoreMetrics.NCLOC, CoreMetrics.DIRECTORIES);
}
+
}