Skip to content

Commit 2c2ecbd

Browse files
committed
support metrics for InputFile
1 parent c980a1e commit 2c2ecbd

14 files changed

Lines changed: 286 additions & 274 deletions

File tree

cxx-sensors/src/main/java/org/sonar/cxx/sensors/tests/dotnet/UnitTestConfiguration.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,12 @@
2020
package org.sonar.cxx.sensors.tests.dotnet;
2121

2222
import org.sonar.api.config.Configuration;
23-
import org.sonar.api.utils.log.Logger;
24-
import org.sonar.api.utils.log.Loggers;
2523

2624
public class UnitTestConfiguration {
2725

2826
public static final String VISUAL_STUDIO_TEST_RESULTS_PROPERTY_KEY = "sonar.cxx.vstest.reportsPaths";
2927
public static final String XUNIT_TEST_RESULTS_PROPERTY_KEY = "sonar.cxx.xunit.reportsPaths";
3028
public static final String NUNIT_TEST_RESULTS_PROPERTY_KEY = "sonar.cxx.nunit.reportsPaths";
31-
private static final String EXIST_CONFIGURATION_PARAMETER = "Exist configuration parameter: '{}':'{}'";
32-
private static final Logger LOG = Loggers.get(UnitTestConfiguration.class);
3329

3430
private final Configuration config;
3531

@@ -38,20 +34,14 @@ public UnitTestConfiguration(Configuration config) {
3834
}
3935

4036
boolean hasVisualStudioTestResultsFile() {
41-
LOG.debug(EXIST_CONFIGURATION_PARAMETER, VISUAL_STUDIO_TEST_RESULTS_PROPERTY_KEY, config.hasKey(
42-
VISUAL_STUDIO_TEST_RESULTS_PROPERTY_KEY));
4337
return config.hasKey(VISUAL_STUDIO_TEST_RESULTS_PROPERTY_KEY);
4438
}
4539

4640
boolean hasXUnitTestResultsFile() {
47-
LOG.debug(EXIST_CONFIGURATION_PARAMETER, XUNIT_TEST_RESULTS_PROPERTY_KEY, config.hasKey(
48-
XUNIT_TEST_RESULTS_PROPERTY_KEY));
4941
return config.hasKey(XUNIT_TEST_RESULTS_PROPERTY_KEY);
5042
}
5143

5244
boolean hasNUnitTestResultsFile() {
53-
LOG.debug(EXIST_CONFIGURATION_PARAMETER, NUNIT_TEST_RESULTS_PROPERTY_KEY,
54-
config.hasKey(NUNIT_TEST_RESULTS_PROPERTY_KEY));
5545
return config.hasKey(NUNIT_TEST_RESULTS_PROPERTY_KEY);
5646
}
5747

cxx-sensors/src/main/java/org/sonar/cxx/sensors/tests/xunit/CxxXunitSensor.java

Lines changed: 45 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
import java.io.IOException;
2424
import java.io.Serializable;
2525
import java.util.Arrays;
26+
import java.util.Collection;
2627
import java.util.Collections;
2728
import java.util.List;
2829
import javax.xml.stream.XMLStreamException;
30+
import org.sonar.api.batch.fs.InputFile;
31+
import org.sonar.api.batch.fs.InputFile.Type;
2932
import org.sonar.api.batch.sensor.SensorContext;
3033
import org.sonar.api.batch.sensor.SensorDescriptor;
3134
import org.sonar.api.config.PropertyDefinition;
@@ -46,6 +49,7 @@ public class CxxXunitSensor extends CxxReportSensor {
4649

4750
public static final String REPORT_PATH_KEY = "sonar.cxx.xunit.reportPath";
4851
private static final Logger LOG = Loggers.get(CxxXunitSensor.class);
52+
private SensorContext context;
4953

5054
public static List<PropertyDefinition> properties() {
5155
return Collections.unmodifiableList(Arrays.asList(
@@ -76,17 +80,17 @@ public void describe(SensorDescriptor descriptor) {
7680
@Override
7781
public void executeImpl(SensorContext context) {
7882
try {
83+
this.context = context;
7984
List<File> reports = getReports(context.config(), context.fileSystem().baseDir(), REPORT_PATH_KEY);
8085
if (!reports.isEmpty()) {
8186
XunitReportParser parserHandler = parseReport(reports);
82-
List<TestCase> testcases = parserHandler.getTestCases();
83-
save(context, testcases);
87+
save(parserHandler.getTestFiles());
8488
} else {
8589
LOG.debug("No xUnit reports found, nothing to process");
8690
}
8791
} catch (IOException | XMLStreamException e) {
8892
var msg = new StringBuilder(256)
89-
.append("Cannot feed the data into SonarQube, details: '")
93+
.append("Cannot feed the xUnit report data into SonarQube, details: '")
9094
.append(e)
9195
.append("'")
9296
.toString();
@@ -102,7 +106,7 @@ public void executeImpl(SensorContext context) {
102106
* @throws IOException
103107
*/
104108
private XunitReportParser parseReport(List<File> reports) throws XMLStreamException, IOException {
105-
var parserHandler = new XunitReportParser();
109+
var parserHandler = new XunitReportParser(context.fileSystem().baseDir().getPath());
106110
var parser = new StaxParser(parserHandler, false);
107111
for (var report : reports) {
108112
LOG.info("Processing xUnit report '{}'", report);
@@ -116,73 +120,61 @@ private XunitReportParser parseReport(List<File> reports) throws XMLStreamExcept
116120
return parserHandler;
117121
}
118122

119-
private void save(final SensorContext context, List<TestCase> testcases) {
123+
private void save(Collection<TestFile> testfiles) {
120124

121125
int testsCount = 0;
122126
int testsSkipped = 0;
123127
int testsErrors = 0;
124128
int testsFailures = 0;
125129
long testsTime = 0;
126-
for (var tc : testcases) {
127-
if (tc.isSkipped()) {
128-
testsSkipped++;
129-
} else if (tc.isFailure()) {
130-
testsFailures++;
131-
} else if (tc.isError()) {
132-
testsErrors++;
130+
for (var tf : testfiles) {
131+
if (!tf.getFilename().isEmpty()) {
132+
InputFile inputFile = getInputFileIfInProject(context, tf.getFilename());
133+
if (inputFile != null) {
134+
if (inputFile.language() != null && inputFile.type() == Type.TEST) {
135+
LOG.debug("Saving xUnit data for '{}': tests={} | errors:{} | failure:{} | skipped:{} | time:{}",
136+
tf.getFilename(), tf.getTests(), tf.getErrors(), tf.getFailures(), tf.getSkipped(),
137+
tf.getExecutionTime());
138+
saveMetric(inputFile, CoreMetrics.TESTS, tf.getTests());
139+
saveMetric(inputFile, CoreMetrics.TEST_ERRORS, tf.getErrors());
140+
saveMetric(inputFile, CoreMetrics.TEST_FAILURES, tf.getFailures());
141+
saveMetric(inputFile, CoreMetrics.SKIPPED_TESTS, tf.getSkipped());
142+
saveMetric(inputFile, CoreMetrics.TEST_EXECUTION_TIME, tf.getExecutionTime());
143+
}
144+
}
133145
}
134-
testsCount++;
135-
testsTime += tc.getTime();
146+
testsTime += tf.getExecutionTime();
147+
testsCount += tf.getTests();
148+
testsFailures += tf.getFailures();
149+
testsErrors += tf.getErrors();
150+
testsSkipped += tf.getSkipped();
136151
}
137-
testsCount -= testsSkipped;
138152

139153
if (testsCount > 0) {
140-
141-
try {
142-
saveProjectMetric(context, CoreMetrics.TESTS, testsCount);
143-
} catch (IllegalArgumentException ex) {
144-
LOG.error("Cannot save measure TESTS : '{}', ignoring measure", ex.getMessage());
145-
CxxUtils.validateRecovery(ex, context.config());
146-
}
147-
148-
try {
149-
saveProjectMetric(context, CoreMetrics.TEST_ERRORS, testsErrors);
150-
} catch (IllegalArgumentException ex) {
151-
LOG.error("Cannot save measure TEST_ERRORS : '{}', ignoring measure", ex.getMessage());
152-
CxxUtils.validateRecovery(ex, context.config());
153-
}
154-
155-
try {
156-
saveProjectMetric(context, CoreMetrics.TEST_FAILURES, testsFailures);
157-
} catch (IllegalArgumentException ex) {
158-
LOG.error("Cannot save measure TEST_FAILURES : '{}', ignoring measure", ex.getMessage());
159-
CxxUtils.validateRecovery(ex, context.config());
160-
}
161-
162-
try {
163-
saveProjectMetric(context, CoreMetrics.SKIPPED_TESTS, testsSkipped);
164-
} catch (IllegalArgumentException ex) {
165-
LOG.error("Cannot save measure SKIPPED_TESTS : '{}', ignoring measure", ex.getMessage());
166-
CxxUtils.validateRecovery(ex, context.config());
167-
}
168-
169-
try {
170-
saveProjectMetric(context, CoreMetrics.TEST_EXECUTION_TIME, testsTime);
171-
} catch (IllegalArgumentException ex) {
172-
LOG.error("Cannot save measure TEST_EXECUTION_TIME : '{}', ignoring measure", ex.getMessage());
173-
CxxUtils.validateRecovery(ex, context.config());
174-
}
175-
} else {
176-
LOG.debug("The reports contain no testcases");
154+
LOG.debug("Saving xUnit report total data: tests={} | errors:{} | failure:{} | skipped:{} | time:{}",
155+
testsCount, testsErrors, testsFailures, testsSkipped, testsTime);
156+
saveMetric(CoreMetrics.TESTS, testsCount);
157+
saveMetric(CoreMetrics.TEST_ERRORS, testsErrors);
158+
saveMetric(CoreMetrics.TEST_FAILURES, testsFailures);
159+
saveMetric(CoreMetrics.SKIPPED_TESTS, testsSkipped);
160+
saveMetric(CoreMetrics.TEST_EXECUTION_TIME, testsTime);
177161
}
178162
}
179163

180-
private <T extends Serializable> void saveProjectMetric(SensorContext context, Metric<T> metric, T value) {
164+
private <T extends Serializable> void saveMetric(Metric<T> metric, T value) {
181165
context.<T>newMeasure()
182166
.withValue(value)
183167
.forMetric(metric)
184168
.on(context.project())
185169
.save();
186170
}
187171

172+
private <T extends Serializable> void saveMetric(InputFile file, Metric<T> metric, T value) {
173+
context.<T>newMeasure()
174+
.withValue(value)
175+
.forMetric(metric)
176+
.on(file)
177+
.save();
178+
}
179+
188180
}

cxx-sensors/src/main/java/org/sonar/cxx/sensors/tests/xunit/TestCase.java

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
*/
2020
package org.sonar.cxx.sensors.tests.xunit;
2121

22-
import org.apache.commons.lang.StringEscapeUtils;
23-
2422
/**
2523
* Represents a unit test case. Has a couple of data items like name, status, time etc. associated. Reports testcase
2624
* details in sonar-conform XML
@@ -38,9 +36,8 @@ public class TestCase {
3836
private final String errorMessage;
3937
private final int time;
4038
private final String classname;
41-
private final String tcFilename;
42-
private final String tsName;
43-
private final String tsFilename;
39+
private final String filename;
40+
private final String testSuiteName;
4441

4542
/**
4643
* Constructs a testcase instance out of following parameters
@@ -51,42 +48,47 @@ public class TestCase {
5148
* @params stack The stack trace occurred while executing of this testcase; pass "" if the testcase passed/skipped.
5249
* @params msg The error message accosiated with this testcase of the execution was errouneous; pass "" if not.
5350
* @params classname The name of the class this testcase is implemented by
54-
* @params tcFilename The path of the file which implements the testcase
55-
* @params tsName The name of the testssuite this testcase is in.
56-
* @params tsFilename The path of the file which implements the testssuite this testcase is in.
51+
* @params filename The path of the file which implements the testcase
52+
* @params testSuiteName The name of the testsuite this testcase is in.
5753
*/
58-
public TestCase(String name, int time, String status, String stack, String msg,
59-
String classname, String tcFilename, String tsName, String tsFilename) {
60-
this.name = name;
54+
public TestCase(String testCaseName, int time, String status, String stack, String msg,
55+
String classname, String filename, String testSuiteName) {
56+
this.name = testCaseName;
6157
this.time = time;
6258
this.stackTrace = stack;
6359
this.errorMessage = msg;
6460
this.status = status;
6561
this.classname = classname;
66-
this.tcFilename = tcFilename;
67-
this.tsName = tsName;
68-
this.tsFilename = tsFilename;
62+
this.filename = filename;
63+
this.testSuiteName = testSuiteName;
6964
}
7065

7166
/**
7267
* Returns the name of the class which is implementing this testcase
7368
*/
7469
public String getClassname() {
75-
return classname != null ? classname : tsName;
70+
return classname != null ? classname : testSuiteName;
7671
}
7772

7873
/**
7974
* Returns the name of the class which is implementing this testcase
8075
*/
8176
public String getFullname() {
82-
return tsName + ":" + name;
77+
return testSuiteName + ":" + name;
8378
}
8479

8580
/**
8681
* Returns the name of the file where this testcase is implemented
8782
*/
8883
public String getFilename() {
89-
return tcFilename != null ? tcFilename : tsFilename;
84+
return filename;
85+
}
86+
87+
/**
88+
* Returns true if this testcase is ok, false otherwise
89+
*/
90+
public boolean isOk() {
91+
return STATUS_OK.equals(status);
9092
}
9193

9294
/**
@@ -110,36 +112,25 @@ public boolean isSkipped() {
110112
return STATUS_SKIPPED.equals(status);
111113
}
112114

113-
public int getTime() {
114-
return time;
115+
/**
116+
* Error message in case testcase is not ok
117+
*/
118+
public String getErrorMessage() {
119+
return errorMessage;
115120
}
116121

117122
/**
118-
* Returns execution details as sonar-conform XML
123+
* Stack trace in case testcase is not ok
119124
*/
120-
public String getDetails() {
121-
var details = new StringBuilder(512);
122-
details.append("<testcase status=\"")
123-
.append(status)
124-
.append("\" time=\"")
125-
.append(time)
126-
.append("\" name=\"")
127-
.append(name)
128-
.append('\"');
129-
if (isError() || isFailure()) {
130-
details.append('>')
131-
.append(isError() ? "<error message=\"" : "<failure message=\"")
132-
.append(StringEscapeUtils.escapeXml(errorMessage))
133-
.append("\"><![CDATA[")
134-
.append(StringEscapeUtils.escapeXml(stackTrace))
135-
.append("]]>")
136-
.append(isError() ? "</error>" : "</failure>")
137-
.append("</testcase>");
138-
} else {
139-
details.append("/>");
140-
}
125+
public String getStackTrace() {
126+
return stackTrace;
127+
}
141128

142-
return details.toString();
129+
/**
130+
* Execution time of testcase
131+
*/
132+
public int getExecutionTime() {
133+
return time;
143134
}
144135

145136
}

0 commit comments

Comments
 (0)