diff options
Diffstat (limited to 'sonar-css-plugin')
| -rw-r--r-- | sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java | 142 | ||||
| -rw-r--r-- | sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java | 109 |
2 files changed, 177 insertions, 74 deletions
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 index 6257b74..1c635c7 100644 --- 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 @@ -20,7 +20,10 @@ package org.sonar.css.plugin; import java.io.IOException; +import java.util.HashSet; import java.util.List; +import java.util.Set; + import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.Sensor; @@ -28,6 +31,9 @@ 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.measures.CoreMetrics; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; @@ -35,10 +41,16 @@ public class MetricSensor implements Sensor { private static final Logger LOG = Loggers.get(MetricSensor.class); + private final FileLinesContextFactory fileLinesContextFactory; + + public MetricSensor(FileLinesContextFactory fileLinesContextFactory) { + this.fileLinesContextFactory = fileLinesContextFactory; + } + @Override public void describe(SensorDescriptor descriptor) { descriptor - .onlyOnLanguage(CssLanguage.KEY); + .onlyOnLanguage(CssLanguage.KEY); } @Override @@ -48,72 +60,98 @@ public class MetricSensor implements Sensor { Tokenizer tokenizer = new Tokenizer(); - for (InputFile input : inputFiles) { - saveHighlights(context, input, tokenizer); + for (InputFile file : inputFiles) { + try { + List<CssToken> tokenList = tokenizer.tokenize(file.contents()); + + saveHighlights(context, file, tokenList); + saveLineTypes(context, file, tokenList); + + } catch (IOException e) { + LOG.error(String.format("Failed to read file '%s'", file.toString()), e); + } } } - private static void saveHighlights(SensorContext sensorContext, InputFile input, Tokenizer tokenizer) { - try { - NewHighlighting highlighting = sensorContext.newHighlighting().onFile(input); - List<CssToken> tokenList = tokenizer.tokenize(input.contents()); + private static void saveHighlights(SensorContext context, InputFile file, List<CssToken> tokenList) { + NewHighlighting highlighting = context.newHighlighting().onFile(file); - for (int i = 0; i < tokenList.size(); i++) { - CssToken currentToken = tokenList.get(i); - CssToken nextToken = i + 1 < tokenList.size() ? tokenList.get(i + 1) : null; + for (int i = 0; i < tokenList.size(); i++) { + CssToken currentToken = tokenList.get(i); + CssToken nextToken = i + 1 < tokenList.size() ? tokenList.get(i + 1) : null; - TypeOfText highlightingType = null; - switch (currentToken.type) { - case COMMENT: - highlightingType = TypeOfText.COMMENT; - break; + TypeOfText highlightingType = null; + switch (currentToken.type) { + case COMMENT: + highlightingType = TypeOfText.COMMENT; + break; - case STRING: - highlightingType = TypeOfText.STRING; - break; + case STRING: + highlightingType = TypeOfText.STRING; + break; - case NUMBER: - highlightingType = TypeOfText.CONSTANT; - break; + case NUMBER: + highlightingType = TypeOfText.CONSTANT; + break; - case AT_IDENTIFIER: - highlightingType = TypeOfText.ANNOTATION; - break; + case AT_IDENTIFIER: + highlightingType = TypeOfText.ANNOTATION; + break; - case DOLLAR_IDENTIFIER: + case DOLLAR_IDENTIFIER: + highlightingType = TypeOfText.KEYWORD; + break; + + case HASH_IDENTIFIER: + if (currentToken.text.matches("^#[0-9a-fA-F]+$")) { + highlightingType = TypeOfText.CONSTANT; + } else { highlightingType = TypeOfText.KEYWORD; - break; - - case HASH_IDENTIFIER: - if (currentToken.text.matches("^#[0-9a-fA-F]+$")) { - highlightingType = TypeOfText.CONSTANT; - } else { - highlightingType = TypeOfText.KEYWORD; - } - break; - - case IDENTIFIER: - // We want to highlight the property key of a css/scss/less file and as the tokenizer is putting the ':' into another token - // we need to look for identifier followed by a PUNCTUATOR token with text ':'. - if (nextToken != null && nextToken.text.equals(":")) { - highlightingType = TypeOfText.KEYWORD_LIGHT; - } - break; - - default: - highlightingType = null; - } + } + break; + + case IDENTIFIER: + // We want to highlight the property key of a css/scss/less file and as the tokenizer is putting the ':' into another token + // we need to look for identifier followed by a PUNCTUATOR token with text ':'. + if (nextToken != null && nextToken.text.equals(":")) { + highlightingType = TypeOfText.KEYWORD_LIGHT; + } + break; + + default: + highlightingType = null; + } - if (highlightingType != null) { - highlighting.highlight(currentToken.startLine, currentToken.startColumn, currentToken.endLine, currentToken.endColumn, highlightingType); - } + if (highlightingType != null) { + highlighting.highlight(currentToken.startLine, currentToken.startColumn, currentToken.endLine, currentToken.endColumn, highlightingType); } + } - highlighting.save(); + highlighting.save(); + } - } catch (IOException e) { - LOG.error(String.format("Failed to read file '%s'", input.toString()), e); + private void saveLineTypes(SensorContext context, InputFile file, List<CssToken> tokenList) { + // collect line types + Set<Integer> linesOfCode = new HashSet<>(); + Set<Integer> linesOfComment = new HashSet<>(); + + for (CssToken token : tokenList) { + for (int line = token.startLine; line <= token.endLine; line++) { + if (token.type.equals(CssTokenType.COMMENT)) { + linesOfComment.add(line); + } else { + linesOfCode.add(line); + } + } } + + context.<Integer>newMeasure().on(file).forMetric(CoreMetrics.NCLOC).withValue(linesOfCode.size()).save(); + context.<Integer>newMeasure().on(file).forMetric(CoreMetrics.COMMENT_LINES).withValue(linesOfComment.size()).save(); + + FileLinesContext fileLinesContext = fileLinesContextFactory.createFor(file); + linesOfCode.forEach(line -> fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, line, 1)); + linesOfComment.forEach(line -> fileLinesContext.setIntValue(CoreMetrics.COMMENT_LINES_DATA_KEY, line, 1)); + fileLinesContext.save(); } } 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 index e8eb31b..7e49277 100644 --- 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 @@ -30,8 +30,13 @@ 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 org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class MetricSensorTest { @@ -44,93 +49,142 @@ public class MetricSensorTest { @Test public void should_describe() { DefaultSensorDescriptor desc = new DefaultSensorDescriptor(); - new MetricSensor().describe(desc); + new MetricSensor(null).describe(desc); assertThat(desc.languages()).containsOnly("css"); } @Test public void empty_input() throws Exception { - highlight("foo"); + executeSensor("foo"); assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 0)).isEmpty(); assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 1)).isEmpty(); } @Test public void comment() throws IOException { - highlight("/* some comment */"); + executeSensor("/* some comment */"); assertHighlighting(1, 0, 18, TypeOfText.COMMENT); - highlight("/* some comment\nmultiline */"); + executeSensor("/* some comment\nmultiline */"); assertHighlighting(1, 0, 15, TypeOfText.COMMENT); assertHighlighting(2, 0, 12, TypeOfText.COMMENT); } @Test public void string() throws IOException { - highlight("\"foo\""); + executeSensor("\"foo\""); assertHighlighting(1, 0, 5, TypeOfText.STRING); - highlight("\"foo\\\nbar\""); + executeSensor("\"foo\\\nbar\""); assertHighlighting(1, 0, 4, TypeOfText.STRING); assertHighlighting(2, 0, 4, TypeOfText.STRING); } @Test public void constant() throws IOException { - highlight("1"); + executeSensor("1"); assertHighlighting(1, 0, 1, TypeOfText.CONSTANT); - highlight("1.0"); + executeSensor("1.0"); assertHighlighting(1, 0, 3, TypeOfText.CONSTANT); - highlight("0px"); + executeSensor("0px"); assertHighlighting(1, 0, 3, TypeOfText.CONSTANT); - highlight("1em"); + executeSensor("1em"); assertHighlighting(1, 0, 3, TypeOfText.CONSTANT); - highlight("#ddd"); + executeSensor("#ddd"); assertHighlighting(1, 0, 4, TypeOfText.CONSTANT); } @Test public void annotation() throws IOException { - highlight("@bar { }"); + executeSensor("@bar { }"); assertHighlighting(1, 0, 4, TypeOfText.ANNOTATION); - highlight("@my-selector: banner;"); + executeSensor("@my-selector: banner;"); assertHighlighting(1, 0, 12, TypeOfText.ANNOTATION); - highlight("@import \"src/themes\""); + executeSensor("@import \"src/themes\""); assertHighlighting(1, 0, 7, TypeOfText.ANNOTATION); - highlight(".element { color: @@color }"); + executeSensor(".element { color: @@color }"); assertHighlighting(1, 18, 7, TypeOfText.ANNOTATION); } @Test public void keyword() throws IOException { - highlight("$foo { }"); + executeSensor("$foo { }"); assertHighlighting(1, 0, 4, TypeOfText.KEYWORD); - highlight("#header { .border-radius(4px); }"); + executeSensor("#header { .border-radius(4px); }"); assertHighlighting(1, 0, 7, TypeOfText.KEYWORD); } @Test public void keyword_light() throws IOException { - highlight("bar: foo { }"); + executeSensor("bar: foo { }"); assertHighlighting(1, 0, 3, TypeOfText.KEYWORD_LIGHT); - highlight("bar { foo: 1px }"); + executeSensor("bar { foo: 1px }"); assertHighlighting(1, 6, 3, TypeOfText.KEYWORD_LIGHT); - highlight("bar { foo-bar: 1px }"); + executeSensor("bar { foo-bar: 1px }"); assertHighlighting(1, 6, 7, TypeOfText.KEYWORD_LIGHT); } - private void highlight(String content) throws IOException { + @Test + public void lines_of_code() throws IOException { + executeSensor("bar { }"); + assertLinesOfCode(1); + + executeSensor("bar\n{ }"); + assertLinesOfCode(2); + + // We don't count empty lines + executeSensor("\n\n\nsomething\n\n\n"); + assertLinesOfCode(1); + + // We don't count comments + executeSensor("// foo"); + assertLinesOfCode(0); + executeSensor("/* dasdsa */"); + assertLinesOfCode(0); + executeSensor("/* das\ndsa */"); + assertLinesOfCode(0); + + // Mix code and comment + executeSensor("foo {} // some comment"); + assertLinesOfCode(1); + } + + @Test + public void lines_of_comment() throws IOException { + executeSensor("// inline comment"); + assertLinesOfComment(1); + + executeSensor("/* single line comment */"); + assertLinesOfComment(1); + + executeSensor("/* multiline\n *\n *\n * comment\n*/"); + assertLinesOfComment(5); + + // We don't count empty lines + executeSensor("\n\n\n/* something */\n\n\n"); + assertLinesOfComment(1); + + // We don't count code + executeSensor("foo {}"); + assertLinesOfComment(0); + + // Mix code and comment + executeSensor("foo {} // some comment"); + assertLinesOfComment(1); + } + + private void executeSensor(String content) throws IOException { File file = tempFolder.newFile(); inputFile = new TestInputFileBuilder("moduleKey", file.getName()) .setLanguage("css") @@ -140,7 +194,10 @@ public class MetricSensorTest { sensorContext = SensorContextTester.create(tempFolder.getRoot()); sensorContext.fileSystem().add(inputFile); - new MetricSensor().execute(sensorContext); + FileLinesContext linesContext = mock(FileLinesContext.class); + FileLinesContextFactory linesContextFactory = mock(FileLinesContextFactory.class); + when(linesContextFactory.createFor(inputFile)).thenReturn(linesContext); + new MetricSensor(linesContextFactory).execute(sensorContext); } private void assertHighlighting(int line, int column, int length, TypeOfText type) { @@ -149,4 +206,12 @@ public class MetricSensorTest { assertThat(typeOfTexts).containsOnly(type); } } + + private void assertLinesOfCode(int expected) { + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(expected); + } + + private void assertLinesOfComment(int expected) { + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COMMENT_LINES).value()).isEqualTo(expected); + } } |
