Skip to content

Commit

Permalink
Add lines of code measure (#3)
Browse files Browse the repository at this point in the history
* Add lines measures
  • Loading branch information
nbihan-mediware authored Feb 1, 2019
1 parent 8f274b1 commit f7c76ed
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 64 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>com.stepstone.sonar.plugin</groupId>
<artifactId>sonar-coldfusion-plugin</artifactId>
<packaging>sonar-plugin</packaging>
<version>1.6.6-SNAPSHOT</version>
<version>1.6.8-SNAPSHOT</version>

<name>SonarQube Coldfusion Analyzer</name>
<description>Enables scanning of ColdFusion source files</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@
import com.stepstone.sonar.plugin.coldfusion.cflint.CFlintAnalysisResultImporter;
import com.stepstone.sonar.plugin.coldfusion.cflint.CFlintConfigExporter;
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.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;

import javax.xml.stream.XMLStreamException;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class ColdFusionSensor implements Sensor {

Expand Down Expand Up @@ -59,6 +67,7 @@ public void execute(SensorContext context) {
try {
analyze(context);
importResults(context);
measureProcessor(context);
} catch (IOException | XMLStreamException e) {
LOGGER.error("",e);
}
Expand Down Expand Up @@ -93,5 +102,75 @@ private void importResults(SensorContext sensorContext) throws IOException {
}
}

private void measureProcessor(SensorContext context) {
LOGGER.info("Starting measure processor");

ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Callable<Integer>> callableTasks = new ArrayList<>();

for (InputFile inputFile : fs.inputFiles(fs.predicates().hasLanguage(ColdFusionPlugin.LANGUAGE_KEY))) {
Callable<Integer> callableTask = () -> {
try {
metricsLinesCounter(inputFile, context);
return 1;
} catch (IOException e) {
return 0;
}
};
callableTasks.add(callableTask);
}

try {
executorService.invokeAll(callableTasks);
executorService.shutdown();
executorService.awaitTermination(2, TimeUnit.MINUTES);
} catch (InterruptedException e) {
LOGGER.error("",e);
}

LOGGER.info("Measure processor done");
}

//Very basic and naive line of code counter for Coldfusion
//Might count a line of code as comment
private void metricsLinesCounter(InputFile inputFile, SensorContext context) throws IOException {
String currentLine;
int commentLines = 0;
int blankLines = 0;
int lines = 0;
Metric metricLinesOfCode = CoreMetrics.NCLOC;
Metric metricLines = CoreMetrics.LINES;
Metric metricCommentLines = CoreMetrics.COMMENT_LINES;
if(inputFile==null){
return;
}

try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputFile.inputStream()))) {
if (inputFile.inputStream() != null) {
while ((currentLine = reader.readLine()) != null) {
lines++;
if (currentLine.contains("<!--")) {
commentLines++;
if (currentLine.contains("-->")) {
continue;
}
commentLines++;
lines++;
while (!(reader.readLine()).contains("-->")) {
lines++;
commentLines++;
}
} else if (currentLine.trim().isEmpty()) {
blankLines++;
}
}
}
}

context.newMeasure().forMetric(metricCommentLines).on(inputFile).withValue(commentLines).save();
context.newMeasure().forMetric(metricLinesOfCode).on(inputFile).withValue(lines-blankLines-commentLines).save();
context.newMeasure().forMetric(metricLines).on(inputFile).withValue(lines).save();
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,35 @@ public CFLintAnalyzer(SensorContext sensorContext) {
}

public void analyze(File configFile) throws IOException, XMLStreamException {

final Command command = Command.create(settings.get(ColdFusionPlugin.CFLINT_JAVA).get());

addCflintJavaOpts(command);

command.addArgument("-jar")
.addArgument(extractCflintJar().getPath())
File executableJar = null;
try {
Command command = Command.create(settings.get(ColdFusionPlugin.CFLINT_JAVA).get());

addCflintJavaOpts(command);
executableJar = extractCflintJar();
command.addArgument("-jar")
.addArgument(executableJar.getPath())
.addArgument("-xml")
.addArgument("-folder")
.addArgument(settings.get("sonar.sources").get())
.addArgument(settings.get("sonar.projectBaseDir").get())
.addArgument("-xmlfile")
.addArgument(fs.workDir() + File.separator + "cflint-result.xml")
.addArgument("-configfile")
.addArgument(configFile.getPath());

int exitCode = CommandExecutor.create().execute(command, new LogInfoStreamConsumer(), new LogErrorStreamConsumer(), Integer.MAX_VALUE);
if (exitCode != 0) {
throw new IllegalStateException("The CFLint analyzer failed with exit code: " + exitCode);
CommandExecutor executor = CommandExecutor.create();
int exitCode = executor.execute(command, new LogInfoStreamConsumer(), new LogErrorStreamConsumer(), Integer.MAX_VALUE);

if (exitCode != 0) {
throw new IllegalStateException("The CFLint analyzer failed with exit code: " + exitCode);
}
} finally {
//cleanup
if(executableJar!= null && executableJar.exists()) {
executableJar.deleteOnExit();
}
}

}

protected File extractCflintJar() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,13 @@
package com.stepstone.sonar.plugin.coldfusion.cflint;

import com.stepstone.sonar.plugin.coldfusion.ColdFusionPlugin;
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.CountsAttributes;
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.IssueAttributes;
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.LocationAttributes;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.issue.NewIssue;
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
Expand Down Expand Up @@ -76,42 +73,11 @@ private void parse() throws XMLStreamException {

if ("issue".equals(tagName)) {
handleIssueTag(new IssueAttributes(stream));
} else if ("counts".equals(tagName)) {
handleCountsTag(new CountsAttributes(stream));
}
}
}
}

private void handleCountsTag(CountsAttributes countsAttributes){
Metric metricLines = new Metric() {
@Override
public String key() {
return CoreMetrics.LINES.key();
}

@Override
public Class valueType() {
return Integer.class;
}
};

Metric metricFiles = new Metric() {
@Override
public String key() {
return CoreMetrics.FILES.key();
}

@Override
public Class valueType() {
return Integer.class;
}
};
LOGGER.info("CFLint analyzed {} lines for {} files", countsAttributes.getTotalLines(), countsAttributes.getTotalFiles());
sensorContext.newMeasure().on(sensorContext.module()).forMetric(metricLines).withValue(countsAttributes.getTotalLines()).save();
sensorContext.newMeasure().on(sensorContext.module()).forMetric(metricFiles).withValue(countsAttributes.getTotalFiles()).save();
}

private void handleIssueTag(IssueAttributes issueAttributes) throws XMLStreamException {
while (stream.hasNext()) {
int next = stream.next();
Expand All @@ -125,11 +91,8 @@ else if (next == XMLStreamConstants.START_ELEMENT) {

if ("location".equals(tagName)) {
LocationAttributes locationAttributes = new LocationAttributes(stream);
//InputFile inputFiletest = fs.inputFiles(fs.predicates().hasFilename())

InputFile inputFile = fs.inputFile(fs.predicates().hasAbsolutePath(locationAttributes.getFile()));
if(inputFile == null){
LOGGER.error("File {} is null", locationAttributes.getFile());
}
createNewIssue(issueAttributes, locationAttributes, inputFile);
}
}
Expand All @@ -138,13 +101,13 @@ else if (next == XMLStreamConstants.START_ELEMENT) {

private void createNewIssue(IssueAttributes issueAttributes, LocationAttributes locationAttributes, InputFile inputFile) {
if(issueAttributes == null){
LOGGER.error("Problem creating issue for file {} issueAttributes is null", inputFile);
LOGGER.debug("Problem creating issue for file {} issueAttributes is null", inputFile);
}
if(locationAttributes == null){
LOGGER.error("Problem creating issue for file {} locationAttributes is null", inputFile);
LOGGER.debug("Problem creating issue for file {} locationAttributes is null", inputFile);
}
if(inputFile==null){
LOGGER.error("Problem creating issue for file inputFile is null");
LOGGER.debug("Problem creating issue for file inputFile is null");
}
if(issueAttributes == null || locationAttributes == null || inputFile == null){
return;
Expand Down
41 changes: 30 additions & 11 deletions src/test/java/com/wellsky/ColdfusionSensorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import org.sonar.api.SonarQubeSide;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.sensor.internal.SensorContextTester;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.api.internal.SonarRuntimeImpl;
import org.sonar.api.internal.apachecommons.codec.Charsets;
import org.sonar.api.measures.CoreMetrics;
Expand All @@ -31,6 +34,8 @@
public class ColdfusionSensorTest {

private RulesProfile rulesProfile = RulesProfile.create(RulesProfile.SONAR_WAY_NAME, ColdFusionPlugin.LANGUAGE_NAME);
private File baseDir = new File("src/test/resources").getAbsoluteFile();
private SensorContextTester context = SensorContextTester.create(baseDir);

@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();
Expand All @@ -41,32 +46,46 @@ public void testBasicCFMAnalysis() throws IOException {
fileSystem.setEncoding(Charsets.UTF_8);
fileSystem.setWorkDir(tmpFolder.getRoot().toPath());

File sourceDir = new File("src/test/resources");
SensorContextTester context = SensorContextTester.create(sourceDir.toPath());
context.setFileSystem(fileSystem);
context.setRuntime(SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SCANNER));

context.settings().appendProperty("sonar.projectBaseDir", baseDir.getPath());
addFilesToFs();

CommandExecutor commandExecutor = CommandExecutor.create();
String javaHome = System.getProperty("java.home");
Assert.assertTrue(javaHome!=null && !javaHome.equals(""));
//FIXME get Java on Linux too and check there is java Home set

if(OSValidator.isWindows()) {
context.settings().appendProperty(ColdFusionPlugin.CFLINT_JAVA, javaHome + "/bin/java.exe");
} else {
context.settings().appendProperty(ColdFusionPlugin.CFLINT_JAVA, javaHome + "/bin/java");
}

context.settings().appendProperty("sonar.sources",sourceDir.getPath());
// Mock visitor for metrics.
FileLinesContext fileLinesContext = mock(FileLinesContext.class);
FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
when(fileLinesContextFactory.createFor(any(InputFile.class))).thenReturn(fileLinesContext);
context = Mockito.spy(context);
ColdFusionSensor sensor = new ColdFusionSensor(context.fileSystem(), rulesProfile);
sensor.execute(context);

assertThat(context.measure(context.module().key(), CoreMetrics.FILES.key()).value()).isEqualTo(2);
Integer nloc = 0;
Integer comments = 0;
for (InputFile o : context.fileSystem().inputFiles()) {
Measure<Integer> measureNloc = context.measure(o.key(),CoreMetrics.NCLOC.key());
Measure<Integer> measureComment = context.measure(o.key(),CoreMetrics.COMMENT_LINES.key());
nloc+=measureNloc.value();
comments+=measureComment.value();
}

assertThat(nloc).isEqualTo(36);
assertThat(comments).isEqualTo(9);

}

assertThat(context.measure(context.module().key(), CoreMetrics.LINES.key()).value()).isEqualTo(19);
private void addFilesToFs() {
DefaultInputFile inputFileMetrics1 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/testmetrics1.cfm").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
context.fileSystem().add(inputFileMetrics1);
DefaultInputFile inputFileMetrics2 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/testmetrics2.cfm").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
context.fileSystem().add(inputFileMetrics2);
DefaultInputFile inputFileMetrics3 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/EpisodeClaim.cfc").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
context.fileSystem().add(inputFileMetrics3);
}

}
35 changes: 35 additions & 0 deletions src/test/resources/EpisodeClaim.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<cfcomponent output="false" extends="com.remoteproxy">
<cfsetting showdebugoutput="true">

<cffunction name="hasEpisode" returntype="struct" access="remote" output="false" returnformat="JSON" hint="hint example">

<cfargument name="episodeKey" type="numeric" required="true" />

<cfset local.resultStruct = {} />
<cfset local.resultStruct.has = false />
<cfset local.resultStruct.result = "success">


<!--testcomment-->


<cftry>

<cfset local.episodes = createObject("component", "packages.system.Episode.Episode.EpisodeGateway").init(application.dsn).getByAttributesQuery(fEpisodeKey=arguments.episodeKey) />

<cfif local.episodes.recordCount GT 0>
<cfset local.resultStruct.has = true />
</cfif>

<cfcatch type="any">
<cfset local.resultStruct.result = "fail">
<cfset local.resultStruct.errorStruct = super.createErrorJSON(cfcatch)>
</cfcatch>

</cftry>

<cfreturn local.resultStruct />

</cffunction>

</cfcomponent>
2 changes: 2 additions & 0 deletions src/test/resources/testmetrics1.cfm
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<cfset firstName = "World">
Hello <cfoutput>#firstName#</cfoutput>!
<!--- this is a comment --->
This CFML tutorial was designed for
<cfif firstName eq "World">
you!
<cfelse >
<!--- this is a second comment --->
the world to see.
</cfif>
8 changes: 8 additions & 0 deletions src/test/resources/testmetrics2.cfm
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<cfset firstName = "Nico">

<!-- comment format
multilines-->

Hello <cfoutput>#firstName#</cfoutput>!
This CFML tutorial was designed for
<cfif firstName eq "Nico">
you!
<cfelse>
<!-- Another comment format
multilines
-->

the world to see.
</cfif>

0 comments on commit f7c76ed

Please sign in to comment.