]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-926 Improve error reporting when invalid use of sonar.profile
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 17 Feb 2014 15:55:45 +0000 (16:55 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Mon, 17 Feb 2014 15:56:26 +0000 (16:56 +0100)
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java
sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java

index 9bef04cd813da3f63ca3cfefdee3626e17a50152..8cc3ee7cee9852606354bd63858e3b29bacf404a 100644 (file)
@@ -47,30 +47,31 @@ public final class PhaseExecutor {
       InitializersExecutor.class, ProjectInitializer.class, UpdateStatusJob.class);
   }
 
-  private EventBus eventBus;
-  private Phases phases;
-  private DecoratorsExecutor decoratorsExecutor;
-  private MavenPhaseExecutor mavenPhaseExecutor;
-  private MavenPluginsConfigurator mavenPluginsConfigurator;
-  private PostJobsExecutor postJobsExecutor;
-  private InitializersExecutor initializersExecutor;
-  private SensorsExecutor sensorsExecutor;
-  private UpdateStatusJob updateStatusJob;
-  private PersistenceManager persistenceManager;
-  private SensorContext sensorContext;
-  private DefaultIndex index;
-  private ProjectInitializer pi;
-  private ScanPersister[] persisters;
-  private FileSystemLogger fsLogger;
+  private final EventBus eventBus;
+  private final Phases phases;
+  private final DecoratorsExecutor decoratorsExecutor;
+  private final MavenPhaseExecutor mavenPhaseExecutor;
+  private final MavenPluginsConfigurator mavenPluginsConfigurator;
+  private final PostJobsExecutor postJobsExecutor;
+  private final InitializersExecutor initializersExecutor;
+  private final SensorsExecutor sensorsExecutor;
+  private final UpdateStatusJob updateStatusJob;
+  private final PersistenceManager persistenceManager;
+  private final SensorContext sensorContext;
+  private final DefaultIndex index;
+  private final ProjectInitializer pi;
+  private final ScanPersister[] persisters;
+  private final FileSystemLogger fsLogger;
   private final JsonReport jsonReport;
-  private DefaultModuleFileSystem fs;
+  private final DefaultModuleFileSystem fs;
+  private final ProfileLogger profileLogger;
 
   public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
     MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
     PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
     PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
     EventBus eventBus, UpdateStatusJob updateStatusJob, ProjectInitializer pi,
-    ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs) {
+    ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, ProfileLogger profileLogger) {
     this.phases = phases;
     this.decoratorsExecutor = decoratorsExecutor;
     this.mavenPhaseExecutor = mavenPhaseExecutor;
@@ -88,15 +89,16 @@ public final class PhaseExecutor {
     this.fsLogger = fsLogger;
     this.jsonReport = jsonReport;
     this.fs = fs;
+    this.profileLogger = profileLogger;
   }
 
   public PhaseExecutor(Phases phases, DecoratorsExecutor decoratorsExecutor, MavenPhaseExecutor mavenPhaseExecutor,
     MavenPluginsConfigurator mavenPluginsConfigurator, InitializersExecutor initializersExecutor,
     PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
     PersistenceManager persistenceManager, SensorContext sensorContext, DefaultIndex index,
-    EventBus eventBus, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs) {
+    EventBus eventBus, ProjectInitializer pi, ScanPersister[] persisters, FileSystemLogger fsLogger, JsonReport jsonReport, DefaultModuleFileSystem fs, ProfileLogger profileLogger) {
     this(phases, decoratorsExecutor, mavenPhaseExecutor, mavenPluginsConfigurator, initializersExecutor, postJobsExecutor,
-      sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport, fs);
+      sensorsExecutor, persistenceManager, sensorContext, index, eventBus, null, pi, persisters, fsLogger, jsonReport, fs, profileLogger);
   }
 
   /**
@@ -117,6 +119,9 @@ public final class PhaseExecutor {
       // Index and lock the filesystem
       fs.index();
 
+      // Log detected languages and their profiles after FS is indexed and languages detected
+      profileLogger.execute();
+
       sensorsExecutor.execute(sensorContext);
     }
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java b/sonar-batch/src/main/java/org/sonar/batch/phases/ProfileLogger.java
new file mode 100644 (file)
index 0000000..5ec98bc
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.phases;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.ModuleLanguages;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Language;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+
+public class ProfileLogger implements BatchComponent {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ProfileLogger.class);
+
+  private final Settings settings;
+  private final ModuleLanguages languages;
+  private final ModuleQProfiles profiles;
+
+  public ProfileLogger(Settings settings, ModuleLanguages languages, ModuleQProfiles profiles) {
+    this.settings = settings;
+    this.languages = languages;
+    this.profiles = profiles;
+  }
+
+  public void execute() {
+    execute(LOG);
+  }
+
+  @VisibleForTesting
+  void execute(Logger logger) {
+    String defaultName = settings.getString(ModuleQProfiles.SONAR_PROFILE_PROP);
+    boolean defaultNameUsed = StringUtils.isBlank(defaultName);
+    for (Language lang : languages.languages()) {
+      QProfile profile = profiles.findByLanguage(lang.getKey());
+      logger.info("Quality profile for {}: {}", lang.getName(), profile.name());
+      if (StringUtils.isNotBlank(defaultName) && defaultName.equals(profile.name())) {
+        defaultNameUsed = true;
+      }
+    }
+    if (!defaultNameUsed) {
+      throw new SonarException("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration.");
+    }
+  }
+
+}
index e83e86acd48472947d1caf9cfc5c3da6a2be5715..24ba6b2136b04a4672705458c526b2bf80165a05 100644 (file)
@@ -21,8 +21,6 @@ package org.sonar.batch.rule;
 
 import com.google.common.collect.ImmutableMap;
 import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.sonar.api.BatchComponent;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Language;
@@ -41,7 +39,7 @@ import java.util.Map;
  */
 public class ModuleQProfiles implements BatchComponent {
 
-  private static final Logger LOG = LoggerFactory.getLogger(ModuleQProfiles.class);
+  public static final String SONAR_PROFILE_PROP = "sonar.profile";
 
   public static class QProfile {
     private final String name, language;
@@ -83,17 +81,17 @@ public class ModuleQProfiles implements BatchComponent {
 
   public ModuleQProfiles(Settings settings, Languages languages, QualityProfileDao dao) {
     ImmutableMap.Builder<String, QProfile> builder = ImmutableMap.builder();
-    String defaultName = settings.getString("sonar.profile");
+    String defaultName = settings.getString(SONAR_PROFILE_PROP);
 
     for (Language language : languages.all()) {
-      QProfile profile;
+      QProfile profile = null;
       if (StringUtils.isNotBlank(defaultName)) {
         profile = loadDefaultQProfile(dao, defaultName, language.getKey());
-      } else {
+      }
+      if (profile == null) {
         profile = loadQProfile(dao, settings, language.getKey());
       }
       if (profile != null) {
-        LOG.info("Quality profile for {}: {}", profile.language(), profile.name());
         builder.put(profile.language(), profile);
       }
     }
index f8524749ec807324362069ba00d4259796a004a3..b6279b2ed27653e12f7771dbee5f43ba716eb8fb 100644 (file)
@@ -52,7 +52,10 @@ public class RulesProfileProvider extends ProviderAdapter {
 
   private RulesProfile loadSingleLanguageProfile(ModuleQProfiles qProfiles, String language, ProfilesDao dao) {
     ModuleQProfiles.QProfile qProfile = qProfiles.findByLanguage(language);
-    return new RulesProfileWrapper(select(qProfile, dao));
+    if (qProfile != null) {
+      return new RulesProfileWrapper(select(qProfile, dao));
+    }
+    return new RulesProfileWrapper(Lists.<RulesProfile>newArrayList());
   }
 
   private RulesProfile loadProfiles(ModuleQProfiles qProfiles, ProfilesDao dao) {
index ab7733a95abc51d7f982ca668c3602041cb5c80e..fbb5c6b94655c12fc16166c8209fa4526c691408 100644 (file)
@@ -47,6 +47,7 @@ import org.sonar.batch.issue.IssueFilters;
 import org.sonar.batch.issue.ModuleIssues;
 import org.sonar.batch.phases.PhaseExecutor;
 import org.sonar.batch.phases.PhasesTimeProfiler;
+import org.sonar.batch.phases.ProfileLogger;
 import org.sonar.batch.rule.ActiveRulesProvider;
 import org.sonar.batch.rule.ModuleQProfiles;
 import org.sonar.batch.rule.QProfileSensor;
@@ -119,6 +120,7 @@ public class ModuleScanContainer extends ComponentContainer {
       DefaultModuleFileSystem.class,
       ModuleFileSystemInitializer.class,
       ProjectFileSystemAdapter.class,
+      ProfileLogger.class,
 
       // the Snapshot component will be removed when asynchronous measures are improved (required for AsynchronousMeasureSensor)
       getComponentByType(ResourcePersister.class).getSnapshot(module),
diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/ProfileLoggerTest.java
new file mode 100644 (file)
index 0000000..eb2d468
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.phases;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.slf4j.Logger;
+import org.sonar.api.batch.ModuleLanguages;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.AbstractLanguage;
+import org.sonar.api.resources.Language;
+import org.sonar.api.utils.SonarException;
+import org.sonar.batch.rule.ModuleQProfiles;
+import org.sonar.batch.rule.ModuleQProfiles.QProfile;
+
+import java.util.List;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ProfileLoggerTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  private ModuleLanguages languages;
+  private ModuleQProfiles profiles;
+  private Settings settings;
+
+  @Before
+  public void prepare() {
+    languages = mock(ModuleLanguages.class);
+    List<Language> languageList = Lists.newArrayList();
+    languageList.add(new AbstractLanguage("java", "Java") {
+
+      @Override
+      public String[] getFileSuffixes() {
+        return null;
+      }
+    });
+    languageList.add(new AbstractLanguage("cobol", "Cobol") {
+
+      @Override
+      public String[] getFileSuffixes() {
+        return null;
+      }
+    });
+    when(languages.languages()).thenReturn(languageList);
+
+    profiles = mock(ModuleQProfiles.class);
+    QProfile javaProfile = mock(QProfile.class);
+    when(javaProfile.name()).thenReturn("My Java profile");
+    when(profiles.findByLanguage("java")).thenReturn(javaProfile);
+    QProfile cobolProfile = mock(QProfile.class);
+    when(cobolProfile.name()).thenReturn("My Cobol profile");
+    when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile);
+
+    settings = new Settings();
+  }
+
+  @Test
+  public void should_log_all_used_profiles() {
+
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+    Logger logger = mock(Logger.class);
+    profileLogger.execute(logger);
+
+    verify(logger).info("Quality profile for {}: {}", "Java", "My Java profile");
+    verify(logger).info("Quality profile for {}: {}", "Cobol", "My Cobol profile");
+  }
+
+  @Test
+  public void should_fail_if_default_profile_not_used() {
+    settings.setProperty("sonar.profile", "Unknow");
+
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("sonar.profile was set to 'Unknow' but didn't match any profile for any language. Please check your configuration.");
+
+    profileLogger.execute();
+
+  }
+
+  @Test
+  public void should_not_fail_if_default_profile_used_at_least_once() {
+    settings.setProperty("sonar.profile", "My Java profile");
+
+    ProfileLogger profileLogger = new ProfileLogger(settings, languages, profiles);
+
+    profileLogger.execute();
+  }
+}
index 09a9c7bded27d1c68dd6b03f6340287359c7816d..23ec48aa37a9a0c06b7f0480079490fde6e71ad4 100644 (file)
@@ -68,7 +68,7 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase {
   }
 
   @Test
-  public void use_deprecated_property() throws Exception {
+  public void use_sonar_profile_property() throws Exception {
     setupData("shared");
     QualityProfileDao dao = new QualityProfileDao(getMyBatis());
 
@@ -78,14 +78,20 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase {
     ModuleQProfiles moduleQProfiles = new ModuleQProfiles(settings, languages, dao);
     List<ModuleQProfiles.QProfile> qProfiles = Lists.newArrayList(moduleQProfiles.findAll());
 
-    assertThat(qProfiles).hasSize(1);
+    assertThat(qProfiles).hasSize(2);
     ModuleQProfiles.QProfile javaProfile = qProfiles.get(0);
     assertThat(javaProfile.id()).isEqualTo(2);
     assertThat(javaProfile.name()).isEqualTo("Java Two");
     assertThat(javaProfile.language()).isEqualTo("java");
     assertThat(javaProfile.version()).isEqualTo(20);
 
-    // the php profile is not found as sonar.profile overrides all other properties.
+    // Fallback to sonar.profile.php if no match for sonar.profile
+    ModuleQProfiles.QProfile phpProfile = qProfiles.get(1);
+    assertThat(phpProfile.id()).isEqualTo(3);
+    assertThat(phpProfile.name()).isEqualTo("Php One");
+    assertThat(phpProfile.language()).isEqualTo("php");
+    assertThat(phpProfile.version()).isEqualTo(30);
+
   }
 
   @Test