Skip to content

Commit 77dfdd1

Browse files
committed
#55 Refactor some code and add tests for coverage
1 parent 0e81174 commit 77dfdd1

File tree

14 files changed

+236
-163
lines changed

14 files changed

+236
-163
lines changed

src/main/java/fr/cnes/sonar/plugins/icode/ICodePlugin.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828

2929
/**
3030
* This class is the entry point for all extensions.
31-
*
32-
* @author lequal
3331
*/
3432
public class ICodePlugin implements Plugin {
3533

src/main/java/fr/cnes/sonar/plugins/icode/check/ICodeSensor.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import com.google.common.collect.Maps;
2121
import fr.cnes.icode.data.AbstractChecker;
2222
import fr.cnes.icode.data.CheckResult;
23-
import fr.cnes.icode.exception.JFlexException;
2423
import fr.cnes.icode.logger.ICodeLogger;
2524
import fr.cnes.icode.services.checkers.CheckerContainer;
2625
import fr.cnes.icode.services.checkers.CheckerService;
@@ -53,6 +52,7 @@
5352
import java.io.FileInputStream;
5453
import java.io.FileNotFoundException;
5554
import java.io.IOException;
55+
import java.net.URI;
5656
import java.nio.file.Paths;
5757
import java.util.*;
5858

@@ -136,13 +136,13 @@ protected void executeExternalResultsImport(final SensorContext sensorContext) {
136136
final ActiveRules activeRules = sensorContext.activeRules();
137137

138138
// Report files found in file system and corresponding to SQ property.
139-
final List<String> reportFiles = getReportFiles(config, fileSystem);
139+
final List<File> reportFiles = getReportFiles(config, fileSystem);
140140

141141
// If exists, unmarshal each xml result file.
142-
for(final String reportPath : reportFiles) {
142+
for(final File reportFile : reportFiles) {
143143
try {
144-
// Unmarshall the xml.
145-
final FileInputStream file = new FileInputStream(reportPath);
144+
// Unmarshal the xml.
145+
final FileInputStream file = new FileInputStream(reportFile);
146146
final AnalysisProject analysisProject = (AnalysisProject) XmlHandler.unmarshal(file, AnalysisProject.class);
147147
// Retrieve file in a SonarQube format.
148148
final Map<String, InputFile> scannedFiles = getScannedFiles(fileSystem, analysisProject);
@@ -185,8 +185,8 @@ private void executeEmbeddedICode(final SensorContext sensorContext) {
185185

186186
// Gather all files in a Set.
187187
for(final InputFile inputFile : inputFiles) {
188-
files.add(inputFile.file());
189-
filesMap.put(inputFile.file().getPath(), inputFile);
188+
files.add(new File(inputFile.uri()));
189+
filesMap.put(inputFile.uri().getPath(), inputFile);
190190
}
191191

192192
// Run all checkers on all files.
@@ -324,7 +324,7 @@ private void executeExternalICode(final SensorContext sensorContext) {
324324
final FilePredicates predicates = fileSystem.predicates();
325325
final Iterable<InputFile> inputFiles = fileSystem.inputFiles(predicates.hasType(InputFile.Type.MAIN));
326326
final StringBuilder files = new StringBuilder();
327-
inputFiles.forEach(file -> files.append(file.file().getPath()).append(" "));
327+
inputFiles.forEach(file -> files.append(file.uri().getPath()).append(" "));
328328
final String executable = config.get(ICodePluginProperties.ICODE_PATH_KEY).orElse(ICodePluginProperties.ICODE_PATH_DEFAULT);
329329
final String outputFile = config.get(ICodePluginProperties.REPORT_PATH_KEY).orElse(ICodePluginProperties.REPORT_PATH_DEFAULT);
330330
final String outputPath = Paths.get(sensorContext.fileSystem().baseDir().toString(),outputFile).toString();
@@ -367,7 +367,10 @@ static void saveIssue(final SensorContext sensorContext, final CheckResult resul
367367
final FileSystem fileSystem = sensorContext.fileSystem();
368368
final FilePredicates predicates = fileSystem.predicates();
369369
final NewIssue issue = sensorContext.newIssue();
370-
final InputFile file = fileSystem.inputFile(predicates.hasPath(result.getFile().getPath()));
370+
final String fileToFind = result.getFile().getPath();
371+
System.err.println(fileToFind);
372+
final FilePredicate predicate = predicates.hasRelativePath(fileToFind);
373+
final InputFile file = fileSystem.inputFile(predicate);
371374
if(Objects.nonNull(file)) {
372375
final String repositoryKey = ICodeRulesDefinition.getRepositoryKeyForLanguage(file.language());
373376
final RuleKey ruleKey = RuleKey.of(repositoryKey, result.getName());
@@ -439,41 +442,39 @@ protected Map<String, InputFile> getScannedFiles(final FileSystem fileSystem, fi
439442
// Looks for each file in file system, print an error if not found.
440443
for(final AnalysisFile file : files) {
441444
// Checks if the file system contains a file with corresponding path (relative or absolute).
442-
FilePredicate predicate = fileSystem.predicates().hasPath(file.getFileName());
443-
InputFile inputFile = fileSystem.inputFile(predicate);
445+
final String fileToFind = new File(fileSystem.baseDir(), file.getFileName()).getPath();
446+
final FilePredicate predicate = fileSystem.predicates().hasRelativePath(fileToFind);
447+
final InputFile inputFile = fileSystem.inputFile(predicate);
444448
if(inputFile!=null) {
445449
result.put(file.getFileName(), inputFile);
446450
} else {
447-
LOGGER.error(String.format(
448-
"The source file '%s' was not found.",
449-
file.getFileName()
450-
));
451+
LOGGER.error(String.format("The source file '%s' was not found.", file.getFileName()));
451452
}
452453
}
453454

454455
return result;
455456
}
456457

457458
/**
458-
* Returns a list of processable result file's path.
459+
* Returns a list of processable result files.
459460
*
460461
* @param config Configuration of the analysis where properties are put.
461462
* @param fileSystem The current file system.
462463
* @return Return a list of path 'findable' in the file system.
463464
*/
464-
private List<String> getReportFiles(final Configuration config, final FileSystem fileSystem) {
465+
protected List<File> getReportFiles(final Configuration config, final FileSystem fileSystem) {
465466
// Contains the result to be returned.
466-
final List<String> result = new ArrayList<>();
467+
final List<File> result = new ArrayList<>();
467468

468469
// Retrieves the non-verified path list from the SonarQube property.
469470
final String[] pathArray = config.getStringArray(ICodePluginProperties.REPORT_PATH_KEY);
470471

471472
// Check if each path is known by the file system and add it to the processable path list,
472473
// otherwise print a warning and ignore this result file.
473-
for(String path : pathArray) {
474+
for(final String path : pathArray) {
474475
final File file = new File(fileSystem.baseDir(), path);
475476
if(file.exists() && file.isFile()) {
476-
result.add(path);
477+
result.add(file);
477478
LOGGER.info(String.format("Results file %s has been found and will be processed.", path));
478479
} else {
479480
LOGGER.warn(String.format("Results file %s has not been found and wont be processed.", path));

src/main/java/fr/cnes/sonar/plugins/icode/converter/AnalysisConverter.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/*
2+
* This file is part of sonar-icode-cnes-plugin.
3+
*
4+
* sonar-icode-cnes-plugin is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* sonar-icode-cnes-plugin is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with sonar-icode-cnes-plugin. If not, see <http://www.gnu.org/licenses/>.
16+
*/
117
package fr.cnes.sonar.plugins.icode.converter;
218

319
import com.thoughtworks.xstream.converters.ConverterLookup;
@@ -9,20 +25,15 @@
925
import fr.cnes.sonar.plugins.icode.model.AnalysisRule;
1026
import fr.cnes.sonar.plugins.icode.model.Result;
1127

28+
/**
29+
* Single purpose class to convert i-Code result into usable java objects.
30+
*/
1231
public class AnalysisConverter extends ToAttributedValueConverter {
1332

14-
public AnalysisConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup) {
15-
super(type, mapper, reflectionProvider, lookup);
16-
}
17-
1833
public AnalysisConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup, String valueFieldName) {
1934
super(type, mapper, reflectionProvider, lookup, valueFieldName);
2035
}
2136

22-
public AnalysisConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup, String valueFieldName, Class valueDefinedIn) {
23-
super(type, mapper, reflectionProvider, lookup, valueFieldName, valueDefinedIn);
24-
}
25-
2637
@Override
2738
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext unmarshallingContext) {
2839
Object r = super.unmarshal(reader, unmarshallingContext);

src/main/java/fr/cnes/sonar/plugins/icode/measures/ICodeMetricsProcessor.java

Lines changed: 59 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public class ICodeMetricsProcessor {
4343

4444
/** Shared part between all i-Code metrics key. **/
4545
private static final String COMMON_METRICS_KEY_PART = ".MET.";
46+
/** Define location in the code **/
47+
public static final String CLASS = "class";
48+
public static final String METHOD = "method";
4649

4750
/**
4851
* Private constructor because this class contains only static methods.
@@ -60,69 +63,68 @@ public static boolean isMetric(final String ruleId) {
6063
}
6164

6265
/**
63-
* Save a measure in a SQ or CNES metric.
66+
* Save a measure in SonarQube from i-Code report format.
6467
*
6568
* @param context Context of the analysis containing services.
6669
* @param files Map containing files in SQ format.
67-
* @param rule Measure considered like a rule in i-Code.
70+
* @param icodeMeasure Measure considered like a rule in i-Code.
6871
*/
69-
public static void saveMeasure(final SensorContext context, final Map<String, InputFile> files, final AnalysisRule rule) {
72+
public static void saveMeasure(final SensorContext context, final Map<String, InputFile> files,
73+
final AnalysisRule icodeMeasure) {
7074

71-
// Create a new measure from the scan context.
72-
final NewMeasure<Integer> newMeasure = context.newMeasure();
73-
// i-Code rule id.
74-
final String metricKey = rule.getAnalysisRuleId();
7575
// Determine if a measure is relative to a file or a method.
76-
final String metricScope = rule.getResult().getResultTypePlace();
77-
// Component concerned by the measure.
78-
final String metricComponent = rule.getResult().getFileName();
79-
// Component concerned by the measure.
80-
final String measureValue = rule.getResult().getResultValue();
81-
82-
// Is true if the metric must be interpreted by the plugin.
83-
boolean isCalculated = false;
76+
final String metricScope = icodeMeasure.getResult().getResultTypePlace();
8477

85-
// Local attribute to set
86-
Metric<Integer> metric = null;
87-
InputComponent component = null;
88-
Integer value = null;
89-
if(metricScope.equals("class")) {
78+
if(metricScope.equals(CLASS)) {
79+
// Get i-Code rule id to test if issue must be saved here.
80+
final String metricKey = icodeMeasure.getAnalysisRuleId();
9081
// Take SHELL / F77 / F90 ncloc into account
9182
if (metricKey.contains("MET.LineOfCode")) {
92-
metric = CoreMetrics.NCLOC;
93-
component = files.getOrDefault(metricComponent, null);
94-
value = Double.valueOf(measureValue).intValue();
95-
isCalculated = true;
83+
saveSonarQubeNewMeasure(context, files, CoreMetrics.NCLOC, icodeMeasure);
9684
}
9785
// Take SHELL / F77 / F90 number of comment lines into account
9886
else if (metricKey.contains("MET.LineOfComment")) {
99-
metric = CoreMetrics.COMMENT_LINES;
100-
component = files.getOrDefault(metricComponent, null);
101-
value = Double.valueOf(measureValue).intValue();
102-
isCalculated = true;
87+
saveSonarQubeNewMeasure(context, files, CoreMetrics.COMMENT_LINES, icodeMeasure);
10388
}
10489
// Take SHELL complexity into account
10590
else if (metricKey.contains("SH.MET.ComplexitySimplified")) {
106-
metric = CoreMetrics.COMPLEXITY;
107-
component = files.getOrDefault(metricComponent, null);
108-
value = Double.valueOf(measureValue).intValue();
109-
isCalculated = true;
91+
saveSonarQubeNewMeasure(context, files, CoreMetrics.COMPLEXITY, icodeMeasure);
11092
}
11193
}
11294

95+
}
96+
97+
/**
98+
* Save an issue or log a warning if the component is not found.
99+
*
100+
* @param context SonarQube context in which measure must be saved.
101+
* @param files All files visible by SonarQube.
102+
* @param sonarMetric The SonarQube metric in which the measure must be saved.
103+
* @param icodeMeasure The value of the i-Code measure.
104+
*/
105+
protected static void saveSonarQubeNewMeasure(final SensorContext context, final Map<String, InputFile> files,
106+
final Metric<Integer> sonarMetric, final AnalysisRule icodeMeasure) {
107+
108+
// Component concerned by the measure.
109+
final String metricComponent = icodeMeasure.getResult().getFileName();
110+
// Component concerned by the measure.
111+
final String measureValue = icodeMeasure.getResult().getResultValue();
112+
// Retrieve the component/file on which the measure has been taken.
113+
final InputComponent component = files.getOrDefault(metricComponent, null);
114+
// i-Code takes measure as Double and SonarQube as Integer: just convert it.
115+
final int value = Double.valueOf(measureValue).intValue();
113116
// Finally save the measure if all value are filled.
114-
if(isCalculated) {
115-
if (metric != null && value != null && component != null) {
116-
newMeasure.forMetric(metric);
117-
newMeasure.withValue(value);
118-
newMeasure.on(component);
119-
newMeasure.save();
120-
} else {
121-
LOGGER.warn(String.format("Measure '%s' for '%s' is ignored on '%s'.",
122-
metricKey, metricScope, metricComponent));
123-
}
117+
if (component != null) {
118+
// Create a new measure from the scan context.
119+
final NewMeasure<Integer> newMeasure = context.newMeasure();
120+
newMeasure.forMetric(sonarMetric);
121+
newMeasure.withValue(value);
122+
newMeasure.on(component);
123+
newMeasure.save();
124+
} else {
125+
LOGGER.warn(String.format("Measure '%s' for '%s' is ignored on '%s'.",
126+
icodeMeasure.getAnalysisRuleId(), CLASS, metricComponent));
124127
}
125-
126128
}
127129

128130
/**
@@ -142,7 +144,7 @@ public static void saveExtraMeasures(final SensorContext context, final Map<Stri
142144
for(final AnalysisRule rule : project.getAnalysisRules()) {
143145
final String type = rule.getResult().getResultTypePlace();
144146
final String id = rule.getAnalysisRuleId();
145-
if(id.contains(COMMON_METRICS_KEY_PART) && type.equals("method")) {
147+
if(id.contains(COMMON_METRICS_KEY_PART) && type.equals(METHOD)) {
146148
final List<AnalysisRule> sub = measures.getOrDefault(id, new ArrayList<>());
147149
sub.add(rule);
148150
measures.put(id, sub);
@@ -173,9 +175,9 @@ public static void saveExtraMeasures(final SensorContext context, final Map<Stri
173175

174176
// Collect all measures on methods into specific list
175177
for(final CheckResult result : results) {
176-
final String type = Objects.isNull(result.getLocation()) || result.getLocation().isEmpty() ? "class" : "method";
178+
final String type = Objects.isNull(result.getLocation()) || result.getLocation().isEmpty() ? CLASS : METHOD;
177179
final String id = result.getName();
178-
if(id.contains(COMMON_METRICS_KEY_PART) && type.equals("method")) {
180+
if(id.contains(COMMON_METRICS_KEY_PART) && type.equals(METHOD)) {
179181
final List<AnalysisRule> sub = measures.getOrDefault(id, new ArrayList<>());
180182
final AnalysisRule rule = new AnalysisRule(result);
181183
sub.add(rule);
@@ -320,71 +322,29 @@ private static void saveMeasure(final SensorContext context, final Map<String, I
320322
}
321323

322324
/**
323-
* Save a measure in a SQ or CNES metric.
325+
* Save a measure in SonarQube from i-Code internal java model format.
324326
*
325-
* @param sensorContext Context of the analysis containing services.
327+
* @param context Context of the analysis containing services.
326328
* @param result Measure considered like a rule in i-Code.
327329
*/
328-
public static void saveMeasure(final SensorContext sensorContext, final CheckResult result) {
330+
public static void saveMeasure(final SensorContext context, final CheckResult result) {
329331

330332
// Filesystem provided by SonarQube.
331-
final FileSystem fileSystem = sensorContext.fileSystem();
333+
final FileSystem fileSystem = context.fileSystem();
332334
// Factory for SonarQube predicates.
333335
final FilePredicates predicates = fileSystem.predicates();
334-
// Create a new measure from the scan context.
335-
final NewMeasure<Integer> newMeasure = sensorContext.newMeasure();
336-
// i-Code rule id.
337-
final String metricKey = result.getName();
338-
// Determine if a measure is relative to a file or a method.
339-
final String metricScope = Objects.isNull(result.getLocation()) || result.getLocation().isEmpty() ? "class" : "method";
340336
// Component concerned by the measure.
341337
final String metricComponent = result.getFile().getPath();
342-
// Component concerned by the measure.
343-
final String measureValue = String.valueOf(result.getValue());
344338

345-
// Is true if the metric must be interpreted by the plugin.
346-
boolean isCalculated = false;
339+
// Create a fake Map of input files to reuse function.
340+
final Map<String, InputFile> files = new HashMap<>();
341+
files.put(metricComponent, fileSystem.inputFile(predicates.hasPath(metricComponent)));
347342

348-
// Local attribute to set
349-
Metric<Integer> metric = null;
350-
InputComponent component = null;
351-
Integer value = null;
352-
if(metricScope.equals("class")) {
353-
// Take SHELL / F77 / F90 ncloc into account
354-
if (metricKey.contains("MET.LineOfCode")) {
355-
metric = CoreMetrics.NCLOC;
356-
component = fileSystem.inputFile(predicates.hasPath(metricComponent));
357-
value = Double.valueOf(measureValue).intValue();
358-
isCalculated = true;
359-
}
360-
// Take SHELL / F77 / F90 number of comment lines into account
361-
else if (metricKey.contains("MET.LineOfComment")) {
362-
metric = CoreMetrics.COMMENT_LINES;
363-
component = fileSystem.inputFile(predicates.hasPath(metricComponent));
364-
value = Double.valueOf(measureValue).intValue();
365-
isCalculated = true;
366-
}
367-
// Take SHELL complexity into account
368-
else if (metricKey.contains("SH.MET.ComplexitySimplified")) {
369-
metric = CoreMetrics.COMPLEXITY;
370-
component = fileSystem.inputFile(predicates.hasPath(metricComponent));
371-
value = Double.valueOf(measureValue).intValue();
372-
isCalculated = true;
373-
}
374-
}
343+
// Create a fake AnalysisRule for reuse purpose.
344+
final AnalysisRule icodeMeasure = new AnalysisRule(result);
375345

376-
// Finally save the measure if all value are filled.
377-
if(isCalculated) {
378-
if (metric != null && value != null && component != null) {
379-
newMeasure.forMetric(metric);
380-
newMeasure.withValue(value);
381-
newMeasure.on(component);
382-
newMeasure.save();
383-
} else {
384-
LOGGER.warn(String.format("Measure '%s' for '%s' is ignored on '%s'.",
385-
metricKey, metricScope, metricComponent));
386-
}
387-
}
346+
// Call method for AnalysisRule saving.
347+
saveMeasure(context, files, icodeMeasure);
388348

389349
}
390350
}

0 commit comments

Comments
 (0)