diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-05-19 10:50:08 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-05-19 11:31:11 +0200 |
commit | 3bd0d648f1ea9b8d8724ec0cd2b3a8d13ff90951 (patch) | |
tree | b64ea01ca143829d8089301c20de866cb51ef187 /plugins | |
parent | 0bab6ec48f16ad6b83c80997cfa6fb69edd3930d (diff) | |
download | sonarqube-3bd0d648f1ea9b8d8724ec0cd2b3a8d13ff90951.tar.gz sonarqube-3bd0d648f1ea9b8d8724ec0cd2b3a8d13ff90951.zip |
SONAR-5216 Generate profile events for multi language projects
Diffstat (limited to 'plugins')
4 files changed, 85 insertions, 279 deletions
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 6a7499e2332..eb8468ba5b6 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,11 @@ 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.core.timemachine.Periods; import org.sonar.plugins.core.batch.IndexProjectPostJob; @@ -28,19 +32,89 @@ 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.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.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.AlertsWidget; +import org.sonar.plugins.core.widgets.BubbleChartWidget; +import org.sonar.plugins.core.widgets.ComplexityWidget; +import org.sonar.plugins.core.widgets.CoverageWidget; +import org.sonar.plugins.core.widgets.CustomMeasuresWidget; +import org.sonar.plugins.core.widgets.DescriptionWidget; +import org.sonar.plugins.core.widgets.DocumentationCommentsWidget; +import org.sonar.plugins.core.widgets.DuplicationsWidget; +import org.sonar.plugins.core.widgets.EventsWidget; +import org.sonar.plugins.core.widgets.HotspotMetricWidget; +import org.sonar.plugins.core.widgets.HotspotMostViolatedResourcesWidget; +import org.sonar.plugins.core.widgets.HotspotMostViolatedRulesWidget; +import org.sonar.plugins.core.widgets.ItCoverageWidget; +import org.sonar.plugins.core.widgets.ProjectFileCloudWidget; +import org.sonar.plugins.core.widgets.SizeWidget; +import org.sonar.plugins.core.widgets.TechnicalDebtPyramidWidget; +import org.sonar.plugins.core.widgets.TimeMachineWidget; +import org.sonar.plugins.core.widgets.TimelineWidget; +import org.sonar.plugins.core.widgets.TreemapWidget; +import org.sonar.plugins.core.widgets.WelcomeWidget; +import org.sonar.plugins.core.widgets.issues.ActionPlansWidget; +import org.sonar.plugins.core.widgets.issues.FalsePositiveIssuesWidget; +import org.sonar.plugins.core.widgets.issues.IssueFilterWidget; +import org.sonar.plugins.core.widgets.issues.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.List; @@ -285,7 +359,6 @@ public final class CorePlugin extends SonarPlugin { NewFalsePositiveNotificationDispatcher.newMetadata(), // batch - ProfileEventsSensor.class, ProjectLinksSensor.class, UnitTestDecorator.class, VersionEventsSensor.class, diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileEventsSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileEventsSensor.java deleted file mode 100644 index c21c6304458..00000000000 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileEventsSensor.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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.sensors; - -import org.sonar.api.batch.*; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Project; -import org.sonar.batch.rule.RulesProfileWrapper; - -import java.util.List; - -public class ProfileEventsSensor implements Sensor { - - private final RulesProfile profile; - private final TimeMachine timeMachine; - private final FileSystem fs; - - public ProfileEventsSensor(RulesProfile profile, TimeMachine timeMachine, FileSystem fs) { - this.profile = profile; - this.timeMachine = timeMachine; - this.fs = fs; - } - - public boolean shouldExecuteOnProject(Project project) { - // Views will define a fake profile - return profile instanceof RulesProfileWrapper; - } - - public void analyse(Project project, SensorContext context) { - RulesProfileWrapper profileWrapper = (RulesProfileWrapper) profile; - for (String languageKey : fs.languages()) { - RulesProfile realProfile = profileWrapper.getProfileByLanguage(languageKey); - Measure pastProfileMeasure = getPreviousMeasure(project, CoreMetrics.PROFILE); - if (pastProfileMeasure == null) { - // first analysis - return; - } - int pastProfileId = pastProfileMeasure.getIntValue(); - Measure pastProfileVersionMeasure = getPreviousMeasure(project, CoreMetrics.PROFILE_VERSION); - final int pastProfileVersion; - // first analysis with versions - if (pastProfileVersionMeasure == null) { - pastProfileVersion = 1; - } else { - pastProfileVersion = pastProfileVersionMeasure.getIntValue(); - } - String pastProfile = formatProfileDescription(pastProfileMeasure.getData(), pastProfileVersion); - - int currentProfileId = realProfile.getId(); - int currentProfileVersion = realProfile.getVersion(); - String currentProfile = formatProfileDescription(realProfile.getName(), currentProfileVersion); - - if ((pastProfileId != currentProfileId) || (pastProfileVersion != currentProfileVersion)) { - // A different profile is used for this project or new version of same profile - context.createEvent(project, currentProfile, currentProfile + " is used instead of " + pastProfile, Event.CATEGORY_PROFILE, null); - } - } - } - - private static String formatProfileDescription(String name, int version) { - return name + " version " + version; - } - - private Measure getPreviousMeasure(Project project, Metric metric) { - TimeMachineQuery query = new TimeMachineQuery(project) - .setOnlyLastAnalysis(true) - .setMetrics(metric); - List<Measure> measures = timeMachine.getMeasures(query); - if (measures.isEmpty()) { - return null; - } - return measures.get(0); - } - - @Override - public String toString() { - return getClass().getSimpleName(); - } -} diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb index 77931f40854..1d0fcb9c409 100644 --- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb +++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/description.html.erb @@ -25,7 +25,7 @@ <td><%= message('widget.description.profiles') -%>:</td> <td><span id="resource_profile"> <% profiles.each_with_index do |profile, i| %> - <%= Api::Utils.language_name(profile['language']) -%>: <%= link_to profile['name'], {:controller => '/rules_configuration', :action => 'index', :id => profile['id']}, :id => profile['language'] + '_profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => profile['version']) -%>) + <%= Api::Utils.language_name(profile['language']) -%>: <%= link_to profile['name'], {:controller => '/profiles', :action => 'show', :id => profile['id']}, :id => profile['language'] + '_profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => profile['version']) -%>) <% if i < (profiles.size - 1) %> <br/> <% end %> @@ -39,7 +39,7 @@ %> <tr> <td><%= message('widget.description.profile') -%>:</td> - <td><span id="resource_profile"><%= link_to profile_measure.data, {:controller => '/rules_configuration', :action => 'index', :id => profile_measure.value.to_i}, :id => 'profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => format_measure('profile_version', :default => '1')) -%>)</td> + <td><span id="resource_profile"><%= link_to profile_measure.data, {:controller => '/profiles', :action => 'show', :id => profile_measure.value.to_i}, :id => 'profile_link' -%></span> (<%= message('widget.description.profile_version_x', :params => format_measure('profile_version', :default => '1')) -%>)</td> </tr> <% end end %> diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileEventsSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileEventsSensorTest.java deleted file mode 100644 index 41e50402699..00000000000 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileEventsSensorTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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.sensors; - -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.batch.Event; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.batch.TimeMachine; -import org.sonar.api.batch.TimeMachineQuery; -import org.sonar.api.batch.fs.FileSystem; -import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Project; -import org.sonar.batch.rule.RulesProfileWrapper; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.*; - -public class ProfileEventsSensorTest { - - Project project; - SensorContext context; - FileSystem fs; - RulesProfileWrapper wrapper; - RulesProfile profile; - - @Before - public void prepare() { - project = mock(Project.class); - context = mock(SensorContext.class); - - fs = new DefaultFileSystem().addLanguages("java"); - profile = mock(RulesProfile.class); - when(profile.getLanguage()).thenReturn("java"); - wrapper = new RulesProfileWrapper(profile); - } - - @Test - public void shouldExecuteWhenProfileWithId() { - when(profile.getId()).thenReturn(123); - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, null, fs); - - assertThat(sensor.shouldExecuteOnProject(project)).isTrue(); - verifyZeroInteractions(project); - } - - @Test - public void shouldNotExecuteIfProfileIsNotWrapper() { - RulesProfile profile = mock(RulesProfile.class); - when(profile.getId()).thenReturn(null); - ProfileEventsSensor sensor = new ProfileEventsSensor(profile, null, fs); - - assertThat(sensor.shouldExecuteOnProject(project)).isFalse(); - verifyZeroInteractions(project); - } - - @Test - public void shouldDoNothingIfNoProfileChange() { - mockProfileWithVersion(1); - TimeMachine timeMachine = mockTM(22.0, "Foo", 1.0); // Same profile, same version - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, timeMachine, fs); - - sensor.analyse(project, context); - - verifyZeroInteractions(context); - } - - @Test - public void shouldCreateEventIfProfileChange() { - mockProfileWithVersion(1); - TimeMachine timeMachine = mockTM(21.0, "Bar", 1.0); // Different profile - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, timeMachine, fs); - - sensor.analyse(project, context); - - verify(context).createEvent(same(project), - eq("Foo version 1"), - eq("Foo version 1 is used instead of Bar version 1"), - same(Event.CATEGORY_PROFILE), any(Date.class)); - } - - @Test - public void shouldCreateEventIfProfileVersionChange() { - mockProfileWithVersion(2); - TimeMachine timeMachine = mockTM(22.0, "Foo", 1.0); // Same profile, different version - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, timeMachine, fs); - - sensor.analyse(project, context); - - verify(context).createEvent(same(project), - eq("Foo version 2"), - eq("Foo version 2 is used instead of Foo version 1"), - same(Event.CATEGORY_PROFILE), any(Date.class)); - } - - @Test - public void shouldNotCreateEventIfFirstAnalysis() { - mockProfileWithVersion(2); - TimeMachine timeMachine = mockTM(null, null); - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, timeMachine, fs); - - sensor.analyse(project, context); - - verifyZeroInteractions(context); - } - - @Test - public void shouldCreateEventIfFirstAnalysisWithVersionsAndVersionMoreThan1() { - mockProfileWithVersion(2); - TimeMachine timeMachine = mockTM(22.0, "Foo", null); - ProfileEventsSensor sensor = new ProfileEventsSensor(wrapper, timeMachine, fs); - - sensor.analyse(project, context); - - verify(context).createEvent(same(project), - eq("Foo version 2"), - eq("Foo version 2 is used instead of Foo version 1"), - same(Event.CATEGORY_PROFILE), any(Date.class)); - } - - private void mockProfileWithVersion(int version) { - when(profile.getId()).thenReturn(22); - when(profile.getName()).thenReturn("Foo"); - when(profile.getVersion()).thenReturn(version); - } - - private TimeMachine mockTM(double profileId, String profileName, Double versionValue) { - return mockTM(new Measure(CoreMetrics.PROFILE, profileId, profileName), versionValue == null ? null : new Measure(CoreMetrics.PROFILE_VERSION, versionValue)); - } - - private TimeMachine mockTM(Measure result1, Measure result2) { - TimeMachine timeMachine = mock(TimeMachine.class); - - when(timeMachine.getMeasures(any(TimeMachineQuery.class))) - .thenReturn(result1 == null ? Collections.<Measure>emptyList() : Arrays.asList(result1)) - .thenReturn(result2 == null ? Collections.<Measure>emptyList() : Arrays.asList(result2)); - - return timeMachine; - } -} |