From 24d7c761a444ec9dd861a7ac7831d4d7946fb9f5 Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Tue, 10 Jul 2012 10:46:19 +0200 Subject: [PATCH] SONAR-2496 Support 'previous-version' value for differential views --- .../org/sonar/plugins/core/CorePlugin.java | 277 +++++++++++------- .../resources/org/sonar/l10n/core.properties | 5 + .../sonar/batch/bootstrap/BatchModule.java | 20 +- .../sonar/batch/components/PastSnapshot.java | 7 + .../batch/components/PastSnapshotFinder.java | 18 +- .../PastSnapshotFinderByPreviousVersion.java | 61 ++++ .../components/TimeMachineConfiguration.java | 12 +- ...stSnapshotFinderByPreviousVersionTest.java | 47 +++ .../PastSnapshotFinderByVersionTest.java | 14 - .../components/PastSnapshotFinderTest.java | 51 +++- .../shared.xml | 42 +++ .../java/org/sonar/api/CoreProperties.java | 3 +- .../WEB-INF/app/helpers/application_helper.rb | 6 + .../WEB-INF/app/helpers/dashboard_helper.rb | 87 +++--- .../WEB-INF/app/helpers/filters_helper.rb | 2 + 15 files changed, 474 insertions(+), 178 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersion.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest.java create mode 100644 sonar-batch/src/test/resources/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest/shared.xml diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java index 6e8241b91f4..7c7052e8c35 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java @@ -20,7 +20,12 @@ package org.sonar.plugins.core; import com.google.common.collect.ImmutableList; -import org.sonar.api.*; +import org.sonar.api.CoreProperties; +import org.sonar.api.Extension; +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.resources.Java; import org.sonar.plugins.core.batch.ExcludedResourceFilter; @@ -31,19 +36,79 @@ 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.dashboards.DefaultDashboard; +import org.sonar.plugins.core.dashboards.HotspotsDashboard; +import org.sonar.plugins.core.dashboards.MyFavouritesDashboard; +import org.sonar.plugins.core.dashboards.ProjectsDashboard; +import org.sonar.plugins.core.dashboards.ReviewsDashboard; +import org.sonar.plugins.core.dashboards.TimeMachineDashboard; +import org.sonar.plugins.core.dashboards.TreemapDashboard; import org.sonar.plugins.core.filters.MyFavouritesFilter; import org.sonar.plugins.core.filters.ProjectFilter; import org.sonar.plugins.core.filters.TreeMapFilter; import org.sonar.plugins.core.security.ApplyProjectRolesDecorator; -import org.sonar.plugins.core.security.DefaultResourcePermissions; -import org.sonar.plugins.core.sensors.*; +import org.sonar.plugins.core.sensors.BranchCoverageDecorator; +import org.sonar.plugins.core.sensors.CheckAlertThresholds; +import org.sonar.plugins.core.sensors.CommentDensityDecorator; +import org.sonar.plugins.core.sensors.CoverageDecorator; +import org.sonar.plugins.core.sensors.DirectoriesDecorator; +import org.sonar.plugins.core.sensors.FilesDecorator; +import org.sonar.plugins.core.sensors.GenerateAlertEvents; +import org.sonar.plugins.core.sensors.ItBranchCoverageDecorator; +import org.sonar.plugins.core.sensors.ItCoverageDecorator; +import org.sonar.plugins.core.sensors.ItLineCoverageDecorator; +import org.sonar.plugins.core.sensors.LineCoverageDecorator; +import org.sonar.plugins.core.sensors.ManualMeasureDecorator; +import org.sonar.plugins.core.sensors.ManualViolationInjector; +import org.sonar.plugins.core.sensors.ProfileEventsSensor; +import org.sonar.plugins.core.sensors.ProfileSensor; +import org.sonar.plugins.core.sensors.ProjectLinksSensor; +import org.sonar.plugins.core.sensors.ReviewNotifications; +import org.sonar.plugins.core.sensors.ReviewWorkflowDecorator; +import org.sonar.plugins.core.sensors.ReviewsMeasuresDecorator; +import org.sonar.plugins.core.sensors.UnitTestDecorator; +import org.sonar.plugins.core.sensors.VersionEventsSensor; +import org.sonar.plugins.core.sensors.ViolationSeverityUpdater; +import org.sonar.plugins.core.sensors.ViolationsDecorator; +import org.sonar.plugins.core.sensors.ViolationsDensityDecorator; +import org.sonar.plugins.core.sensors.WeightedViolationsDecorator; import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition; -import org.sonar.plugins.core.timemachine.*; +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.NewViolationsDecorator; +import org.sonar.plugins.core.timemachine.ReferenceAnalysis; +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.timemachine.ViolationPersisterDecorator; +import org.sonar.plugins.core.timemachine.ViolationTrackingDecorator; import org.sonar.plugins.core.web.Lcom4Viewer; -import org.sonar.plugins.core.widgets.*; +import org.sonar.plugins.core.widgets.AlertsWidget; +import org.sonar.plugins.core.widgets.CommentsDuplicationsWidget; +import org.sonar.plugins.core.widgets.ComplexityWidget; +import org.sonar.plugins.core.widgets.CoverageWidget; +import org.sonar.plugins.core.widgets.CustomMeasuresWidget; +import org.sonar.plugins.core.widgets.DescriptionWidget; +import org.sonar.plugins.core.widgets.EventsWidget; +import org.sonar.plugins.core.widgets.FilterWidget; +import org.sonar.plugins.core.widgets.HotspotMetricWidget; +import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget; +import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget; +import org.sonar.plugins.core.widgets.ItCoverageWidget; +import org.sonar.plugins.core.widgets.RulesWidget; +import org.sonar.plugins.core.widgets.SizeWidget; +import org.sonar.plugins.core.widgets.TimeMachineWidget; +import org.sonar.plugins.core.widgets.TimelineWidget; +import org.sonar.plugins.core.widgets.TreemapWidget; import org.sonar.plugins.core.widgets.actionPlans.ActionPlansWidget; -import org.sonar.plugins.core.widgets.reviews.*; +import org.sonar.plugins.core.widgets.reviews.FalsePositiveReviewsWidget; +import org.sonar.plugins.core.widgets.reviews.MyReviewsWidget; +import org.sonar.plugins.core.widgets.reviews.PlannedReviewsWidget; +import org.sonar.plugins.core.widgets.reviews.ProjectReviewsWidget; +import org.sonar.plugins.core.widgets.reviews.ReviewsMetricsWidget; +import org.sonar.plugins.core.widgets.reviews.ReviewsPerDeveloperWidget; +import org.sonar.plugins.core.widgets.reviews.UnplannedReviewsWidget; import java.util.List; @@ -139,7 +204,8 @@ import java.util.List; name = "Period 1", description = "Period used to compare measures and track new violations. Values are : " + "

When specifying a number of days or a date, the snapshot selected for comparison is " + + "compare to previous analysis

  • 'previous_version' to compare to the previous version in the project history
  • " + + "

    When specifying a number of days or a date, the snapshot selected for comparison is " + " the first one available inside the corresponding time range.

    " + "

    Changing this property only takes effect after subsequent project inspections.

    ", project = false, @@ -167,7 +233,8 @@ import java.util.List; name = "Period 4", description = "Period used to compare measures and track new violations. This property is specific to the project. Values are : " + "

    " + + "for example 2010-12-25
  • 'previous_analysis' to compare to previous analysis
  • " + + "
  • 'previous_version' to compare to the previous version in the project history
  • A version, for example 1.2
  • " + "

    When specifying a number of days or a date, the snapshot selected for comparison is the first one available inside the corresponding time range.

    " + "

    Changing this property only takes effect after subsequent project inspections.

    ", project = true, @@ -226,111 +293,111 @@ public final class CorePlugin extends SonarPlugin { @SuppressWarnings("unchecked") public List> getExtensions() { return ImmutableList.of( - DefaultResourceTypes.class, - UserManagedMetrics.class, - ProjectFileSystemLogger.class, + DefaultResourceTypes.class, + UserManagedMetrics.class, + ProjectFileSystemLogger.class, - // maven - MavenInitializer.class, + // maven + MavenInitializer.class, - // languages - Java.class, + // languages + Java.class, - // pages - TestsViewerDefinition.class, - Lcom4Viewer.class, + // pages + TestsViewerDefinition.class, + Lcom4Viewer.class, - // filters - ProjectFilter.class, - TreeMapFilter.class, - MyFavouritesFilter.class, + // filters + ProjectFilter.class, + TreeMapFilter.class, + MyFavouritesFilter.class, - // widgets - AlertsWidget.class, - CoverageWidget.class, - ItCoverageWidget.class, - CommentsDuplicationsWidget.class, - DescriptionWidget.class, - ComplexityWidget.class, - RulesWidget.class, - SizeWidget.class, - EventsWidget.class, - CustomMeasuresWidget.class, - TimelineWidget.class, - TimeMachineWidget.class, - HotspotMetricWidget.class, - HotspotMostViolatedResourcesWidget.class, - HotspotMostViolatedRulesWidget.class, - MyReviewsWidget.class, - ProjectReviewsWidget.class, - FalsePositiveReviewsWidget.class, - ReviewsPerDeveloperWidget.class, - PlannedReviewsWidget.class, - UnplannedReviewsWidget.class, - ActionPlansWidget.class, - ReviewsMetricsWidget.class, - TreemapWidget.class, - FilterWidget.class, + // widgets + AlertsWidget.class, + CoverageWidget.class, + ItCoverageWidget.class, + CommentsDuplicationsWidget.class, + DescriptionWidget.class, + ComplexityWidget.class, + RulesWidget.class, + SizeWidget.class, + EventsWidget.class, + CustomMeasuresWidget.class, + TimelineWidget.class, + TimeMachineWidget.class, + HotspotMetricWidget.class, + HotspotMostViolatedResourcesWidget.class, + HotspotMostViolatedRulesWidget.class, + MyReviewsWidget.class, + ProjectReviewsWidget.class, + FalsePositiveReviewsWidget.class, + ReviewsPerDeveloperWidget.class, + PlannedReviewsWidget.class, + UnplannedReviewsWidget.class, + ActionPlansWidget.class, + ReviewsMetricsWidget.class, + TreemapWidget.class, + FilterWidget.class, - // dashboards - DefaultDashboard.class, - HotspotsDashboard.class, - ReviewsDashboard.class, - TimeMachineDashboard.class, - ProjectsDashboard.class, - TreemapDashboard.class, - MyFavouritesDashboard.class, + // dashboards + DefaultDashboard.class, + HotspotsDashboard.class, + ReviewsDashboard.class, + TimeMachineDashboard.class, + ProjectsDashboard.class, + TreemapDashboard.class, + MyFavouritesDashboard.class, - // chart - XradarChart.class, - DistributionBarChart.class, - DistributionAreaChart.class, + // chart + XradarChart.class, + DistributionBarChart.class, + DistributionAreaChart.class, - // colorizers - JavaColorizerFormat.class, + // colorizers + JavaColorizerFormat.class, - // batch - ProfileSensor.class, - ProfileEventsSensor.class, - ProjectLinksSensor.class, - UnitTestDecorator.class, - VersionEventsSensor.class, - CheckAlertThresholds.class, - GenerateAlertEvents.class, - ViolationsDecorator.class, - WeightedViolationsDecorator.class, - ViolationsDensityDecorator.class, - LineCoverageDecorator.class, - CoverageDecorator.class, - BranchCoverageDecorator.class, - ItLineCoverageDecorator.class, - ItCoverageDecorator.class, - ItBranchCoverageDecorator.class, - DefaultResourcePermissions.class, - ApplyProjectRolesDecorator.class, - ExcludedResourceFilter.class, - CommentDensityDecorator.class, - NoSonarFilter.class, - DirectoriesDecorator.class, - FilesDecorator.class, - ReviewNotifications.class, - ReviewWorkflowDecorator.class, - ReferenceAnalysis.class, - ManualMeasureDecorator.class, - ManualViolationInjector.class, - ViolationSeverityUpdater.class, - IndexProjectPostJob.class, - ReviewsMeasuresDecorator.class, + // batch + ProfileSensor.class, + ProfileEventsSensor.class, + ProjectLinksSensor.class, + UnitTestDecorator.class, + VersionEventsSensor.class, + CheckAlertThresholds.class, + GenerateAlertEvents.class, + ViolationsDecorator.class, + WeightedViolationsDecorator.class, + ViolationsDensityDecorator.class, + LineCoverageDecorator.class, + CoverageDecorator.class, + BranchCoverageDecorator.class, + ItLineCoverageDecorator.class, + ItCoverageDecorator.class, + ItBranchCoverageDecorator.class, + DefaultResourcePermissions.class, + ApplyProjectRolesDecorator.class, + ExcludedResourceFilter.class, + CommentDensityDecorator.class, + NoSonarFilter.class, + DirectoriesDecorator.class, + FilesDecorator.class, + ReviewNotifications.class, + ReviewWorkflowDecorator.class, + ReferenceAnalysis.class, + ManualMeasureDecorator.class, + ManualViolationInjector.class, + ViolationSeverityUpdater.class, + IndexProjectPostJob.class, + ReviewsMeasuresDecorator.class, - // time machine - TendencyDecorator.class, - VariationDecorator.class, - ViolationTrackingDecorator.class, - ViolationPersisterDecorator.class, - NewViolationsDecorator.class, - TimeMachineConfigurationPersister.class, - NewCoverageFileAnalyzer.class, - NewItCoverageFileAnalyzer.class, - NewCoverageAggregator.class); + // time machine + TendencyDecorator.class, + VariationDecorator.class, + ViolationTrackingDecorator.class, + ViolationPersisterDecorator.class, + NewViolationsDecorator.class, + TimeMachineConfigurationPersister.class, + NewCoverageFileAnalyzer.class, + NewItCoverageFileAnalyzer.class, + NewCoverageAggregator.class); } } diff --git a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties index d1fb5fb7af7..e6020ecbdcc 100644 --- a/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties +++ b/plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties @@ -152,6 +152,8 @@ added_over_x_days=Added over {0} days added_since=Added since {0} added_since_previous_analysis=Added since previous analysis added_since_previous_analysis_detailed=Added since previous analysis ({0}) +added_since_previous_version=Added since previous version +added_since_previous_version_detailed=Added since previous version ({0}) added_since_version=Added since version {0} alerts_feed=Alerts feed all_violations=All violations @@ -167,6 +169,7 @@ deactivate_all=Deactivate all default_severity=Default severity default_sort_on=Default sort on delta_since_previous_analysis=Δ since previous analysis +delta_since_previous_version=Δ since previous version delta_over_x_days=Δ over {0} days delta_since=Δ since {0} delta_since_version=Δ since version {0} @@ -208,6 +211,8 @@ since_previous_analysis=since previous analysis since_previous_analysis_detailed=since previous analysis ({0}) since_version=since version {0} since_version_detailed=since version {0} ({1}) +since_previous_version=since previous version +since_previous_version_detailed=since previous version ({0}) time_changes=Time changes #------------------------------------------------------------------------------ diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java index d4ebebb4889..45020ddf099 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchModule.java @@ -29,8 +29,23 @@ import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.ProjectTree; -import org.sonar.batch.components.*; -import org.sonar.batch.index.*; +import org.sonar.batch.components.PastMeasuresLoader; +import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.batch.components.PastSnapshotFinderByDate; +import org.sonar.batch.components.PastSnapshotFinderByDays; +import org.sonar.batch.components.PastSnapshotFinderByPreviousAnalysis; +import org.sonar.batch.components.PastSnapshotFinderByPreviousVersion; +import org.sonar.batch.components.PastSnapshotFinderByVersion; +import org.sonar.batch.index.DefaultIndex; +import org.sonar.batch.index.DefaultPersistenceManager; +import org.sonar.batch.index.DefaultResourcePersister; +import org.sonar.batch.index.DependencyPersister; +import org.sonar.batch.index.EventPersister; +import org.sonar.batch.index.LinkPersister; +import org.sonar.batch.index.MeasurePersister; +import org.sonar.batch.index.MemoryOptimizer; +import org.sonar.batch.index.ReadOnlyPersistenceManager; +import org.sonar.batch.index.SourcePersister; import org.sonar.core.metric.CacheMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.rule.CacheRuleFinder; @@ -79,6 +94,7 @@ public class BatchModule extends Module { addCoreSingleton(PastSnapshotFinderByDays.class); addCoreSingleton(PastSnapshotFinderByPreviousAnalysis.class); addCoreSingleton(PastSnapshotFinderByVersion.class); + addCoreSingleton(PastSnapshotFinderByPreviousVersion.class); addCoreSingleton(PastMeasuresLoader.class); addCoreSingleton(PastSnapshotFinder.class); addCoreSingleton(DefaultNotificationManager.class); diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshot.java b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshot.java index ad5c54a59e9..f4087abe039 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshot.java +++ b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshot.java @@ -121,6 +121,13 @@ public class PastSnapshot { } return label; } + if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION)) { + String label = "Compare to previous version"; + if (isRelatedToSnapshot()) { + label += String.format(" (%s)", DateUtils.formatDate(getDate())); + } + return label; + } if (StringUtils.equals(mode, CoreProperties.TIMEMACHINE_MODE_DATE)) { String label = "Compare to date " + DateUtils.formatDate(getTargetDate()); if (isRelatedToSnapshot()) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java index 2734e165077..7968679b26e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinder.java @@ -39,13 +39,16 @@ public class PastSnapshotFinder implements BatchExtension { private PastSnapshotFinderByVersion finderByVersion; private PastSnapshotFinderByDate finderByDate; private PastSnapshotFinderByPreviousAnalysis finderByPreviousAnalysis; + private PastSnapshotFinderByPreviousVersion finderByPreviousVersion; public PastSnapshotFinder(PastSnapshotFinderByDays finderByDays, PastSnapshotFinderByVersion finderByVersion, - PastSnapshotFinderByDate finderByDate, PastSnapshotFinderByPreviousAnalysis finderByPreviousAnalysis) { + PastSnapshotFinderByDate finderByDate, PastSnapshotFinderByPreviousAnalysis finderByPreviousAnalysis, + PastSnapshotFinderByPreviousVersion finderByPreviousVersion) { this.finderByDays = finderByDays; this.finderByVersion = finderByVersion; this.finderByDate = finderByDate; this.finderByPreviousAnalysis = finderByPreviousAnalysis; + this.finderByPreviousVersion = finderByPreviousVersion; } public PastSnapshot find(Snapshot projectSnapshot, Configuration conf, int index) { @@ -94,7 +97,10 @@ public class PastSnapshotFinder implements BatchExtension { if (result == null) { result = findByPreviousAnalysis(projectSnapshot, property); if (result == null) { - result = findByVersion(projectSnapshot, property); + result = findByPreviousVersion(projectSnapshot, property); + if (result == null) { + result = findByVersion(projectSnapshot, property); + } } } } @@ -114,6 +120,14 @@ public class PastSnapshotFinder implements BatchExtension { return pastSnapshot; } + private PastSnapshot findByPreviousVersion(Snapshot projectSnapshot, String property) { + PastSnapshot pastSnapshot = null; + if (StringUtils.equals(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, property)) { + pastSnapshot = finderByPreviousVersion.findByPreviousVersion(projectSnapshot); + } + return pastSnapshot; + } + private PastSnapshot findByDate(Snapshot projectSnapshot, String property) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); try { diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersion.java b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersion.java new file mode 100644 index 00000000000..ea2f23ee7b1 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersion.java @@ -0,0 +1,61 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.components; + +import org.sonar.api.BatchExtension; +import org.sonar.api.CoreProperties; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Qualifiers; + +import java.util.Date; +import java.util.List; + +public class PastSnapshotFinderByPreviousVersion implements BatchExtension { + + private final DatabaseSession session; + + public PastSnapshotFinderByPreviousVersion(DatabaseSession session) { + this.session = session; + } + + PastSnapshot findByPreviousVersion(Snapshot projectSnapshot) { + String hql = "from " + Snapshot.class.getSimpleName() + + " where version!=:version AND version!='' AND resourceId=:resourceId AND status=:status AND qualifier<>:lib order by createdAt desc"; + List snapshots = session.createQuery(hql) + .setParameter("version", projectSnapshot.getVersion()) + .setParameter("resourceId", projectSnapshot.getResourceId()) + .setParameter("status", Snapshot.STATUS_PROCESSED) + .setParameter("lib", Qualifiers.LIBRARY) + .setMaxResults(1) + .getResultList(); + + PastSnapshot result; + if (snapshots.isEmpty()) { + result = new PastSnapshot(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION); + } else { + Snapshot snapshot = snapshots.get(0); + Date targetDate = snapshot.getCreatedAt(); + result = new PastSnapshot(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, targetDate, snapshot).setModeParameter(snapshot.getVersion()); + } + return result; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/TimeMachineConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/components/TimeMachineConfiguration.java index b5e567ce18d..f162a65dc2f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/components/TimeMachineConfiguration.java +++ b/sonar-batch/src/main/java/org/sonar/batch/components/TimeMachineConfiguration.java @@ -19,11 +19,7 @@ */ package org.sonar.batch.components; -import java.util.Date; -import java.util.List; - -import javax.persistence.Query; - +import com.google.common.collect.Lists; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; import org.slf4j.LoggerFactory; @@ -35,7 +31,10 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.utils.Logs; -import com.google.common.collect.Lists; +import javax.persistence.Query; + +import java.util.Date; +import java.util.List; public class TimeMachineConfiguration implements BatchExtension { @@ -83,6 +82,7 @@ public class TimeMachineConfiguration implements BatchExtension { snapshot.setResourceId(projectId.intValue()); snapshot.setCreatedAt(project.getAnalysisDate()); snapshot.setBuildDate(new Date()); + snapshot.setVersion(project.getAnalysisVersion()); } return snapshot; } diff --git a/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest.java b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest.java new file mode 100644 index 00000000000..793c800758d --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest.java @@ -0,0 +1,47 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.components; + +import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.database.model.Snapshot; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import static org.fest.assertions.Assertions.assertThat; + +public class PastSnapshotFinderByPreviousVersionTest extends AbstractDbUnitTestCase { + + @Test + public void shouldFindByPreviousVersion() { + setupData("shared"); + PastSnapshotFinderByPreviousVersion finder = new PastSnapshotFinderByPreviousVersion(getSession()); + + Snapshot currentProjectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1010); + PastSnapshot foundSnapshot = finder.findByPreviousVersion(currentProjectSnapshot); + assertThat(foundSnapshot.getProjectSnapshotId()).isEqualTo(1009); + assertThat(foundSnapshot.getMode()).isEqualTo(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION); + assertThat(foundSnapshot.getModeParameter()).isEqualTo("1.1"); + + // and test also another version to verify that unprocessed snapshots are ignored + currentProjectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1009); + assertThat(finder.findByPreviousVersion(currentProjectSnapshot).getProjectSnapshotId()).isEqualTo(1003); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByVersionTest.java b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByVersionTest.java index 3b8b61236ad..2c952a4cf9a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByVersionTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderByVersionTest.java @@ -37,18 +37,4 @@ public class PastSnapshotFinderByVersionTest extends AbstractDbUnitTestCase { assertThat(finder.findByVersion(currentProjectSnapshot, "1.1").getProjectSnapshotId()).isEqualTo(1009); } - /** - * Revert SONAR-3407 - */ - @Test - public void doNotFailIfUnknownVersion() { - setupData("shared"); - - Snapshot currentProjectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1010); - PastSnapshotFinderByVersion finder = new PastSnapshotFinderByVersion(getSession()); - - PastSnapshot pastSnapshot = finder.findByVersion(currentProjectSnapshot, "0.1.2"); - assertThat(pastSnapshot).isNotNull(); - assertThat(pastSnapshot.getDate()).isNull(); - } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderTest.java b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderTest.java index 5f03dce9160..f25775962c6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/components/PastSnapshotFinderTest.java @@ -19,10 +19,14 @@ */ package org.sonar.batch.components; +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.PropertiesConfiguration; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.sonar.api.CoreProperties; import org.sonar.api.database.model.Snapshot; @@ -38,25 +42,44 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class PastSnapshotFinderTest { + @Mock private PastSnapshotFinderByDays finderByDays; + @Mock private PastSnapshotFinderByDate finderByDate; + @Mock private PastSnapshotFinderByVersion finderByVersion; + @Mock private PastSnapshotFinderByPreviousAnalysis finderByPreviousAnalysis; + @Mock + private PastSnapshotFinderByPreviousVersion finderByPreviousVersion; + private PastSnapshotFinder finder; @Before public void initFinders() { - finderByDays = mock(PastSnapshotFinderByDays.class); - finderByDate = mock(PastSnapshotFinderByDate.class); - finderByVersion = mock(PastSnapshotFinderByVersion.class); - finderByPreviousAnalysis = mock(PastSnapshotFinderByPreviousAnalysis.class); - finder = new PastSnapshotFinder(finderByDays, finderByVersion, finderByDate, finderByPreviousAnalysis); + MockitoAnnotations.initMocks(this); + + finder = new PastSnapshotFinder(finderByDays, finderByVersion, finderByDate, finderByPreviousAnalysis, finderByPreviousVersion); + } + + @Test + public void shouldFind() { + Configuration conf = new BaseConfiguration(); + conf.addProperty("sonar.timemachine.period5", "1.2"); + + when(finderByVersion.findByVersion(null, "1.2")).thenReturn(new PastSnapshot("version", new Date(), new Snapshot())); + + PastSnapshot variationSnapshot = finder.find(null, conf, 5); + + verify(finderByVersion).findByVersion(null, "1.2"); + assertThat(variationSnapshot.getIndex(), is(5)); + assertThat(variationSnapshot.getMode(), is("version")); + assertThat(variationSnapshot.getProjectSnapshot(), not(nullValue())); } @Test @@ -136,6 +159,22 @@ public class PastSnapshotFinderTest { assertNull(variationSnapshot); } + @Test + public void shouldFindByPreviousVersion() throws ParseException { + final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + final Date date = format.parse("2010-05-18"); + Snapshot snapshot = new Snapshot(); + snapshot.setCreatedAt(date); + when(finderByPreviousVersion.findByPreviousVersion(null)).thenReturn(new PastSnapshot(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, date, snapshot)); + + PastSnapshot variationSnapshot = finder.find(null, 2, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION); + + verify(finderByPreviousVersion).findByPreviousVersion(null); + assertThat(variationSnapshot.getIndex(), is(2)); + assertThat(variationSnapshot.getMode(), is(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION)); + assertThat(variationSnapshot.getProjectSnapshot(), not(nullValue())); + } + @Test public void shouldFindByVersion() { when(finderByVersion.findByVersion(null, "1.2")).thenReturn(new PastSnapshot("version", new Date(), new Snapshot())); diff --git a/sonar-batch/src/test/resources/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest/shared.xml new file mode 100644 index 00000000000..98c40a5fc53 --- /dev/null +++ b/sonar-batch/src/test/resources/org/sonar/batch/components/PastSnapshotFinderByPreviousVersionTest/shared.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index 8bae9424b1c..17e0c48776e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -32,7 +32,6 @@ public interface CoreProperties { */ String ENCRYPTION_SECRET_KEY_PATH = "sonar.secretKeyPath"; - /** * @since 2.11 */ @@ -68,7 +67,6 @@ public interface CoreProperties { */ String CATEGORY_DIFFERENTIAL_VIEWS = "differentialViews"; - /* Global settings */ String SONAR_HOME = "SONAR_HOME"; String PROJECT_BRANCH_PROPERTY = "sonar.branch"; @@ -272,6 +270,7 @@ public interface CoreProperties { String TIMEMACHINE_MODE_DATE = "date"; String TIMEMACHINE_MODE_VERSION = "version"; String TIMEMACHINE_MODE_DAYS = "days"; + String TIMEMACHINE_MODE_PREVIOUS_VERSION = "previous_version"; String TIMEMACHINE_DEFAULT_PERIOD_1 = TIMEMACHINE_MODE_PREVIOUS_ANALYSIS; String TIMEMACHINE_DEFAULT_PERIOD_2 = "5"; String TIMEMACHINE_DEFAULT_PERIOD_3 = "30"; diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb index 871c773e30a..fb2dbf84bd2 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb @@ -102,6 +102,12 @@ module ApplicationHelper else label = message('since_previous_analysis') end + elsif mode=='previous_version' + unless mode_param.nil? + label = message('since_previous_version_detailed', :params => mode_param.to_s) + else + label = message('since_previous_version') + end elsif mode=='date' label = message('since_x', :params => date.strftime("%Y %b %d").to_s) end diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb index c46fa0cb7b6..1461413d936 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/dashboard_helper.rb @@ -21,7 +21,6 @@ module DashboardHelper include WidgetPropertiesHelper include MetricsHelper include FiltersHelper - def dashboard_action(action_name, opts={}) if @resource { :action => action_name, :did => @dashboard.id, :id => @resource.id }.merge!(opts) @@ -35,59 +34,65 @@ module DashboardHelper end def formatted_value(measure, default='') - measure ? measure.formatted_value : default + measure ? measure.formatted_value : default end def measure(metric_key) - @snapshot.measure(metric_key) + @snapshot.measure(metric_key) end def period_select_options(snapshot, index) - label=period_label(snapshot, index) - if label - selected=(params[:period]==index.to_s ? 'selected' : '') - "" - else - nil - end + label=period_label(snapshot, index) + if label + selected=(params[:period]==index.to_s ? 'selected' : '') + "" + else + nil + end end def violation_period_select_options(snapshot, index) - return nil if snapshot.nil? || snapshot.project_snapshot.nil? - mode=snapshot.project_snapshot.send "period#{index}_mode" - mode_param=snapshot.project_snapshot.send "period#{index}_param" - date=snapshot.project_snapshot.send "period#{index}_date" - - if mode - if mode=='days' - label = message('added_over_x_days', :params => mode_param.to_s) - elsif mode=='version' - label = message('added_since_version', :params => mode_param.to_s) - elsif mode=='previous_analysis' - if !date.nil? - label = message('added_since_previous_analysis_detailed', :params => date.strftime("%Y %b. %d").to_s) - else - label = message('added_since_previous_analysis') - end - elsif mode=='date' - label = message('added_since', :params => date.strftime("%Y %b %d").to_s) - end - if label - selected=(params[:period]==index.to_s ? 'selected' : '') - "" - end - else - nil - end + return nil if snapshot.nil? || snapshot.project_snapshot.nil? + mode=snapshot.project_snapshot.send "period#{index}_mode" + mode_param=snapshot.project_snapshot.send "period#{index}_param" + date=snapshot.project_snapshot.send "period#{index}_date" + + if mode + if mode=='days' + label = message('added_over_x_days', :params => mode_param.to_s) + elsif mode=='version' + label = message('added_since_version', :params => mode_param.to_s) + elsif mode=='previous_analysis' + if !date.nil? + label = message('added_since_previous_analysis_detailed', :params => date.strftime("%Y %b. %d").to_s) + else + label = message('added_since_previous_analysis') + end + elsif mode=='previous_version' + unless mode_param.nil? + label = message('added_since_previous_version_detailed', :params => mode_param.to_s) + else + label = message('added_since_previous_version') + end + elsif mode=='date' + label = message('added_since', :params => date.strftime("%Y %b %d").to_s) + end + if label + selected=(params[:period]==index.to_s ? 'selected' : '') + "" + end + else + nil + end end def measure_or_variation_value(measure) - if measure - @period_index ? measure.variation(@period_index) : measure.value - else - nil - end + if measure + @period_index ? measure.variation(@period_index) : measure.value + else + nil + end end def switch_to_widget_resource(widget) diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb index b608b9fae2b..1d303e9541a 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/helpers/filters_helper.rb @@ -72,6 +72,8 @@ module FiltersHelper def period_name(property) if property=='previous_analysis' message('delta_since_previous_analysis') + elsif property=='previous_version' + message('delta_since_previous_version') elsif property =~ /^[\d]+(\.[\d]+){0,1}$/ # is integer message('delta_over_x_days', :params => property) -- 2.39.5