@@ -20,15 +20,19 @@ | |||
package org.sonar.server.qualityprofile; | |||
import com.google.common.collect.ImmutableList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashSet; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.profiles.RulesProfile; | |||
import org.sonar.api.resources.Language; | |||
import org.sonar.api.resources.Languages; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; | |||
@@ -76,6 +80,7 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository | |||
} | |||
Map<String, Map<String, BuiltInQualityProfile>> rulesProfilesByLanguage = validateAndClean(context); | |||
this.qProfiles = toFlatList(rulesProfilesByLanguage); | |||
ensureAllLanguagesHaveAtLeastOneBuiltInQP(); | |||
profiler.stopDebug(); | |||
} | |||
@@ -86,22 +91,29 @@ public class BuiltInQProfileRepositoryImpl implements BuiltInQProfileRepository | |||
return qProfiles; | |||
} | |||
private void ensureAllLanguagesHaveAtLeastOneBuiltInQP() { | |||
Set<String> languagesWithBuiltInQProfiles = qProfiles.stream().map(BuiltInQProfile::getLanguage).collect(Collectors.toSet()); | |||
Set<String> languagesWithoutBuiltInQProfiles = Arrays.stream(languages.all()) | |||
.map(Language::getKey) | |||
.filter(key -> !languagesWithBuiltInQProfiles.contains(key)) | |||
.collect(Collectors.toSet()); | |||
checkState(languagesWithoutBuiltInQProfiles.isEmpty(), "The following languages have no built-in quality profiles: %s", | |||
languagesWithoutBuiltInQProfiles.stream().collect(Collectors.joining())); | |||
} | |||
private Map<String, Map<String, BuiltInQualityProfile>> validateAndClean(BuiltInQualityProfilesDefinition.Context context) { | |||
Map<String, Map<String, BuiltInQualityProfile>> profilesByLanguageAndName = context.profilesByLanguageAndName(); | |||
profilesByLanguageAndName.entrySet() | |||
.removeIf(entry -> { | |||
String language = entry.getKey(); | |||
if (languages.get(language) == null) { | |||
LOGGER.info("Language {} is not installed, related Quality profiles are ignored", language); | |||
return true; | |||
} | |||
Collection<BuiltInQualityProfile> profiles = entry.getValue().values(); | |||
if (profiles.isEmpty()) { | |||
LOGGER.warn("No Quality profiles defined for language: {}", language); | |||
LOGGER.info("Language {} is not installed, related quality profiles are ignored", language); | |||
return true; | |||
} | |||
return false; | |||
}); | |||
return profilesByLanguageAndName; | |||
} | |||
@@ -71,12 +71,13 @@ public class BuiltInQProfileRepositoryImplTest { | |||
} | |||
@Test | |||
public void initialize_creates_no_BuiltInQProfile_when_there_is_no_definition() { | |||
public void initialize_throws_ISE_if_language_has_no_builtin_qp() { | |||
BuiltInQProfileRepository underTest = new BuiltInQProfileRepositoryImpl(mock(DbClient.class), new Languages(FOO_LANGUAGE)); | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("The following languages have no built-in quality profiles: foo"); | |||
underTest.initialize(); | |||
assertThat(underTest.get()).isEmpty(); | |||
} | |||
@Test |
@@ -44,6 +44,7 @@ include 'tests:plugins:fake-governance-plugin' | |||
include 'tests:plugins:foo-plugin-v1' | |||
include 'tests:plugins:foo-plugin-v2' | |||
include 'tests:plugins:foo-plugin-v3' | |||
include 'tests:plugins:foo-plugin-without-qprofile' | |||
include 'tests:plugins:global-property-change-plugin' | |||
include 'tests:plugins:issue-filter-plugin' | |||
include 'tests:plugins:l10n-fr-pack' |
@@ -24,6 +24,7 @@ def pluginsForITs = [ | |||
':tests:plugins:foo-plugin-v1', | |||
':tests:plugins:foo-plugin-v2', | |||
':tests:plugins:foo-plugin-v3', | |||
':tests:plugins:foo-plugin-without-qprofile', | |||
':tests:plugins:global-property-change-plugin', | |||
':tests:plugins:issue-filter-plugin', | |||
':tests:plugins:l10n-fr-pack', |
@@ -0,0 +1,29 @@ | |||
sonarqube { | |||
skipProject = true | |||
} | |||
dependencies { | |||
compile 'com.google.guava:guava' | |||
compile 'commons-io:commons-io' | |||
compile 'commons-lang:commons-lang' | |||
compileOnly 'com.google.code.findbugs:jsr305' | |||
compileOnly project(path: ':sonar-plugin-api', configuration: 'shadow') | |||
} | |||
jar { | |||
manifest { | |||
attributes( | |||
'Plugin-Key': 'foo', | |||
'Plugin-Version': version, | |||
'Plugin-Class': 'org.sonar.foo.FooPlugin', | |||
'Plugin-ChildFirstClassLoader': 'false', | |||
'Sonar-Version': version, | |||
'SonarLint-Supported': 'false', | |||
'Plugin-Name': 'Foo', | |||
'Plugin-License': 'GNU LGPL 3' | |||
) | |||
} | |||
into('META-INF/lib') { | |||
from configurations.compile | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.foo; | |||
import org.sonar.api.resources.Language; | |||
public class Foo implements Language { | |||
public static final String KEY = "foo"; | |||
public static final String NAME = "Foo"; | |||
@Override | |||
public String getKey() { | |||
return KEY; | |||
} | |||
@Override | |||
public String getName() { | |||
return NAME; | |||
} | |||
@Override | |||
public String[] getFileSuffixes() { | |||
return new String[0]; | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.foo; | |||
import org.sonar.api.Plugin; | |||
/** | |||
* Plugin entry-point, as declared in pom.xml. | |||
*/ | |||
public class FooPlugin implements Plugin { | |||
@Override | |||
public void define(Context context) { | |||
context.addExtension( | |||
Foo.class); | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.foo; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -28,6 +28,7 @@ import org.sonarqube.tests.issue.IssueCreationDatePluginChangedTest; | |||
import org.sonarqube.tests.marketplace.UpdateCenterTest; | |||
import org.sonarqube.tests.qualityProfile.ActiveRuleEsResilienceTest; | |||
import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesNotificationTest; | |||
import org.sonarqube.tests.qualityProfile.PluginWithoutBuiltinQualityProfile; | |||
import org.sonarqube.tests.rule.RuleEsResilienceTest; | |||
import org.sonarqube.tests.serverSystem.BlueGreenTest; | |||
import org.sonarqube.tests.serverSystem.RestartTest; | |||
@@ -64,6 +65,7 @@ import org.sonarqube.tests.user.UserEsResilienceTest; | |||
HttpHeadersAuthenticationTest.class, | |||
OnboardingTest.class, | |||
BuiltInQualityProfilesNotificationTest.class, | |||
PluginWithoutBuiltinQualityProfile.class, | |||
ActiveRuleEsResilienceTest.class, | |||
AnalysisEsResilienceTest.class, | |||
RuleEsResilienceTest.class, |
@@ -0,0 +1,58 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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.sonarqube.tests.qualityProfile; | |||
import com.sonar.orchestrator.Orchestrator; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
import org.apache.commons.io.FileUtils; | |||
import org.junit.After; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static util.ItUtils.newOrchestratorBuilder; | |||
import static util.ItUtils.pluginArtifact; | |||
public class PluginWithoutBuiltinQualityProfile { | |||
private static Orchestrator orchestrator; | |||
@Test | |||
public void should_fail_if_plugin_defines_language_and_no_builtin_qprofile() throws IOException { | |||
orchestrator = newOrchestratorBuilder() | |||
.addPlugin(pluginArtifact("foo-plugin-without-qprofile")) | |||
.build(); | |||
try { | |||
orchestrator.start(); | |||
fail("Expected to fail to start"); | |||
} catch (IllegalStateException e) { | |||
String logs = FileUtils.readFileToString(orchestrator.getServer().getWebLogs(), StandardCharsets.UTF_8); | |||
assertThat(logs).contains("java.lang.IllegalStateException: The following languages have no built-in quality profiles: foo"); | |||
} | |||
} | |||
@After | |||
public void after() { | |||
if (orchestrator != null) { | |||
orchestrator.stop(); | |||
} | |||
} | |||
} |