Skip to content

Commit f7c76ed

Browse files
Add lines of code measure (#3)
* Add lines measures
1 parent 8f274b1 commit f7c76ed

File tree

8 files changed

+180
-64
lines changed

8 files changed

+180
-64
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<groupId>com.stepstone.sonar.plugin</groupId>
77
<artifactId>sonar-coldfusion-plugin</artifactId>
88
<packaging>sonar-plugin</packaging>
9-
<version>1.6.6-SNAPSHOT</version>
9+
<version>1.6.8-SNAPSHOT</version>
1010

1111
<name>SonarQube Coldfusion Analyzer</name>
1212
<description>Enables scanning of ColdFusion source files</description>

src/main/java/com/stepstone/sonar/plugin/coldfusion/ColdFusionSensor.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,25 @@
2121
import com.stepstone.sonar.plugin.coldfusion.cflint.CFlintAnalysisResultImporter;
2222
import com.stepstone.sonar.plugin.coldfusion.cflint.CFlintConfigExporter;
2323
import org.sonar.api.batch.fs.FileSystem;
24+
import org.sonar.api.batch.fs.InputFile;
2425
import org.sonar.api.batch.sensor.Sensor;
2526
import org.sonar.api.batch.sensor.SensorContext;
2627
import org.sonar.api.batch.sensor.SensorDescriptor;
28+
import org.sonar.api.measures.CoreMetrics;
29+
import org.sonar.api.measures.Metric;
2730
import org.sonar.api.profiles.RulesProfile;
2831
import org.sonar.api.utils.log.Logger;
2932
import org.sonar.api.utils.log.Loggers;
3033

3134
import javax.xml.stream.XMLStreamException;
35+
import java.io.BufferedReader;
3236
import java.io.File;
3337
import java.io.IOException;
38+
import java.io.InputStreamReader;
3439
import java.nio.file.Files;
40+
import java.util.ArrayList;
41+
import java.util.List;
42+
import java.util.concurrent.*;
3543

3644
public class ColdFusionSensor implements Sensor {
3745

@@ -59,6 +67,7 @@ public void execute(SensorContext context) {
5967
try {
6068
analyze(context);
6169
importResults(context);
70+
measureProcessor(context);
6271
} catch (IOException | XMLStreamException e) {
6372
LOGGER.error("",e);
6473
}
@@ -93,5 +102,75 @@ private void importResults(SensorContext sensorContext) throws IOException {
93102
}
94103
}
95104

105+
private void measureProcessor(SensorContext context) {
106+
LOGGER.info("Starting measure processor");
107+
108+
ExecutorService executorService = Executors.newFixedThreadPool(2);
109+
List<Callable<Integer>> callableTasks = new ArrayList<>();
110+
111+
for (InputFile inputFile : fs.inputFiles(fs.predicates().hasLanguage(ColdFusionPlugin.LANGUAGE_KEY))) {
112+
Callable<Integer> callableTask = () -> {
113+
try {
114+
metricsLinesCounter(inputFile, context);
115+
return 1;
116+
} catch (IOException e) {
117+
return 0;
118+
}
119+
};
120+
callableTasks.add(callableTask);
121+
}
122+
123+
try {
124+
executorService.invokeAll(callableTasks);
125+
executorService.shutdown();
126+
executorService.awaitTermination(2, TimeUnit.MINUTES);
127+
} catch (InterruptedException e) {
128+
LOGGER.error("",e);
129+
}
130+
131+
LOGGER.info("Measure processor done");
132+
}
133+
134+
//Very basic and naive line of code counter for Coldfusion
135+
//Might count a line of code as comment
136+
private void metricsLinesCounter(InputFile inputFile, SensorContext context) throws IOException {
137+
String currentLine;
138+
int commentLines = 0;
139+
int blankLines = 0;
140+
int lines = 0;
141+
Metric metricLinesOfCode = CoreMetrics.NCLOC;
142+
Metric metricLines = CoreMetrics.LINES;
143+
Metric metricCommentLines = CoreMetrics.COMMENT_LINES;
144+
if(inputFile==null){
145+
return;
146+
}
147+
148+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputFile.inputStream()))) {
149+
if (inputFile.inputStream() != null) {
150+
while ((currentLine = reader.readLine()) != null) {
151+
lines++;
152+
if (currentLine.contains("<!--")) {
153+
commentLines++;
154+
if (currentLine.contains("-->")) {
155+
continue;
156+
}
157+
commentLines++;
158+
lines++;
159+
while (!(reader.readLine()).contains("-->")) {
160+
lines++;
161+
commentLines++;
162+
}
163+
} else if (currentLine.trim().isEmpty()) {
164+
blankLines++;
165+
}
166+
}
167+
}
168+
}
169+
170+
context.newMeasure().forMetric(metricCommentLines).on(inputFile).withValue(commentLines).save();
171+
context.newMeasure().forMetric(metricLinesOfCode).on(inputFile).withValue(lines-blankLines-commentLines).save();
172+
context.newMeasure().forMetric(metricLines).on(inputFile).withValue(lines).save();
173+
}
174+
96175
}
97176

src/main/java/com/stepstone/sonar/plugin/coldfusion/cflint/CFLintAnalyzer.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,35 @@ public CFLintAnalyzer(SensorContext sensorContext) {
4646
}
4747

4848
public void analyze(File configFile) throws IOException, XMLStreamException {
49-
50-
final Command command = Command.create(settings.get(ColdFusionPlugin.CFLINT_JAVA).get());
51-
52-
addCflintJavaOpts(command);
53-
54-
command.addArgument("-jar")
55-
.addArgument(extractCflintJar().getPath())
49+
File executableJar = null;
50+
try {
51+
Command command = Command.create(settings.get(ColdFusionPlugin.CFLINT_JAVA).get());
52+
53+
addCflintJavaOpts(command);
54+
executableJar = extractCflintJar();
55+
command.addArgument("-jar")
56+
.addArgument(executableJar.getPath())
5657
.addArgument("-xml")
5758
.addArgument("-folder")
58-
.addArgument(settings.get("sonar.sources").get())
59+
.addArgument(settings.get("sonar.projectBaseDir").get())
5960
.addArgument("-xmlfile")
6061
.addArgument(fs.workDir() + File.separator + "cflint-result.xml")
6162
.addArgument("-configfile")
6263
.addArgument(configFile.getPath());
6364

64-
int exitCode = CommandExecutor.create().execute(command, new LogInfoStreamConsumer(), new LogErrorStreamConsumer(), Integer.MAX_VALUE);
65-
if (exitCode != 0) {
66-
throw new IllegalStateException("The CFLint analyzer failed with exit code: " + exitCode);
65+
CommandExecutor executor = CommandExecutor.create();
66+
int exitCode = executor.execute(command, new LogInfoStreamConsumer(), new LogErrorStreamConsumer(), Integer.MAX_VALUE);
67+
68+
if (exitCode != 0) {
69+
throw new IllegalStateException("The CFLint analyzer failed with exit code: " + exitCode);
70+
}
71+
} finally {
72+
//cleanup
73+
if(executableJar!= null && executableJar.exists()) {
74+
executableJar.deleteOnExit();
75+
}
6776
}
77+
6878
}
6979

7080
protected File extractCflintJar() throws IOException {

src/main/java/com/stepstone/sonar/plugin/coldfusion/cflint/CFlintAnalysisResultImporter.java

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
package com.stepstone.sonar.plugin.coldfusion.cflint;
1818

1919
import com.stepstone.sonar.plugin.coldfusion.ColdFusionPlugin;
20-
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.CountsAttributes;
2120
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.IssueAttributes;
2221
import com.stepstone.sonar.plugin.coldfusion.cflint.xml.LocationAttributes;
2322
import org.sonar.api.batch.fs.FileSystem;
2423
import org.sonar.api.batch.fs.InputFile;
25-
import org.sonar.api.batch.measure.Metric;
2624
import org.sonar.api.batch.sensor.SensorContext;
2725
import org.sonar.api.batch.sensor.issue.NewIssue;
2826
import org.sonar.api.batch.sensor.issue.NewIssueLocation;
29-
import org.sonar.api.measures.CoreMetrics;
3027
import org.sonar.api.rule.RuleKey;
3128
import org.sonar.api.utils.log.Logger;
3229
import org.sonar.api.utils.log.Loggers;
@@ -76,42 +73,11 @@ private void parse() throws XMLStreamException {
7673

7774
if ("issue".equals(tagName)) {
7875
handleIssueTag(new IssueAttributes(stream));
79-
} else if ("counts".equals(tagName)) {
80-
handleCountsTag(new CountsAttributes(stream));
8176
}
8277
}
8378
}
8479
}
8580

86-
private void handleCountsTag(CountsAttributes countsAttributes){
87-
Metric metricLines = new Metric() {
88-
@Override
89-
public String key() {
90-
return CoreMetrics.LINES.key();
91-
}
92-
93-
@Override
94-
public Class valueType() {
95-
return Integer.class;
96-
}
97-
};
98-
99-
Metric metricFiles = new Metric() {
100-
@Override
101-
public String key() {
102-
return CoreMetrics.FILES.key();
103-
}
104-
105-
@Override
106-
public Class valueType() {
107-
return Integer.class;
108-
}
109-
};
110-
LOGGER.info("CFLint analyzed {} lines for {} files", countsAttributes.getTotalLines(), countsAttributes.getTotalFiles());
111-
sensorContext.newMeasure().on(sensorContext.module()).forMetric(metricLines).withValue(countsAttributes.getTotalLines()).save();
112-
sensorContext.newMeasure().on(sensorContext.module()).forMetric(metricFiles).withValue(countsAttributes.getTotalFiles()).save();
113-
}
114-
11581
private void handleIssueTag(IssueAttributes issueAttributes) throws XMLStreamException {
11682
while (stream.hasNext()) {
11783
int next = stream.next();
@@ -125,11 +91,8 @@ else if (next == XMLStreamConstants.START_ELEMENT) {
12591

12692
if ("location".equals(tagName)) {
12793
LocationAttributes locationAttributes = new LocationAttributes(stream);
128-
//InputFile inputFiletest = fs.inputFiles(fs.predicates().hasFilename())
94+
12995
InputFile inputFile = fs.inputFile(fs.predicates().hasAbsolutePath(locationAttributes.getFile()));
130-
if(inputFile == null){
131-
LOGGER.error("File {} is null", locationAttributes.getFile());
132-
}
13396
createNewIssue(issueAttributes, locationAttributes, inputFile);
13497
}
13598
}
@@ -138,13 +101,13 @@ else if (next == XMLStreamConstants.START_ELEMENT) {
138101

139102
private void createNewIssue(IssueAttributes issueAttributes, LocationAttributes locationAttributes, InputFile inputFile) {
140103
if(issueAttributes == null){
141-
LOGGER.error("Problem creating issue for file {} issueAttributes is null", inputFile);
104+
LOGGER.debug("Problem creating issue for file {} issueAttributes is null", inputFile);
142105
}
143106
if(locationAttributes == null){
144-
LOGGER.error("Problem creating issue for file {} locationAttributes is null", inputFile);
107+
LOGGER.debug("Problem creating issue for file {} locationAttributes is null", inputFile);
145108
}
146109
if(inputFile==null){
147-
LOGGER.error("Problem creating issue for file inputFile is null");
110+
LOGGER.debug("Problem creating issue for file inputFile is null");
148111
}
149112
if(issueAttributes == null || locationAttributes == null || inputFile == null){
150113
return;

src/test/java/com/wellsky/ColdfusionSensorTest.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import org.sonar.api.SonarQubeSide;
1111
import org.sonar.api.batch.fs.InputFile;
1212
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
13+
import org.sonar.api.batch.fs.internal.DefaultInputFile;
14+
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
1315
import org.sonar.api.batch.sensor.internal.SensorContextTester;
16+
import org.sonar.api.batch.sensor.measure.Measure;
1417
import org.sonar.api.internal.SonarRuntimeImpl;
1518
import org.sonar.api.internal.apachecommons.codec.Charsets;
1619
import org.sonar.api.measures.CoreMetrics;
@@ -31,6 +34,8 @@
3134
public class ColdfusionSensorTest {
3235

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

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

44-
File sourceDir = new File("src/test/resources");
45-
SensorContextTester context = SensorContextTester.create(sourceDir.toPath());
4649
context.setFileSystem(fileSystem);
4750
context.setRuntime(SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SCANNER));
51+
52+
context.settings().appendProperty("sonar.projectBaseDir", baseDir.getPath());
53+
addFilesToFs();
54+
4855
CommandExecutor commandExecutor = CommandExecutor.create();
4956
String javaHome = System.getProperty("java.home");
5057
Assert.assertTrue(javaHome!=null && !javaHome.equals(""));
51-
//FIXME get Java on Linux too and check there is java Home set
58+
5259
if(OSValidator.isWindows()) {
5360
context.settings().appendProperty(ColdFusionPlugin.CFLINT_JAVA, javaHome + "/bin/java.exe");
5461
} else {
5562
context.settings().appendProperty(ColdFusionPlugin.CFLINT_JAVA, javaHome + "/bin/java");
5663
}
5764

58-
context.settings().appendProperty("sonar.sources",sourceDir.getPath());
59-
// Mock visitor for metrics.
60-
FileLinesContext fileLinesContext = mock(FileLinesContext.class);
61-
FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class);
62-
when(fileLinesContextFactory.createFor(any(InputFile.class))).thenReturn(fileLinesContext);
63-
context = Mockito.spy(context);
6465
ColdFusionSensor sensor = new ColdFusionSensor(context.fileSystem(), rulesProfile);
6566
sensor.execute(context);
6667

67-
assertThat(context.measure(context.module().key(), CoreMetrics.FILES.key()).value()).isEqualTo(2);
68+
Integer nloc = 0;
69+
Integer comments = 0;
70+
for (InputFile o : context.fileSystem().inputFiles()) {
71+
Measure<Integer> measureNloc = context.measure(o.key(),CoreMetrics.NCLOC.key());
72+
Measure<Integer> measureComment = context.measure(o.key(),CoreMetrics.COMMENT_LINES.key());
73+
nloc+=measureNloc.value();
74+
comments+=measureComment.value();
75+
}
76+
77+
assertThat(nloc).isEqualTo(36);
78+
assertThat(comments).isEqualTo(9);
79+
80+
}
6881

69-
assertThat(context.measure(context.module().key(), CoreMetrics.LINES.key()).value()).isEqualTo(19);
82+
private void addFilesToFs() {
83+
DefaultInputFile inputFileMetrics1 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/testmetrics1.cfm").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
84+
context.fileSystem().add(inputFileMetrics1);
85+
DefaultInputFile inputFileMetrics2 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/testmetrics2.cfm").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
86+
context.fileSystem().add(inputFileMetrics2);
87+
DefaultInputFile inputFileMetrics3 = new TestInputFileBuilder(context.module().key(), baseDir.getAbsoluteFile(), new File("src/test/resources/EpisodeClaim.cfc").getAbsoluteFile()).setLanguage(ColdFusionPlugin.LANGUAGE_KEY).build();
88+
context.fileSystem().add(inputFileMetrics3);
7089
}
7190

7291
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<cfcomponent output="false" extends="com.remoteproxy">
2+
<cfsetting showdebugoutput="true">
3+
4+
<cffunction name="hasEpisode" returntype="struct" access="remote" output="false" returnformat="JSON" hint="hint example">
5+
6+
<cfargument name="episodeKey" type="numeric" required="true" />
7+
8+
<cfset local.resultStruct = {} />
9+
<cfset local.resultStruct.has = false />
10+
<cfset local.resultStruct.result = "success">
11+
12+
13+
<!--testcomment-->
14+
15+
16+
<cftry>
17+
18+
<cfset local.episodes = createObject("component", "packages.system.Episode.Episode.EpisodeGateway").init(application.dsn).getByAttributesQuery(fEpisodeKey=arguments.episodeKey) />
19+
20+
<cfif local.episodes.recordCount GT 0>
21+
<cfset local.resultStruct.has = true />
22+
</cfif>
23+
24+
<cfcatch type="any">
25+
<cfset local.resultStruct.result = "fail">
26+
<cfset local.resultStruct.errorStruct = super.createErrorJSON(cfcatch)>
27+
</cfcatch>
28+
29+
</cftry>
30+
31+
<cfreturn local.resultStruct />
32+
33+
</cffunction>
34+
35+
</cfcomponent>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<cfset firstName = "World">
22
Hello <cfoutput>#firstName#</cfoutput>!
3+
<!--- this is a comment --->
34
This CFML tutorial was designed for
45
<cfif firstName eq "World">
56
you!
67
<cfelse >
8+
<!--- this is a second comment --->
79
the world to see.
810
</cfif>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
<cfset firstName = "Nico">
22

3+
<!-- comment format
4+
multilines-->
5+
36
Hello <cfoutput>#firstName#</cfoutput>!
47
This CFML tutorial was designed for
58
<cfif firstName eq "Nico">
69
you!
710
<cfelse>
11+
<!-- Another comment format
12+
multilines
13+
14+
-->
15+
816
the world to see.
917
</cfif>

0 commit comments

Comments
 (0)