From 81abdbffd8c1adcf0020f31606df1e0b90bd0890 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 26 Jun 2015 11:35:47 +0200 Subject: [PATCH] First draft of integration tests --- it/plugins/pom.xml | 32 +++ it/pom.xml | 37 ++++ .../xoo-sample/sonar-project.properties | 5 + .../xoo-sample/src/sample/Sample.xoo | 12 ++ .../xoo-sample/src/sample/Sample.xoo.measures | 11 + .../xoo-sample/src/sample/Sample2.xoo | 12 ++ .../src/sample/Sample2.xoo.measures | 10 + it/tests/pom.xml | 94 +++++++++ .../src/test/java/org/sonar/it/ItUtils.java | 63 ++++++ .../sonar/it/qualitygate/QualityGateTest.java | 194 ++++++++++++++++++ .../rule/AbstractDeprecatedXooRuleSensor.java | 71 +++++++ .../sonar/xoo/rule/CustomMessageSensor.java | 59 ++++++ .../java/org/sonar/xoo/rule/HasTagSensor.java | 85 ++++++++ .../rule/OneBlockerIssuePerFileSensor.java | 57 +++++ .../sonar/xoo/rule/OneIssuePerFileSensor.java | 60 ++++++ .../xoo/rule/OneIssuePerModuleSensor.java | 59 ++++++ .../org/sonar/xoo/rule/XooQualityProfile.java | 2 +- .../sonar/xoo/rule/XooRulesDefinition.java | 67 +++--- .../xoo/rule/XooRulesDefinitionTest.java | 19 +- pom.xml | 12 +- 20 files changed, 916 insertions(+), 45 deletions(-) create mode 100644 it/plugins/pom.xml create mode 100644 it/pom.xml create mode 100644 it/projects/qualitygate/xoo-sample/sonar-project.properties create mode 100644 it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo create mode 100644 it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo.measures create mode 100644 it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo create mode 100644 it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo.measures create mode 100644 it/tests/pom.xml create mode 100644 it/tests/src/test/java/org/sonar/it/ItUtils.java create mode 100644 it/tests/src/test/java/org/sonar/it/qualitygate/QualityGateTest.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AbstractDeprecatedXooRuleSensor.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CustomMessageSensor.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/HasTagSensor.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneBlockerIssuePerFileSensor.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerFileSensor.java create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerModuleSensor.java diff --git a/it/plugins/pom.xml b/it/plugins/pom.xml new file mode 100644 index 00000000000..75d3eeda78c --- /dev/null +++ b/it/plugins/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + + org.sonarsource.it + it-parent + 5.2-SNAPSHOT + + plugins + SonarQube Integration Tests :: Plugins + The fake plugins used by integration tests + pom + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + + + + diff --git a/it/pom.xml b/it/pom.xml new file mode 100644 index 00000000000..cbb65650c6b --- /dev/null +++ b/it/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + org.codehaus.sonar + sonar + 5.2-SNAPSHOT + + + org.sonarsource.it + it-parent + 5.2-SNAPSHOT + SonarQube Integration Tests + pom + + + plugins + tests + + + + + org.codehaus.sonar.plugins + sonar-xoo-plugin + ${pom.version} + provided + + + org.codehaus.sonar + sonar-application + ${pom.version} + provided + + + diff --git a/it/projects/qualitygate/xoo-sample/sonar-project.properties b/it/projects/qualitygate/xoo-sample/sonar-project.properties new file mode 100644 index 00000000000..7fe05081304 --- /dev/null +++ b/it/projects/qualitygate/xoo-sample/sonar-project.properties @@ -0,0 +1,5 @@ +sonar.projectKey=sample +sonar.projectName=Sample +sonar.projectVersion=1.0-SNAPSHOT +sonar.sources=src +sonar.language=xoo diff --git a/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo b/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo new file mode 100644 index 00000000000..b1210973dd9 --- /dev/null +++ b/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo @@ -0,0 +1,12 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod() { + return "hello"; + } +} diff --git a/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo.measures b/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo.measures new file mode 100644 index 00000000000..4f638a7a00b --- /dev/null +++ b/it/projects/qualitygate/xoo-sample/src/sample/Sample.xoo.measures @@ -0,0 +1,11 @@ +ncloc:13 +complexity:7 +coverage:89.0 +test_execution_time:630 +sqale_index:4830 +#Used by dashboard/widgets tests +complexity_in_classes:3 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo b/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo new file mode 100644 index 00000000000..6c1b6459c34 --- /dev/null +++ b/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo @@ -0,0 +1,12 @@ +package sample; + +public class Sample { + + public Sample(int i) { + int j = i++; + } + + private String myMethod2() { + return "hello"; + } +} diff --git a/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo.measures b/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo.measures new file mode 100644 index 00000000000..768513ad374 --- /dev/null +++ b/it/projects/qualitygate/xoo-sample/src/sample/Sample2.xoo.measures @@ -0,0 +1,10 @@ +ncloc:13 +complexity:7 +coverage:89.0 +test_execution_time:630 +sqale_index:4830 +complexity_in_classes:3 +classes:1 +comment_lines:3 +public_api:5 +public_undocumented_api:2 diff --git a/it/tests/pom.xml b/it/tests/pom.xml new file mode 100644 index 00000000000..9249fd32ad7 --- /dev/null +++ b/it/tests/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + org.sonarsource.it + it-parent + 5.2-SNAPSHOT + + tests + SonarQube Integration Tests :: Tests + + + * + -Xmx128m -server + + + + + org.codehaus.sonar + sonar-ws-client + 5.0 + test + + + com.sonarsource.orchestrator + sonar-orchestrator + 3.2 + test + + + junit + junit + test + + + org.assertj + assertj-core + test + + + org.hamcrest + hamcrest-all + test + + + com.googlecode.json-simple + json-simple + 1.1 + test + + + org.skyscreamer + jsonassert + 1.2.0 + test + + + + + + org.apache.commons + commons-email + 1.2 + + + org.subethamail + subethasmtp + 3.1.6 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org/sonar/it/${category}/suite/*TestSuite.java + + org/sonar/it/${category}/*Test.java + + + + org/sonar/it/*/suite/*Test.java + + + + + + + diff --git a/it/tests/src/test/java/org/sonar/it/ItUtils.java b/it/tests/src/test/java/org/sonar/it/ItUtils.java new file mode 100644 index 00000000000..aba015afea8 --- /dev/null +++ b/it/tests/src/test/java/org/sonar/it/ItUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009-2014 SonarSource SA + * All rights reserved + * mailto:contact AT sonarsource DOT com + */ +package org.sonar.it; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.sonar.orchestrator.locator.FileLocation; +import java.io.File; +import org.apache.commons.io.FileUtils; + +public class ItUtils { + + private ItUtils() { + } + + private static final Supplier HOME_DIR = Suppliers.memoize(new Supplier() { + @Override + public File get() { + File dir = new File("it"); + + // intellij way + if (dir.exists() && dir.isDirectory()) { + return dir.getParentFile(); + } + + // maven way + dir = new File("../it"); + if (dir.exists() && dir.isDirectory()) { + return dir.getParentFile(); + } + + throw new IllegalStateException("Fail to locate home directory"); + } + }); + + public static FileLocation xooPlugin() { + File target = new File(HOME_DIR.get(), "plugins/sonar-xoo-plugin/target"); + if (target.exists()) { + for (File jar : FileUtils.listFiles(target, new String[] {"jar"}, false)) { + if (jar.getName().startsWith("sonar-xoo-plugin-") && !jar.getName().contains("-source")) { + return FileLocation.of(jar); + } + } + } + throw new IllegalStateException("XOO plugin is not built"); + } + + /** + * Locate the directory of sample project + * + * @param relativePath path related to the directory it/projects, for example "qualitygate/xoo-sample" + */ + public static File projectDir(String relativePath) { + File dir = new File(HOME_DIR.get(), "it/projects/" + relativePath); + if (!dir.exists() || !dir.isDirectory()) { + throw new IllegalStateException("Directory does not exist: " + dir.getAbsolutePath()); + } + return dir; + } +} diff --git a/it/tests/src/test/java/org/sonar/it/qualitygate/QualityGateTest.java b/it/tests/src/test/java/org/sonar/it/qualitygate/QualityGateTest.java new file mode 100644 index 00000000000..3fbefcaf26b --- /dev/null +++ b/it/tests/src/test/java/org/sonar/it/qualitygate/QualityGateTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2009-2014 SonarSource SA + * All rights reserved + * mailto:contact AT sonarsource DOT com + */ +package org.sonar.it.qualitygate; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarRunner; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.it.ItUtils; +import org.sonar.wsclient.project.NewProject; +import org.sonar.wsclient.qualitygate.NewCondition; +import org.sonar.wsclient.qualitygate.QualityGate; +import org.sonar.wsclient.qualitygate.QualityGateClient; +import org.sonar.wsclient.services.Measure; +import org.sonar.wsclient.services.Resource; +import org.sonar.wsclient.services.ResourceQuery; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.it.ItUtils.projectDir; + +public class QualityGateTest { + + private static final String PROJECT_KEY = "sample"; + + private long provisionnedProjectId = -1L; + + @ClassRule + public static Orchestrator orchestrator = Orchestrator.builderEnv() + .addPlugin(ItUtils.xooPlugin()) + .build(); + + @Before + public void cleanUp() throws Exception { + orchestrator.resetData(); + provisionnedProjectId = Long.parseLong(orchestrator.getServer().adminWsClient().projectClient().create(NewProject.create().key(PROJECT_KEY).name("Sample")).id()); + } + + @Test + public void do_not_compute_status_if_no_gate() { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchResourceWithGateStatus()).isNull(); + } + + @Test + public void status_ok_if_empty_gate() { + QualityGate empty = qgClient().create("Empty"); + qgClient().setDefault(empty.id()); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchGateStatus().getData()).isEqualTo("OK"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(empty.id()); + } + } + + @Test + public void test_status_ok() { + QualityGate simple = qgClient().create("SimpleWithHighThreshold"); + qgClient().setDefault(simple.id()); + qgClient().createCondition(NewCondition.create(simple.id()).metricKey("ncloc").operator("GT").warningThreshold("40")); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchGateStatus().getData()).isEqualTo("OK"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(simple.id()); + } + } + + @Test + public void test_status_warning() { + QualityGate simple = qgClient().create("SimpleWithLowThreshold"); + qgClient().setDefault(simple.id()); + qgClient().createCondition(NewCondition.create(simple.id()).metricKey("ncloc").operator("GT").warningThreshold("10")); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchGateStatus().getData()).isEqualTo("WARN"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(simple.id()); + } + + } + + @Test + public void test_status_error() { + QualityGate simple = qgClient().create("SimpleWithLowThreshold"); + qgClient().setDefault(simple.id()); + qgClient().createCondition(NewCondition.create(simple.id()).metricKey("ncloc").operator("GT").errorThreshold("10")); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchGateStatus().getData()).isEqualTo("ERROR"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(simple.id()); + } + } + + @Test + public void use_server_settings_instead_of_default_gate() { + QualityGate alert = qgClient().create("AlertWithLowThreshold"); + qgClient().createCondition(NewCondition.create(alert.id()).metricKey("ncloc").operator("GT").warningThreshold("10")); + QualityGate error = qgClient().create("ErrorWithLowThreshold"); + qgClient().createCondition(NewCondition.create(error.id()).metricKey("ncloc").operator("GT").errorThreshold("10")); + + qgClient().setDefault(alert.id()); + qgClient().selectProject(error.id(), provisionnedProjectId); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + + assertThat(fetchGateStatus().getData()).isEqualTo("ERROR"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(alert.id()); + qgClient().destroy(error.id()); + } + } + + @Test + public void conditions_on_multiple_metric_types() { + QualityGate allTypes = qgClient().create("AllMetricTypes"); + qgClient().createCondition(NewCondition.create(allTypes.id()).metricKey("ncloc").operator("GT").warningThreshold("10")); + qgClient().createCondition(NewCondition.create(allTypes.id()).metricKey("duplicated_lines_density").operator("GT").warningThreshold("20")); + qgClient().setDefault(allTypes.id()); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")) + .setProperty("sonar.cpd.xoo.minimumLines", "2") + .setProperty("sonar.cpd.xoo.minimumTokens", "5"); + orchestrator.executeBuild(build); + + Measure alertStatus = fetchGateStatus(); + assertThat(alertStatus.getData()).isEqualTo("WARN"); + assertThat(alertStatus.getAlertText()) + .contains("Lines of code > 10") + .contains("Duplicated lines (%) > 20"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(allTypes.id()); + } + } + + @Test + public void compute_gate_status_on_metric_variation() { + QualityGate simple = qgClient().create("SimpleWithDifferential"); + qgClient().setDefault(simple.id()); + qgClient().createCondition(NewCondition.create(simple.id()).metricKey("ncloc").period(1).operator("EQ").warningThreshold("0")); + + try { + SonarRunner build = SonarRunner.create(projectDir("qualitygate/xoo-sample")); + orchestrator.executeBuild(build); + assertThat(fetchGateStatus().getData()).isEqualTo("OK"); + + orchestrator.executeBuild(build); + assertThat(fetchGateStatus().getData()).isEqualTo("WARN"); + } finally { + qgClient().unsetDefault(); + qgClient().destroy(simple.id()); + } + } + + private Measure fetchGateStatus() { + return fetchResourceWithGateStatus().getMeasure("alert_status"); + } + + private Resource fetchResourceWithGateStatus() { + return orchestrator.getServer().getWsClient().find(ResourceQuery.createForMetrics(PROJECT_KEY, "alert_status").setIncludeAlerts(true)); + } + + private static QualityGateClient qgClient() { + return orchestrator.getServer().adminWsClient().qualityGateClient(); + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AbstractDeprecatedXooRuleSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AbstractDeprecatedXooRuleSensor.java new file mode 100644 index 00000000000..31eeefdbcb3 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/AbstractDeprecatedXooRuleSensor.java @@ -0,0 +1,71 @@ +/* + * 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.xoo.rule; + +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.xoo.Xoo; + +/** + * @deprecated to be replaced by {@link org.sonar.api.batch.sensor.Sensor} + */ +public abstract class AbstractDeprecatedXooRuleSensor implements Sensor { + + private final FileSystem fs; + private final ActiveRules activeRules; + + public AbstractDeprecatedXooRuleSensor(FileSystem fs, ActiveRules activeRules) { + this.fs = fs; + this.activeRules = activeRules; + } + + protected abstract String getRuleKey(); + + @Override + public boolean shouldExecuteOnProject(Project project) { + return fs.hasFiles(fs.predicates().hasLanguages(Xoo.KEY)) + && (activeRules.find(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, getRuleKey())) != null); + } + + @Override + public final void analyse(Project project, SensorContext context) { + doAnalyse(context, Xoo.KEY); + } + + private void doAnalyse(SensorContext context, String languageKey) { + RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, getRuleKey()); + if (activeRules.find(ruleKey) == null) { + return; + } + for (InputFile inputFile : fs.inputFiles(fs.predicates().hasLanguage(languageKey))) { + File sonarFile = File.create(inputFile.relativePath()); + sonarFile = context.getResource(sonarFile); + processFile(inputFile, sonarFile, context, ruleKey, languageKey); + } + } + + protected abstract void processFile(InputFile inputFile, File sonarFile, SensorContext context, RuleKey ruleKey, String languageKey); +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CustomMessageSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CustomMessageSensor.java new file mode 100644 index 00000000000..2738c485bba --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CustomMessageSensor.java @@ -0,0 +1,59 @@ +/* + * 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.xoo.rule; + +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; +import org.sonar.api.issue.Issuable; +import org.sonar.api.rule.RuleKey; + +public class CustomMessageSensor extends AbstractDeprecatedXooRuleSensor { + + public static final String RULE_KEY = "CustomMessage"; + + private static final String MESSAGE_PROPERTY = "sonar.customMessage.message"; + + private final ResourcePerspectives perspectives; + private final Settings settings; + + public CustomMessageSensor(ResourcePerspectives perspectives, Settings settings, FileSystem fs, ActiveRules activeRules) { + super(fs, activeRules); + this.perspectives = perspectives; + this.settings = settings; + } + + @Override + protected String getRuleKey() { + return RULE_KEY; + } + + @Override + protected void processFile(InputFile inputFile, org.sonar.api.resources.File sonarFile, SensorContext context, RuleKey ruleKey, String languageKey) { + Issuable issuable = perspectives.as(Issuable.class, sonarFile); + issuable.addIssue(issuable.newIssueBuilder() + .ruleKey(ruleKey) + .message(settings.getString(MESSAGE_PROPERTY)) + .build()); + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/HasTagSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/HasTagSensor.java new file mode 100644 index 00000000000..1294f271818 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/HasTagSensor.java @@ -0,0 +1,85 @@ +/* + * 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.xoo.rule; + +import java.io.IOException; +import java.util.List; +import org.apache.commons.io.FileUtils; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; +import org.sonar.api.issue.Issuable; +import org.sonar.api.rule.RuleKey; + +/** + * Generate issues on all the occurrences of a given tag in xoo sources. + */ +public class HasTagSensor extends AbstractDeprecatedXooRuleSensor { + + public static final String RULE_KEY = "HasTag"; + + private static final String EFFORT_TO_FIX_PROPERTY = "sonar.hasTag.effortToFix"; + + private final ResourcePerspectives perspectives; + private final Settings settings; + private final ActiveRules activeRules; + + private FileSystem fs; + + public HasTagSensor(FileSystem fs, ResourcePerspectives perspectives, Settings settings, ActiveRules activeRules) { + super(fs, activeRules); + this.fs = fs; + this.perspectives = perspectives; + this.settings = settings; + this.activeRules = activeRules; + } + + @Override + protected String getRuleKey() { + return RULE_KEY; + } + + @Override + protected void processFile(InputFile inputFile, org.sonar.api.resources.File sonarFile, SensorContext context, RuleKey ruleKey, String languageKey) { + org.sonar.api.batch.rule.ActiveRule activeRule = activeRules.find(ruleKey); + String tag = activeRule.param("tag"); + if (tag == null) { + throw new IllegalStateException("Rule is badly configured. The parameter 'tag' is missing."); + } + try { + Issuable issuable = perspectives.as(Issuable.class, sonarFile); + List lines = FileUtils.readLines(inputFile.file(), fs.encoding().name()); + for (int index = 0; index < lines.size(); index++) { + if (lines.get(index).contains(tag)) { + issuable.addIssue(issuable.newIssueBuilder() + .effortToFix(settings.getDouble(EFFORT_TO_FIX_PROPERTY)) + .line(index + 1) + .ruleKey(ruleKey) + .build()); + } + } + } catch (IOException e) { + throw new IllegalStateException("Fail to process " + inputFile, e); + } + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneBlockerIssuePerFileSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneBlockerIssuePerFileSensor.java new file mode 100644 index 00000000000..c1ba09fd539 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneBlockerIssuePerFileSensor.java @@ -0,0 +1,57 @@ +/* + * 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.xoo.rule; + +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; + +public class OneBlockerIssuePerFileSensor extends AbstractDeprecatedXooRuleSensor { + + public static final String RULE_KEY = "OneBlockerIssuePerFile"; + + private final ResourcePerspectives perspectives; + + public OneBlockerIssuePerFileSensor(ResourcePerspectives perspectives, FileSystem fs, ActiveRules activeRules) { + super(fs, activeRules); + this.perspectives = perspectives; + } + + @Override + protected String getRuleKey() { + return RULE_KEY; + } + + @Override + protected void processFile(InputFile inputFile, org.sonar.api.resources.File sonarFile, SensorContext context, RuleKey ruleKey, String languageKey) { + Issuable issuable = perspectives.as(Issuable.class, sonarFile); + issuable.addIssue(issuable.newIssueBuilder() + .ruleKey(ruleKey) + .severity(Severity.BLOCKER) + .message("This issue is generated on each file. Severity is blocker, whatever quality profile") + .build()); + } + +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerFileSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerFileSensor.java new file mode 100644 index 00000000000..d2a3c409d09 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerFileSensor.java @@ -0,0 +1,60 @@ +/* + * 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.xoo.rule; + +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.config.Settings; +import org.sonar.api.issue.Issuable; +import org.sonar.api.rule.RuleKey; + +public class OneIssuePerFileSensor extends AbstractDeprecatedXooRuleSensor { + + public static final String RULE_KEY = "OneIssuePerFile"; + + private static final String EFFORT_TO_FIX_PROPERTY = "sonar.oneIssuePerFile.effortToFix"; + + private final ResourcePerspectives perspectives; + private final Settings settings; + + public OneIssuePerFileSensor(ResourcePerspectives perspectives, Settings settings, FileSystem fs, ActiveRules activeRules) { + super(fs, activeRules); + this.perspectives = perspectives; + this.settings = settings; + } + + @Override + protected String getRuleKey() { + return RULE_KEY; + } + + @Override + protected void processFile(InputFile inputFile, org.sonar.api.resources.File sonarFile, SensorContext context, RuleKey ruleKey, String languageKey) { + Issuable issuable = perspectives.as(Issuable.class, sonarFile); + issuable.addIssue(issuable.newIssueBuilder() + .ruleKey(ruleKey) + .effortToFix(settings.getDouble(EFFORT_TO_FIX_PROPERTY)) + .message("This issue is generated on each file") + .build()); + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerModuleSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerModuleSensor.java new file mode 100644 index 00000000000..26ed7499297 --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerModuleSensor.java @@ -0,0 +1,59 @@ +/* + * 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.xoo.rule; + +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.component.ResourcePerspectives; +import org.sonar.api.issue.Issuable; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.xoo.Xoo; + +public class OneIssuePerModuleSensor implements Sensor { + + public static final String RULE_KEY = "OneIssuePerModule"; + + private final ResourcePerspectives perspectives; + private final FileSystem fs; + private final ActiveRules activeRules; + + public OneIssuePerModuleSensor(ResourcePerspectives perspectives, FileSystem fs, ActiveRules activeRules) { + this.perspectives = perspectives; + this.fs = fs; + this.activeRules = activeRules; + } + + @Override + public void analyse(Project project, SensorContext context) { + Issuable issuable = perspectives.as(Issuable.class, project); + issuable.addIssue(issuable.newIssueBuilder() + .ruleKey(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY)) + .message("This issue is generated on each module") + .build()); + } + + @Override + public boolean shouldExecuteOnProject(Project project) { + return fs.hasFiles(fs.predicates().hasLanguages(Xoo.KEY)) && (activeRules.find(RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY)) != null); + } +} diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooQualityProfile.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooQualityProfile.java index f5143163b57..b2a011fd383 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooQualityProfile.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooQualityProfile.java @@ -32,7 +32,7 @@ public class XooQualityProfile extends ProfileDefinition { public RulesProfile createProfile(ValidationMessages validation) { final RulesProfile profile = RulesProfile.create("Basic", Xoo.KEY); - profile.activateRule(Rule.create(XooRulesDefinition.XOO_REPOSITORY, "x1"), RulePriority.MAJOR); + profile.activateRule(Rule.create(XooRulesDefinition.XOO_REPOSITORY, HasTagSensor.RULE_KEY), RulePriority.MAJOR); return profile; } diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java index 7880f8a4b69..4843a0525f9 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java @@ -19,13 +19,9 @@ */ package org.sonar.xoo.rule; -import org.sonar.api.rule.RuleStatus; -import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.rule.RulesDefinition; -import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader; import org.sonar.xoo.Xoo; -import org.sonar.xoo.checks.Check; /** * Define all the coding rules that are supported on the repository named "xoo". @@ -36,41 +32,50 @@ public class XooRulesDefinition implements RulesDefinition { @Override public void define(Context context) { - NewRepository repository = context.createRepository(XOO_REPOSITORY, Xoo.KEY).setName("Xoo"); + NewRepository repo = context.createRepository(XOO_REPOSITORY, Xoo.KEY).setName("Xoo"); - // Load checks - new RulesDefinitionAnnotationLoader().load(repository, Check.ALL); + NewRule hasTag = repo.createRule(HasTagSensor.RULE_KEY).setName("Has Tag") + .setHtmlDescription("Search for a given tag in Xoo files"); + hasTag.setDebtSubCharacteristic(RulesDefinition.SubCharacteristics.READABILITY) + .setDebtRemediationFunction(hasTag.debtRemediationFunctions().constantPerIssue("2min")); + hasTag.createParam("tag") + .setDefaultValue("xoo") + .setDescription("The tag to search for"); - // define a single rule programmatically. Note that rules - // can be loaded from JSON or XML files too. - NewRule x1Rule = repository.createRule("x1") - .setName("No empty line") - .setMarkdownDescription("Generate an issue on *empty* lines of Xoo source files") + NewRule ruleWithParameters = repo.createRule("RuleWithParameters").setName("Rule with parameters") + .setHtmlDescription("Rule containing parameter of different types : boolean, integer, etc. For information, no issue will be linked to this rule."); + ruleWithParameters.createParam("string").setType(RuleParamType.STRING); + ruleWithParameters.createParam("text").setType(RuleParamType.TEXT); + ruleWithParameters.createParam("boolean").setType(RuleParamType.BOOLEAN); + ruleWithParameters.createParam("integer").setType(RuleParamType.INTEGER); + ruleWithParameters.createParam("float").setType(RuleParamType.FLOAT); - // optional tags - .setTags("style", "security") + NewRule oneIssuePerLine = repo.createRule(OneIssuePerLineSensor.RULE_KEY).setName("One Issue Per Line") + .setHtmlDescription("Generate an issue on each line of a file. It requires the metric \"lines\"."); + oneIssuePerLine.setDebtSubCharacteristic(RulesDefinition.SubCharacteristics.MEMORY_EFFICIENCY) + .setDebtRemediationFunction(hasTag.debtRemediationFunctions().linear("1min")) + .setEffortToFixDescription("It takes about 1 minute to an experienced software craftsman to remove a line of code"); - // optional status. Default value is READY. - .setStatus(RuleStatus.BETA) + NewRule oneIssuePerFile = repo.createRule(OneIssuePerFileSensor.RULE_KEY).setName("One Issue Per File") + .setHtmlDescription("Generate an issue on each file"); + oneIssuePerFile.setDebtSubCharacteristic(RulesDefinition.SubCharacteristics.ARCHITECTURE_CHANGEABILITY) + .setDebtRemediationFunction(hasTag.debtRemediationFunctions().linear("10min")); - // default severity when the rule is activated on a Quality profile. Default value is MAJOR. - .setSeverity(Severity.MINOR); + NewRule oneIssuePerModule = repo.createRule(OneIssuePerModuleSensor.RULE_KEY).setName("One Issue Per Module") + .setHtmlDescription("Generate an issue on each module"); + oneIssuePerModule.setDebtSubCharacteristic(RulesDefinition.SubCharacteristics.API_ABUSE) + .setDebtRemediationFunction(hasTag.debtRemediationFunctions().linearWithOffset("25min", "1h")) + .setEffortToFixDescription("A certified architect will need roughly half an hour to start working on removal of modules, " + + "then it's about one hour per module."); - // debt-related information - x1Rule - .setDebtSubCharacteristic(SubCharacteristics.INTEGRATION_TESTABILITY) - .setDebtRemediationFunction(x1Rule.debtRemediationFunctions().linearWithOffset("1h", "30min")) - .setEffortToFixDescription("Effort to fix issue on one line"); + repo.createRule(OneBlockerIssuePerFileSensor.RULE_KEY).setName("One Blocker Issue Per File") + .setHtmlDescription("Generate a blocker issue on each file, whatever the severity declared in the Quality profile"); - x1Rule.createParam("acceptWhitespace") - .setDefaultValue("false") - .setType(RuleParamType.BOOLEAN) - .setDescription("= Accept whitespace (``\\s|\\t``) on the line\nThis property is available so that a line containing only whitespace is not considered empty.\n" - + "== Example with property set to ``false``\n``xoo\n <- One issue here\n<- And one here\n``\n\n" - + "== Example with property set to ``true``\n``xoo\n <- No issue here\n<- But one here\n``\n"); + repo.createRule(CustomMessageSensor.RULE_KEY).setName("Issue With Custom Message") + .setHtmlDescription("Generate an issue on each file with a custom message"); + + repo.done(); - // don't forget to call done() to finalize the definition - repository.done(); } } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java index 7d1d8385e83..399f77cf3a6 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/rule/XooRulesDefinitionTest.java @@ -37,17 +37,14 @@ public class XooRulesDefinitionTest { assertThat(repo).isNotNull(); assertThat(repo.name()).isEqualTo("Xoo"); assertThat(repo.language()).isEqualTo("xoo"); - assertThat(repo.rules()).hasSize(2); + assertThat(repo.rules()).hasSize(7); - RulesDefinition.Rule x1 = repo.rule("x1"); - assertThat(x1.key()).isEqualTo("x1"); - assertThat(x1.tags()).containsOnly("style", "security"); - assertThat(x1.markdownDescription()).isNotEmpty(); - - assertThat(x1.debtSubCharacteristic()).isEqualTo(RulesDefinition.SubCharacteristics.INTEGRATION_TESTABILITY); - assertThat(x1.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET); - assertThat(x1.debtRemediationFunction().coefficient()).isEqualTo("1h"); - assertThat(x1.debtRemediationFunction().offset()).isEqualTo("30min"); - assertThat(x1.effortToFixDescription()).isEqualTo("Effort to fix issue on one line"); + RulesDefinition.Rule rule = repo.rule(OneIssuePerLineSensor.RULE_KEY); + assertThat(rule.name()).isNotEmpty(); + assertThat(rule.debtSubCharacteristic()).isEqualTo(RulesDefinition.SubCharacteristics.MEMORY_EFFICIENCY); + assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR); + assertThat(rule.debtRemediationFunction().coefficient()).isEqualTo("1min"); + assertThat(rule.debtRemediationFunction().offset()).isNull(); + assertThat(rule.effortToFixDescription()).isNotEmpty(); } } diff --git a/pom.xml b/pom.xml index f1421deae85..0132290d622 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.codehaus.sonar @@ -1134,7 +1135,6 @@ - org.apache.tomcat.embed @@ -1613,6 +1613,14 @@ true + + + + it + + it + + -- 2.39.5