package org.sonar.plugins.core;
import com.google.common.collect.ImmutableList;
-import org.sonar.api.*;
+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.checks.NoSonarFilter;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
-import org.sonar.batch.components.PastSnapshotFinder;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
import org.sonar.core.timemachine.Periods;
import org.sonar.plugins.core.batch.IndexProjectPostJob;
import org.sonar.plugins.core.charts.DistributionAreaChart;
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.*;
-import org.sonar.plugins.core.issue.*;
-import org.sonar.plugins.core.issue.notification.*;
+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.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.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.*;
-import org.sonar.plugins.core.technicaldebt.DebtDecorator;
-import org.sonar.plugins.core.technicaldebt.NewDebtDecorator;
-import org.sonar.plugins.core.timemachine.*;
+import org.sonar.plugins.core.sensors.BranchCoverageDecorator;
+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.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.ProjectLinksSensor;
+import org.sonar.plugins.core.sensors.UnitTestDecorator;
+import org.sonar.plugins.core.sensors.VersionEventsSensor;
+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.web.TestsViewer;
import org.sonar.plugins.core.widgets.*;
-import org.sonar.plugins.core.widgets.issues.*;
-import org.sonar.plugins.core.widgets.measures.*;
+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.IssuesWidget;
+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.measures.MeasureFilterAsBubbleChartWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsCloudWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsHistogramWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterAsPieChartWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterListWidget;
+import org.sonar.plugins.core.widgets.measures.MeasureFilterTreemapWidget;
-import java.util.Arrays;
import java.util.List;
@Properties({
NewFalsePositiveNotificationDispatcher.class,
NewFalsePositiveNotificationDispatcher.newMetadata(),
- // technical debt
- DebtDecorator.class,
- NewDebtDecorator.class,
- IssueChangelogDebtCalculator.class,
-
// batch
ProfileEventsSensor.class,
ProjectLinksSensor.class,
NewAlerts.class,
NewAlerts.newMetadata());
- extensions.addAll(ExclusionProperties.definitions());
- extensions.addAll(IssueExclusionsConfiguration.getPropertyDefinitions());
- extensions.addAll(CoverageMeasurementFilter.getPropertyDefinitions());
- extensions.addAll(PastSnapshotFinder.getPropertyDefinitions());
- extensions.addAll(DebtDecorator.definitions());
- extensions.addAll(propertyDefinitions());
-
return extensions.build();
}
- static List<PropertyDefinition> propertyDefinitions() {
- return Arrays.asList(
- PropertyDefinition.builder(CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY)
- .defaultValue("en")
- .name("Locale used for issue messages")
- .description("Deprecated property. Keep default value for backward compatibility.")
- .hidden()
- .build(),
- PropertyDefinition.builder(CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY)
- .defaultValue("" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE)
- .name("Allow users to sign up online")
- .description("Users can sign up online.")
- .type(PropertyType.BOOLEAN)
- .category(CoreProperties.CATEGORY_SECURITY)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.CORE_DEFAULT_GROUP)
- .defaultValue(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE)
- .name("Default user group")
- .description("Any new users will automatically join this group.")
- .category(CoreProperties.CATEGORY_SECURITY)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY)
- .defaultValue("" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE)
- .name("Import sources")
- .description("Set to false if sources should not be imported and therefore not available in the Web UI (e.g. for security reasons).")
- .type(PropertyType.BOOLEAN)
- .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
- .category(CoreProperties.CATEGORY_SECURITY)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY)
- .defaultValue("" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)
- .name("Force user authentication")
- .description("Forcing user authentication stops un-logged users to access SonarQube.")
- .type(PropertyType.BOOLEAN)
- .category(CoreProperties.CATEGORY_SECURITY)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION)
- .defaultValue(Boolean.toString(false))
- .name("Prevent automatic project creation")
- .description("Set to true to prevent automatic project creation at first analysis and force project provisioning.")
- .type(PropertyType.BOOLEAN)
- .category(CoreProperties.CATEGORY_SECURITY)
- .build()
- );
- }
-
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.resources.Qualifiers;
-
-import java.util.List;
-
-class ExclusionProperties {
-
- private ExclusionProperties() {
- // only static stuff
- }
-
- static List<PropertyDefinition> definitions() {
- return ImmutableList.of(
- PropertyDefinition.builder(CoreProperties.PROJECT_INCLUSIONS_PROPERTY)
- .name("Source File Inclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onQualifiers(Qualifiers.PROJECT)
- .index(3)
- .build(),
- PropertyDefinition.builder(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY)
- .name("Test File Inclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onQualifiers(Qualifiers.PROJECT)
- .index(5)
- .build(),
- PropertyDefinition.builder(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)
- .name("Global Source File Exclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .index(0)
- .build(),
- PropertyDefinition.builder(CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY)
- .name("Global Test File Exclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .index(1)
- .build(),
- PropertyDefinition.builder(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY)
- .name("Source File Exclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onQualifiers(Qualifiers.PROJECT)
- .index(2)
- .build(),
- PropertyDefinition.builder(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY)
- .name("Test File Exclusions")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onQualifiers(Qualifiers.PROJECT)
- .index(4)
- .build(),
- PropertyDefinition.builder(CoreProperties.CORE_SKIPPED_MODULES_PROPERTY)
- .name("Exclude Modules")
- .description("Maven artifact ids of modules to exclude.")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onlyOnQualifiers(Qualifiers.PROJECT)
- .index(0)
- .build(),
- PropertyDefinition.builder(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY)
- .name("Include Modules")
- .description("Maven artifact ids of modules to include.")
- .multiValues(true)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
- .onlyOnQualifiers(Qualifiers.PROJECT)
- .index(1)
- .build());
- }
-}
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;
import org.sonar.api.utils.WildcardPattern;
import org.sonar.core.measure.MeasurementFilter;
import java.util.Collection;
import java.util.Iterator;
-import java.util.List;
public class CoverageMeasurementFilter implements MeasurementFilter {
- public static final String PROPERTY_COVERAGE_EXCLUSIONS = "sonar.coverage.exclusions";
- public static final String PROPERTY_COVERAGE_INCLUSIONS = "sonar.coverage.inclusions";
-
private final Settings settings;
private final ImmutableSet<Metric> coverageMetrics;
private Collection<WildcardPattern> resourcePatterns;
public CoverageMeasurementFilter(Settings settings,
- CoverageDecorator coverageDecorator,
- LineCoverageDecorator lineCoverageDecorator,
- BranchCoverageDecorator branchCoverageDecorator) {
+ CoverageDecorator coverageDecorator,
+ LineCoverageDecorator lineCoverageDecorator,
+ BranchCoverageDecorator branchCoverageDecorator) {
this.settings = settings;
this.coverageMetrics = ImmutableSet.<Metric>builder()
.addAll(coverageDecorator.generatedMetrics())
@VisibleForTesting
final void initPatterns() {
- Builder<WildcardPattern> builder = ImmutableList.<WildcardPattern>builder();
- for (String pattern : settings.getStringArray(PROPERTY_COVERAGE_EXCLUSIONS)) {
+ Builder<WildcardPattern> builder = ImmutableList.builder();
+ for (String pattern : settings.getStringArray("sonar.coverage.exclusions")) {
builder.add(WildcardPattern.create(pattern));
}
resourcePatterns = builder.build();
}
-
- public static List<PropertyDefinition> getPropertyDefinitions() {
- return ImmutableList.of(
- PropertyDefinition.builder(PROPERTY_COVERAGE_EXCLUSIONS)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(CoreProperties.SUBCATEGORY_COVERAGE_EXCLUSIONS)
- .type(PropertyType.STRING)
- .multiValues(true)
- .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
- .build()
- );
- }
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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 com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.batch.*;
-import org.sonar.api.batch.rule.Rule;
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.*;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.resources.ResourceUtils;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Maps.newHashMap;
-
-/**
- * Decorator that computes the technical debt metric
- */
-@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
-public final class DebtDecorator implements Decorator {
-
- private final ResourcePerspectives perspectives;
- private final TechnicalDebtModel model;
- private final Rules rules;
-
- /**
- * ruleFinder is needed to load "old" rule in order to persist rule measure
- */
- private final RuleFinder ruleFinder;
-
- public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) {
- this.perspectives = perspectives;
- this.model = model;
- this.rules = rules;
- this.ruleFinder = ruleFinder;
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- @DependedUpon
- public List<Metric> generatesMetrics() {
- return Arrays.asList(CoreMetrics.TECHNICAL_DEBT);
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- Issuable issuable = perspectives.as(Issuable.class, resource);
- if (issuable != null && shouldSaveMeasure(context)) {
- List<Issue> issues = newArrayList(issuable.issues());
- saveMeasures(context, issues);
- }
- }
-
- private void saveMeasures(DecoratorContext context, List<Issue> issues) {
- Long total = 0L;
- SumMap<RuleKey> ruleDebts = new SumMap<RuleKey>();
- SumMap<Characteristic> characteristicDebts = new SumMap<Characteristic>();
-
- // Aggregate rules debt from current issues (and populate current characteristic debt)
- for (Issue issue : issues) {
- Long debt = ((DefaultIssue) issue).debtInMinutes();
- total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts);
- }
-
- // Aggregate rules debt from children (and populate children characteristics debt)
- for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
- Long debt = measure.getValue().longValue();
- RuleMeasure ruleMeasure = (RuleMeasure) measure;
- total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts);
- }
-
- context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
- saveOnRule(context, ruleDebts);
- for (Characteristic characteristic : model.characteristics()) {
- Long debt = characteristicDebts.get(characteristic);
- saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false);
- }
- }
-
- private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap<RuleKey> ruleDebts, SumMap<Characteristic> characteristicDebts) {
- if (debt != null) {
- Rule rule = rules.find(ruleKey);
- if (rule != null) {
- String characteristicKey = rule.debtSubCharacteristic();
- if (characteristicKey != null) {
- Characteristic characteristic = model.characteristicByKey(characteristicKey);
- if (characteristic != null) {
- ruleDebts.add(ruleKey, debt);
- characteristicDebts.add(characteristic, debt);
- propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts);
- return debt;
- }
- }
- }
- }
- return 0L;
- }
-
- private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap<Characteristic> characteristicDebts) {
- if (characteristic != null) {
- characteristicDebts.add(characteristic, value);
- propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts);
- }
- }
-
- private void saveOnRule(DecoratorContext context, SumMap<RuleKey> ruleDebts) {
- for (Map.Entry<RuleKey, Long> entry : ruleDebts.entrySet()) {
- org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey());
- if (oldRule != null) {
- saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource()));
- }
- }
- }
-
- @VisibleForTesting
- void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) {
- // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
- // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
- if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) {
- Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
- measure.setCharacteristic(characteristic);
- saveMeasure(context, measure, value, inMemory);
- }
- }
-
- @VisibleForTesting
- void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) {
- // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
- // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
- if (value > 0.0) {
- RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null);
- saveMeasure(context, measure, value, inMemory);
- }
- }
-
- private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
- measure.setValue(value);
- if (inMemory) {
- measure.setPersistenceMode(PersistenceMode.MEMORY);
- }
- context.saveMeasure(measure);
- }
-
- private boolean shouldSaveMeasure(DecoratorContext context) {
- return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null;
- }
-
- public static List<PropertyDefinition> definitions() {
- return ImmutableList.of(
- PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY)
- .name("Number of working hours in a day")
- .type(PropertyType.INTEGER)
- .defaultValue("8")
- .category(CoreProperties.CATEGORY_TECHNICAL_DEBT)
- .deprecatedKey("sqale.hoursInDay")
- .build()
- );
- }
-
- private static class SumMap<E> {
- private Map<E, Long> sumByKeys;
-
- public SumMap() {
- sumByKeys = newHashMap();
- }
-
- public void add(@Nullable E key, Long value) {
- if (key != null) {
- Long currentValue = sumByKeys.get(key);
- sumByKeys.put(key, currentValue != null ? currentValue + value : value);
- }
- }
-
- @CheckForNull
- public Long get(E key) {
- return sumByKeys.get(key);
- }
-
- public Set<Map.Entry<E, Long>> entrySet() {
- return sumByKeys.entrySet();
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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 com.google.common.collect.ImmutableList;
-import org.sonar.api.batch.*;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.MeasureUtils;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.batch.components.Period;
-import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-
-import javax.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-import static com.google.common.collect.Lists.newArrayList;
-
-/**
- * Decorator that computes the technical debt metric
- */
-@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
-public final class NewDebtDecorator implements Decorator {
-
- private final ResourcePerspectives perspectives;
- private final TimeMachineConfiguration timeMachineConfiguration;
- private final IssueChangelogDebtCalculator issueChangelogDebtCalculator;
-
- public NewDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration,
- IssueChangelogDebtCalculator issueChangelogDebtCalculator) {
- this.perspectives = perspectives;
- this.timeMachineConfiguration = timeMachineConfiguration;
- this.issueChangelogDebtCalculator = issueChangelogDebtCalculator;
- }
-
- public boolean shouldExecuteOnProject(Project project) {
- return true;
- }
-
- @DependedUpon
- public List<Metric> generatesMetrics() {
- return ImmutableList.of(
- CoreMetrics.NEW_TECHNICAL_DEBT
- );
- }
-
- public void decorate(Resource resource, DecoratorContext context) {
- Issuable issuable = perspectives.as(Issuable.class, resource);
- if (issuable != null && shouldSaveNewMetrics(context)) {
- List<Issue> issues = newArrayList(issuable.issues());
- saveMeasures(context, issues);
- }
- }
-
- private void saveMeasures(DecoratorContext context, Collection<Issue> issues) {
- Measure measure = new Measure(CoreMetrics.NEW_TECHNICAL_DEBT);
- for (Period period : timeMachineConfiguration.periods()) {
- Date periodDate = period.getDate();
- double value = calculateNewTechnicalDebtValue(issues, periodDate);
- Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
- double sum = MeasureUtils.sumOnVariation(true, period.getIndex(), children) + value;
- measure.setVariation(period.getIndex(), sum);
- }
- context.saveMeasure(measure);
- }
-
- private long calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
- long result = 0;
- for (Issue issue : issues) {
- Long debt = issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, periodDate);
- if (debt != null) {
- result += debt;
- }
- }
- return result;
- }
-
- private boolean shouldSaveNewMetrics(DecoratorContext context) {
- return context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT) == null;
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.plugins.core.technicaldebt;
-
-import javax.annotation.ParametersAreNonnullByDefault;
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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;
-
-import org.junit.Test;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class ExclusionPropertiesTest {
- @Test
- public void definitions() throws Exception {
- assertThat(ExclusionProperties.definitions().size()).isGreaterThan(0);
- for (PropertyDefinition definition : ExclusionProperties.definitions()) {
- assertThat(definition.category()).isEqualTo(CoreProperties.CATEGORY_EXCLUSIONS);
- }
- }
-}
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Resource;
+import org.sonar.core.config.ExclusionProperties;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@Before
public void createFilter() {
- settings = new Settings(new PropertyDefinitions(CoverageMeasurementFilter.getPropertyDefinitions()));
+ settings = new Settings(new PropertyDefinitions(ExclusionProperties.all()));
filter = new CoverageMeasurementFilter(settings, new CoverageDecorator(), new LineCoverageDecorator(), new BranchCoverageDecorator());
}
Measure coverageMeasure = mock(Measure.class);
when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.LINES_TO_COVER);
- settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "src/org/polop/*");
+ settings.setProperty("sonar.coverage.exclusions", "src/org/polop/*");
filter.initPatterns();
assertThat(filter.accept(resource, coverageMeasure)).isFalse();
}
Measure coverageMeasure = mock(Measure.class);
when(coverageMeasure.getMetric()).thenReturn(CoreMetrics.COVERAGE);
- settings.setProperty(CoverageMeasurementFilter.PROPERTY_COVERAGE_EXCLUSIONS, "src/org/other/*");
+ settings.setProperty("sonar.coverage.exclusions", "src/org/other/*");
filter.initPatterns();
assertThat(filter.accept(resource, coverageMeasure)).isTrue();
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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 com.google.common.collect.Lists;
-import org.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
-import org.hamcrest.Description;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.batch.rule.Rules;
-import org.sonar.api.batch.rule.internal.RulesBuilder;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.*;
-import org.sonar.api.resources.File;
-import org.sonar.api.resources.Project;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.rules.RuleFinder;
-import org.sonar.api.technicaldebt.batch.Characteristic;
-import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
-import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.api.utils.Duration;
-
-import java.util.Collections;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class DebtDecoratorTest {
-
- static final int HOURS_IN_DAY = 8;
-
- static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
-
- @Mock
- DecoratorContext context;
-
- @Mock
- Resource resource;
-
- @Mock
- TechnicalDebtModel debtModel;
-
- @Mock
- Issuable issuable;
-
- @Mock
- ResourcePerspectives perspectives;
-
- @Mock
- RuleFinder ruleFinder;
-
- RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
- RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
- Rules rules;
-
- DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY");
- DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency);
-
- DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY");
- DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability);
-
- DebtDecorator decorator;
-
- @Before
- public void before() throws Exception {
- when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
- RulesBuilder rulesBuilder = new RulesBuilder();
- rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY");
- rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY");
- rules = rulesBuilder.build();
-
- when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()));
- when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()));
-
- when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity));
- when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency);
- when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency);
- when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability);
- when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity);
-
- decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder);
- }
-
- @Test
- public void generates_metrics() throws Exception {
- assertThat(decorator.generatesMetrics()).hasSize(1);
- }
-
- @Test
- public void execute_on_project() throws Exception {
- assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
- }
-
- @Test
- public void not_save_if_measure_already_computed() {
- when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure());
-
- decorator.decorate(resource, context);
-
- verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT)));
- }
-
- @Test
- public void add_technical_debt_from_one_issue_and_no_parent() throws Exception {
- Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
- }
-
- @Test
- public void add_technical_debt_from_one_issue_without_debt() throws Exception {
- Issue issue = createIssue("rule1", "repo1").setDebt(null);
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0);
- }
-
- @Test
- public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception {
- Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue())));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue())));
- }
-
- @Test
- public void add_technical_debt_from_issues() throws Exception {
- Long technicalDebt1 = ONE_DAY_IN_MINUTES;
- Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES;
-
- Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
- Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
- Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
- Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
- when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
-
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES);
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void add_technical_debt_from_current_and_children_measures() throws Exception {
- Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
- Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
- when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
-
- when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
- new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
- org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null)
- .setValue(5d * ONE_DAY_IN_MINUTES)
- ));
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void add_technical_debt_only_from_children_measures() throws Exception {
- when(issuable.issues()).thenReturn(Collections.<Issue>emptyList());
-
- when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
- new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
- org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())
- , null, null).setValue(5d * ONE_DAY_IN_MINUTES),
-
- new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
- org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())
- , null, null).setValue(10d * ONE_DAY_IN_MINUTES)
- ));
- decorator.decorate(resource, context);
-
- verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES);
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES)));
- verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void always_save_technical_debt_for_positive_values() throws Exception {
- // for a project
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getResource()).thenReturn(new Project("foo"));
- decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
- verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
-
- // or for a file
- context = mock(DecoratorContext.class);
- when(context.getResource()).thenReturn(new File("foo"));
- decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
- verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
- }
-
- @Test
- public void always_save_technical_debt_for_project_if_top_characteristic() throws Exception {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getResource()).thenReturn(new Project("foo"));
-
- // this is a top characteristic
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root");
-
- decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true);
- verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic));
- }
-
- /**
- * SQALE-147
- */
- @Test
- public void never_save_technical_debt_for_project_if_not_top_characteristic() throws Exception {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getResource()).thenReturn(new Project("foo"));
-
- DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
- DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic);
-
- decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true);
- verify(context, never()).saveMeasure(any(Measure.class));
- }
-
- @Test
- public void not_save_technical_debt_for_file_if_zero() throws Exception {
- DecoratorContext context = mock(DecoratorContext.class);
- when(context.getResource()).thenReturn(new File("foo"));
-
- decorator.saveCharacteristicMeasure(context, (Characteristic) null, 0.0, true);
- verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
- }
-
- @Test
- public void check_definitions() {
- assertThat(DebtDecorator.definitions()).hasSize(1);
- }
-
- private DefaultIssue createIssue(String ruleKey, String repositoryKey) {
- return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey));
- }
-
- class IsCharacteristicMeasure extends ArgumentMatcher<Measure> {
- Metric metric = null;
- Characteristic characteristic = null;
- Double value = null;
-
- public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) {
- this.metric = metric;
- this.characteristic = characteristic;
- this.value = value;
- }
-
- @Override
- public boolean matches(Object o) {
- if (!(o instanceof Measure)) {
- return false;
- }
- Measure m = (Measure) o;
- return ObjectUtils.equals(metric, m.getMetric()) &&
- ObjectUtils.equals(characteristic, m.getCharacteristic()) &&
- ObjectUtils.equals(value, m.getValue());
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText(new StringBuilder()
- .append("value=").append(value).append(",")
- .append("characteristic=").append(characteristic.key()).append(",")
- .append("metric=").append(metric.getKey()).toString())
- ;
- }
- }
-
- class IsRuleMeasure extends ArgumentMatcher<RuleMeasure> {
- Metric metric = null;
- RuleKey ruleKey = null;
- Double value = null;
-
- public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) {
- this.metric = metric;
- this.ruleKey = ruleKey;
- this.value = value;
- }
-
- @Override
- public boolean matches(Object o) {
- if (!(o instanceof RuleMeasure)) {
- return false;
- }
- RuleMeasure m = (RuleMeasure) o;
- return ObjectUtils.equals(metric, m.getMetric()) &&
- ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
- ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
- ObjectUtils.equals(value, m.getValue());
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE));
- }
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.apache.commons.lang.ObjectUtils;
-import org.apache.commons.lang.time.DateUtils;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.DecoratorContext;
-import org.sonar.api.component.ResourcePerspectives;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.Issuable;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.resources.Resource;
-import org.sonar.api.test.IsMeasure;
-import org.sonar.api.utils.Duration;
-import org.sonar.batch.components.Period;
-import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-
-import java.util.Date;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Mockito.*;
-
-@RunWith(MockitoJUnitRunner.class)
-public class NewDebtDecoratorTest {
-
- NewDebtDecorator decorator;
-
- @Mock
- TimeMachineConfiguration timeMachineConfiguration;
-
- @Mock
- Resource resource;
-
- @Mock
- Issuable issuable;
-
- @Mock
- DecoratorContext context;
-
- Date rightNow;
- Date elevenDaysAgo;
- Date tenDaysAgo;
- Date nineDaysAgo;
- Date fiveDaysAgo;
- Date fourDaysAgo;
-
- static final int HOURS_IN_DAY = 8;
-
- static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
- static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60;
- static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60;
-
- @Before
- public void setup() {
- Settings settings = new Settings();
- settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-
- ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
- when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
-
- rightNow = new Date();
- elevenDaysAgo = DateUtils.addDays(rightNow, -11);
- tenDaysAgo = DateUtils.addDays(rightNow, -10);
- nineDaysAgo = DateUtils.addDays(rightNow, -9);
- fiveDaysAgo = DateUtils.addDays(rightNow, -5);
- fourDaysAgo = DateUtils.addDays(rightNow, -4);
-
- when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
-
- decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator());
- }
-
- @Test
- public void generates_metrics() throws Exception {
- assertThat(decorator.generatesMetrics()).hasSize(1);
- }
-
- @Test
- public void execute_on_project() throws Exception {
- assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
- }
-
- @Test
- public void save_on_one_issue_with_one_new_changelog() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- // changelog created at is null because it has just been created on the current analysis
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_with_changelog() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_with_changelog_only_in_the_past() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
- newArrayList(
- // Change before all periods
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
- }
-
- @Test
- public void save_on_one_issue_with_changelog_having_null_value() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
- when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs()
- .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
- .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_issues_with_changelog() {
- Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
- Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_without_changelog() {
- when(issuable.issues()).thenReturn(newArrayList(
- (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
- );
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_one_issue_without_technical_debt_and_without_changelog() {
- when(issuable.issues()).thenReturn(newArrayList(
- (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null))
- );
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
- }
-
- @Test
- public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
- when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-
- when(issuable.issues()).thenReturn(newArrayList(
- (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
- );
-
- decorator.decorate(resource, context);
-
- // remember : period1 is null, period2 is null
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_issues_without_changelog() {
- when(issuable.issues()).thenReturn(newArrayList(
- (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
- new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
- ));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void save_on_issues_with_changelog_and_issues_without_changelog() {
- // issue1 and issue2 have changelog
- Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
- Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
- newArrayList(
- new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
- new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
- )
- );
-
- // issue3 and issue4 have no changelog
- Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES));
- Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES));
- when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES)));
- }
-
- @Test
- public void not_save_if_measure_already_computed() {
- when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
- when(issuable.issues()).thenReturn(newArrayList(
- (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
- new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
- ));
-
- decorator.decorate(resource, context);
-
- verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
- }
-
- /**
- * SONAR-5059
- */
- @Test
- public void not_return_negative_debt() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
- newArrayList(
- // changelog created at is null because it has just been created on the current analysis
- new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null)
- )
- );
- when(issuable.issues()).thenReturn(newArrayList(issue));
-
- decorator.decorate(resource, context);
-
- // remember : period1 is 5daysAgo, period2 is 10daysAgo
- verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
- }
-
-
- class IsVariationMeasure extends ArgumentMatcher<Measure> {
- Metric metric = null;
- Double var1 = null;
- Double var2 = null;
-
- public IsVariationMeasure(Metric metric, Double var1, Double var2) {
- this.metric = metric;
- this.var1 = var1;
- this.var2 = var2;
- }
-
- public boolean matches(Object o) {
- if (!(o instanceof Measure)) {
- return false;
- }
- Measure m = (Measure) o;
- return ObjectUtils.equals(metric, m.getMetric()) &&
- ObjectUtils.equals(var1, m.getVariation1()) &&
- ObjectUtils.equals(var2, m.getVariation2());
- }
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-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>
- <parent>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar</artifactId>
- <version>4.3-SNAPSHOT</version>
- <relativePath>../..</relativePath>
- </parent>
-
- <groupId>org.codehaus.sonar.plugins</groupId>
- <artifactId>sonar-maven-batch-plugin</artifactId>
- <packaging>sonar-plugin</packaging>
- <name>SonarQube :: Maven Batch Plugin</name>
-
- <properties>
- <maven.version>3.0</maven.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>com.google.code.findbugs</groupId>
- <artifactId>jsr305</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-batch</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-plugin-api</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-plugin-api</artifactId>
- <version>${maven.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.maven</groupId>
- <artifactId>maven-core</artifactId>
- <version>${maven.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- unit tests -->
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-testing-harness</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-all</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
-
- <plugin>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-packaging-maven-plugin</artifactId>
- <configuration>
- <pluginName>Maven Batch Plugin</pluginName>
- <pluginClass>org.sonar.plugins.maven.MavenBatchPlugin</pluginClass>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.execution.ReactorManager;
-import org.apache.maven.lifecycle.LifecycleExecutor;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.maven.MavenPlugin;
-import org.sonar.api.batch.maven.MavenPluginHandler;
-import org.sonar.api.resources.Project;
-import org.sonar.api.utils.SonarException;
-import org.sonar.api.utils.TimeProfiler;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.batch.scan.maven.MavenPluginExecutor;
-
-import javax.annotation.Nullable;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
-@SupportedEnvironment("maven")
-public class DefaultMavenPluginExecutor implements MavenPluginExecutor {
-
- private final MavenProjectConverter pomConverter;
- private LifecycleExecutor lifecycleExecutor;
- private MavenSession mavenSession;
-
- public DefaultMavenPluginExecutor(LifecycleExecutor le, MavenSession mavenSession, MavenProjectConverter pomConverter) {
- this.lifecycleExecutor = le;
- this.mavenSession = mavenSession;
- this.pomConverter = pomConverter;
- }
-
- @Override
- public final MavenPluginHandler execute(Project project, DefaultModuleFileSystem fs, MavenPluginHandler handler) {
- for (String goal : handler.getGoals()) {
- if (goal == null) {
- throw new IllegalStateException("Maven goal can't be null");
- }
- MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId());
- execute(project,
- fs,
- getGoal(handler.getGroupId(), handler.getArtifactId(), plugin != null && plugin.getPlugin() != null ? plugin.getPlugin().getVersion() : null, goal));
- }
- return handler;
- }
-
- @Override
- public final void execute(Project project, DefaultModuleFileSystem fs, String goal) {
- if (project.getPom() != null) {
- TimeProfiler profiler = new TimeProfiler().start("Execute " + goal);
- ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
- try {
- concreteExecute(project.getPom(), goal);
- } catch (Exception e) {
- throw new SonarException("Unable to execute maven plugin", e);
- } finally {
- // Reset original ClassLoader that may have been changed during Maven Execution (see SONAR-1800)
- Thread.currentThread().setContextClassLoader(currentClassLoader);
- profiler.stop();
- }
- if (!fs.isInitialized()) {
- pomConverter.synchronizeFileSystem(project.getPom(), fs);
- }
- }
- }
-
- static String getGoal(String groupId, String artifactId, @Nullable String version, String goal) {
- String defaultVersion = version == null ? "" : version;
- return new StringBuilder()
- .append(groupId).append(":")
- .append(artifactId).append(":")
- .append(defaultVersion)
- .append(":")
- .append(goal)
- .toString();
- }
-
- public void concreteExecute(MavenProject pom, String goal) {
- Method executeMethod = null;
- for (Method m : lifecycleExecutor.getClass().getMethods()) {
- if ("execute".equals(m.getName())) {
- executeMethod = m;
- break;
- }
- }
- if (executeMethod == null) {
- throw new SonarException("Unable to find execute method on Maven LifecycleExecutor. Please check your Maven version.");
- }
- if (executeMethod.getParameterTypes().length == 1) {
- concreteExecuteMaven3(pom, goal);
- } else if (executeMethod.getParameterTypes().length == 3) {
- concreteExecuteMaven2(executeMethod, pom, goal);
- } else {
- throw new SonarException("Unexpected parameter count on Maven LifecycleExecutor#execute method. Please check your Maven version.");
- }
- }
-
- public void concreteExecuteMaven3(MavenProject pom, String goal) {
- MavenSession projectSession = mavenSession.clone();
- projectSession.setCurrentProject(pom);
- projectSession.setProjects(Arrays.asList(pom));
- projectSession.getRequest().setRecursive(false);
- projectSession.getRequest().setPom(pom.getFile());
- projectSession.getRequest().setGoals(Arrays.asList(goal));
- projectSession.getRequest().setInteractiveMode(false);
- lifecycleExecutor.execute(projectSession);
- if (projectSession.getResult().hasExceptions()) {
- throw new SonarException("Exception during execution of " + goal);
- }
- }
-
- public void concreteExecuteMaven2(Method executeMethod, MavenProject pom, String goal) {
- try {
- ReactorManager reactor = new ReactorManager(Arrays.asList(pom));
- MavenSession clonedSession = new MavenSession(mavenSession.getContainer(),
- mavenSession.getSettings(),
- mavenSession.getLocalRepository(),
- mavenSession.getEventDispatcher(),
- reactor,
- Arrays.asList(goal),
- mavenSession.getExecutionRootDirectory(),
- mavenSession.getExecutionProperties(),
- mavenSession.getStartTime());
- executeMethod.invoke(lifecycleExecutor, clonedSession, reactor, clonedSession.getEventDispatcher());
- } catch (Exception e) {
- throw new SonarException("Unable to execute Maven 2 plugin", e);
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.SonarPlugin;
-
-import java.util.List;
-
-public final class MavenBatchPlugin extends SonarPlugin {
-
- public List getExtensions() {
- return ImmutableList.of(MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.List;
-
-/**
- * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
- */
-@Deprecated
-@SupportedEnvironment("maven")
-public class MavenProjectBootstrapper implements ProjectBootstrapper {
-
- private MavenSession session;
- private MavenProjectConverter mavenProjectConverter;
-
- public MavenProjectBootstrapper(MavenSession session, MavenProjectConverter mavenProjectConverter) {
- this.session = session;
- this.mavenProjectConverter = mavenProjectConverter;
- }
-
- @Override
- public ProjectReactor bootstrap() {
- // Don't use session.getTopLevelProject or session.getProjects to keep compatibility with Maven 2
- List<MavenProject> sortedProjects = session.getSortedProjects();
- MavenProject topLevelProject = null;
- for (MavenProject project : sortedProjects) {
- if (project.isExecutionRoot()) {
- topLevelProject = project;
- break;
- }
- }
- if (topLevelProject == null) {
- throw new IllegalStateException("Maven session does not declare a top level project");
- }
- return new ProjectReactor(mavenProjectConverter.configure(sortedProjects, topLevelProject));
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectBuilder;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.List;
-
-/**
- * Class that inject MavenProject in each module container
- */
-@SupportedEnvironment("maven")
-public class MavenProjectBuilder extends ProjectBuilder {
-
- private final MavenSession mavenSession;
-
- public MavenProjectBuilder(MavenSession mavenSession) {
- this.mavenSession = mavenSession;
- }
-
- @Override
- public void build(Context context) {
- ProjectReactor reactor = context.projectReactor();
- for (ProjectDefinition moduleDef : reactor.getProjects()) {
- setMavenProjectIfApplicable(moduleDef);
- }
- }
-
- private void setMavenProjectIfApplicable(ProjectDefinition definition) {
- if (mavenSession != null) {
- String moduleKey = definition.getKey();
- for (MavenProject mavenModule : (List<MavenProject>) mavenSession.getSortedProjects()) {
- String mavenModuleKey = mavenModule.getGroupId() + ":" + mavenModule.getArtifactId();
- if (mavenModuleKey.equals(moduleKey) && !definition.getContainerExtensions().contains(mavenModule)) {
- definition.addContainerExtension(mavenModule);
- }
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.apache.commons.lang.StringUtils;
-import org.apache.maven.model.CiManagement;
-import org.apache.maven.model.IssueManagement;
-import org.apache.maven.model.Scm;
-import org.apache.maven.project.MavenProject;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.SupportedEnvironment;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.maven.MavenUtils;
-import org.sonar.api.task.TaskExtension;
-import org.sonar.api.utils.MessageException;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.java.api.JavaUtils;
-
-import javax.annotation.Nullable;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
- */
-@Deprecated
-@SupportedEnvironment("maven")
-public class MavenProjectConverter implements TaskExtension {
-
- private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
- " Probably you use Maven Advanced Reactor Options, which is not supported by SonarQube and should not be used.";
-
- public ProjectDefinition configure(List<MavenProject> poms, MavenProject root) {
- // projects by canonical path to pom.xml
- Map<String, MavenProject> paths = Maps.newHashMap();
- Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
-
- try {
- configureModules(poms, paths, defs);
-
- rebuildModuleHierarchy(paths, defs);
- } catch (IOException e) {
- throw new IllegalStateException("Cannot configure project", e);
- }
-
- ProjectDefinition rootProject = defs.get(root);
- if (rootProject == null) {
- throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
- }
- return rootProject;
- }
-
- private void rebuildModuleHierarchy(Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
- for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
- MavenProject pom = entry.getValue();
- for (Object m : pom.getModules()) {
- String moduleId = (String) m;
- File modulePath = new File(pom.getBasedir(), moduleId);
- MavenProject module = findMavenProject(modulePath, paths);
-
- ProjectDefinition parentProject = defs.get(pom);
- if (parentProject == null) {
- throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
- }
- ProjectDefinition subProject = defs.get(module);
- if (subProject == null) {
- throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
- }
- parentProject.addSubProject(subProject);
- }
- }
- }
-
- private void configureModules(List<MavenProject> poms, Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
- for (MavenProject pom : poms) {
- paths.put(pom.getFile().getCanonicalPath(), pom);
- ProjectDefinition def = ProjectDefinition.create();
- merge(pom, def);
- defs.put(pom, def);
- }
- }
-
- private static MavenProject findMavenProject(final File modulePath, Map<String, MavenProject> paths) throws IOException {
- if (modulePath.exists() && modulePath.isDirectory()) {
- for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
- String pomFileParentDir = new File(entry.getKey()).getParent();
- if (pomFileParentDir.equals(modulePath.getCanonicalPath())) {
- return entry.getValue();
- }
- }
- return null;
- }
- return paths.get(modulePath.getCanonicalPath());
- }
-
- @VisibleForTesting
- void merge(MavenProject pom, ProjectDefinition definition) {
- String key = getSonarKey(pom);
- // IMPORTANT NOTE : reference on properties from POM model must not be saved,
- // instead they should be copied explicitly - see SONAR-2896
- definition
- .setProperties(pom.getModel().getProperties())
- .setKey(key)
- .setVersion(pom.getVersion())
- .setName(pom.getName())
- .setDescription(pom.getDescription())
- .addContainerExtension(pom);
- guessJavaVersion(pom, definition);
- guessEncoding(pom, definition);
- convertMavenLinksToProperties(definition, pom);
- synchronizeFileSystem(pom, definition);
- }
-
- private static String getSonarKey(MavenProject pom) {
- return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
- }
-
- private static void guessEncoding(MavenProject pom, ProjectDefinition definition) {
- // See http://jira.codehaus.org/browse/SONAR-2151
- String encoding = MavenUtils.getSourceEncoding(pom);
- if (encoding != null) {
- definition.setProperty(CoreProperties.ENCODING_PROPERTY, encoding);
- }
- }
-
- private static void guessJavaVersion(MavenProject pom, ProjectDefinition definition) {
- // See http://jira.codehaus.org/browse/SONAR-2148
- // Get Java source and target versions from maven-compiler-plugin.
- String version = MavenUtils.getJavaSourceVersion(pom);
- if (version != null) {
- definition.setProperty(JavaUtils.JAVA_SOURCE_PROPERTY, version);
- }
- version = MavenUtils.getJavaVersion(pom);
- if (version != null) {
- definition.setProperty(JavaUtils.JAVA_TARGET_PROPERTY, version);
- }
- }
-
- /**
- * For SONAR-3676
- */
- private static void convertMavenLinksToProperties(ProjectDefinition definition, MavenProject pom) {
- setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
-
- Scm scm = pom.getScm();
- if (scm == null) {
- scm = new Scm();
- }
- setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES, scm.getUrl());
- setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
-
- CiManagement ci = pom.getCiManagement();
- if (ci == null) {
- ci = new CiManagement();
- }
- setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_CI, ci.getUrl());
-
- IssueManagement issues = pom.getIssueManagement();
- if (issues == null) {
- issues = new IssueManagement();
- }
- setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
- }
-
- private static void setPropertyIfNotAlreadyExists(ProjectDefinition definition, String propertyKey, String propertyValue) {
- if (StringUtils.isBlank(definition.getProperties().getProperty(propertyKey))) {
- definition.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
- }
- }
-
- public void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
- into.setBaseDir(pom.getBasedir());
- File buildDir = getBuildDir(pom);
- if (buildDir != null) {
- into.setBuildDir(buildDir);
- into.setWorkDir(getSonarWorkDir(pom));
- }
- into.setSourceDirs(toPaths(mainDirs(pom)));
- into.setTestDirs(toPaths(testDirs(pom)));
- File binaryDir = resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir());
- if (binaryDir != null) {
- into.addBinaryDir(binaryDir);
- }
- }
-
- public static File getSonarWorkDir(MavenProject pom) {
- return new File(getBuildDir(pom), "sonar");
- }
-
- private static File getBuildDir(MavenProject pom) {
- return resolvePath(pom.getBuild().getDirectory(), pom.getBasedir());
- }
-
- public void synchronizeFileSystem(MavenProject pom, DefaultModuleFileSystem into) {
- into.resetDirs(
- pom.getBasedir(),
- getBuildDir(pom),
- mainDirs(pom),
- testDirs(pom),
- Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir())));
- }
-
- static File resolvePath(@Nullable String path, File basedir) {
- if (path != null) {
- File file = new File(StringUtils.trim(path));
- if (!file.isAbsolute()) {
- try {
- file = new File(basedir, path).getCanonicalFile();
- } catch (IOException e) {
- throw new IllegalStateException("Unable to resolve path '" + path + "'", e);
- }
- }
- return file;
- }
- return null;
- }
-
- static List<File> resolvePaths(List<String> paths, File basedir) {
- List<File> result = Lists.newArrayList();
- for (String path : paths) {
- File dir = resolvePath(path, basedir);
- if (dir != null) {
- result.add(dir);
- }
- }
- return result;
- }
-
- private List<File> mainDirs(MavenProject pom) {
- return sourceDirs(pom, ProjectDefinition.SOURCE_DIRS_PROPERTY, pom.getCompileSourceRoots());
- }
-
- private List<File> testDirs(MavenProject pom) {
- return sourceDirs(pom, ProjectDefinition.TEST_DIRS_PROPERTY, pom.getTestCompileSourceRoots());
- }
-
- private List<File> sourceDirs(MavenProject pom, String propertyKey, List mavenDirs) {
- List<String> paths;
- String prop = pom.getProperties().getProperty(propertyKey);
- if (prop != null) {
- paths = Arrays.asList(StringUtils.split(prop, ","));
- // do not remove dirs that do not exist. They must be kept in order to
- // notify users that value of sonar.sources has a typo.
- return existingDirsOrFail(resolvePaths(paths, pom.getBasedir()), pom, propertyKey);
- }
-
- List<File> dirs = resolvePaths(mavenDirs, pom.getBasedir());
-
- // Maven provides some directories that do not exist. They
- // should be removed
- return keepExistingDirs(dirs);
- }
-
- private List<File> existingDirsOrFail(List<File> dirs, MavenProject pom, String propertyKey) {
- for (File dir : dirs) {
- if (!dir.isDirectory() || !dir.exists()) {
- throw MessageException.of(String.format(
- "The directory '%s' does not exist for Maven module %s. Please check the property %s",
- dir.getAbsolutePath(), pom.getId(), propertyKey));
- }
- }
- return dirs;
- }
-
- private static List<File> keepExistingDirs(List<File> files) {
- return Lists.newArrayList(Collections2.filter(files, new Predicate<File>() {
- @Override
- public boolean apply(File dir) {
- return dir != null && dir.exists() && dir.isDirectory();
- }
- }));
- }
-
- private static String[] toPaths(Collection<File> dirs) {
- Collection<String> paths = Collections2.transform(dirs, new Function<File, String>() {
- @Override
- public String apply(File dir) {
- return dir.getAbsolutePath();
- }
- });
- return paths.toArray(new String[paths.size()]);
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.
- */
-
-/**
- * This package is a part of bootstrap process, so we should take care about backward compatibility.
- */
-@ParametersAreNonnullByDefault
-package org.sonar.plugins.maven;
-
-import javax.annotation.ParametersAreNonnullByDefault;
-
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.project.MavenProject;
-import org.junit.Test;
-import org.sonar.api.batch.maven.MavenPlugin;
-import org.sonar.api.batch.maven.MavenPluginHandler;
-import org.sonar.api.resources.Project;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-
-import java.io.File;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyList;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-public class DefaultMavenPluginExecutorTest {
-
- @Test
- public void plugin_version_should_be_optional() {
- assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", null, "goal"), is("group:artifact::goal"));
- }
-
- @Test
- public void test_plugin_version() {
- assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", "3.54", "goal"), is("group:artifact:3.54:goal"));
- }
-
- @Test
- public void should_ignore_non_maven_projects() {
- DefaultMavenPluginExecutor executor = new DefaultMavenPluginExecutor(null, null, mock(MavenProjectConverter.class)) {
- @Override
- public void concreteExecute(MavenProject pom, String goal) {
- pom.addCompileSourceRoot("src/java");
- }
- };
- Project foo = new Project("foo");
- DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
- executor.execute(foo, fs, new AddSourceMavenPluginHandler());
-
- verify(fs, never()).resetDirs(any(File.class), any(File.class), anyList(), anyList(), anyList());
- }
-
- static class AddSourceMavenPluginHandler implements MavenPluginHandler {
- public String getGroupId() {
- return "fake";
- }
-
- public String getArtifactId() {
- return "fake";
- }
-
- public String getVersion() {
- return "2.2";
- }
-
- public boolean isFixedVersion() {
- return false;
- }
-
- public String[] getGoals() {
- return new String[]{"fake"};
- }
-
- public void configure(Project project, MavenPlugin plugin) {
- }
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.junit.Test;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class MavenBatchPluginTest {
-
- @Test
- public void testGetExtensions() {
- MavenBatchPlugin plugin = new MavenBatchPlugin();
- assertThat(plugin.getExtensions()).hasSize(4);
- }
-
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.api.batch.bootstrap.ProjectReactor;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class MavenProjectBootstrapperTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- @Test
- public void bootstrap() throws Exception {
- ProjectDefinition def = mock(ProjectDefinition.class);
- MavenSession session = mock(MavenSession.class);
- MavenProject rootProject = new MavenProject();
- rootProject.setExecutionRoot(true);
- List<MavenProject> projects = Arrays.asList(rootProject);
- when(session.getSortedProjects()).thenReturn(projects);
-
- MavenProjectConverter pomConverter = mock(MavenProjectConverter.class);
- when(pomConverter.configure(projects, rootProject)).thenReturn(def);
- MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
-
- ProjectReactor reactor = bootstrapper.bootstrap();
-
- assertThat(reactor).isNotNull();
- verify(pomConverter).configure(projects, rootProject);
- }
-
- @Test
- public void should_fail_if_no_top_level_project() throws Exception {
- thrown.expect(IllegalStateException.class);
- thrown.expectMessage("Maven session does not declare a top level project");
-
- MavenSession session = mock(MavenSession.class);
- MavenProjectConverter pomConverter = new MavenProjectConverter();
- MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
-
- bootstrapper.bootstrap();
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.maven.model.Model;
-import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
-import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
-import org.hamcrest.core.Is;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-import org.sonar.test.TestUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.URISyntaxException;
-import java.util.Arrays;
-import java.util.Properties;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-public class MavenProjectConverterTest {
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
-
- MavenProjectConverter converter = new MavenProjectConverter();
-
- /**
- * See SONAR-2681
- */
- @Test
- public void shouldThrowExceptionWhenUnableToDetermineProjectStructure() {
- MavenProject root = new MavenProject();
- root.setFile(new File("/foo/pom.xml"));
- root.getBuild().setDirectory("target");
- root.getModules().add("module/pom.xml");
-
- try {
- converter.configure(Arrays.asList(root), root);
- fail();
- } catch (IllegalStateException e) {
- assertThat(e.getMessage(), containsString("Advanced Reactor Options"));
- }
- }
-
- @Test
- public void shouldConvertModules() throws IOException {
- File basedir = temp.newFolder();
-
- MavenProject root = newMavenProject("com.foo", "parent", "1.0-SNAPSHOT");
- root.setFile(new File(basedir, "pom.xml"));
- root.getBuild().setDirectory("target");
- root.getBuild().setOutputDirectory("target/classes");
- root.getModules().add("module/pom.xml");
- MavenProject module = newMavenProject("com.foo", "moduleA", "1.0-SNAPSHOT");
- module.setFile(new File(basedir, "module/pom.xml"));
- module.getBuild().setDirectory("target");
- module.getBuild().setOutputDirectory("target/classes");
- ProjectDefinition project = converter.configure(Arrays.asList(root, module), root);
-
- assertThat(project.getSubProjects().size(), is(1));
- }
-
- private MavenProject newMavenProject(String groupId, String artifactId, String version) {
- Model model = new Model();
- model.setGroupId(groupId);
- model.setArtifactId(artifactId);
- model.setVersion(version);
- return new MavenProject(model);
- }
-
- @Test
- public void shouldConvertProperties() {
- MavenProject pom = new MavenProject();
- pom.setGroupId("foo");
- pom.setArtifactId("bar");
- pom.setVersion("1.0.1");
- pom.setName("Test");
- pom.setDescription("just test");
- pom.setFile(new File("/foo/pom.xml"));
- pom.getBuild().setDirectory("target");
- ProjectDefinition project = ProjectDefinition.create();
- converter.merge(pom, project);
-
- Properties properties = project.getProperties();
- assertThat(properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY), is("foo:bar"));
- assertThat(properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY), is("1.0.1"));
- assertThat(properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY), is("Test"));
- assertThat(properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY), is("just test"));
- }
-
- @Test
- public void moduleNameShouldEqualArtifactId() throws Exception {
- File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/");
- MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml", true);
- MavenProject module1 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml", false);
- MavenProject module2 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml", false);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
-
- assertThat(rootDef.getSubProjects().size(), Is.is(2));
- assertThat(rootDef.getKey(), Is.is("org.test:parent"));
- assertNull(rootDef.getParent());
- assertThat(rootDef.getBaseDir(), is(rootDir));
-
- ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
- assertThat(module1Def.getKey(), Is.is("org.test:module1"));
- assertThat(module1Def.getParent(), Is.is(rootDef));
- assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module1")));
- assertThat(module1Def.getSubProjects().size(), Is.is(0));
- }
-
- @Test
- public void moduleNameDifferentThanArtifactId() throws Exception {
- File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/");
- MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml", true);
- MavenProject module1 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml", false);
- MavenProject module2 = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml", false);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
-
- assertThat(rootDef.getSubProjects().size(), is(2));
- assertThat(rootDef.getKey(), is("org.test:parent"));
- assertNull(rootDef.getParent());
- assertThat(rootDef.getBaseDir(), is(rootDir));
-
- ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
- assertThat(module1Def.getKey(), Is.is("org.test:module1"));
- assertThat(module1Def.getParent(), Is.is(rootDef));
- assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "path1")));
- assertThat(module1Def.getSubProjects().size(), Is.is(0));
- }
-
- @Test
- public void should_find_module_with_maven_project_file_naming_different_from_pom_xml() throws Exception {
- File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/");
- MavenProject parent = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml", true);
- MavenProject module = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml", false);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module), parent);
-
- assertThat(rootDef.getSubProjects().size(), Is.is(1));
- assertThat(rootDef.getKey(), Is.is("org.test:parent"));
- assertNull(rootDef.getParent());
- assertThat(rootDef.getBaseDir(), is(rootDir));
-
- ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
- assertThat(module1Def.getKey(), Is.is("org.test:module"));
- assertThat(module1Def.getParent(), Is.is(rootDef));
- assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module")));
- assertThat(module1Def.getSubProjects().size(), Is.is(0));
- }
-
- @Test
- public void testSingleProjectWithoutModules() throws Exception {
- File rootDir = TestUtils.getResource("/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/");
- MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml", true);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
- assertThat(rootDef.getKey(), is("org.test:parent"));
- assertThat(rootDef.getSubProjects().size(), is(0));
- assertNull(rootDef.getParent());
- assertThat(rootDef.getBaseDir(), is(rootDir));
- }
-
- @Test
- public void shouldConvertLinksToProperties() throws Exception {
- MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinks/pom.xml", true);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
- Properties props = rootDef.getProperties();
- assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com");
- assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
- assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
- assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com");
- assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
- }
-
- @Test
- public void shouldNotConvertLinksToPropertiesIfPropertyAlreadyDefined() throws Exception {
- MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml", true);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
- Properties props = rootDef.getProperties();
-
- // Those properties have been fed by the POM elements <ciManagement>, <issueManagement>, ...
- assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
- assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
- assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
-
- // ... but those ones have been overridden by <properties> in the POM
- assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com-OVERRIDEN-BY-PROPS");
- assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com-OVERRIDEN-BY-PROPS");
- }
-
- @Test
- public void shouldLoadSourceEncoding() throws Exception {
- MavenProject pom = loadPom("/org/sonar/plugins/maven/MavenProjectConverterTest/sourceEncoding/pom.xml", true);
-
- ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
-
- assertThat(rootDef.getProperties().getProperty(CoreProperties.ENCODING_PROPERTY)).isEqualTo("Shift_JIS");
- }
-
- private MavenProject loadPom(String pomPath, boolean isRoot) throws URISyntaxException, IOException, XmlPullParserException {
- File pomFile = new File(getClass().getResource(pomPath).toURI());
- Model model = new MavenXpp3Reader().read(new StringReader(FileUtils.readFileToString(pomFile)));
- MavenProject pom = new MavenProject(model);
- pom.setFile(pomFile);
- pom.getBuild().setDirectory("target");
- pom.setExecutionRoot(isRoot);
- return pom;
- }
-}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.maven;
-
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.project.MavenProject;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.batch.bootstrap.ProjectDefinition;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class SonarMavenProjectBuilderTest {
-
- @Test
- public void testSimpleProject() {
- MavenSession session = mock(MavenSession.class);
- MavenProject rootProject = mock(MavenProject.class);
- when(rootProject.isExecutionRoot()).thenReturn(true);
- when(session.getSortedProjects()).thenReturn(Arrays.asList(rootProject));
-
- MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
- ProjectDefinition projectDefinition = ProjectDefinition.create();
- when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
- MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
-
- assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
-
- ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
- verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
- assertThat(argument.getValue()).contains(rootProject);
- }
-
- @Test
- public void testMultimoduleProject() {
- MavenSession session = mock(MavenSession.class);
- MavenProject rootProject = mock(MavenProject.class);
- MavenProject module1 = mock(MavenProject.class);
- MavenProject module2 = mock(MavenProject.class);
- when(rootProject.isExecutionRoot()).thenReturn(true);
- when(module1.isExecutionRoot()).thenReturn(false);
- when(module2.isExecutionRoot()).thenReturn(false);
- when(session.getSortedProjects()).thenReturn(Arrays.asList(module1, module2, rootProject));
-
- MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
- ProjectDefinition projectDefinition = ProjectDefinition.create();
- when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
- MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
-
- assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
-
- ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
- verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
- assertThat(argument.getValue()).contains(module1, module2, rootProject);
- }
-
-}
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <modules>
- <module>module</module>
- </modules>
-</project>
\ No newline at end of file
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module1</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module2</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <modules>
- <module>path1</module>
- <module>path2</module>
- </modules>
-</project>
\ No newline at end of file
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module1</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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>
- <parent>
- <groupId>org.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- </parent>
- <artifactId>module2</artifactId>
- <packaging>jar</packaging>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <modules>
- <module>module1</module>
- <module>module2</module>
- </modules>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <url>http://home.com</url>
- <ciManagement>
- <url>http://ci.com</url>
- </ciManagement>
- <issueManagement>
- <url>http://issues.com</url>
- </issueManagement>
- <scm>
- <url>http://sources.com</url>
- <developerConnection>http://sources-dev.com</developerConnection>
- </scm>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <url>http://home.com</url>
- <ciManagement>
- <url>http://ci.com</url>
- </ciManagement>
- <issueManagement>
- <url>http://issues.com</url>
- </issueManagement>
- <scm>
- <url>http://sources.com</url>
- <developerConnection>http://sources-dev.com</developerConnection>
- </scm>
-
-
-
- <properties>
- <sonar.links.homepage>http://home.com-OVERRIDEN-BY-PROPS</sonar.links.homepage>
- <sonar.links.scm>http://sources.com-OVERRIDEN-BY-PROPS</sonar.links.scm>
- </properties>
-
-
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
-</project>
\ No newline at end of file
+++ /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.test</groupId>
- <artifactId>parent</artifactId>
- <version>0.1-SNAPSHOT</version>
- <packaging>pom</packaging>
- <url>http://home.com</url>
- <properties>
- <project.build.sourceEncoding>Shift_JIS</project.build.sourceEncoding>
- </properties>
-</project>
\ No newline at end of file
<module>plugins/sonar-cpd-plugin</module>
<module>plugins/sonar-design-plugin</module>
<module>plugins/sonar-email-notifications-plugin</module>
- <module>plugins/sonar-maven-batch-plugin</module>
<module>plugins/sonar-xoo-plugin</module>
</modules>
<exclude>net.sourceforge.jtds:jtds</exclude>
<exclude>org.codehaus.sonar.plugins:*</exclude>
<exclude>org.codehaus.sonar-plugins.*:*</exclude>
+ <exclude>org.codehaus.sonar:sonar-batch-maven-compat</exclude>
</excludes>
</dependencySet>
+ <dependencySet>
+ <outputDirectory>lib/batch</outputDirectory>
+ <useTransitiveDependencies>false</useTransitiveDependencies>
+ <includes>
+ <include>org.codehaus.sonar:sonar-batch-maven-compat</include>
+ </includes>
+ </dependencySet>
<!-- Plugins -->
<dependencySet>
<outputDirectory>lib/core-plugins</outputDirectory>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>sonar-batch-maven-compat</artifactId>
+ <version>${pom.version}</version>
+ <scope>runtime</scope>
+ </dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<type>sonar-plugin</type>
<scope>runtime</scope>
</dependency>
- <dependency>
- <groupId>org.codehaus.sonar.plugins</groupId>
- <artifactId>sonar-maven-batch-plugin</artifactId>
- <version>${project.version}</version>
- <type>sonar-plugin</type>
- <scope>runtime</scope>
- </dependency>
<dependency>
<groupId>org.sonatype.jsw-binaries</groupId>
<artifactId>jsw-binaries</artifactId>
<configuration>
<rules>
<requireFilesSize>
- <minsize>55000000</minsize>
- <maxsize>75000000</maxsize>
+ <minsize>80000000</minsize>
+ <maxsize>88000000</maxsize>
<files>
<file>${project.build.directory}/sonarqube-${project.version}.zip</file>
</files>
<description>Compatibility layer, which provides MavenProject for non-Maven environments.</description>
<dependencies>
+ <dependency>
+ <groupId>${pom.groupId}</groupId>
+ <artifactId>sonar-batch</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>2.0.7</version>
- <scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
</plugin>
</plugins>
</build>
-
- <profiles>
- <profile>
- <id>release</id>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-deploy-plugin</artifactId>
- <configuration>
- <skip>true</skip>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
</project>
<version>4.3-SNAPSHOT</version>
</parent>
- <groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-batch</artifactId>
<name>SonarQube :: Batch</name>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-plugin-api</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>jfree</groupId>
+ <artifactId>jcommon</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>jfree</groupId>
+ <artifactId>jfreechat</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
+ <version>3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
+ <version>3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.bootstrap;
+
+import com.google.common.collect.Lists;
+import org.sonar.batch.debt.DebtDecorator;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.debt.NewDebtDecorator;
+import org.sonar.batch.maven.DefaultMavenPluginExecutor;
+import org.sonar.batch.maven.MavenProjectBootstrapper;
+import org.sonar.batch.maven.MavenProjectBuilder;
+import org.sonar.batch.maven.MavenProjectConverter;
+import org.sonar.core.config.CorePropertyDefinitions;
+
+import java.util.Collection;
+import java.util.List;
+
+public class BatchComponents {
+ public static Collection all() {
+ List components = Lists.newArrayList(
+ // Maven
+ MavenProjectBootstrapper.class, DefaultMavenPluginExecutor.class, MavenProjectConverter.class, MavenProjectBuilder.class,
+
+ // Debt
+ IssueChangelogDebtCalculator.class, DebtDecorator.class, NewDebtDecorator.class
+ );
+ components.addAll(CorePropertyDefinitions.all());
+ return components;
+ }
+}
public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) {
boolean preview = analysisMode.isPreview();
+
+ // core components
+ for (Object o : BatchComponents.all()) {
+ doInstall(container, matcher, null, preview, o);
+ }
+
+ // plugin extensions
for (Map.Entry<PluginMetadata, Plugin> entry : pluginRepository.getPluginsByMetadata().entrySet()) {
PluginMetadata metadata = entry.getKey();
Plugin plugin = entry.getValue();
import org.sonar.api.platform.ComponentContainer;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.task.Task;
+import org.sonar.api.task.TaskComponent;
import org.sonar.api.task.TaskDefinition;
import org.sonar.api.task.TaskExtension;
import org.sonar.api.utils.SonarException;
private void installTaskExtensions() {
getComponentByType(ExtensionInstaller.class).install(this, new ExtensionMatcher() {
public boolean accept(Object extension) {
- return ExtensionUtils.isType(extension, TaskExtension.class);
+ return ExtensionUtils.isType(extension, TaskComponent.class);
}
});
}
package org.sonar.batch.components;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.BatchExtension;
import org.sonar.api.CoreProperties;
-import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.Settings;
import org.sonar.api.database.model.Snapshot;
-import org.sonar.api.resources.Qualifiers;
import javax.annotation.Nullable;
-
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
-import java.util.List;
public class PastSnapshotFinder implements BatchExtension {
}
}
- public static List<PropertyDefinition> getPropertyDefinitions() {
- return ImmutableList.of(
- PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 1)
- .name("Period 1")
- .description("Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
- "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
- "compare to previous analysis</li><li>'previous_version' to compare to the previous version in the project history</li></ul>" +
- "<p>When specifying a number of days or a date, the snapshot selected for comparison is " +
- " the first one available inside the corresponding time range. </p>" +
- "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
- .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 2)
- .name("Period 2")
- .description("See the property 'Period 1'")
- .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 3)
- .name("Period 3")
- .description("See the property 'Period 1'")
- .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 4)
- .name("Period 4")
- .description("Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
- "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
- "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li>" +
- "<li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example 1.2</li></ul>" +
- "<p>When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range. </p>" +
- "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
- .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4)
- .onlyOnQualifiers(Qualifiers.PROJECT)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
- .build(),
-
- PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 5)
- .name("Period 5")
- .description("See the property 'Period 4'")
- .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5)
- .onlyOnQualifiers(Qualifiers.PROJECT)
- .category(CoreProperties.CATEGORY_GENERAL)
- .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
- .build()
- );
- }
-
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.rule.Rule;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
+/**
+ * Decorator that computes the technical debt metric
+ */
+@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
+public final class DebtDecorator implements Decorator {
+
+ private final ResourcePerspectives perspectives;
+ private final TechnicalDebtModel model;
+ private final Rules rules;
+
+ /**
+ * ruleFinder is needed to load "old" rule in order to persist rule measure
+ */
+ private final RuleFinder ruleFinder;
+
+ public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) {
+ this.perspectives = perspectives;
+ this.model = model;
+ this.rules = rules;
+ this.ruleFinder = ruleFinder;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesMetrics() {
+ return Arrays.asList(CoreMetrics.TECHNICAL_DEBT);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ Issuable issuable = perspectives.as(Issuable.class, resource);
+ if (issuable != null && shouldSaveMeasure(context)) {
+ List<Issue> issues = newArrayList(issuable.issues());
+ saveMeasures(context, issues);
+ }
+ }
+
+ private void saveMeasures(DecoratorContext context, List<Issue> issues) {
+ Long total = 0L;
+ SumMap<RuleKey> ruleDebts = new SumMap<RuleKey>();
+ SumMap<Characteristic> characteristicDebts = new SumMap<Characteristic>();
+
+ // Aggregate rules debt from current issues (and populate current characteristic debt)
+ for (Issue issue : issues) {
+ Long debt = ((DefaultIssue) issue).debtInMinutes();
+ total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts);
+ }
+
+ // Aggregate rules debt from children (and populate children characteristics debt)
+ for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
+ Long debt = measure.getValue().longValue();
+ RuleMeasure ruleMeasure = (RuleMeasure) measure;
+ total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts);
+ }
+
+ context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
+ saveOnRule(context, ruleDebts);
+ for (Characteristic characteristic : model.characteristics()) {
+ Long debt = characteristicDebts.get(characteristic);
+ saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false);
+ }
+ }
+
+ private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap<RuleKey> ruleDebts, SumMap<Characteristic> characteristicDebts) {
+ if (debt != null) {
+ Rule rule = rules.find(ruleKey);
+ if (rule != null) {
+ String characteristicKey = rule.debtSubCharacteristic();
+ if (characteristicKey != null) {
+ Characteristic characteristic = model.characteristicByKey(characteristicKey);
+ if (characteristic != null) {
+ ruleDebts.add(ruleKey, debt);
+ characteristicDebts.add(characteristic, debt);
+ propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts);
+ return debt;
+ }
+ }
+ }
+ }
+ return 0L;
+ }
+
+ private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap<Characteristic> characteristicDebts) {
+ if (characteristic != null) {
+ characteristicDebts.add(characteristic, value);
+ propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts);
+ }
+ }
+
+ private void saveOnRule(DecoratorContext context, SumMap<RuleKey> ruleDebts) {
+ for (Map.Entry<RuleKey, Long> entry : ruleDebts.entrySet()) {
+ org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey());
+ if (oldRule != null) {
+ saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource()));
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) {
+ // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
+ // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
+ if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) {
+ Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
+ measure.setCharacteristic(characteristic);
+ saveMeasure(context, measure, value, inMemory);
+ }
+ }
+
+ @VisibleForTesting
+ void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) {
+ // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
+ // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
+ if (value > 0.0) {
+ RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null);
+ saveMeasure(context, measure, value, inMemory);
+ }
+ }
+
+ private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
+ measure.setValue(value);
+ if (inMemory) {
+ measure.setPersistenceMode(PersistenceMode.MEMORY);
+ }
+ context.saveMeasure(measure);
+ }
+
+ private boolean shouldSaveMeasure(DecoratorContext context) {
+ return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null;
+ }
+
+ private static class SumMap<E> {
+ private Map<E, Long> sumByKeys;
+
+ public SumMap() {
+ sumByKeys = newHashMap();
+ }
+
+ public void add(@Nullable E key, Long value) {
+ if (key != null) {
+ Long currentValue = sumByKeys.get(key);
+ sumByKeys.put(key, currentValue != null ? currentValue + value : value);
+ }
+ }
+
+ @CheckForNull
+ public Long get(E key) {
+ return sumByKeys.get(key);
+ }
+
+ public Set<Map.Entry<E, Long>> entrySet() {
+ return sumByKeys.entrySet();
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.batch.*;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.components.Period;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * Decorator that computes the technical debt metric
+ */
+@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
+public final class NewDebtDecorator implements Decorator {
+
+ private final ResourcePerspectives perspectives;
+ private final TimeMachineConfiguration timeMachineConfiguration;
+ private final IssueChangelogDebtCalculator issueChangelogDebtCalculator;
+
+ public NewDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration,
+ IssueChangelogDebtCalculator issueChangelogDebtCalculator) {
+ this.perspectives = perspectives;
+ this.timeMachineConfiguration = timeMachineConfiguration;
+ this.issueChangelogDebtCalculator = issueChangelogDebtCalculator;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesMetrics() {
+ return ImmutableList.of(
+ CoreMetrics.NEW_TECHNICAL_DEBT
+ );
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ Issuable issuable = perspectives.as(Issuable.class, resource);
+ if (issuable != null && shouldSaveNewMetrics(context)) {
+ List<Issue> issues = newArrayList(issuable.issues());
+ saveMeasures(context, issues);
+ }
+ }
+
+ private void saveMeasures(DecoratorContext context, Collection<Issue> issues) {
+ Measure measure = new Measure(CoreMetrics.NEW_TECHNICAL_DEBT);
+ for (Period period : timeMachineConfiguration.periods()) {
+ Date periodDate = period.getDate();
+ double value = calculateNewTechnicalDebtValue(issues, periodDate);
+ Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
+ double sum = MeasureUtils.sumOnVariation(true, period.getIndex(), children) + value;
+ measure.setVariation(period.getIndex(), sum);
+ }
+ context.saveMeasure(measure);
+ }
+
+ private long calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
+ long result = 0;
+ for (Issue issue : issues) {
+ Long debt = issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, periodDate);
+ if (debt != null) {
+ result += debt;
+ }
+ }
+ return result;
+ }
+
+ private boolean shouldSaveNewMetrics(DecoratorContext context) {
+ return context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT) == null;
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.batch.issue.ignore;
-
-import com.google.common.collect.ImmutableList;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.PropertyType;
-import org.sonar.api.config.PropertyDefinition;
-import org.sonar.api.config.PropertyFieldDefinition;
-import org.sonar.api.resources.Qualifiers;
-
-import java.util.List;
-
-public final class IssueExclusionsConfiguration {
-
- public static final String SUB_CATEGORY_IGNORE_ISSUES = "issues";
-
- public static final String EXCLUSION_KEY_PREFIX = "sonar.issue.ignore";
- public static final String INCLUSION_KEY_PREFIX = "sonar.issue.enforce";
-
- public static final String MULTICRITERIA_SUFFIX = ".multicriteria";
- public static final String PATTERNS_MULTICRITERIA_EXCLUSION_KEY = EXCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
- public static final String PATTERNS_MULTICRITERIA_INCLUSION_KEY = INCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
- public static final String RESOURCE_KEY = "resourceKey";
- private static final String PROPERTY_FILE_PATH_PATTERN = "File Path Pattern";
- public static final String RULE_KEY = "ruleKey";
- private static final String PROPERTY_RULE_KEY_PATTERN = "Rule Key Pattern";
- private static final String PROPERTY_RULE_KEY_PATTERN_HELP = "<br/>A rule key pattern consists of the rule repository name, followed by a colon, followed by a rule key "
- + "or rule name fragment. For example:"
- + "<ul><li>squid:S1195</li><li>squid:*Naming*</li></ul>";
-
- public static final String BLOCK_SUFFIX = ".block";
- public static final String PATTERNS_BLOCK_KEY = EXCLUSION_KEY_PREFIX + BLOCK_SUFFIX;
- public static final String BEGIN_BLOCK_REGEXP = "beginBlockRegexp";
- public static final String END_BLOCK_REGEXP = "endBlockRegexp";
-
- public static final String ALLFILE_SUFFIX = ".allfile";
- public static final String PATTERNS_ALLFILE_KEY = EXCLUSION_KEY_PREFIX + ALLFILE_SUFFIX;
- public static final String FILE_REGEXP = "fileRegexp";
-
- static final int LARGE_SIZE = 40;
- static final int SMALL_SIZE = 10;
-
- private IssueExclusionsConfiguration() {
- // static configuration declaration only
- }
-
- public static List<PropertyDefinition> getPropertyDefinitions() {
- return ImmutableList.of(
- PropertyDefinition.builder(PATTERNS_MULTICRITERIA_EXCLUSION_KEY)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
- .name("Ignore Issues on Multiple Criteria")
- .description("Patterns to ignore issues on certain components and for certain coding rules." + PROPERTY_RULE_KEY_PATTERN_HELP)
- .onQualifiers(Qualifiers.PROJECT)
- .index(3)
- .fields(
- PropertyFieldDefinition.build(RULE_KEY)
- .name(PROPERTY_RULE_KEY_PATTERN)
- .description("Pattern to match rules which should be ignored.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build(),
- PropertyFieldDefinition.build(RESOURCE_KEY)
- .name(PROPERTY_FILE_PATH_PATTERN)
- .description("Pattern to match files which should be ignored.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build())
- .build(),
- PropertyDefinition.builder(PATTERNS_BLOCK_KEY)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
- .name("Ignore Issues in Blocks")
- .description("Patterns to ignore all issues on specific blocks of code, while continuing to scan and mark issues on the remainder of the file.")
- .onQualifiers(Qualifiers.PROJECT)
- .index(2)
- .fields(
- PropertyFieldDefinition.build(BEGIN_BLOCK_REGEXP)
- .name("Regular Expression for Start of Block")
- .description("If this regular expression is found in a file, then following lines are ignored until end of block.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build(),
- PropertyFieldDefinition.build(END_BLOCK_REGEXP)
- .name("Regular Expression for End of Block")
- .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build())
- .build(),
- PropertyDefinition.builder(PATTERNS_ALLFILE_KEY)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
- .name("Ignore Issues on Files")
- .description("Patterns to ignore all issues on files that contain a block of code matching a given regular expression.")
- .onQualifiers(Qualifiers.PROJECT)
- .index(1)
- .fields(
- PropertyFieldDefinition.build(FILE_REGEXP)
- .name("Regular Expression")
- .description("If this regular expression is found in a file, then the whole file is ignored.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build())
- .build(),
- PropertyDefinition.builder(PATTERNS_MULTICRITERIA_INCLUSION_KEY)
- .category(CoreProperties.CATEGORY_EXCLUSIONS)
- .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
- .name("Restrict Scope of Coding Rules")
- .description("Patterns to restrict the application of a rule to only certain components, ignoring all others." + PROPERTY_RULE_KEY_PATTERN_HELP)
- .onQualifiers(Qualifiers.PROJECT)
- .index(4)
- .fields(
- PropertyFieldDefinition.build(RULE_KEY)
- .name(PROPERTY_RULE_KEY_PATTERN)
- .description("Pattern used to match rules which should be restricted.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build(),
- PropertyFieldDefinition.build(RESOURCE_KEY)
- .name(PROPERTY_FILE_PATH_PATTERN)
- .description("Pattern used to match files to which the rules should be restricted.")
- .type(PropertyType.STRING)
- .indicativeSize(LARGE_SIZE)
- .build())
- .build());
- }
-}
*/
package org.sonar.batch.issue.ignore.pattern;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
String patternConf = StringUtils.defaultIfBlank(settings.getString(getMulticriteriaConfigurationKey()), "");
for (String id : StringUtils.split(patternConf, ',')) {
String propPrefix = getMulticriteriaConfigurationKey() + "." + id + ".";
- String resourceKeyPattern = settings.getString(propPrefix + IssueExclusionsConfiguration.RESOURCE_KEY);
- String ruleKeyPattern = settings.getString(propPrefix + IssueExclusionsConfiguration.RULE_KEY);
+ String resourceKeyPattern = settings.getString(propPrefix + "resourceKey");
+ String ruleKeyPattern = settings.getString(propPrefix + "ruleKey");
String lineRange = "*";
String[] fields = new String[] { resourceKeyPattern, ruleKeyPattern, lineRange };
PatternDecoder.checkRegularLineConstraints(StringUtils.join(fields, ","), fields);
package org.sonar.batch.issue.ignore.pattern;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.config.Settings;
+import org.sonar.core.config.IssueExclusionProperties;
import java.util.List;
@Override
protected String getMulticriteriaConfigurationKey() {
- return IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY;
+ return "sonar.issue.ignore" + ".multicriteria";
}
public PatternMatcher getPatternMatcher() {
@Override
public void initializePatternsForPath(String relativePath, String componentKey) {
- for (IssuePattern pattern: getMulticriteriaPatterns()) {
+ for (IssuePattern pattern : getMulticriteriaPatterns()) {
if (pattern.matchResource(relativePath)) {
getPatternMatcher().addPatternForComponent(componentKey, pattern);
}
protected final void loadFileContentPatterns() {
// Patterns Block
blockPatterns = Lists.newArrayList();
- String patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY), "");
+ String patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionProperties.PATTERNS_BLOCK_KEY), "");
for (String id : StringUtils.split(patternConf, ',')) {
- String propPrefix = IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + "." + id + ".";
- String beginBlockRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP);
- String endBlockRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.END_BLOCK_REGEXP);
- String[] fields = new String[] { beginBlockRegexp, endBlockRegexp };
+ String propPrefix = IssueExclusionProperties.PATTERNS_BLOCK_KEY + "." + id + ".";
+ String beginBlockRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.BEGIN_BLOCK_REGEXP);
+ String endBlockRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.END_BLOCK_REGEXP);
+ String[] fields = new String[]{beginBlockRegexp, endBlockRegexp};
PatternDecoder.checkDoubleRegexpLineConstraints(StringUtils.join(fields, ","), fields);
IssuePattern pattern = new IssuePattern().setBeginBlockRegexp(nullToEmpty(beginBlockRegexp)).setEndBlockRegexp(nullToEmpty(endBlockRegexp));
blockPatterns.add(pattern);
// Patterns All File
allFilePatterns = Lists.newArrayList();
- patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY), "");
+ patternConf = StringUtils.defaultIfBlank(getSettings().getString(IssueExclusionProperties.PATTERNS_ALLFILE_KEY), "");
for (String id : StringUtils.split(patternConf, ',')) {
- String propPrefix = IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + "." + id + ".";
- String allFileRegexp = getSettings().getString(propPrefix + IssueExclusionsConfiguration.FILE_REGEXP);
+ String propPrefix = IssueExclusionProperties.PATTERNS_ALLFILE_KEY + "." + id + ".";
+ String allFileRegexp = getSettings().getString(propPrefix + IssueExclusionProperties.FILE_REGEXP);
PatternDecoder.checkWholeFileRegexp(allFileRegexp);
IssuePattern pattern = new IssuePattern().setAllFileRegexp(nullToEmpty(allFileRegexp));
allFilePatterns.add(pattern);
}
public boolean hasFileContentPattern() {
- return ! (blockPatterns.isEmpty() && allFilePatterns.isEmpty());
+ return !(blockPatterns.isEmpty() && allFilePatterns.isEmpty());
}
}
package org.sonar.batch.issue.ignore.pattern;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
import com.google.common.collect.Maps;
import org.sonar.api.config.Settings;
@Override
protected String getMulticriteriaConfigurationKey() {
- return IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY;
+ return "sonar.issue.enforce" + ".multicriteria";
}
@Override
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.execution.ReactorManager;
+import org.apache.maven.lifecycle.LifecycleExecutor;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.maven.MavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.TimeProfiler;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.maven.MavenPluginExecutor;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+@SupportedEnvironment("maven")
+public class DefaultMavenPluginExecutor implements MavenPluginExecutor {
+
+ private final MavenProjectConverter pomConverter;
+ private LifecycleExecutor lifecycleExecutor;
+ private MavenSession mavenSession;
+
+ public DefaultMavenPluginExecutor(LifecycleExecutor le, MavenSession mavenSession, MavenProjectConverter pomConverter) {
+ this.lifecycleExecutor = le;
+ this.mavenSession = mavenSession;
+ this.pomConverter = pomConverter;
+ }
+
+ @Override
+ public final MavenPluginHandler execute(Project project, DefaultModuleFileSystem fs, MavenPluginHandler handler) {
+ for (String goal : handler.getGoals()) {
+ if (goal == null) {
+ throw new IllegalStateException("Maven goal can't be null");
+ }
+ MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId());
+ execute(project,
+ fs,
+ getGoal(handler.getGroupId(), handler.getArtifactId(), plugin != null && plugin.getPlugin() != null ? plugin.getPlugin().getVersion() : null, goal));
+ }
+ return handler;
+ }
+
+ @Override
+ public final void execute(Project project, DefaultModuleFileSystem fs, String goal) {
+ if (project.getPom() != null) {
+ TimeProfiler profiler = new TimeProfiler().start("Execute " + goal);
+ ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ concreteExecute(project.getPom(), goal);
+ } catch (Exception e) {
+ throw new SonarException("Unable to execute maven plugin", e);
+ } finally {
+ // Reset original ClassLoader that may have been changed during Maven Execution (see SONAR-1800)
+ Thread.currentThread().setContextClassLoader(currentClassLoader);
+ profiler.stop();
+ }
+ if (!fs.isInitialized()) {
+ pomConverter.synchronizeFileSystem(project.getPom(), fs);
+ }
+ }
+ }
+
+ static String getGoal(String groupId, String artifactId, @Nullable String version, String goal) {
+ String defaultVersion = version == null ? "" : version;
+ return new StringBuilder()
+ .append(groupId).append(":")
+ .append(artifactId).append(":")
+ .append(defaultVersion)
+ .append(":")
+ .append(goal)
+ .toString();
+ }
+
+ public void concreteExecute(MavenProject pom, String goal) {
+ Method executeMethod = null;
+ for (Method m : lifecycleExecutor.getClass().getMethods()) {
+ if ("execute".equals(m.getName())) {
+ executeMethod = m;
+ break;
+ }
+ }
+ if (executeMethod == null) {
+ throw new SonarException("Unable to find execute method on Maven LifecycleExecutor. Please check your Maven version.");
+ }
+ if (executeMethod.getParameterTypes().length == 1) {
+ concreteExecuteMaven3(pom, goal);
+ } else if (executeMethod.getParameterTypes().length == 3) {
+ concreteExecuteMaven2(executeMethod, pom, goal);
+ } else {
+ throw new SonarException("Unexpected parameter count on Maven LifecycleExecutor#execute method. Please check your Maven version.");
+ }
+ }
+
+ public void concreteExecuteMaven3(MavenProject pom, String goal) {
+ MavenSession projectSession = mavenSession.clone();
+ projectSession.setCurrentProject(pom);
+ projectSession.setProjects(Arrays.asList(pom));
+ projectSession.getRequest().setRecursive(false);
+ projectSession.getRequest().setPom(pom.getFile());
+ projectSession.getRequest().setGoals(Arrays.asList(goal));
+ projectSession.getRequest().setInteractiveMode(false);
+ lifecycleExecutor.execute(projectSession);
+ if (projectSession.getResult().hasExceptions()) {
+ throw new SonarException("Exception during execution of " + goal);
+ }
+ }
+
+ public void concreteExecuteMaven2(Method executeMethod, MavenProject pom, String goal) {
+ try {
+ ReactorManager reactor = new ReactorManager(Arrays.asList(pom));
+ MavenSession clonedSession = new MavenSession(mavenSession.getContainer(),
+ mavenSession.getSettings(),
+ mavenSession.getLocalRepository(),
+ mavenSession.getEventDispatcher(),
+ reactor,
+ Arrays.asList(goal),
+ mavenSession.getExecutionRootDirectory(),
+ mavenSession.getExecutionProperties(),
+ mavenSession.getStartTime());
+ executeMethod.invoke(lifecycleExecutor, clonedSession, reactor, clonedSession.getEventDispatcher());
+ } catch (Exception e) {
+ throw new SonarException("Unable to execute Maven 2 plugin", e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.List;
+
+/**
+ * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
+ */
+@Deprecated
+@SupportedEnvironment("maven")
+public class MavenProjectBootstrapper implements ProjectBootstrapper {
+
+ private MavenSession session;
+ private MavenProjectConverter mavenProjectConverter;
+
+ public MavenProjectBootstrapper(MavenSession session, MavenProjectConverter mavenProjectConverter) {
+ this.session = session;
+ this.mavenProjectConverter = mavenProjectConverter;
+ }
+
+ @Override
+ public ProjectReactor bootstrap() {
+ // Don't use session.getTopLevelProject or session.getProjects to keep compatibility with Maven 2
+ List<MavenProject> sortedProjects = session.getSortedProjects();
+ MavenProject topLevelProject = null;
+ for (MavenProject project : sortedProjects) {
+ if (project.isExecutionRoot()) {
+ topLevelProject = project;
+ break;
+ }
+ }
+ if (topLevelProject == null) {
+ throw new IllegalStateException("Maven session does not declare a top level project");
+ }
+ return new ProjectReactor(mavenProjectConverter.configure(sortedProjects, topLevelProject));
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectBuilder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.List;
+
+/**
+ * Class that inject MavenProject in each module container
+ */
+@SupportedEnvironment("maven")
+public class MavenProjectBuilder extends ProjectBuilder {
+
+ private final MavenSession mavenSession;
+
+ public MavenProjectBuilder(MavenSession mavenSession) {
+ this.mavenSession = mavenSession;
+ }
+
+ @Override
+ public void build(Context context) {
+ ProjectReactor reactor = context.projectReactor();
+ for (ProjectDefinition moduleDef : reactor.getProjects()) {
+ setMavenProjectIfApplicable(moduleDef);
+ }
+ }
+
+ private void setMavenProjectIfApplicable(ProjectDefinition definition) {
+ if (mavenSession != null) {
+ String moduleKey = definition.getKey();
+ for (MavenProject mavenModule : (List<MavenProject>) mavenSession.getSortedProjects()) {
+ String mavenModuleKey = mavenModule.getGroupId() + ":" + mavenModule.getArtifactId();
+ if (mavenModuleKey.equals(moduleKey) && !definition.getContainerExtensions().contains(mavenModule)) {
+ definition.addContainerExtension(mavenModule);
+ }
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.model.CiManagement;
+import org.apache.maven.model.IssueManagement;
+import org.apache.maven.model.Scm;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.SupportedEnvironment;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.maven.MavenUtils;
+import org.sonar.api.task.TaskExtension;
+import org.sonar.api.utils.MessageException;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.java.api.JavaUtils;
+
+import javax.annotation.Nullable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @deprecated since 4.3 kept only to support old version of SonarQube Mojo
+ */
+@Deprecated
+@SupportedEnvironment("maven")
+public class MavenProjectConverter implements TaskExtension {
+
+ private static final String UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE = "Unable to determine structure of project." +
+ " Probably you use Maven Advanced Reactor Options, which is not supported by SonarQube and should not be used.";
+
+ public ProjectDefinition configure(List<MavenProject> poms, MavenProject root) {
+ // projects by canonical path to pom.xml
+ Map<String, MavenProject> paths = Maps.newHashMap();
+ Map<MavenProject, ProjectDefinition> defs = Maps.newHashMap();
+
+ try {
+ configureModules(poms, paths, defs);
+
+ rebuildModuleHierarchy(paths, defs);
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot configure project", e);
+ }
+
+ ProjectDefinition rootProject = defs.get(root);
+ if (rootProject == null) {
+ throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+ }
+ return rootProject;
+ }
+
+ private void rebuildModuleHierarchy(Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
+ for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
+ MavenProject pom = entry.getValue();
+ for (Object m : pom.getModules()) {
+ String moduleId = (String) m;
+ File modulePath = new File(pom.getBasedir(), moduleId);
+ MavenProject module = findMavenProject(modulePath, paths);
+
+ ProjectDefinition parentProject = defs.get(pom);
+ if (parentProject == null) {
+ throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+ }
+ ProjectDefinition subProject = defs.get(module);
+ if (subProject == null) {
+ throw new IllegalStateException(UNABLE_TO_DETERMINE_PROJECT_STRUCTURE_EXCEPTION_MESSAGE);
+ }
+ parentProject.addSubProject(subProject);
+ }
+ }
+ }
+
+ private void configureModules(List<MavenProject> poms, Map<String, MavenProject> paths, Map<MavenProject, ProjectDefinition> defs) throws IOException {
+ for (MavenProject pom : poms) {
+ paths.put(pom.getFile().getCanonicalPath(), pom);
+ ProjectDefinition def = ProjectDefinition.create();
+ merge(pom, def);
+ defs.put(pom, def);
+ }
+ }
+
+ private static MavenProject findMavenProject(final File modulePath, Map<String, MavenProject> paths) throws IOException {
+ if (modulePath.exists() && modulePath.isDirectory()) {
+ for (Map.Entry<String, MavenProject> entry : paths.entrySet()) {
+ String pomFileParentDir = new File(entry.getKey()).getParent();
+ if (pomFileParentDir.equals(modulePath.getCanonicalPath())) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+ return paths.get(modulePath.getCanonicalPath());
+ }
+
+ @VisibleForTesting
+ void merge(MavenProject pom, ProjectDefinition definition) {
+ String key = getSonarKey(pom);
+ // IMPORTANT NOTE : reference on properties from POM model must not be saved,
+ // instead they should be copied explicitly - see SONAR-2896
+ definition
+ .setProperties(pom.getModel().getProperties())
+ .setKey(key)
+ .setVersion(pom.getVersion())
+ .setName(pom.getName())
+ .setDescription(pom.getDescription())
+ .addContainerExtension(pom);
+ guessJavaVersion(pom, definition);
+ guessEncoding(pom, definition);
+ convertMavenLinksToProperties(definition, pom);
+ synchronizeFileSystem(pom, definition);
+ }
+
+ private static String getSonarKey(MavenProject pom) {
+ return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString();
+ }
+
+ private static void guessEncoding(MavenProject pom, ProjectDefinition definition) {
+ // See http://jira.codehaus.org/browse/SONAR-2151
+ String encoding = MavenUtils.getSourceEncoding(pom);
+ if (encoding != null) {
+ definition.setProperty(CoreProperties.ENCODING_PROPERTY, encoding);
+ }
+ }
+
+ private static void guessJavaVersion(MavenProject pom, ProjectDefinition definition) {
+ // See http://jira.codehaus.org/browse/SONAR-2148
+ // Get Java source and target versions from maven-compiler-plugin.
+ String version = MavenUtils.getJavaSourceVersion(pom);
+ if (version != null) {
+ definition.setProperty(JavaUtils.JAVA_SOURCE_PROPERTY, version);
+ }
+ version = MavenUtils.getJavaVersion(pom);
+ if (version != null) {
+ definition.setProperty(JavaUtils.JAVA_TARGET_PROPERTY, version);
+ }
+ }
+
+ /**
+ * For SONAR-3676
+ */
+ private static void convertMavenLinksToProperties(ProjectDefinition definition, MavenProject pom) {
+ setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_HOME_PAGE, pom.getUrl());
+
+ Scm scm = pom.getScm();
+ if (scm == null) {
+ scm = new Scm();
+ }
+ setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES, scm.getUrl());
+ setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_SOURCES_DEV, scm.getDeveloperConnection());
+
+ CiManagement ci = pom.getCiManagement();
+ if (ci == null) {
+ ci = new CiManagement();
+ }
+ setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_CI, ci.getUrl());
+
+ IssueManagement issues = pom.getIssueManagement();
+ if (issues == null) {
+ issues = new IssueManagement();
+ }
+ setPropertyIfNotAlreadyExists(definition, CoreProperties.LINKS_ISSUE_TRACKER, issues.getUrl());
+ }
+
+ private static void setPropertyIfNotAlreadyExists(ProjectDefinition definition, String propertyKey, String propertyValue) {
+ if (StringUtils.isBlank(definition.getProperties().getProperty(propertyKey))) {
+ definition.setProperty(propertyKey, StringUtils.defaultString(propertyValue));
+ }
+ }
+
+ public void synchronizeFileSystem(MavenProject pom, ProjectDefinition into) {
+ into.setBaseDir(pom.getBasedir());
+ File buildDir = getBuildDir(pom);
+ if (buildDir != null) {
+ into.setBuildDir(buildDir);
+ into.setWorkDir(getSonarWorkDir(pom));
+ }
+ into.setSourceDirs(toPaths(mainDirs(pom)));
+ into.setTestDirs(toPaths(testDirs(pom)));
+ File binaryDir = resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir());
+ if (binaryDir != null) {
+ into.addBinaryDir(binaryDir);
+ }
+ }
+
+ public static File getSonarWorkDir(MavenProject pom) {
+ return new File(getBuildDir(pom), "sonar");
+ }
+
+ private static File getBuildDir(MavenProject pom) {
+ return resolvePath(pom.getBuild().getDirectory(), pom.getBasedir());
+ }
+
+ public void synchronizeFileSystem(MavenProject pom, DefaultModuleFileSystem into) {
+ into.resetDirs(
+ pom.getBasedir(),
+ getBuildDir(pom),
+ mainDirs(pom),
+ testDirs(pom),
+ Arrays.asList(resolvePath(pom.getBuild().getOutputDirectory(), pom.getBasedir())));
+ }
+
+ static File resolvePath(@Nullable String path, File basedir) {
+ if (path != null) {
+ File file = new File(StringUtils.trim(path));
+ if (!file.isAbsolute()) {
+ try {
+ file = new File(basedir, path).getCanonicalFile();
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to resolve path '" + path + "'", e);
+ }
+ }
+ return file;
+ }
+ return null;
+ }
+
+ static List<File> resolvePaths(List<String> paths, File basedir) {
+ List<File> result = Lists.newArrayList();
+ for (String path : paths) {
+ File dir = resolvePath(path, basedir);
+ if (dir != null) {
+ result.add(dir);
+ }
+ }
+ return result;
+ }
+
+ private List<File> mainDirs(MavenProject pom) {
+ return sourceDirs(pom, ProjectDefinition.SOURCE_DIRS_PROPERTY, pom.getCompileSourceRoots());
+ }
+
+ private List<File> testDirs(MavenProject pom) {
+ return sourceDirs(pom, ProjectDefinition.TEST_DIRS_PROPERTY, pom.getTestCompileSourceRoots());
+ }
+
+ private List<File> sourceDirs(MavenProject pom, String propertyKey, List mavenDirs) {
+ List<String> paths;
+ String prop = pom.getProperties().getProperty(propertyKey);
+ if (prop != null) {
+ paths = Arrays.asList(StringUtils.split(prop, ","));
+ // do not remove dirs that do not exist. They must be kept in order to
+ // notify users that value of sonar.sources has a typo.
+ return existingDirsOrFail(resolvePaths(paths, pom.getBasedir()), pom, propertyKey);
+ }
+
+ List<File> dirs = resolvePaths(mavenDirs, pom.getBasedir());
+
+ // Maven provides some directories that do not exist. They
+ // should be removed
+ return keepExistingDirs(dirs);
+ }
+
+ private List<File> existingDirsOrFail(List<File> dirs, MavenProject pom, String propertyKey) {
+ for (File dir : dirs) {
+ if (!dir.isDirectory() || !dir.exists()) {
+ throw MessageException.of(String.format(
+ "The directory '%s' does not exist for Maven module %s. Please check the property %s",
+ dir.getAbsolutePath(), pom.getId(), propertyKey));
+ }
+ }
+ return dirs;
+ }
+
+ private static List<File> keepExistingDirs(List<File> files) {
+ return Lists.newArrayList(Collections2.filter(files, new Predicate<File>() {
+ @Override
+ public boolean apply(File dir) {
+ return dir != null && dir.exists() && dir.isDirectory();
+ }
+ }));
+ }
+
+ private static String[] toPaths(Collection<File> dirs) {
+ Collection<String> paths = Collections2.transform(dirs, new Function<File, String>() {
+ @Override
+ public String apply(File dir) {
+ return dir.getAbsolutePath();
+ }
+ });
+ return paths.toArray(new String[paths.size()]);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+/**
+ * This package is a part of bootstrap process, so we should take care about backward compatibility.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.maven;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
*/
package org.sonar.batch.scan;
+import org.sonar.api.BatchComponent;
import org.sonar.batch.qualitygate.GenerateQualityGateEvents;
import org.slf4j.Logger;
ExtensionInstaller installer = getComponentByType(ExtensionInstaller.class);
installer.install(this, new ExtensionMatcher() {
public boolean accept(Object extension) {
- if (ExtensionUtils.isType(extension, BatchExtension.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) {
+ if (ExtensionUtils.isType(extension, BatchComponent.class) && ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_PROJECT)) {
// Special use-case: the extension point ProjectBuilder is used in a Maven environment to define some
// new sub-projects without pom.
// Example : C# plugin adds sub-projects at runtime, even if they are not defined in root pom.
package org.sonar.batch.scan;
import com.google.common.annotations.VisibleForTesting;
+import org.sonar.api.BatchComponent;
import org.sonar.api.BatchExtension;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.InstantiationStrategy;
static class BatchExtensionFilter implements ExtensionMatcher {
public boolean accept(Object extension) {
- return ExtensionUtils.isType(extension, BatchExtension.class)
+ return ExtensionUtils.isType(extension, BatchComponent.class)
&& ExtensionUtils.isInstantiationStrategy(extension, InstantiationStrategy.PER_BATCH);
}
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.hamcrest.Description;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.rule.Rules;
+import org.sonar.api.batch.rule.internal.RulesBuilder;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.technicaldebt.batch.Characteristic;
+import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
+import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.Duration;
+
+import java.util.Collections;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DebtDecoratorTest {
+
+ static final int HOURS_IN_DAY = 8;
+
+ static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
+
+ @Mock
+ DecoratorContext context;
+
+ @Mock
+ Resource resource;
+
+ @Mock
+ TechnicalDebtModel debtModel;
+
+ @Mock
+ Issuable issuable;
+
+ @Mock
+ ResourcePerspectives perspectives;
+
+ @Mock
+ RuleFinder ruleFinder;
+
+ RuleKey ruleKey1 = RuleKey.of("repo1", "rule1");
+ RuleKey ruleKey2 = RuleKey.of("repo2", "rule2");
+ Rules rules;
+
+ DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY");
+ DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency);
+
+ DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY");
+ DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability);
+
+ DebtDecorator decorator;
+
+ @Before
+ public void before() throws Exception {
+ when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+ RulesBuilder rulesBuilder = new RulesBuilder();
+ rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY");
+ rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY");
+ rules = rulesBuilder.build();
+
+ when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()));
+ when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()));
+
+ when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity));
+ when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency);
+ when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency);
+ when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability);
+ when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity);
+
+ decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder);
+ }
+
+ @Test
+ public void generates_metrics() throws Exception {
+ assertThat(decorator.generatesMetrics()).hasSize(1);
+ }
+
+ @Test
+ public void execute_on_project() throws Exception {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+ }
+
+ @Test
+ public void not_save_if_measure_already_computed() {
+ when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure());
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT)));
+ }
+
+ @Test
+ public void add_technical_debt_from_one_issue_and_no_parent() throws Exception {
+ Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
+ }
+
+ @Test
+ public void add_technical_debt_from_one_issue_without_debt() throws Exception {
+ Issue issue = createIssue("rule1", "repo1").setDebt(null);
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0);
+ }
+
+ @Test
+ public void add_technical_debt_from_one_issue_and_propagate_to_parents() throws Exception {
+ Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue());
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue())));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue())));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue())));
+ }
+
+ @Test
+ public void add_technical_debt_from_issues() throws Exception {
+ Long technicalDebt1 = ONE_DAY_IN_MINUTES;
+ Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES;
+
+ Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
+ Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1));
+ Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
+ Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2));
+ when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES);
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void add_technical_debt_from_current_and_children_measures() throws Exception {
+ Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+ Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES));
+ when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
+
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
+ new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+ org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null)
+ .setValue(5d * ONE_DAY_IN_MINUTES)
+ ));
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void add_technical_debt_only_from_children_measures() throws Exception {
+ when(issuable.issues()).thenReturn(Collections.<Issue>emptyList());
+
+ when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.<Measure>newArrayList(
+ new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+ org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())
+ , null, null).setValue(5d * ONE_DAY_IN_MINUTES),
+
+ new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
+ org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())
+ , null, null).setValue(10d * ONE_DAY_IN_MINUTES)
+ ));
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES);
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES)));
+ verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void always_save_technical_debt_for_positive_values() throws Exception {
+ // for a project
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(new Project("foo"));
+ decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
+ verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+
+ // or for a file
+ context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(new File("foo"));
+ decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false);
+ verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+ }
+
+ @Test
+ public void always_save_technical_debt_for_project_if_top_characteristic() throws Exception {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(new Project("foo"));
+
+ // this is a top characteristic
+ DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root");
+
+ decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true);
+ verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic));
+ }
+
+ /**
+ * SQALE-147
+ */
+ @Test
+ public void never_save_technical_debt_for_project_if_not_top_characteristic() throws Exception {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(new Project("foo"));
+
+ DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY");
+ DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic);
+
+ decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true);
+ verify(context, never()).saveMeasure(any(Measure.class));
+ }
+
+ @Test
+ public void not_save_technical_debt_for_file_if_zero() throws Exception {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(new File("foo"));
+
+ decorator.saveCharacteristicMeasure(context, null, 0.0, true);
+ verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT));
+ }
+
+ private DefaultIssue createIssue(String ruleKey, String repositoryKey) {
+ return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey));
+ }
+
+ class IsCharacteristicMeasure extends ArgumentMatcher<Measure> {
+ Metric metric = null;
+ Characteristic characteristic = null;
+ Double value = null;
+
+ public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) {
+ this.metric = metric;
+ this.characteristic = characteristic;
+ this.value = value;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (!(o instanceof Measure)) {
+ return false;
+ }
+ Measure m = (Measure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(characteristic, m.getCharacteristic()) &&
+ ObjectUtils.equals(value, m.getValue());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(new StringBuilder()
+ .append("value=").append(value).append(",")
+ .append("characteristic=").append(characteristic.key()).append(",")
+ .append("metric=").append(metric.getKey()).toString())
+ ;
+ }
+ }
+
+ class IsRuleMeasure extends ArgumentMatcher<RuleMeasure> {
+ Metric metric = null;
+ RuleKey ruleKey = null;
+ Double value = null;
+
+ public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) {
+ this.metric = metric;
+ this.ruleKey = ruleKey;
+ this.value = value;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (!(o instanceof RuleMeasure)) {
+ return false;
+ }
+ RuleMeasure m = (RuleMeasure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
+ ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
+ ObjectUtils.equals(value, m.getValue());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE));
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.debt;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.component.ResourcePerspectives;
+import org.sonar.api.config.Settings;
+import org.sonar.api.issue.Issuable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.utils.Duration;
+import org.sonar.batch.components.Period;
+import org.sonar.batch.components.TimeMachineConfiguration;
+import org.sonar.batch.debt.IssueChangelogDebtCalculator;
+import org.sonar.batch.debt.NewDebtDecorator;
+
+import java.util.Date;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class NewDebtDecoratorTest {
+
+ NewDebtDecorator decorator;
+
+ @Mock
+ TimeMachineConfiguration timeMachineConfiguration;
+
+ @Mock
+ Resource resource;
+
+ @Mock
+ Issuable issuable;
+
+ @Mock
+ DecoratorContext context;
+
+ Date rightNow;
+ Date elevenDaysAgo;
+ Date tenDaysAgo;
+ Date nineDaysAgo;
+ Date fiveDaysAgo;
+ Date fourDaysAgo;
+
+ static final int HOURS_IN_DAY = 8;
+
+ static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
+ static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60;
+ static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60;
+
+ @Before
+ public void setup() {
+ Settings settings = new Settings();
+ settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
+
+ ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
+ when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
+
+ rightNow = new Date();
+ elevenDaysAgo = DateUtils.addDays(rightNow, -11);
+ tenDaysAgo = DateUtils.addDays(rightNow, -10);
+ nineDaysAgo = DateUtils.addDays(rightNow, -9);
+ fiveDaysAgo = DateUtils.addDays(rightNow, -5);
+ fourDaysAgo = DateUtils.addDays(rightNow, -4);
+
+ when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
+
+ decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator());
+ }
+
+ @Test
+ public void generates_metrics() throws Exception {
+ assertThat(decorator.generatesMetrics()).hasSize(1);
+ }
+
+ @Test
+ public void execute_on_project() throws Exception {
+ assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
+ }
+
+ @Test
+ public void save_on_one_issue_with_one_new_changelog() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ // changelog created at is null because it has just been created on the current analysis
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_with_changelog() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_with_changelog_only_in_the_past() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
+ newArrayList(
+ // Change before all periods
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+ }
+
+ @Test
+ public void save_on_one_issue_with_changelog_having_null_value() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
+ when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
+
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs()
+ .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
+ .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_issues_with_changelog() {
+ Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+ Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_without_changelog() {
+ when(issuable.issues()).thenReturn(newArrayList(
+ (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
+ );
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_one_issue_without_technical_debt_and_without_changelog() {
+ when(issuable.issues()).thenReturn(newArrayList(
+ (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null))
+ );
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+ }
+
+ @Test
+ public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
+ when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
+
+ when(issuable.issues()).thenReturn(newArrayList(
+ (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
+ );
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is null, period2 is null
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_issues_without_changelog() {
+ when(issuable.issues()).thenReturn(newArrayList(
+ (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
+ new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
+ ));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void save_on_issues_with_changelog_and_issues_without_changelog() {
+ // issue1 and issue2 have changelog
+ Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+ Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
+ )
+ );
+
+ // issue3 and issue4 have no changelog
+ Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES));
+ Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES));
+ when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES)));
+ }
+
+ @Test
+ public void not_save_if_measure_already_computed() {
+ when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
+ when(issuable.issues()).thenReturn(newArrayList(
+ (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
+ new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
+ ));
+
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
+ }
+
+ /**
+ * SONAR-5059
+ */
+ @Test
+ public void not_return_negative_debt() {
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
+ newArrayList(
+ // changelog created at is null because it has just been created on the current analysis
+ new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null)
+ )
+ );
+ when(issuable.issues()).thenReturn(newArrayList(issue));
+
+ decorator.decorate(resource, context);
+
+ // remember : period1 is 5daysAgo, period2 is 10daysAgo
+ verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
+ }
+
+
+ class IsVariationMeasure extends ArgumentMatcher<Measure> {
+ Metric metric = null;
+ Double var1 = null;
+ Double var2 = null;
+
+ public IsVariationMeasure(Metric metric, Double var1, Double var2) {
+ this.metric = metric;
+ this.var1 = var1;
+ this.var2 = var2;
+ }
+
+ public boolean matches(Object o) {
+ if (!(o instanceof Measure)) {
+ return false;
+ }
+ Measure m = (Measure) o;
+ return ObjectUtils.equals(metric, m.getMetric()) &&
+ ObjectUtils.equals(var1, m.getVariation1()) &&
+ ObjectUtils.equals(var2, m.getVariation2());
+ }
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.batch.issue.ignore;
-
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-
-import org.junit.Test;
-import static org.fest.assertions.Assertions.assertThat;
-
-public class IssueExclusionsConfigurationTest {
- @Test
- public void justForCoverage() {
- assertThat(IssueExclusionsConfiguration.getPropertyDefinitions()).hasSize(4);
- }
-}
package org.sonar.batch.issue.ignore.pattern;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.SonarException;
+import org.sonar.core.config.IssueExclusionProperties;
+
import static org.fest.assertions.Assertions.assertThat;
public class IssueExclusionPatternInitializerTest {
@Before
public void init() {
- settings = new Settings(new PropertyDefinitions(IssueExclusionsConfiguration.getPropertyDefinitions()));
+ settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
patternsInitializer = new IssueExclusionPatternInitializer(settings);
}
@Test
public void shouldHavePatternsBasedOnMulticriteriaPattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1,2");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Bar.java");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Hello.java");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RULE_KEY, "checkstyle:MagicNumber");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1,2");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
patternsInitializer.initPatterns();
assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@Test(expected = SonarException.class)
public void shouldLogInvalidResourceKey() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "*");
patternsInitializer.initPatterns();
}
@Test(expected = SonarException.class)
public void shouldLogInvalidRuleKey() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY, "1");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "*");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_EXCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria", "1");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "resourceKey", "*");
+ settings.setProperty("sonar.issue.ignore" + ".multicriteria" + ".1." + "ruleKey", "");
patternsInitializer.initPatterns();
}
@Test
public void shouldReturnBlockPattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY, "1,2,3");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// SONAR-ON");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// FOO-ON");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1,2,3");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".2." + IssueExclusionProperties.END_BLOCK_REGEXP, "// FOO-ON");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "// IGNORE-TO-EOF");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".3." + IssueExclusionProperties.END_BLOCK_REGEXP, "");
patternsInitializer.loadFileContentPatterns();
assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@Test(expected = SonarException.class)
public void shouldLogInvalidStartBlockPattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY, "1");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.BEGIN_BLOCK_REGEXP, "");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionsConfiguration.END_BLOCK_REGEXP, "// SONAR-ON");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY, "1");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.BEGIN_BLOCK_REGEXP, "");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_BLOCK_KEY + ".1." + IssueExclusionProperties.END_BLOCK_REGEXP, "// SONAR-ON");
patternsInitializer.loadFileContentPatterns();
}
@Test
public void shouldReturnAllFilePattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY, "1,2");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionsConfiguration.FILE_REGEXP, "@SONAR-IGNORE-ALL");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionsConfiguration.FILE_REGEXP, "//FOO-IGNORE-ALL");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1,2");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "@SONAR-IGNORE-ALL");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".2." + IssueExclusionProperties.FILE_REGEXP, "//FOO-IGNORE-ALL");
patternsInitializer.loadFileContentPatterns();
assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
@Test(expected = SonarException.class)
public void shouldLogInvalidAllFilePattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY, "1");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionsConfiguration.FILE_REGEXP, "");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY, "1");
+ settings.setProperty(IssueExclusionProperties.PATTERNS_ALLFILE_KEY + ".1." + IssueExclusionProperties.FILE_REGEXP, "");
patternsInitializer.loadFileContentPatterns();
}
}
package org.sonar.batch.issue.ignore.pattern;
-import org.sonar.batch.issue.ignore.IssueExclusionsConfiguration;
-import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
-
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.Settings;
+import org.sonar.core.config.IssueExclusionProperties;
+
import static org.fest.assertions.Assertions.assertThat;
public class IssueInclusionPatternInitializerTest {
@Before
public void init() {
- settings = new Settings(new PropertyDefinitions(IssueExclusionsConfiguration.getPropertyDefinitions()));
+ settings = new Settings(new PropertyDefinitions(IssueExclusionProperties.all()));
patternsInitializer = new IssueInclusionPatternInitializer(settings);
}
@Test
public void shouldHavePatternsBasedOnMulticriteriaPattern() {
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY, "1,2");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Bar.java");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".1." + IssueExclusionsConfiguration.RULE_KEY, "*");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RESOURCE_KEY, "org/foo/Hello.java");
- settings.setProperty(IssueExclusionsConfiguration.PATTERNS_MULTICRITERIA_INCLUSION_KEY + ".2." + IssueExclusionsConfiguration.RULE_KEY, "checkstyle:MagicNumber");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria", "1,2");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "resourceKey", "org/foo/Bar.java");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".1." + "ruleKey", "*");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "resourceKey", "org/foo/Hello.java");
+ settings.setProperty("sonar.issue.enforce" + ".multicriteria" + ".2." + "ruleKey", "checkstyle:MagicNumber");
patternsInitializer.initPatterns();
assertThat(patternsInitializer.hasConfiguredPatterns()).isTrue();
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+import org.sonar.api.batch.maven.MavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+
+import java.io.File;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+public class DefaultMavenPluginExecutorTest {
+
+ @Test
+ public void plugin_version_should_be_optional() {
+ assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", null, "goal"), is("group:artifact::goal"));
+ }
+
+ @Test
+ public void test_plugin_version() {
+ assertThat(DefaultMavenPluginExecutor.getGoal("group", "artifact", "3.54", "goal"), is("group:artifact:3.54:goal"));
+ }
+
+ @Test
+ public void should_ignore_non_maven_projects() {
+ DefaultMavenPluginExecutor executor = new DefaultMavenPluginExecutor(null, null, mock(MavenProjectConverter.class)) {
+ @Override
+ public void concreteExecute(MavenProject pom, String goal) {
+ pom.addCompileSourceRoot("src/java");
+ }
+ };
+ Project foo = new Project("foo");
+ DefaultModuleFileSystem fs = mock(DefaultModuleFileSystem.class);
+ executor.execute(foo, fs, new AddSourceMavenPluginHandler());
+
+ verify(fs, never()).resetDirs(any(File.class), any(File.class), anyList(), anyList(), anyList());
+ }
+
+ static class AddSourceMavenPluginHandler implements MavenPluginHandler {
+ public String getGroupId() {
+ return "fake";
+ }
+
+ public String getArtifactId() {
+ return "fake";
+ }
+
+ public String getVersion() {
+ return "2.2";
+ }
+
+ public boolean isFixedVersion() {
+ return false;
+ }
+
+ public String[] getGoals() {
+ return new String[]{"fake"};
+ }
+
+ public void configure(Project project, MavenPlugin plugin) {
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.batch.bootstrap.ProjectReactor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MavenProjectBootstrapperTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void bootstrap() throws Exception {
+ ProjectDefinition def = mock(ProjectDefinition.class);
+ MavenSession session = mock(MavenSession.class);
+ MavenProject rootProject = new MavenProject();
+ rootProject.setExecutionRoot(true);
+ List<MavenProject> projects = Arrays.asList(rootProject);
+ when(session.getSortedProjects()).thenReturn(projects);
+
+ MavenProjectConverter pomConverter = mock(MavenProjectConverter.class);
+ when(pomConverter.configure(projects, rootProject)).thenReturn(def);
+ MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
+
+ ProjectReactor reactor = bootstrapper.bootstrap();
+
+ assertThat(reactor).isNotNull();
+ verify(pomConverter).configure(projects, rootProject);
+ }
+
+ @Test
+ public void should_fail_if_no_top_level_project() throws Exception {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Maven session does not declare a top level project");
+
+ MavenSession session = mock(MavenSession.class);
+ MavenProjectConverter pomConverter = new MavenProjectConverter();
+ MavenProjectBootstrapper bootstrapper = new MavenProjectBootstrapper(session, pomConverter);
+
+ bootstrapper.bootstrap();
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.hamcrest.core.Is;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.test.TestUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Properties;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+public class MavenProjectConverterTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ MavenProjectConverter converter = new MavenProjectConverter();
+
+ /**
+ * See SONAR-2681
+ */
+ @Test
+ public void shouldThrowExceptionWhenUnableToDetermineProjectStructure() {
+ MavenProject root = new MavenProject();
+ root.setFile(new File("/foo/pom.xml"));
+ root.getBuild().setDirectory("target");
+ root.getModules().add("module/pom.xml");
+
+ try {
+ converter.configure(Arrays.asList(root), root);
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e.getMessage(), containsString("Advanced Reactor Options"));
+ }
+ }
+
+ @Test
+ public void shouldConvertModules() throws IOException {
+ File basedir = temp.newFolder();
+
+ MavenProject root = newMavenProject("com.foo", "parent", "1.0-SNAPSHOT");
+ root.setFile(new File(basedir, "pom.xml"));
+ root.getBuild().setDirectory("target");
+ root.getBuild().setOutputDirectory("target/classes");
+ root.getModules().add("module/pom.xml");
+ MavenProject module = newMavenProject("com.foo", "moduleA", "1.0-SNAPSHOT");
+ module.setFile(new File(basedir, "module/pom.xml"));
+ module.getBuild().setDirectory("target");
+ module.getBuild().setOutputDirectory("target/classes");
+ ProjectDefinition project = converter.configure(Arrays.asList(root, module), root);
+
+ assertThat(project.getSubProjects().size(), is(1));
+ }
+
+ private MavenProject newMavenProject(String groupId, String artifactId, String version) {
+ Model model = new Model();
+ model.setGroupId(groupId);
+ model.setArtifactId(artifactId);
+ model.setVersion(version);
+ return new MavenProject(model);
+ }
+
+ @Test
+ public void shouldConvertProperties() {
+ MavenProject pom = new MavenProject();
+ pom.setGroupId("foo");
+ pom.setArtifactId("bar");
+ pom.setVersion("1.0.1");
+ pom.setName("Test");
+ pom.setDescription("just test");
+ pom.setFile(new File("/foo/pom.xml"));
+ pom.getBuild().setDirectory("target");
+ ProjectDefinition project = ProjectDefinition.create();
+ converter.merge(pom, project);
+
+ Properties properties = project.getProperties();
+ assertThat(properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY), is("foo:bar"));
+ assertThat(properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY), is("1.0.1"));
+ assertThat(properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY), is("Test"));
+ assertThat(properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY), is("just test"));
+ }
+
+ @Test
+ public void moduleNameShouldEqualArtifactId() throws Exception {
+ File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/");
+ MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/pom.xml", true);
+ MavenProject module1 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module1/pom.xml", false);
+ MavenProject module2 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameShouldEqualArtifactId/module2/pom.xml", false);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
+
+ assertThat(rootDef.getSubProjects().size(), Is.is(2));
+ assertThat(rootDef.getKey(), Is.is("org.test:parent"));
+ assertNull(rootDef.getParent());
+ assertThat(rootDef.getBaseDir(), is(rootDir));
+
+ ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+ assertThat(module1Def.getKey(), Is.is("org.test:module1"));
+ assertThat(module1Def.getParent(), Is.is(rootDef));
+ assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module1")));
+ assertThat(module1Def.getSubProjects().size(), Is.is(0));
+ }
+
+ @Test
+ public void moduleNameDifferentThanArtifactId() throws Exception {
+ File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/");
+ MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/pom.xml", true);
+ MavenProject module1 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path1/pom.xml", false);
+ MavenProject module2 = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/moduleNameDifferentThanArtifactId/path2/pom.xml", false);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module1, module2), parent);
+
+ assertThat(rootDef.getSubProjects().size(), is(2));
+ assertThat(rootDef.getKey(), is("org.test:parent"));
+ assertNull(rootDef.getParent());
+ assertThat(rootDef.getBaseDir(), is(rootDir));
+
+ ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+ assertThat(module1Def.getKey(), Is.is("org.test:module1"));
+ assertThat(module1Def.getParent(), Is.is(rootDef));
+ assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "path1")));
+ assertThat(module1Def.getSubProjects().size(), Is.is(0));
+ }
+
+ @Test
+ public void should_find_module_with_maven_project_file_naming_different_from_pom_xml() throws Exception {
+ File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/");
+ MavenProject parent = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/pom.xml", true);
+ MavenProject module = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/mavenProjectFileNameNotEqualsToPomXml/module/pom_having_different_name.xml", false);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(parent, module), parent);
+
+ assertThat(rootDef.getSubProjects().size(), Is.is(1));
+ assertThat(rootDef.getKey(), Is.is("org.test:parent"));
+ assertNull(rootDef.getParent());
+ assertThat(rootDef.getBaseDir(), is(rootDir));
+
+ ProjectDefinition module1Def = rootDef.getSubProjects().get(0);
+ assertThat(module1Def.getKey(), Is.is("org.test:module"));
+ assertThat(module1Def.getParent(), Is.is(rootDef));
+ assertThat(module1Def.getBaseDir(), Is.is(new File(rootDir, "module")));
+ assertThat(module1Def.getSubProjects().size(), Is.is(0));
+ }
+
+ @Test
+ public void testSingleProjectWithoutModules() throws Exception {
+ File rootDir = TestUtils.getResource("/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/");
+ MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/singleProjectWithoutModules/pom.xml", true);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+ assertThat(rootDef.getKey(), is("org.test:parent"));
+ assertThat(rootDef.getSubProjects().size(), is(0));
+ assertNull(rootDef.getParent());
+ assertThat(rootDef.getBaseDir(), is(rootDir));
+ }
+
+ @Test
+ public void shouldConvertLinksToProperties() throws Exception {
+ MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinks/pom.xml", true);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+ Properties props = rootDef.getProperties();
+ assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+ }
+
+ @Test
+ public void shouldNotConvertLinksToPropertiesIfPropertyAlreadyDefined() throws Exception {
+ MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/projectWithLinksAndProperties/pom.xml", true);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+ Properties props = rootDef.getProperties();
+
+ // Those properties have been fed by the POM elements <ciManagement>, <issueManagement>, ...
+ assertThat(props.getProperty(CoreProperties.LINKS_CI)).isEqualTo("http://ci.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_ISSUE_TRACKER)).isEqualTo("http://issues.com");
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES_DEV)).isEqualTo("http://sources-dev.com");
+
+ // ... but those ones have been overridden by <properties> in the POM
+ assertThat(props.getProperty(CoreProperties.LINKS_SOURCES)).isEqualTo("http://sources.com-OVERRIDEN-BY-PROPS");
+ assertThat(props.getProperty(CoreProperties.LINKS_HOME_PAGE)).isEqualTo("http://home.com-OVERRIDEN-BY-PROPS");
+ }
+
+ @Test
+ public void shouldLoadSourceEncoding() throws Exception {
+ MavenProject pom = loadPom("/org/sonar/batch/maven/MavenProjectConverterTest/sourceEncoding/pom.xml", true);
+
+ ProjectDefinition rootDef = converter.configure(Arrays.asList(pom), pom);
+
+ assertThat(rootDef.getProperties().getProperty(CoreProperties.ENCODING_PROPERTY)).isEqualTo("Shift_JIS");
+ }
+
+ private MavenProject loadPom(String pomPath, boolean isRoot) throws URISyntaxException, IOException, XmlPullParserException {
+ File pomFile = new File(getClass().getResource(pomPath).toURI());
+ Model model = new MavenXpp3Reader().read(new StringReader(FileUtils.readFileToString(pomFile)));
+ MavenProject pom = new MavenProject(model);
+ pom.setFile(pomFile);
+ pom.getBuild().setDirectory("target");
+ pom.setExecutionRoot(isRoot);
+ return pom;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.batch.maven;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SonarMavenProjectBuilderTest {
+
+ @Test
+ public void testSimpleProject() {
+ MavenSession session = mock(MavenSession.class);
+ MavenProject rootProject = mock(MavenProject.class);
+ when(rootProject.isExecutionRoot()).thenReturn(true);
+ when(session.getSortedProjects()).thenReturn(Arrays.asList(rootProject));
+
+ MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
+ ProjectDefinition projectDefinition = ProjectDefinition.create();
+ when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
+ MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
+
+ assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
+
+ ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
+ verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
+ assertThat(argument.getValue()).contains(rootProject);
+ }
+
+ @Test
+ public void testMultimoduleProject() {
+ MavenSession session = mock(MavenSession.class);
+ MavenProject rootProject = mock(MavenProject.class);
+ MavenProject module1 = mock(MavenProject.class);
+ MavenProject module2 = mock(MavenProject.class);
+ when(rootProject.isExecutionRoot()).thenReturn(true);
+ when(module1.isExecutionRoot()).thenReturn(false);
+ when(module2.isExecutionRoot()).thenReturn(false);
+ when(session.getSortedProjects()).thenReturn(Arrays.asList(module1, module2, rootProject));
+
+ MavenProjectConverter mavenProjectConverter = mock(MavenProjectConverter.class);
+ ProjectDefinition projectDefinition = ProjectDefinition.create();
+ when(mavenProjectConverter.configure(any(List.class), any(MavenProject.class))).thenReturn(projectDefinition);
+ MavenProjectBootstrapper builder = new MavenProjectBootstrapper(session, mavenProjectConverter);
+
+ assertThat(builder.bootstrap().getRoot()).isEqualTo(projectDefinition);
+
+ ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
+ verify(mavenProjectConverter).configure(argument.capture(), eq(rootProject));
+ assertThat(argument.getValue()).contains(module1, module2, rootProject);
+ }
+
+}
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>module</module>
+ </modules>
+</project>
\ No newline at end of file
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module1</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module2</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>path1</module>
+ <module>path2</module>
+ </modules>
+</project>
\ No newline at end of file
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module1</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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>
+ <parent>
+ <groupId>org.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <artifactId>module2</artifactId>
+ <packaging>jar</packaging>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <modules>
+ <module>module1</module>
+ <module>module2</module>
+ </modules>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <url>http://home.com</url>
+ <ciManagement>
+ <url>http://ci.com</url>
+ </ciManagement>
+ <issueManagement>
+ <url>http://issues.com</url>
+ </issueManagement>
+ <scm>
+ <url>http://sources.com</url>
+ <developerConnection>http://sources-dev.com</developerConnection>
+ </scm>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <url>http://home.com</url>
+ <ciManagement>
+ <url>http://ci.com</url>
+ </ciManagement>
+ <issueManagement>
+ <url>http://issues.com</url>
+ </issueManagement>
+ <scm>
+ <url>http://sources.com</url>
+ <developerConnection>http://sources-dev.com</developerConnection>
+ </scm>
+
+
+
+ <properties>
+ <sonar.links.homepage>http://home.com-OVERRIDEN-BY-PROPS</sonar.links.homepage>
+ <sonar.links.scm>http://sources.com-OVERRIDEN-BY-PROPS</sonar.links.scm>
+ </properties>
+
+
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+</project>
\ No newline at end of file
--- /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.test</groupId>
+ <artifactId>parent</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <url>http://home.com</url>
+ <properties>
+ <project.build.sourceEncoding>Shift_JIS</project.build.sourceEncoding>
+ </properties>
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.core.config;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public class CorePropertyDefinitions {
+
+ private CorePropertyDefinitions() {
+ // only static stuff
+ }
+
+ public static List<PropertyDefinition> all() {
+ List<PropertyDefinition> defs = Lists.newArrayList();
+ defs.addAll(IssueExclusionProperties.all());
+ defs.addAll(ExclusionProperties.all());
+ defs.addAll(SecurityProperties.all());
+
+ defs.addAll(ImmutableList.of(
+
+ // DEBT
+ PropertyDefinition.builder(CoreProperties.HOURS_IN_DAY)
+ .name("Number of working hours in a day")
+ .type(PropertyType.INTEGER)
+ .defaultValue("8")
+ .category(CoreProperties.CATEGORY_TECHNICAL_DEBT)
+ .deprecatedKey("sqale.hoursInDay")
+ .build(),
+
+ // BATCH
+
+ PropertyDefinition.builder(CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY)
+ .defaultValue("en")
+ .name("Locale used for issue messages")
+ .description("Deprecated property. Keep default value for backward compatibility.")
+ .hidden()
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 1)
+ .name("Period 1")
+ .description("Period used to compare measures and track new violations. Values are : <ul class='bullet'><li>Number of days before " +
+ "analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, for example 2010-12-25</li><li>'previous_analysis' to " +
+ "compare to previous analysis</li><li>'previous_version' to compare to the previous version in the project history</li></ul>" +
+ "<p>When specifying a number of days or a date, the snapshot selected for comparison is " +
+ " the first one available inside the corresponding time range. </p>" +
+ "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
+ .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 2)
+ .name("Period 2")
+ .description("See the property 'Period 1'")
+ .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 3)
+ .name("Period 3")
+ .description("See the property 'Period 1'")
+ .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 4)
+ .name("Period 4")
+ .description("Period used to compare measures and track new violations. This property is specific to the project. Values are : " +
+ "<ul class='bullet'><li>Number of days before analysis, for example 5.</li><li>A custom date. Format is yyyy-MM-dd, " +
+ "for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li>" +
+ "<li>'previous_version' to compare to the previous version in the project history</li><li>A version, for example 1.2</li></ul>" +
+ "<p>When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range. </p>" +
+ "<p>Changing this property only takes effect after subsequent project inspections.<p/>")
+ .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4)
+ .onlyOnQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + 5)
+ .name("Period 5")
+ .description("See the property 'Period 4'")
+ .defaultValue(CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5)
+ .onlyOnQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
+ .build()
+ ));
+ return defs;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public class ExclusionProperties {
+
+ private ExclusionProperties() {
+ // only static stuff
+ }
+
+ public static List<PropertyDefinition> all() {
+ return ImmutableList.of(
+
+ // COVERAGE
+ PropertyDefinition.builder("sonar.coverage.exclusions")
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_COVERAGE_EXCLUSIONS)
+ .type(PropertyType.STRING)
+ .multiValues(true)
+ .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+ .build(),
+
+ // FILES
+ PropertyDefinition.builder(CoreProperties.PROJECT_INCLUSIONS_PROPERTY)
+ .name("Source File Inclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(3)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY)
+ .name("Test File Inclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(5)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)
+ .name("Global Source File Exclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .index(0)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY)
+ .name("Global Test File Exclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .index(1)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY)
+ .name("Source File Exclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(2)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY)
+ .name("Test File Exclusions")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(4)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.CORE_SKIPPED_MODULES_PROPERTY)
+ .name("Exclude Modules")
+ .description("Maven artifact ids of modules to exclude.")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onlyOnQualifiers(Qualifiers.PROJECT)
+ .index(0)
+ .build(),
+ PropertyDefinition.builder(CoreProperties.CORE_INCLUDED_MODULES_PROPERTY)
+ .name("Include Modules")
+ .description("Maven artifact ids of modules to include.")
+ .multiValues(true)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(CoreProperties.SUBCATEGORY_FILES_EXCLUSIONS)
+ .onlyOnQualifiers(Qualifiers.PROJECT)
+ .index(1)
+ .build()
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.config.PropertyFieldDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+public final class IssueExclusionProperties {
+
+ public static final String SUB_CATEGORY_IGNORE_ISSUES = "issues";
+
+ public static final String EXCLUSION_KEY_PREFIX = "sonar.issue.ignore";
+ public static final String INCLUSION_KEY_PREFIX = "sonar.issue.enforce";
+
+ public static final String MULTICRITERIA_SUFFIX = ".multicriteria";
+ public static final String PATTERNS_MULTICRITERIA_EXCLUSION_KEY = EXCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+ public static final String PATTERNS_MULTICRITERIA_INCLUSION_KEY = INCLUSION_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+ public static final String RESOURCE_KEY = "resourceKey";
+ private static final String PROPERTY_FILE_PATH_PATTERN = "File Path Pattern";
+ public static final String RULE_KEY = "ruleKey";
+ private static final String PROPERTY_RULE_KEY_PATTERN = "Rule Key Pattern";
+ private static final String PROPERTY_RULE_KEY_PATTERN_HELP = "<br/>A rule key pattern consists of the rule repository name, followed by a colon, followed by a rule key "
+ + "or rule name fragment. For example:"
+ + "<ul><li>squid:S1195</li><li>squid:*Naming*</li></ul>";
+
+ public static final String BLOCK_SUFFIX = ".block";
+ public static final String PATTERNS_BLOCK_KEY = EXCLUSION_KEY_PREFIX + BLOCK_SUFFIX;
+ public static final String BEGIN_BLOCK_REGEXP = "beginBlockRegexp";
+ public static final String END_BLOCK_REGEXP = "endBlockRegexp";
+
+ public static final String ALLFILE_SUFFIX = ".allfile";
+ public static final String PATTERNS_ALLFILE_KEY = EXCLUSION_KEY_PREFIX + ALLFILE_SUFFIX;
+ public static final String FILE_REGEXP = "fileRegexp";
+
+ public static final int LARGE_SIZE = 40;
+
+ private IssueExclusionProperties() {
+ // only static
+ }
+
+ public static List<PropertyDefinition> all() {
+ return ImmutableList.of(
+ PropertyDefinition.builder(PATTERNS_MULTICRITERIA_EXCLUSION_KEY)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+ .name("Ignore Issues on Multiple Criteria")
+ .description("Patterns to ignore issues on certain components and for certain coding rules." + PROPERTY_RULE_KEY_PATTERN_HELP)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(3)
+ .fields(
+ PropertyFieldDefinition.build(RULE_KEY)
+ .name(PROPERTY_RULE_KEY_PATTERN)
+ .description("Pattern to match rules which should be ignored.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build(),
+ PropertyFieldDefinition.build(RESOURCE_KEY)
+ .name(PROPERTY_FILE_PATH_PATTERN)
+ .description("Pattern to match files which should be ignored.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build()
+ )
+ .build(),
+ PropertyDefinition.builder(PATTERNS_BLOCK_KEY)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+ .name("Ignore Issues in Blocks")
+ .description("Patterns to ignore all issues on specific blocks of code, while continuing to scan and mark issues on the remainder of the file.")
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(2)
+ .fields(
+ PropertyFieldDefinition.build(BEGIN_BLOCK_REGEXP)
+ .name("Regular Expression for Start of Block")
+ .description("If this regular expression is found in a file, then following lines are ignored until end of block.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build(),
+ PropertyFieldDefinition.build(END_BLOCK_REGEXP)
+ .name("Regular Expression for End of Block")
+ .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build()
+ )
+ .build(),
+ PropertyDefinition.builder(PATTERNS_ALLFILE_KEY)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+ .name("Ignore Issues on Files")
+ .description("Patterns to ignore all issues on files that contain a block of code matching a given regular expression.")
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(1)
+ .fields(
+ PropertyFieldDefinition.build(FILE_REGEXP)
+ .name("Regular Expression")
+ .description("If this regular expression is found in a file, then the whole file is ignored.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build()
+ )
+ .build(),
+ PropertyDefinition.builder(PATTERNS_MULTICRITERIA_INCLUSION_KEY)
+ .category(CoreProperties.CATEGORY_EXCLUSIONS)
+ .subCategory(SUB_CATEGORY_IGNORE_ISSUES)
+ .name("Restrict Scope of Coding Rules")
+ .description("Patterns to restrict the application of a rule to only certain components, ignoring all others." + PROPERTY_RULE_KEY_PATTERN_HELP)
+ .onQualifiers(Qualifiers.PROJECT)
+ .index(4)
+ .fields(
+ PropertyFieldDefinition.build(RULE_KEY)
+ .name(PROPERTY_RULE_KEY_PATTERN)
+ .description("Pattern used to match rules which should be restricted.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build(),
+ PropertyFieldDefinition.build(RESOURCE_KEY)
+ .name(PROPERTY_FILE_PATH_PATTERN)
+ .description("Pattern used to match files to which the rules should be restricted.")
+ .type(PropertyType.STRING)
+ .indicativeSize(LARGE_SIZE)
+ .build()
+ )
+ .build()
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.core.config;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.List;
+
+class SecurityProperties {
+
+ private SecurityProperties() {
+ // only static stuff
+ }
+
+ static List<PropertyDefinition> all() {
+ return ImmutableList.of(
+ PropertyDefinition.builder(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY)
+ .defaultValue("" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE)
+ .name("Import sources")
+ .description("Set to false if sources should not be imported and therefore not available in the Web UI (e.g. for security reasons).")
+ .type(PropertyType.BOOLEAN)
+ .onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
+ .category(CoreProperties.CATEGORY_SECURITY)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY)
+ .defaultValue("" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE)
+ .name("Allow users to sign up online")
+ .description("Users can sign up online.")
+ .type(PropertyType.BOOLEAN)
+ .category(CoreProperties.CATEGORY_SECURITY)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.CORE_DEFAULT_GROUP)
+ .defaultValue(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE)
+ .name("Default user group")
+ .description("Any new users will automatically join this group.")
+ .category(CoreProperties.CATEGORY_SECURITY)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY)
+ .defaultValue("" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE)
+ .name("Force user authentication")
+ .description("Forcing user authentication stops un-logged users to access SonarQube.")
+ .type(PropertyType.BOOLEAN)
+ .category(CoreProperties.CATEGORY_SECURITY)
+ .build(),
+
+ PropertyDefinition.builder(CoreProperties.CORE_PREVENT_AUTOMATIC_PROJECT_CREATION)
+ .defaultValue(Boolean.toString(false))
+ .name("Prevent automatic project creation")
+ .description("Set to true to prevent automatic project creation at first analysis and force project provisioning.")
+ .type(PropertyType.BOOLEAN)
+ .category(CoreProperties.CATEGORY_SECURITY)
+ .build()
+ );
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.core.config;
+
+import org.junit.Test;
+import org.sonar.api.config.PropertyDefinition;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class CorePropertyDefinitionsTest {
+ @Test
+ public void all() {
+ List<PropertyDefinition> defs = CorePropertyDefinitions.all();
+ assertThat(defs.size()).isGreaterThan(10);
+ }
+}
<artifactId>gson</artifactId>
</dependency>
<dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-batch</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-batch-maven-compat</artifactId>
- <version>${project.version}</version>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-deprecated</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<type>sonar-plugin</type>
<scope>provided</scope>
</dependency>
- <dependency>
- <groupId>org.codehaus.sonar.plugins</groupId>
- <artifactId>sonar-maven-batch-plugin</artifactId>
- <version>${project.version}</version>
- <type>sonar-plugin</type>
- <scope>provided</scope>
- </dependency>
<dependency>
<groupId>org.codehaus.sonar-plugins.java</groupId>
<artifactId>sonar-jacoco-plugin</artifactId>
</plugin>
</plugins>
</build>
- <dependencies>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- </dependency>
- </dependencies>
</profile>
<profile>
return new File(getDeployDir(), "plugins/index.txt");
}
- public File getBootstrapIndex() {
- return new File(getDeployDir(), "bootstrap/index.txt");
- }
-
/**
* @deprecated since 4.1
*/
import org.sonar.api.utils.internal.TempFolderCleaner;
import org.sonar.core.component.SnapshotPerspectives;
import org.sonar.core.component.db.ComponentDao;
+import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.core.config.Logback;
import org.sonar.core.i18n.DefaultI18n;
import org.sonar.core.i18n.GwtI18n;
new TempFolderProvider(),
System2.INSTANCE
));
+ components.addAll(CorePropertyDefinitions.all());
components.addAll(DatabaseMigrations.CLASSES);
components.addAll(DaoUtils.getDaoClasses());
return components;
// ws
RestartHandler.class,
- SystemWs.class
+ SystemWs.class,
+ BatchWs.class
);
}
startupContainer.addSingleton(JdbcDriverDeployer.class);
startupContainer.addSingleton(RegisterDebtModel.class);
startupContainer.addSingleton(GeneratePluginIndex.class);
- startupContainer.addSingleton(GenerateBootstrapIndex.class);
startupContainer.addSingleton(RegisterNewMeasureFilters.class);
startupContainer.addSingleton(RegisterDashboards.class);
startupContainer.addSingleton(RegisterPermissionTemplates.class);
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.server.plugins;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * This servlet allows to load libraries from directory "WEB-INF/lib" in order to provide them for batch-bootstrapper.
- * Most probably this is not a best solution.
- */
-public class BatchResourcesServlet extends HttpServlet {
-
- private static final Logger LOG = LoggerFactory.getLogger(BatchResourcesServlet.class);
- private static final long serialVersionUID = -2100128371794649028L;
-
- @Override
- public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- String filename = filename(request);
- if (StringUtils.isBlank(filename)) {
- throw new IllegalArgumentException("Filename is missing.");
- } else {
- InputStream in = null;
- OutputStream out = null;
- try {
- in = getServletContext().getResourceAsStream("/WEB-INF/lib/" + filename);
- if (in == null) {
- // TODO
- } else {
- response.setContentType("application/java-archive");
- out = response.getOutputStream();
- IOUtils.copy(in, out);
- }
- } catch (Exception e) {
- LOG.error("Unable to load batch resource '" + filename + "'", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- } finally {
- IOUtils.closeQuietly(in);
- IOUtils.closeQuietly(out);
- }
- }
- }
-
- /**
- * @return part of request URL after servlet path
- */
- String filename(HttpServletRequest request) {
- String filename = null;
- if (StringUtils.endsWithIgnoreCase(request.getRequestURI(), "jar")) {
- filename = StringUtils.substringAfterLast(request.getRequestURI(), "/");
- }
- return filename;
- }
-
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.server.plugins;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.HiddenFileFilter;
+import org.apache.commons.lang.CharUtils;
+import org.apache.commons.lang.StringUtils;
+import org.picocontainer.Startable;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.home.cache.FileHashes;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * JAR files to be downloaded by sonar-runner.
+ */
+public class BatchWs implements WebService, Startable {
+
+ private final Server server;
+ private String index;
+ private File batchDir;
+
+ public BatchWs(Server server) {
+ this.server = server;
+ }
+
+ @Override
+ public void start() {
+ StringBuilder sb = new StringBuilder();
+ batchDir = new File(server.getRootDir(), "lib/batch");
+ if (batchDir.exists()) {
+ Collection<File> files = FileUtils.listFiles(batchDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
+ for (File file : files) {
+ String filename = file.getName();
+ if (StringUtils.endsWith(filename, ".jar")) {
+ sb.append(filename).append('|').append(new FileHashes().of(file)).append(CharUtils.LF);
+ }
+ }
+ }
+ this.index = sb.toString();
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController("batch");
+ controller.createAction("index")
+ .setInternal(true)
+ .setDescription("List the JAR files to be downloaded by source analyzer")
+ .setHandler(new RequestHandler() {
+ @Override
+ public void handle(Request request, Response response) {
+ index(response);
+ }
+ });
+ controller.createAction("file")
+ .setInternal(true)
+ .setDescription("Download a JAR file required by source analyzer")
+ .setHandler(new RequestHandler() {
+ @Override
+ public void handle(Request request, Response response) {
+ file(request, response);
+ }
+ }).createParam("name", "Filename");
+
+ controller.done();
+ }
+
+ private void index(Response response) {
+ try {
+ response.stream().setMediaType("text/plain");
+ IOUtils.write(index, response.stream().output());
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to send batch index", e);
+ }
+ }
+
+ private void file(Request request, Response response) {
+ String filename = request.mandatoryParam("name");
+ try {
+ File input = new File(batchDir, filename);
+ if (!input.exists() || !FileUtils.directoryContains(batchDir, input)) {
+ throw new IllegalArgumentException("Bad filename: " + filename);
+ }
+ response.stream().setMediaType("application/java-archive");
+ FileUtils.copyFile(input, response.stream().output());
+
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to send batch file " + filename, e);
+ }
+ }
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.server.startup;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.filefilter.FileFilterUtils;
-import org.apache.commons.io.filefilter.HiddenFileFilter;
-import org.apache.commons.lang.CharUtils;
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.platform.Server;
-import org.sonar.home.cache.FileHashes;
-import org.sonar.server.platform.DefaultServerFileSystem;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * @since 3.5
- */
-public final class GenerateBootstrapIndex {
-
- // JARs starting with one of these prefixes are excluded from batch
- private static final String[] IGNORE = {"jtds", "mysql", "postgresql", "jruby", "jfreechart", "eastwood",
- "elasticsearch", "lucene"};
-
- private static final String LIB_DIR = "/web/WEB-INF/lib";
-
- private final Server server;
- private final DefaultServerFileSystem fileSystem;
-
- public GenerateBootstrapIndex(DefaultServerFileSystem fileSystem, Server server) {
- this.server = server;
- this.fileSystem = fileSystem;
- }
-
- public void start() throws IOException {
- writeIndex(fileSystem.getBootstrapIndex());
- }
-
- void writeIndex(File indexFile) throws IOException {
- FileUtils.forceMkdir(indexFile.getParentFile());
- FileWriter writer = new FileWriter(indexFile, false);
- try {
- File libDir = new File(server.getRootDir(), LIB_DIR);
- // TODO hack for Medium tests
- if (libDir.exists()) {
- for (String path : getLibs(libDir)) {
- writer.append(path);
- File is = new File(libDir, path);
- writer.append("|").append(new FileHashes().of(is));
- writer.append(CharUtils.LF);
- }
- writer.flush();
- }
-
- } finally {
- IOUtils.closeQuietly(writer);
- }
- }
-
- @VisibleForTesting
- static List<String> getLibs(File libDir) {
- List<String> libs = Lists.newArrayList();
-
- Collection<File> files = FileUtils.listFiles(libDir, HiddenFileFilter.VISIBLE, FileFilterUtils.directoryFileFilter());
- for (File file : files) {
- String path = file.getPath();
- if (StringUtils.endsWith(path, ".jar")) {
- String filename = StringUtils.removeStart(path, libDir.getAbsolutePath() + "/");
- if (!isIgnored(filename)) {
- libs.add(filename);
- }
- }
- }
- return libs;
- }
-
- /**
- * Dirty hack to disable downloading for certain files.
- */
- static boolean isIgnored(String filename) {
- for (String prefix : IGNORE) {
- if (StringUtils.startsWith(filename, prefix)) {
- return true;
- }
- }
- return false;
- }
-}
import org.sonar.api.test.MutableTestable;
import org.sonar.api.test.TestPlan;
import org.sonar.api.test.Testable;
-import org.sonar.api.web.*;
+import org.sonar.api.web.Footer;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.Page;
+import org.sonar.api.web.RubyRailsWebservice;
+import org.sonar.api.web.Widget;
import org.sonar.core.component.SnapshotPerspectives;
import org.sonar.core.measure.MeasureFilterEngine;
import org.sonar.core.measure.MeasureFilterResult;
import org.sonar.core.resource.ResourceKeyUpdaterDao;
import org.sonar.core.timemachine.Periods;
import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.platform.*;
-import org.sonar.server.plugins.*;
+import org.sonar.server.platform.Platform;
+import org.sonar.server.platform.ServerIdGenerator;
+import org.sonar.server.platform.ServerSettings;
+import org.sonar.server.platform.SettingsChangeNotifier;
+import org.sonar.server.plugins.InstalledPluginReferentialFactory;
+import org.sonar.server.plugins.PluginDownloader;
+import org.sonar.server.plugins.ServerPluginJarsInstaller;
+import org.sonar.server.plugins.ServerPluginRepository;
+import org.sonar.server.plugins.UpdateCenterMatrixFactory;
import org.sonar.server.rule.RuleRepositories;
import org.sonar.server.source.CodeColorizers;
import org.sonar.server.user.NewUserNotifier;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.io.IOException;
import java.net.InetAddress;
import java.sql.Connection;
import java.util.Collection;
return Platform.getInstance().getContainer();
}
- public String boostrapIndexPath() throws IOException {
- return getContainer().getComponentByType(DefaultServerFileSystem.class).getBootstrapIndex().getCanonicalPath();
- }
-
// UPDATE PROJECT KEY ------------------------------------------------------------------
public void updateResourceKey(long projectId, String newKey) {
get(ResourceKeyUpdaterDao.class).updateKey(projectId, newKey);
render :json => JSON(json_properties)
end
- # GET /batch_bootstrap/index
- def index
- render :file => java_facade.boostrapIndexPath(), :layout => false
- end
-
private
def render_unauthorized(message, status=403)
end
map.connect 'api', :controller => 'api/java_ws', :action => 'redirect_to_ws_listing'
+
+ # deprecated, sonar-runner should use batch/index and batch/file?name=xxx
+ map.connect 'batch_bootstrap/index', :controller => 'api/java_ws', :action => 'index', :wspath => 'batch', :wsaction => 'index'
+ map.connect 'batch/:name', :controller => 'api/java_ws', :action => 'index', :wspath => 'batch', :wsaction => 'file', :requirements => { :name => /.*/ }
+
map.connect 'api/metrics', :controller => 'api/metrics', :action => 'index', :conditions => { :method => :get }
map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'show', :conditions => { :method => :get }
map.connect 'api/metrics/:id', :controller => 'api/metrics', :action => 'create', :conditions => { :method => :post }
<servlet-name>static</servlet-name>
<servlet-class>org.sonar.server.plugins.StaticResourcesServlet</servlet-class>
</servlet>
- <servlet>
- <servlet-name>batch</servlet-name>
- <servlet-class>org.sonar.server.plugins.BatchResourcesServlet</servlet-class>
- </servlet>
<servlet-mapping>
<servlet-name>chart</servlet-name>
<servlet-name>static</servlet-name>
<url-pattern>/static/*</url-pattern>
</servlet-mapping>
- <servlet-mapping>
- <servlet-name>batch</servlet-name>
- <url-pattern>/batch/*</url-pattern>
- </servlet-mapping>
<session-config>
<!-- in minutes -->
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.server.plugins;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletRequest;
-
-import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class BatchResourcesServletTest {
- private BatchResourcesServlet servlet;
- private HttpServletRequest request;
-
- @Before
- public void setUp() throws Exception {
- servlet = new BatchResourcesServlet();
- request = mock(HttpServletRequest.class);
- }
-
- @Test
- public void test_filename() {
- when(request.getContextPath()).thenReturn("sonar");
- when(request.getServletPath()).thenReturn("/batch");
-
- when(request.getRequestURI()).thenReturn("/sonar/batch/sonar-core-2.6.jar");
- assertThat(servlet.filename(request)).isEqualTo("sonar-core-2.6.jar");
-
- when(request.getRequestURI()).thenReturn("/sonar/batch/");
- assertThat(servlet.filename(request)).isNull();
-
- when(request.getRequestURI()).thenReturn("/sonar/batch");
- assertThat(servlet.filename(request)).isNull();
-
- when(request.getRequestURI()).thenReturn("/sonar/batch.html");
- assertThat(servlet.filename(request)).isNull();
-
- when(request.getRequestURI()).thenReturn("/sonar/batch/index.html");
- assertThat(servlet.filename(request)).isNull();
- }
-
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.server.plugins;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.CharUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.platform.Server;
+import org.sonar.api.server.ws.WsTester;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BatchWsTest {
+
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ Server server = mock(Server.class);
+
+ @Before
+ public void prepare_fs() throws IOException {
+ File rootDir = temp.newFolder();
+ when(server.getRootDir()).thenReturn(rootDir);
+
+ File batchDir = new File(rootDir, "lib/batch");
+ FileUtils.forceMkdir(batchDir);
+ FileUtils.writeStringToFile(new File(batchDir, "sonar-batch.jar"), "foo");
+ }
+
+ @Test
+ public void download_index() throws Exception {
+ BatchWs ws = new BatchWs(server);
+ ws.start();
+ WsTester tester = new WsTester(ws);
+
+ String index = tester.newRequest("index").execute().outputAsString();
+ assertThat(index).isEqualTo("sonar-batch.jar|acbd18db4cc2f85cedef654fccc4a4d8" + CharUtils.LF);
+
+ ws.stop();
+ }
+
+ @Test
+ public void download_file() throws Exception {
+ BatchWs ws = new BatchWs(server);
+ ws.start();
+ WsTester tester = new WsTester(ws);
+
+ String jar = tester.newRequest("file").setParam("name", "sonar-batch.jar").execute().outputAsString();
+ assertThat(jar).isEqualTo("foo");
+ }
+
+ /**
+ * Do not allow to download files located outside the directory lib/batch, for example
+ * /etc/passwd
+ */
+ @Test
+ public void check_location_of_file() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Bad filename: ../sonar-batch.jar");
+
+ BatchWs ws = new BatchWs(server);
+ ws.start();
+ WsTester tester = new WsTester(ws);
+
+ tester.newRequest("file").setParam("name", "../sonar-batch.jar").execute();
+ }
+
+ @Test
+ public void file_does_not_exist() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Bad filename: other.jar");
+
+ BatchWs ws = new BatchWs(server);
+ ws.start();
+ WsTester tester = new WsTester(ws);
+
+ tester.newRequest("file").setParam("name", "other.jar").execute();
+ }
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.server.startup;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.IOException;
-
-import static org.fest.assertions.Assertions.assertThat;
-
-public class GenerateBootstrapIndexTest {
-
- @Rule
- public TemporaryFolder rootDir = new TemporaryFolder();
-
- @Before
- public void setUp() throws Exception {
- }
-
- @Test
- public void determine_list_of_resources() throws IOException {
- new File(rootDir.getRoot(), "/web/WEB-INF/lib").mkdirs();
- File webInf = new File(rootDir.getRoot(), "/web/WEB-INF");
- File lib = new File(rootDir.getRoot(), "/web/WEB-INF/lib");
- new File(webInf, "directory").mkdir();
- new File(lib, "sonar-core-2.6.jar").createNewFile();
- new File(lib, "treemap.rbr").createNewFile();
- new File(lib, "sonar-core-2.6.jar").createNewFile();
-
- assertThat(GenerateBootstrapIndex.getLibs(lib)).hasSize(1);
- assertThat(GenerateBootstrapIndex.getLibs(lib).get(0)).isEqualTo("sonar-core-2.6.jar");
- }
-
- @Test
- public void ignore_some_jars() {
- assertThat(GenerateBootstrapIndex.isIgnored("sonar-batch-2.6-SNAPSHOT.jar")).isFalse();
- assertThat(GenerateBootstrapIndex.isIgnored("mysql-connector-java-5.1.13.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("postgresql-9.0-801.jdbc3.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("jtds-1.2.4.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("jfreechart-1.0.9.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("eastwood-1.1.0.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("jruby-complete-1.5.6.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("jruby-rack-1.0.5.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("elasticsearch-0.90.6.jar")).isTrue();
- assertThat(GenerateBootstrapIndex.isIgnored("lucene-core-4.5.1.jar")).isTrue();
- }
-
-}