Skip to content

Commit 4b68b88

Browse files
thibaultfalquetfalque.ext
andauthored
JUnit report (#37)
* XUnit report * updates README * updates DOC * adds unit tests * more tests * license header * sonar.test.reportPath into sonar.rust.test.reportPath #37 * fixes a breaking test #37 Co-authored-by: tfalque.ext <[email protected]>
1 parent 63c60e9 commit 4b68b88

File tree

16 files changed

+901
-2
lines changed

16 files changed

+901
-2
lines changed

DOC.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,13 @@ e.g
6969
But [other coverage tools](https://vladfilippov.com/blog/rust-code-coverage-tools/) might work as well
7070

7171

72+
## Adding test measures
7273

74+
Optionally SonarQube can also display tests measures.
75+
76+
This Community Rust plugin doesn't run your tests or generate tests reports for you. That has to be done before analysis and provided in the form of reports.
77+
78+
Currently, only `junit report` formats are supported :
79+
80+
Insert a parameter `sonar.rust.test.reportPath` into you `sonar-project.properties` file. As an example, one of such tool
81+
for Rust than converts `cargo test` report to `junit report` is [cargo2junit](https://crates.io/crates/cargo2junit).

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ It leverages [Clippy lints](https://rust-lang.github.io/rust-clippy/master/) to
2020
* Import it into SonarQube
2121

2222
set analysis parameter `community.rust.clippy.reportPaths=<CLIPPY REPORT FILE>`
23+
24+
* Optionally import tests measures (`junit` report)
25+
26+
use `sonar.rust.test.reportPath`
27+
2328
* Optionally import coverage measures
2429

2530
use either

community-rust-plugin/src/main/java/org/elegoff/plugins/communityrust/CommunityRustPlugin.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elegoff.plugins.communityrust.coverage.cobertura.CoberturaSensor;
2424
import org.elegoff.plugins.communityrust.coverage.lcov.LCOVSensor;
2525
import org.elegoff.plugins.communityrust.rules.RustRulesDefinition;
26+
import org.elegoff.plugins.communityrust.xunit.XUnitSensor;
2627
import org.sonar.api.Plugin;
2728
import org.sonar.api.config.PropertyDefinition;
2829
import org.elegoff.plugins.communityrust.clippy.ClippySensor;
@@ -92,5 +93,17 @@ public void define(Context context) {
9293
);
9394

9495

96+
context.addExtensions(
97+
PropertyDefinition.builder(XUnitSensor.REPORT_PATH_KEY)
98+
.name("Path to xunit report(s)")
99+
.description("Path to the report of test execution, relative to project's root. Ant patterns are accepted. The reports have to conform to the junitreport XML format.")
100+
.category("Rust")
101+
.subCategory("Test and Coverage")
102+
.onQualifiers(Qualifiers.PROJECT)
103+
.defaultValue(XUnitSensor.DEFAULT_REPORT_PATH)
104+
.build(),
105+
XUnitSensor.class);
106+
107+
95108
}
96109
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Community Rust Plugin
3+
* Copyright (C) 2021 Eric Le Goff
4+
* mailto:community-rust AT pm DOT me
5+
* http://github.com/elegoff/sonar-rust
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 3 of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*/
21+
package org.elegoff.plugins.communityrust.xunit;
22+
23+
import org.apache.commons.lang.StringUtils;
24+
import org.codehaus.staxmate.SMInputFactory;
25+
import org.codehaus.staxmate.in.SMHierarchicCursor;
26+
27+
import javax.xml.stream.XMLInputFactory;
28+
import javax.xml.stream.XMLResolver;
29+
import javax.xml.stream.XMLStreamException;
30+
import java.io.File;
31+
import java.io.FileInputStream;
32+
import java.io.IOException;
33+
import java.io.InputStream;
34+
public class StaxParser {
35+
36+
private SMInputFactory inf;
37+
38+
private XmlStreamHandler streamHandler;
39+
40+
public StaxParser(XmlStreamHandler streamHandler) {
41+
this.streamHandler = streamHandler;
42+
XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
43+
44+
inf = new SMInputFactory(xmlFactory);
45+
}
46+
47+
public void parse(File xmlFile) throws XMLStreamException {
48+
try (FileInputStream input = new FileInputStream(xmlFile)) {
49+
parse(input);
50+
} catch (IOException e) {
51+
throw new XMLStreamException(e);
52+
}
53+
}
54+
55+
public void parse(InputStream xmlInput) throws XMLStreamException {
56+
SMHierarchicCursor rootCursor = inf.rootElementCursor(xmlInput);
57+
try {
58+
streamHandler.stream(rootCursor);
59+
} finally {
60+
rootCursor.getStreamReader().closeCompletely();
61+
}
62+
}
63+
64+
private static class UndeclaredEntitiesXMLResolver implements XMLResolver {
65+
66+
@Override
67+
public Object resolveEntity(String arg0, String arg1, String fileName, String undeclaredEntity) throws XMLStreamException {
68+
String undeclared = undeclaredEntity;
69+
// avoid problems with XML docs containing undeclared entities.. return the entity under its raw form if not a Unicode expression
70+
if (StringUtils.startsWithIgnoreCase(undeclaredEntity, "u") && undeclaredEntity.length() == 5) {
71+
int unicodeCharHexValue = Integer.parseInt(undeclaredEntity.substring(1), 16);
72+
if (Character.isDefined(unicodeCharHexValue)) {
73+
undeclared = new String(new char[] {(char) unicodeCharHexValue});
74+
}
75+
}
76+
return undeclared;
77+
}
78+
}
79+
80+
/**
81+
* Simple interface for handling XML stream to parse.
82+
*/
83+
@FunctionalInterface
84+
public interface XmlStreamHandler {
85+
void stream(SMHierarchicCursor rootCursor) throws XMLStreamException;
86+
}
87+
88+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Community Rust Plugin
3+
* Copyright (C) 2021 Eric Le Goff
4+
* mailto:community-rust AT pm DOT me
5+
* http://github.com/elegoff/sonar-rust
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 3 of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*/
21+
package org.elegoff.plugins.communityrust.xunit;
22+
23+
import javax.annotation.Nullable;
24+
25+
/**
26+
* Represents a unit test case. Has a couple of data items like name, status, time etc. associated. Reports testcase details in
27+
* sonar-conform XML
28+
*/
29+
public class TestCase {
30+
private final String name;
31+
private final TestCaseStatus status;
32+
private final String stackTrace;
33+
private final String errorMessage;
34+
private final int time;
35+
private final String file;
36+
private final String testClassname;
37+
38+
/**
39+
* Constructs a testcase instance out of following parameters
40+
* @param name The name of this testcase
41+
* @param status The execution status of the testcase
42+
* @param stackTrace The stack trace occurred while executing of this testcase; pass "" if the testcase passed/skipped.
43+
* @param errorMessage The error message associated with this testcase of the execution was erroneous; pass "" if not.
44+
* @param time The execution time in milliseconds
45+
* @param file The optional file to which this test case applies.
46+
* @param testClassname The classname of the test.
47+
*/
48+
public TestCase(String name, TestCaseStatus status, String stackTrace, String errorMessage, int time, @Nullable String file, @Nullable String testClassname) {
49+
this.name = name;
50+
this.status = status;
51+
this.stackTrace = stackTrace;
52+
this.errorMessage = errorMessage;
53+
this.time = time;
54+
this.file = file;
55+
this.testClassname = testClassname;
56+
}
57+
/**
58+
* Returns true if this testcase is an error, false otherwise
59+
*/
60+
public boolean isError(){
61+
return TestCaseStatus.ERROR.equals(status);
62+
}
63+
/**
64+
* Returns true if this testcase is a failure, false otherwise
65+
*/
66+
public boolean isFailure(){
67+
return TestCaseStatus.FAILURE.equals(status);
68+
}
69+
70+
/**
71+
* Returns true if this testcase has been skipped, failure, false otherwise
72+
*/
73+
public boolean isSkipped() {
74+
return TestCaseStatus.SKIPPED.equals(status);
75+
}
76+
77+
public int getTime() {
78+
return time;
79+
}
80+
81+
public String getFile() {
82+
return file;
83+
}
84+
85+
public String getTestClassname() {
86+
return testClassname;
87+
}
88+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Community Rust Plugin
3+
* Copyright (C) 2021 Eric Le Goff
4+
* mailto:community-rust AT pm DOT me
5+
* http://github.com/elegoff/sonar-rust
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 3 of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*/
21+
package org.elegoff.plugins.communityrust.xunit;
22+
23+
public enum TestCaseStatus {
24+
OK("ok"),
25+
ERROR("error"),
26+
FAILURE("failure"),
27+
SKIPPED("skipped");
28+
private final String text;
29+
30+
TestCaseStatus(String name) {
31+
this.text = name;
32+
}
33+
34+
@Override
35+
public String toString() {
36+
return text;
37+
}
38+
39+
public static TestCaseStatus getFromIgnoreCaseString(String value) {
40+
return TestCaseStatus.valueOf(value.toUpperCase());
41+
}
42+
}
43+
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* Community Rust Plugin
3+
* Copyright (C) 2021 Eric Le Goff
4+
* mailto:community-rust AT pm DOT me
5+
* http://github.com/elegoff/sonar-rust
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 3 of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20+
*/
21+
package org.elegoff.plugins.communityrust.xunit;
22+
23+
public class TestResult {
24+
private int errors = 0;
25+
private int skipped = 0;
26+
private int tests = 0;
27+
private int time = 0;
28+
private int failures = 0;
29+
30+
31+
public int getErrors() {
32+
return errors;
33+
}
34+
35+
public int getSkipped() {
36+
return skipped;
37+
}
38+
39+
public int getTests() {
40+
return tests;
41+
}
42+
43+
public int getExecutedTests() {
44+
return tests - skipped;
45+
}
46+
47+
public int getTime() {
48+
return time;
49+
}
50+
51+
public int getFailures() {
52+
return failures;
53+
}
54+
55+
public void addTestCase(TestCase tc) {
56+
if (tc.isSkipped()) {
57+
skipped++;
58+
} else if (tc.isFailure()) {
59+
failures++;
60+
} else if (tc.isError()) {
61+
errors++;
62+
}
63+
tests++;
64+
time += tc.getTime();
65+
}
66+
}

0 commit comments

Comments
 (0)