Skip to content

Commit 1fb0357

Browse files
SONARPY-985 Show UI warning when errors occur in coverage report parsing (#1103)
1 parent 1fab039 commit 1fb0357

File tree

5 files changed

+54
-15
lines changed

5 files changed

+54
-15
lines changed

sonar-python-plugin/src/main/java/org/sonar/plugins/python/PythonReportSensor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ public void execute(SensorContext context) {
5858
String reportPathPropertyKey = reportPathKey();
5959
String reportPath = conf.get(reportPathPropertyKey).orElse(defaultReportPath());
6060
try {
61-
List<File> reports = getReports(conf, context.fileSystem().baseDir().getPath(), reportPathPropertyKey, reportPath);
61+
List<File> reports = getReports(conf, context.fileSystem().baseDir().getPath(), reportPathPropertyKey, reportPath, analysisWarnings);
6262
processReports(context, reports);
6363
} catch (Exception e) {
6464
LOG.warn("Cannot read report '{}', the following exception occurred: {}", reportPath, e.getMessage());
6565
analysisWarnings.addUnique(String.format("An error occurred while trying to import %s report(s): '%s'", reportType, reportPath));
6666
}
6767
}
6868

69-
public static List<File> getReports(Configuration conf, String baseDirPath, String reportPathPropertyKey, String reportPath) {
69+
public static List<File> getReports(Configuration conf, String baseDirPath, String reportPathPropertyKey, String reportPath, AnalysisWarningsWrapper analysisWarnings) {
7070
LOG.debug("Using pattern '{}' to find reports", reportPath);
7171

7272
FileProvider provider = new FileProvider(new File(baseDirPath), reportPath);
@@ -77,7 +77,9 @@ public static List<File> getReports(Configuration conf, String baseDirPath, Stri
7777
// try absolute path
7878
File file = new File(reportPath);
7979
if (!file.exists()) {
80-
LOG.warn("No report was found for {} using pattern {}", reportPathPropertyKey, reportPath);
80+
String formattedMessage = String.format("No report was found for %s using pattern %s", reportPathPropertyKey, reportPath);
81+
LOG.warn(formattedMessage);
82+
analysisWarnings.addUnique(formattedMessage);
8183
} else {
8284
matchingFiles.add(file);
8385
}

sonar-python-plugin/src/main/java/org/sonar/plugins/python/coverage/CoberturaParser.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import java.io.File;
2323
import java.util.ArrayList;
2424
import java.util.Collections;
25+
import java.util.HashSet;
2526
import java.util.List;
2627
import java.util.Map;
28+
import java.util.Set;
2729
import java.util.stream.Collectors;
2830
import javax.annotation.Nullable;
2931
import javax.xml.stream.XMLStreamException;
@@ -42,6 +44,7 @@ public class CoberturaParser {
4244

4345
private static final Logger LOG = Loggers.get(CoberturaParser.class);
4446

47+
private final Set<String> errors = new HashSet<>();
4548
private int unresolvedFilenameCount;
4649

4750
public void parseReport(File xmlFile, SensorContext context, final Map<InputFile, NewCoverage> coverageData) throws XMLStreamException {
@@ -68,11 +71,13 @@ public void parseReport(File xmlFile, SensorContext context, final Map<InputFile
6871
});
6972
parser.parse(xmlFile);
7073
if (unresolvedFilenameCount > 1) {
71-
LOG.error("Cannot resolve {} file paths, ignoring coverage measures for those files", unresolvedFilenameCount);
74+
String message = String.format("Cannot resolve %d file paths, ignoring coverage measures for those files", unresolvedFilenameCount);
75+
LOG.error(message);
76+
errors.add(message);
7277
}
7378
}
7479

75-
private static List<File> extractBaseDirectories(SMInputCursor sources, File defaultBaseDirectory) throws XMLStreamException {
80+
private List<File> extractBaseDirectories(SMInputCursor sources, File defaultBaseDirectory) throws XMLStreamException {
7681
List<File> baseDirectories = new ArrayList<>();
7782
SMInputCursor source = sources.childElementCursor("source");
7883
while (source.getNext() != null) {
@@ -82,7 +87,9 @@ private static List<File> extractBaseDirectories(SMInputCursor sources, File def
8287
if (baseDirectory.isDirectory()) {
8388
baseDirectories.add(baseDirectory);
8489
} else {
85-
LOG.warn("Invalid directory path in 'source' element: {}", path);
90+
String formattedMessage = String.format("Invalid directory path in 'source' element: %s", path);
91+
LOG.warn(formattedMessage);
92+
errors.add(formattedMessage);
8693
}
8794
}
8895
}
@@ -112,7 +119,7 @@ private InputFile resolve(SensorContext context, List<File> baseDirectories, Str
112119
File file = new File(filename);
113120
if (file.isAbsolute()) {
114121
if (!file.exists()) {
115-
logUnresolvedFile("Cannot resolve the file path '{}' of the coverage report, the file does not exist in all <source>.", filename);
122+
logUnresolvedFile("Cannot resolve the file path '%s' of the coverage report, the file does not exist in all 'source'.", filename);
116123
}
117124
absolutePath = file.getAbsolutePath();
118125
} else {
@@ -121,11 +128,11 @@ private InputFile resolve(SensorContext context, List<File> baseDirectories, Str
121128
.filter(File::exists)
122129
.collect(Collectors.toList());
123130
if (fileList.isEmpty()) {
124-
logUnresolvedFile("Cannot resolve the file path '{}' of the coverage report, the file does not exist in all <source>.", filename);
131+
logUnresolvedFile("Cannot resolve the file path '%s' of the coverage report, the file does not exist in all 'source'.", filename);
125132
return null;
126133
}
127134
if (fileList.size() > 1) {
128-
logUnresolvedFile("Cannot resolve the file path '{}' of the coverage report, ambiguity, the file exists in several <source>.", filename);
135+
logUnresolvedFile("Cannot resolve the file path '%s' of the coverage report, ambiguity, the file exists in several 'source'.", filename);
129136
return null;
130137
}
131138
absolutePath = fileList.get(0).getAbsolutePath();
@@ -136,7 +143,9 @@ private InputFile resolve(SensorContext context, List<File> baseDirectories, Str
136143
private void logUnresolvedFile(String message, String filename) {
137144
unresolvedFilenameCount++;
138145
if (unresolvedFilenameCount == 1) {
139-
LOG.error(message, filename);
146+
String formattedMessage = String.format(message, filename);
147+
LOG.error(formattedMessage);
148+
errors.add(formattedMessage);
140149
}
141150
}
142151

@@ -154,4 +163,8 @@ private static void collectFileData(SMInputCursor classCursor, NewCoverage cover
154163
}
155164
}
156165
}
166+
167+
public Set<String> errors() {
168+
return errors;
169+
}
157170
}

sonar-python-plugin/src/main/java/org/sonar/plugins/python/coverage/PythonCoverageSensor.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ public void execute(SensorContext context) {
8484
}
8585
}
8686

87-
private static List<File> getCoverageReports(String baseDir, Configuration config) {
87+
private List<File> getCoverageReports(String baseDir, Configuration config) {
8888
if (!config.hasKey(REPORT_PATHS_KEY)) {
89-
return getReports(config, baseDir, REPORT_PATHS_KEY, DEFAULT_REPORT_PATH);
89+
return getReports(config, baseDir, REPORT_PATHS_KEY, DEFAULT_REPORT_PATH, analysisWarnings);
9090
}
9191

9292
return Arrays.stream(config.getStringArray(REPORT_PATHS_KEY))
93-
.flatMap(path -> getReports(config, baseDir, REPORT_PATHS_KEY, path).stream())
93+
.flatMap(path -> getReports(config, baseDir, REPORT_PATHS_KEY, path, analysisWarnings).stream())
9494
.collect(Collectors.toList());
9595
}
9696

@@ -108,12 +108,17 @@ private static Set<File> uniqueAbsolutePaths(List<File> reports) {
108108
.collect(Collectors.toCollection(LinkedHashSet::new));
109109
}
110110

111-
private static Map<InputFile, NewCoverage> parseReport(File report, SensorContext context) {
111+
private Map<InputFile, NewCoverage> parseReport(File report, SensorContext context) {
112112
Map<InputFile, NewCoverage> coverageMeasures = new HashMap<>();
113113
try {
114114
CoberturaParser parser = new CoberturaParser();
115115
parser.parseReport(report, context, coverageMeasures);
116+
if (!parser.errors().isEmpty()) {
117+
String parseErrors = String.join("%n", parser.errors());
118+
analysisWarnings.addUnique(String.format("The following error(s) occurred while trying to import coverage report:%n%s", parseErrors));
119+
}
116120
} catch (EmptyReportException e) {
121+
analysisWarnings.addUnique(String.format("The coverage report '%s' has been ignored because it seems to be empty.", report));
117122
LOG.warn("The report '{}' seems to be empty, ignoring. '{}'", report, e);
118123
} catch (XMLStreamException e) {
119124
throw new IllegalStateException("Error parsing the report '" + report + "'", e);

sonar-python-plugin/src/test/java/org/sonar/plugins/python/coverage/PythonCoverageSensorTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ public void test_unresolved_path() {
237237
coverageSensor.execute(context);
238238

239239
String expectedLogMessage = String.format(
240-
"Cannot resolve the file path '%sabsolute%ssources%snot_exist.py' of the coverage report, the file does not exist in all <source>.",
240+
"Cannot resolve the file path '%sabsolute%ssources%snot_exist.py' of the coverage report, the file does not exist in all 'source'.",
241241
currentFileSeparator,
242242
currentFileSeparator,
243243
currentFileSeparator);
@@ -300,6 +300,16 @@ public void should_do_nothing_on_empty_report() {
300300
assertThat(context.lineHits(FILE1_KEY, 1)).isNull();
301301
}
302302

303+
@Test
304+
public void should_warn_if_source_is_not_directory() {
305+
settings.setProperty(PythonCoverageSensor.REPORT_PATHS_KEY, "coverage_source_invalid_directory.xml");
306+
coverageSensor.execute(context);
307+
File file = new File("src/test/resources/org/sonar/plugins/python/coverage-reports/sources/file1.py");
308+
String message = "Invalid directory path in 'source' element: " + file.getPath();
309+
assertThat(logTester.logs(LoggerLevel.WARN)).contains(message);
310+
verify(analysisWarnings, times(1)).addUnique("The following error(s) occurred while trying to import coverage report:" + System.lineSeparator() + message);
311+
}
312+
303313
@Test
304314
public void no_default_report_log() {
305315
settings.clear();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" ?>
2+
<coverage branch-rate="0.5" branches-covered="2" branches-valid="4" complexity="0" line-rate="0.75" lines-covered="6" lines-valid="8" timestamp="1515594208483" version="4.4.2">
3+
<!-- Generated by coverage.py: https://coverage.readthedocs.io -->
4+
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5+
<sources>
6+
<source>src/test/resources/org/sonar/plugins/python/coverage-reports/sources/file1.py</source>
7+
</sources>
8+
<packages></packages>
9+
</coverage>

0 commit comments

Comments
 (0)