diff options
Diffstat (limited to 'sonar-css-plugin/src')
5 files changed, 205 insertions, 8 deletions
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java index 07b4feb..65e6453 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java @@ -34,6 +34,7 @@ public class CssPlugin implements Plugin { @Override public void define(Context context) { context.addExtensions( + MetricSensor.class, CssLanguage.class, SonarWayProfile.class, diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java new file mode 100644 index 0000000..fb9c522 --- /dev/null +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java @@ -0,0 +1,86 @@ +/* + * 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 org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +import javax.script.ScriptException; + +import java.io.IOException; +import java.util.Optional; + +public class MetricSensor implements Sensor { + + private static final Logger LOG = Loggers.get(MetricSensor.class); + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .onlyOnLanguage(CssLanguage.KEY); + } + + @Override + public void execute(SensorContext context) { + FileSystem fileSystem = context.fileSystem(); + Iterable<InputFile> inputFiles = fileSystem.inputFiles(fileSystem.predicates().hasLanguage(CssLanguage.KEY)); + + Tokenizer tokenizer = new Tokenizer(); + + for (InputFile input : inputFiles) { + saveHighlights(context, input, tokenizer); + } + } + + private static void saveHighlights(SensorContext sensorContext, InputFile input, Tokenizer tokenizer) { + try { + NewHighlighting highlighting = sensorContext.newHighlighting().onFile(input); + tokenizer.tokenize(input.contents()) + .forEach(token -> getHighlightingType(token).ifPresent(type -> + highlighting.highlight(token.startLine, token.startColumn, token.endLine, token.endColumn, type))); + highlighting.save(); + + } catch (ScriptException e) { + LOG.error(String.format("Failed to tokenize file '%s'", input.toString()), e); + } catch (IOException e) { + LOG.error(String.format("Failed to read file '%s'", input.toString()), e); + } + } + + private static Optional<TypeOfText> getHighlightingType(Token token) { + switch (token.type) { + case COMMENT: + return Optional.of(TypeOfText.COMMENT); + + case STRING: + return Optional.of(TypeOfText.STRING); + + default: + return Optional.empty(); + } + } +} diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java index 8f03492..220bfaa 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java @@ -26,16 +26,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; import javax.script.ScriptException; -import org.sonar.api.internal.apachecommons.lang.StringEscapeUtils; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; +import org.apache.commons.lang.StringEscapeUtils; import org.sonar.css.plugin.Token.Type; public class Tokenizer { public List<Token> tokenize(String css) throws ScriptException { - ScriptEngineManager factory = new ScriptEngineManager(); - ScriptEngine engine = factory.getEngineByName("JavaScript"); + ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(); InputStream tokenizeScript = Tokenizer.class.getClassLoader().getResourceAsStream("tokenize.js"); engine.eval(new InputStreamReader(tokenizeScript, StandardCharsets.UTF_8)); String cssInput = "tokenize('" + StringEscapeUtils.escapeJavaScript(css) + "')"; @@ -105,7 +104,7 @@ public class Tokenizer { if (value instanceof Double) { return ((Double) value).intValue(); } else if (value instanceof Integer) { - return (Integer) value; + return (Integer) value; } else { throw new IllegalStateException("Failed to convert to number: " + value); } 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 3da86ca..33bd4dd 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 @@ -28,15 +28,14 @@ import org.sonar.api.utils.Version; import static org.assertj.core.api.Assertions.assertThat; - public class CssPluginTest { @Test - public void count_extensions() throws Exception { + public void count_extensions() { SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SCANNER); Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(3); + assertThat(context.getExtensions()).hasSize(4); } } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java new file mode 100644 index 0000000..c93c16a --- /dev/null +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java @@ -0,0 +1,112 @@ +/* + * 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.IOException; +import java.util.List; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetricSensorTest { + + private DefaultInputFile inputFile; + private SensorContextTester sensorContext; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Before + public void setUp() { + sensorContext = SensorContextTester.create(tempFolder.getRoot()); + } + + @Test + public void should_describe() { + DefaultSensorDescriptor desc = new DefaultSensorDescriptor(); + new MetricSensor().describe(desc); + + assertThat(desc.languages()).containsOnly("css"); + } + + @Test + public void empty_input() throws Exception { + highlight("foo"); + + assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 0)).isEmpty(); + assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 1)).isEmpty(); + } + + @Test + public void singleline_multiline_comment() throws IOException { + highlight("/* some comment */"); + + assertHighlighting(1, 1, 17, TypeOfText.COMMENT); + } + + @Test + public void multiline_comment() throws IOException { + String content = "/* some comment\nmultiline */"; + highlight(content); + + assertHighlighting(1, 1, 15, TypeOfText.COMMENT); + assertHighlighting(2, 1, 11, TypeOfText.COMMENT); + } + + @Test + public void string() throws IOException { + String content = "\"foo\""; + highlight(content); + + assertHighlighting(1, 1, content.length() - 1, TypeOfText.STRING); + } + + private void highlight(String content) throws IOException { + File file = tempFolder.newFile(); + inputFile = new TestInputFileBuilder("moduleKey", file.getName()) + .setLanguage("css") + .setContents(content) + .build(); + + sensorContext.fileSystem().add(inputFile); + + new MetricSensor().execute(sensorContext); + } + + private void assertHighlighting(int line, int column, int length, TypeOfText type) { + if (column < 1) { + throw new IllegalStateException("Column should be greater than or equal to 1"); + } + + for (int i = column; i < column + length; i++) { + List<TypeOfText> typeOfTexts = sensorContext.highlightingTypeAt(inputFile.key(), line, i); + assertThat(typeOfTexts).containsOnly(type); + } + } +} |
