From 88cc2bbfbda30d8ba8148dd4a7d0392a3171fe50 Mon Sep 17 00:00:00 2001 From: Elena Vilchik Date: Fri, 22 Jun 2018 18:03:05 +0200 Subject: Support import of external stylelint issues report (#65) --- .../java/org/sonar/css/plugin/CssPluginTest.java | 2 +- .../org/sonar/css/plugin/CssRuleSensorTest.java | 1 - .../sonar/css/plugin/CssRulesDefinitionTest.java | 27 ++- .../css/plugin/StylelintReportSensorTest.java | 212 +++++++++++++++++++++ 4 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintReportSensorTest.java (limited to 'sonar-css-plugin/src/test/java/org/sonar/css') diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java index 1cbade9..c5d9bbd 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java @@ -36,6 +36,6 @@ public class CssPluginTest { Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(8); + assertThat(context.getExtensions()).hasSize(9); } } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java index f86e24f..471c4ef 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java @@ -60,7 +60,6 @@ public class CssRuleSensorTest { sensor.describe(sensorDescriptor); assertThat(sensorDescriptor.name()).isEqualTo("SonarCSS Rules"); assertThat(sensorDescriptor.languages()).containsOnly("css"); - assertThat(sensorDescriptor.type()).isEqualTo(Type.MAIN); } @Test diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java index 7dffb35..167aeef 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java @@ -27,14 +27,35 @@ import static org.assertj.core.api.Assertions.assertThat; public class CssRulesDefinitionTest { @Test - public void test() { - CssRulesDefinition rulesDefinition = new CssRulesDefinition(); + public void test_with_external_rules() { + CssRulesDefinition rulesDefinition = new CssRulesDefinition(true); + RulesDefinition.Context context = new RulesDefinition.Context(); + rulesDefinition.define(context); + + assertThat(context.repositories()).hasSize(2); + RulesDefinition.Repository mainRepository = context.repository("css"); + RulesDefinition.Repository externalRepository = context.repository("external_stylelint"); + + assertThat(externalRepository.name()).isEqualTo("stylelint"); + assertThat(externalRepository.language()).isEqualTo("css"); + assertThat(externalRepository.isExternal()).isEqualTo(true); + assertThat(externalRepository.rules()).hasSize(170); + + assertThat(mainRepository.name()).isEqualTo("SonarAnalyzer"); + assertThat(mainRepository.language()).isEqualTo("css"); + assertThat(mainRepository.isExternal()).isEqualTo(false); + assertThat(mainRepository.rules()).hasSize(CssRules.getRuleClasses().size()); + } + + @Test + public void test_no_external_rules() throws Exception { + CssRulesDefinition rulesDefinition = new CssRulesDefinition(false); RulesDefinition.Context context = new RulesDefinition.Context(); rulesDefinition.define(context); - RulesDefinition.Repository repository = context.repository("css"); assertThat(context.repositories()).hasSize(1); + RulesDefinition.Repository repository = context.repository("css"); assertThat(repository.name()).isEqualTo("SonarAnalyzer"); assertThat(repository.language()).isEqualTo("css"); assertThat(repository.isExternal()).isEqualTo(false); diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintReportSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintReportSensorTest.java new file mode 100644 index 0000000..bbbc8d8 --- /dev/null +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintReportSensorTest.java @@ -0,0 +1,212 @@ +/* + * SonarCSS + * Copyright (C) 2018-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.css.plugin; + +import java.io.File; +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.rules.RuleType; +import org.sonar.api.utils.Version; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; + +import static org.assertj.core.api.Assertions.assertThat; + +public class StylelintReportSensorTest { + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + @Rule + public final LogTester logTester = new LogTester(); + + private static final File BASE_DIR = new File("src/test/resources/stylelint-report/").getAbsoluteFile(); + private static final String CONTENT = ".foo {\n}"; + + private SensorContextTester context = SensorContextTester.create(BASE_DIR); + private static final CheckFactory EMPTY_CHECK_FACTORY = new CheckFactory(new TestActiveRules()); + private static final CheckFactory CHECK_FACTORY_WITH_RULE = new CheckFactory(new TestActiveRules("S4647")); + + private StylelintReportSensor stylelintReportSensor = new StylelintReportSensor(EMPTY_CHECK_FACTORY); + private DefaultInputFile inputFile = createInputFile(context, CONTENT, "file.css"); + + @Before + public void setUp() throws Exception { + context.setRuntime(getRuntime(7, 2)); + context.fileSystem().add(inputFile); + } + + @Test + public void should_add_issues_from_report() throws Exception { + setReport("report.json"); + stylelintReportSensor.execute(context); + + Collection externalIssues = context.allExternalIssues(); + assertThat(externalIssues).hasSize(2); + Iterator iterator = externalIssues.iterator(); + ExternalIssue first = iterator.next(); + ExternalIssue second = iterator.next(); + + assertThat(first.type()).isEqualTo(RuleType.BUG); + assertThat(second.type()).isEqualTo(RuleType.CODE_SMELL); + + assertThat(first.remediationEffort()).isEqualTo(5); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.primaryLocation().message()).isEqualTo("external issue message (color-no-invalid-hex)"); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(1); + } + + @Test + public void should_support_absolute_file_paths_in_report() throws Exception { + String report = "[\n" + + " {\n" + + " \"source\": \"%s\",\n" + + " \"warnings\": [\n" + + " {\n" + + " \"line\": 1,\n" + + " \"rule\": \"color\\-no\\-invalid-hex\",\n" + + " \"text\": \"external issue message\"\n" + + " }\n" + + " ]\n" + + " }\n" + + "]\n"; + + File reportFile = tmpDir.newFile(); + FileWriter writer = new FileWriter(reportFile); + writer.write(String.format(report, inputFile.absolutePath())); + writer.close(); + + setReport(reportFile.getAbsolutePath()); + stylelintReportSensor.execute(context); + + assertThat(context.allExternalIssues()).hasSize(1); + } + + @Test + public void should_skip_duplicates() throws Exception { + // when in SQ CSS profile there is an activated rule matching to an external issue, + // that external issue is ignored + setReport("report.json"); + new StylelintReportSensor(CHECK_FACTORY_WITH_RULE).execute(context); + + Collection externalIssues = context.allExternalIssues(); + assertThat(externalIssues).hasSize(1); + assertThat(externalIssues.iterator().next().primaryLocation().message()).isEqualTo("external issue message (comment-no-empty)"); + + assertThat(logTester.logs(LoggerLevel.DEBUG)) + .contains("Stylelint issue for rule 'color-no-invalid-hex' is skipped because this rule is activated in your SonarQube profile for CSS (rule key in SQ css:S4647)"); + } + + @Test + public void should_ignore_report_on_older_sonarqube() throws Exception { + context.setRuntime(getRuntime(7, 1)); + setReport("report.json"); + stylelintReportSensor.execute(context); + + assertThat(context.allExternalIssues()).isEmpty(); + assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Import of external issues requires SonarQube 7.2 or greater."); + } + + @Test + public void should_do_nothing_when_no_report() throws Exception { + setReport(""); + stylelintReportSensor.execute(context); + + assertThat(context.allExternalIssues()).isEmpty(); + } + + @Test + public void should_log_when_not_existing_report_file() throws Exception { + setReport("not-exist.json"); + stylelintReportSensor.execute(context); + + assertThat(context.allExternalIssues()).isEmpty(); + assertThat(logTester.logs(LoggerLevel.ERROR)).contains("No issues information will be saved as the report file can't be read."); + } + + @Test + public void should_log_when_not_found_input_file() throws Exception { + setReport("invalid-file.json"); + stylelintReportSensor.execute(context); + + assertThat(context.allExternalIssues()).hasSize(1); + assertThat(logTester.logs(LoggerLevel.WARN)).contains("No input file found for not-exist.css. No stylelint issues will be imported on this file."); + } + + @Test + public void should_accept_absolute_path_to_report() throws Exception { + setReport(new File(BASE_DIR, "report.json").getAbsolutePath()); + stylelintReportSensor.execute(context); + assertThat(context.allExternalIssues()).hasSize(2); + } + + @Test + public void should_accept_several_reports() throws Exception { + setReport("report.json, invalid-file.json"); + stylelintReportSensor.execute(context); + assertThat(context.allExternalIssues()).hasSize(3); + } + + @Test + public void test_descriptor() throws Exception { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + stylelintReportSensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of stylelint issues"); + assertThat(sensorDescriptor.languages()).containsOnly(CssLanguage.KEY); + } + + private void setReport(String reportFileName) { + context.settings().setProperty(CssPlugin.STYLELINT_REPORT_PATHS, reportFileName); + } + + private SonarRuntime getRuntime(int major, int minor) { + return SonarRuntimeImpl.forSonarQube(Version.create(major, minor), SonarQubeSide.SERVER); + } + + private static DefaultInputFile createInputFile(SensorContextTester sensorContext, String content, String relativePath) { + DefaultInputFile testInputFile = new TestInputFileBuilder("moduleKey", relativePath) + .setModuleBaseDir(sensorContext.fileSystem().baseDirPath()) + .setType(Type.MAIN) + .setLanguage(CssLanguage.KEY) + .setCharset(StandardCharsets.UTF_8) + .setContents(content) + .build(); + + sensorContext.fileSystem().add(testInputFile); + return testInputFile; + } +} -- cgit v1.2.3