diff --git a/its/plugin/projects/nosetests_project/pytest.xml b/its/plugin/projects/nosetests_project/pytest.xml
new file mode 100644
index 0000000000..7779e003c1
--- /dev/null
+++ b/its/plugin/projects/nosetests_project/pytest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ def test_failing():
+ > assert False
+ E assert False
+
+ tests/test_prod.py:5: AssertionError
+
+
+
+ def test_crashing():
+ > raise RuntimeError()
+ E RuntimeError
+
+ tests/test_prod.py:8: RuntimeError
+
+
+
+ skipped
+
+
\ No newline at end of file
diff --git a/its/plugin/src/test/java/com/sonar/python/it/plugin/TestReportTest.java b/its/plugin/src/test/java/com/sonar/python/it/plugin/TestReportTest.java
index af3f7a5546..e2cb7c6476 100644
--- a/its/plugin/src/test/java/com/sonar/python/it/plugin/TestReportTest.java
+++ b/its/plugin/src/test/java/com/sonar/python/it/plugin/TestReportTest.java
@@ -69,6 +69,19 @@ public void import_report() throws Exception {
.build());
}
+ @Test
+ public void import_pytest_report() {
+ orchestrator.executeBuild(createBuild("pytest.xml"));
+ assertProjectMeasures(PROJECT, new ImmutableMap.Builder()
+ .put("tests", 3)
+ .put("test_failures", 2)
+ .put("test_errors", 0)
+ .put("skipped_tests", 1)
+ .put("test_success_density", 33)
+ .put("test_execution_time", 1)
+ .build());
+ }
+
@Test
public void simple_mode() throws Exception {
orchestrator.executeBuild(createBuild("nosetests.xml").setProperty("sonar.python.xunit.skipDetails", "true"));
diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/PythonXUnitSensor.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/PythonXUnitSensor.java
index e2aaef5fa8..e0c6528789 100644
--- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/PythonXUnitSensor.java
+++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/PythonXUnitSensor.java
@@ -24,6 +24,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.annotation.CheckForNull;
import javax.xml.stream.XMLStreamException;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.fs.FileSystem;
@@ -78,26 +79,16 @@ private static void simpleMode(final SensorContext context, List reports)
parser.parse(report);
}
- int testsCount = 0;
- int testsSkipped = 0;
- int testsErrors = 0;
- int testsFailures = 0;
- long testsTime = 0;
- for (TestSuite report : parserHandler.getParsedReports()) {
- testsCount += report.getTests() - report.getSkipped();
- testsSkipped += report.getSkipped();
- testsErrors += report.getErrors();
- testsFailures += report.getFailures();
- testsTime += report.getTime();
- }
+ TestResult total = new TestResult();
+ parserHandler.getParsedReports().forEach(testSuite -> testSuite.getTestCases().forEach(total::addTestCase));
- if (testsCount > 0) {
+ if (total.getTests() > 0) {
InputComponent module = context.module();
- saveMeasure(context, module, CoreMetrics.TESTS, testsCount);
- saveMeasure(context, module, CoreMetrics.SKIPPED_TESTS, testsSkipped);
- saveMeasure(context, module, CoreMetrics.TEST_ERRORS, testsErrors);
- saveMeasure(context, module, CoreMetrics.TEST_FAILURES, testsFailures);
- saveMeasure(context, module, CoreMetrics.TEST_EXECUTION_TIME, testsTime);
+ saveMeasure(context, module, CoreMetrics.TESTS, total.getExecutedTests());
+ saveMeasure(context, module, CoreMetrics.SKIPPED_TESTS, total.getSkipped());
+ saveMeasure(context, module, CoreMetrics.TEST_ERRORS, total.getErrors());
+ saveMeasure(context, module, CoreMetrics.TEST_FAILURES, total.getFailures());
+ saveMeasure(context, module, CoreMetrics.TEST_EXECUTION_TIME, total.getTime());
}
}
@@ -114,24 +105,35 @@ private void detailedMode(final SensorContext context, List reports) throw
}
private void processReportDetailed(SensorContext context, Collection parsedReports) {
- Collection locatedResources = lookupResources(parsedReports);
- for (TestSuite fileReport : locatedResources) {
- InputFile inputFile = fileReport.getInputFile();
+ Map locatedResources = lookupResources(parsedReports);
+ for (Map.Entry entry : locatedResources.entrySet()) {
+ InputFile inputFile = entry.getKey();
+ TestResult fileTestResult = entry.getValue();
+ LOG.debug("Saving test execution measures for '{}'", inputFile.toString());
+
+ saveMeasure(context, inputFile, CoreMetrics.SKIPPED_TESTS, fileTestResult.getSkipped());
+ saveMeasure(context, inputFile, CoreMetrics.TESTS, fileTestResult.getExecutedTests());
+ saveMeasure(context, inputFile, CoreMetrics.TEST_ERRORS, fileTestResult.getErrors());
+ saveMeasure(context, inputFile, CoreMetrics.TEST_FAILURES, fileTestResult.getFailures());
+ saveMeasure(context, inputFile, CoreMetrics.TEST_EXECUTION_TIME, fileTestResult.getTime());
+ }
+ }
- if (LOG.isDebugEnabled()) {
- LOG.debug("Saving test execution measures for '{}' under resource '{}'", fileReport.getKey(), inputFile.relativePath());
- }
+ @CheckForNull
+ private InputFile findResource(TestCase testCase, String fileKey) {
+ InputFile unitTestFile = null;
- saveMeasure(context, inputFile, CoreMetrics.SKIPPED_TESTS, fileReport.getSkipped());
- saveMeasure(context, inputFile, CoreMetrics.TESTS, fileReport.getTests() - fileReport.getSkipped());
- saveMeasure(context, inputFile, CoreMetrics.TEST_ERRORS, fileReport.getErrors());
- saveMeasure(context, inputFile, CoreMetrics.TEST_FAILURES, fileReport.getFailures());
- saveMeasure(context, inputFile, CoreMetrics.TEST_EXECUTION_TIME, fileReport.getTime());
+ if (testCase.getFile() != null) {
+ unitTestFile = getSonarTestFile(new File(testCase.getFile()));
}
- }
- private InputFile findResource(String fileKey) {
- return findResourceUsingNoseTestsStrategy(fileKey);
+ if (unitTestFile == null) {
+ String testClassname = testCase.getTestClassname();
+ String key = testClassname != null ? testClassname : fileKey;
+ return findResourceUsingNoseTestsStrategy(key);
+ }
+
+ return unitTestFile;
}
private InputFile findResourceUsingNoseTestsStrategy(String fileKey) {
@@ -151,32 +153,27 @@ private InputFile findResourceUsingNoseTestsStrategy(String fileKey) {
return unitTestFile;
}
- private Collection lookupResources(Collection testReports) {
- Map locatedReports = new HashMap<>();
-
- for (TestSuite report : testReports) {
- String fileKey = report.getKey();
-
- LOG.debug("Trying to find a SonarQube resource for '{}'", fileKey);
- InputFile inputFile = findResource(fileKey);
- if (inputFile != null) {
- LOG.debug("The resource was found '{}'", inputFile);
-
- TestSuite summaryReport = locatedReports.get(inputFile.absolutePath());
- if (summaryReport != null) {
- summaryReport.addMeasures(report);
+ private Map lookupResources(Collection testReports) {
+ Map testResultsByFile = new HashMap<>();
+
+ for (TestSuite testSuite : testReports) {
+ testSuite.getTestCases().forEach(testCase -> {
+ String testClassname = testCase.getTestClassname();
+ LOG.debug("Trying to find a SonarQube resource for test case '{}'", testClassname);
+ InputFile inputFile = findResource(testCase, testSuite.getKey());
+ if (inputFile != null) {
+ LOG.debug("The resource was found '{}'", inputFile);
+ testResultsByFile.computeIfAbsent(inputFile, k -> new TestResult()).addTestCase(testCase);
} else {
- report.setInputFile(inputFile);
- locatedReports.put(inputFile.absolutePath(), report);
+ LOG.warn("The resource for '{}' is not found, drilling down to the details of this test won't be possible", testClassname);
}
- } else {
- LOG.warn("The resource for '{}' is not found, drilling down to the details of this test won't be possible", fileKey);
- }
+ });
}
- return locatedReports.values();
+ return testResultsByFile;
}
+ @CheckForNull
private InputFile getSonarTestFile(File file) {
LOG.debug("Using the key '{}' to lookup the resource in SonarQube", file.getPath());
return fileSystem.inputFile(fileSystem.predicates().is(file));
diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestCase.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestCase.java
index 85810e9c8c..92711a16a3 100644
--- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestCase.java
+++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestCase.java
@@ -19,6 +19,8 @@
*/
package org.sonar.plugins.python.xunit;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.apache.commons.lang.StringEscapeUtils;
/**
@@ -27,20 +29,22 @@
*/
public class TestCase {
- private static final String STATUS_OK = "ok";
- private static final String STATUS_ERROR = "error";
- private static final String STATUS_FAILURE = "failure";
- private static final String STATUS_SKIPPED = "skipped";
+ public static final String STATUS_OK = "ok";
+ public static final String STATUS_ERROR = "error";
+ public static final String STATUS_FAILURE = "failure";
+ public static final String STATUS_SKIPPED = "skipped";
- private String name;
- private String status = STATUS_OK;
- private String stackTrace;
- private String errorMessage;
- private int time = 0;
+ private final String name;
+ private final String status;
+ private final String stackTrace;
+ private final String errorMessage;
+ private final int time;
+ private final String file;
+ private final String testClassname;
/**
* Constructs a testcase instance out of following parameters
- *
+ *
* @param name
* The name of this testcase
* @param time
@@ -51,13 +55,19 @@ public class TestCase {
* The stack trace occurred while executing of this testcase; pass "" if the testcase passed/skipped.
* @param msg
* The error message associated with this testcase of the execution was erroneous; pass "" if not.
+ * @param file
+ * The optional file to which this test case applies.
+ * @param testClassname
+ * The classname of the test.
*/
- public TestCase(String name, int time, String status, String stack, String msg) {
+ public TestCase(String name, int time, String status, String stack, String msg, @Nullable String file, @Nullable String testClassname) {
this.name = name;
this.time = time;
this.stackTrace = stack;
this.errorMessage = msg;
this.status = status;
+ this.file = file;
+ this.testClassname = testClassname;
}
/**
@@ -85,6 +95,16 @@ public int getTime() {
return time;
}
+ @CheckForNull
+ public String getFile() {
+ return file;
+ }
+
+ @CheckForNull
+ public String getTestClassname() {
+ return testClassname;
+ }
+
/**
* Returns execution details as sonar-conform XML
*/
diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestResult.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestResult.java
new file mode 100644
index 0000000000..42e7e81f65
--- /dev/null
+++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestResult.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube Python Plugin
+ * Copyright (C) 2011-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.python.xunit;
+
+public class TestResult {
+
+ private int errors = 0;
+ private int skipped = 0;
+ private int tests = 0;
+ private int time = 0;
+ private int failures = 0;
+
+ public int getErrors() {
+ return errors;
+ }
+
+ public int getSkipped() {
+ return skipped;
+ }
+
+ public int getTests() {
+ return tests;
+ }
+
+ public int getExecutedTests() {
+ return tests - skipped;
+ }
+
+ public int getTime() {
+ return time;
+ }
+
+ public int getFailures() {
+ return failures;
+ }
+
+ public void addTestCase(TestCase tc) {
+ if (tc.isSkipped()) {
+ skipped++;
+ } else if (tc.isFailure()) {
+ failures++;
+ } else if (tc.isError()) {
+ errors++;
+ }
+ tests++;
+ time += tc.getTime();
+ }
+
+}
diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuite.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuite.java
index 7470873baf..35f822d3f8 100644
--- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuite.java
+++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuite.java
@@ -19,8 +19,6 @@
*/
package org.sonar.plugins.python.xunit;
-import org.sonar.api.batch.fs.InputFile;
-
import java.util.ArrayList;
import java.util.List;
@@ -30,12 +28,6 @@
public class TestSuite {
private String key;
- private InputFile inputFile = null;
- private int errors = 0;
- private int skipped = 0;
- private int tests = 0;
- private int time = 0;
- private int failures = 0;
private List testCases;
/**
@@ -53,79 +45,10 @@ public String getKey() {
return key;
}
- public int getErrors() {
- return errors;
- }
-
- public int getSkipped() {
- return skipped;
- }
-
- public int getTests() {
- return tests;
- }
-
- public int getTime() {
- return time;
- }
-
- public int getFailures() {
- return failures;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- TestSuite that = (TestSuite) o;
- return key.equals(that.key);
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- /**
- * Adds the given test case to this testsuite maintaining the internal statistics
- *
- * @param tc
- * the test case to add
- */
public void addTestCase(TestCase tc) {
- if (tc.isSkipped()) {
- skipped++;
- } else if (tc.isFailure()) {
- failures++;
- } else if (tc.isError()) {
- errors++;
- }
- tests++;
- time += tc.getTime();
testCases.add(tc);
}
- /**
- * Adds the measures contained by the given test suite to this test suite
- *
- * @param ts
- * the test suite to add the measures from
- */
- public TestSuite addMeasures(TestSuite ts) {
- for (TestCase tc : ts.getTestCases()) {
- addTestCase(tc);
- }
- return this;
- }
-
- /**
- * Returns the testcases contained by this test suite
- */
public List getTestCases() {
return testCases;
}
@@ -143,11 +66,4 @@ public String getDetails() {
return details.toString();
}
- public void setInputFile(InputFile inputFile) {
- this.inputFile = inputFile;
- }
-
- public InputFile getInputFile() {
- return inputFile;
- }
}
diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuiteParser.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuiteParser.java
index 78fe2bc02e..5d8cf453a9 100644
--- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuiteParser.java
+++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/xunit/TestSuiteParser.java
@@ -19,6 +19,8 @@
*/
package org.sonar.plugins.python.xunit;
+import java.util.ArrayList;
+import java.util.List;
import org.codehaus.staxmate.in.ElementFilter;
import org.codehaus.staxmate.in.SMHierarchicCursor;
import org.codehaus.staxmate.in.SMInputCursor;
@@ -27,29 +29,23 @@
import javax.xml.stream.XMLStreamException;
import java.text.ParseException;
import java.util.Collection;
-import java.util.HashMap;
import java.util.Locale;
-import java.util.Map;
public class TestSuiteParser implements XmlStreamHandler {
- private Map testSuites = new HashMap<>();
+ private List testSuites = new ArrayList<>();
@Override
public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
SMInputCursor testSuiteCursor = rootCursor.constructDescendantCursor(new ElementFilter("testsuite"));
while (testSuiteCursor.getNext() != null) {
String testSuiteClassName = testSuiteCursor.getAttrValue("name");
-
+ TestSuite testSuite = new TestSuite(testSuiteClassName);
+ testSuites.add(testSuite);
SMInputCursor testCaseCursor = testSuiteCursor.childElementCursor("testcase");
+
while (testCaseCursor.getNext() != null) {
- String testClassName = getClassname(testCaseCursor, testSuiteClassName);
- TestSuite report = testSuites.get(testClassName);
- if (report == null) {
- report = new TestSuite(testClassName);
- testSuites.put(testClassName, report);
- }
- report.addTestCase(parseTestCaseTag(testCaseCursor));
+ testSuite.addTestCase(parseTestCaseTag(testCaseCursor));
}
}
}
@@ -58,40 +54,35 @@ public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
* Returns successfully parsed reports as a collection of TestSuite objects.
*/
public Collection getParsedReports() {
- return testSuites.values();
- }
-
- private static String getClassname(SMInputCursor testCaseCursor, String defaultClassname) throws XMLStreamException {
- String testClassName = testCaseCursor.getAttrValue("classname");
- return testClassName == null ? defaultClassname : testClassName;
+ return testSuites;
}
private static TestCase parseTestCaseTag(SMInputCursor testCaseCursor) throws XMLStreamException {
- // TODO: get a decent grammar for the junit format and check the
- // logic inside this method against it.
-
String name = parseTestCaseName(testCaseCursor);
Double time = parseTime(testCaseCursor);
- String status = "ok";
+ String status = TestCase.STATUS_OK;
String stack = "";
String msg = "";
+ String file = testCaseCursor.getAttrValue("file");
+ String testClassName = testCaseCursor.getAttrValue("classname");
+
SMInputCursor childCursor = testCaseCursor.childElementCursor();
if (childCursor.getNext() != null) {
String elementName = childCursor.getLocalName();
- if ("skipped".equals(elementName)) {
- status = "skipped";
- } else if ("failure".equals(elementName)) {
- status = "failure";
+ if (TestCase.STATUS_SKIPPED.equals(elementName)) {
+ status = TestCase.STATUS_SKIPPED;
+ } else if (TestCase.STATUS_FAILURE.equals(elementName)) {
+ status = TestCase.STATUS_FAILURE;
msg = childCursor.getAttrValue("message");
stack = childCursor.collectDescendantText();
- } else if ("error".equals(elementName)) {
- status = "error";
+ } else if (TestCase.STATUS_ERROR.equals(elementName)) {
+ status = TestCase.STATUS_ERROR;
msg = childCursor.getAttrValue("message");
stack = childCursor.collectDescendantText();
}
}
- return new TestCase(name, time.intValue(), status, stack, msg);
+ return new TestCase(name, time.intValue(), status, stack, msg, file, testClassName);
}
private static double parseTime(SMInputCursor testCaseCursor) throws XMLStreamException {
diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/PythonXUnitSensorTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/PythonXUnitSensorTest.java
index 75ef2118c0..6c8dbaa30d 100644
--- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/PythonXUnitSensorTest.java
+++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/PythonXUnitSensorTest.java
@@ -35,6 +35,9 @@
public class PythonXUnitSensorTest {
+ private static final String FILE_SAMPLE1 = "test_sample1.py";
+ private static final String FILE_SAMPLE2 = "tests/dir/test_sample2.py";
+
private File baseDir = new File("src/test/resources/org/sonar/plugins/python");
Settings settings;
PythonXUnitSensor sensor;
@@ -50,8 +53,8 @@ public void setUp() {
@Test
public void shouldSaveCorrectMeasures() {
- DefaultInputFile testFile1 = TestInputFileBuilder.create("", "test_sample1.py").build();
- DefaultInputFile testFile2 = TestInputFileBuilder.create("", "tests/dir/test_sample2.py").build();
+ DefaultInputFile testFile1 = TestInputFileBuilder.create("", FILE_SAMPLE1).build();
+ DefaultInputFile testFile2 = TestInputFileBuilder.create("", FILE_SAMPLE2).build();
fs.add(testFile1);
fs.add(testFile2);
sensor.execute(context);
@@ -85,7 +88,7 @@ public void shouldSaveCorrectMeasuresSimpleMode() {
@Test
public void shouldReportNothingWhenNoReportFound() {
- DefaultInputFile testFile1 = TestInputFileBuilder.create("", "test_sample1.py").build();
+ DefaultInputFile testFile1 = TestInputFileBuilder.create("", FILE_SAMPLE1).build();
fs.add(testFile1);
settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "notexistingpath");
@@ -103,6 +106,65 @@ public void shouldThrowWhenGivenInvalidTime() {
sensor.execute(context);
}
+ @Test
+ public void shouldSaveCorrectMeasuresWithPyTestFormat() {
+ DefaultInputFile testFile1 = TestInputFileBuilder.create("", FILE_SAMPLE1).build();
+ DefaultInputFile testFile2 = TestInputFileBuilder.create("", FILE_SAMPLE2).build();
+ fs.add(testFile1);
+ fs.add(testFile2);
+ settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "xunit-reports/pytest-xunit-result.xml");
+ sensor.execute(context);
+
+ assertThat(measure(testFile1, CoreMetrics.TESTS)).isEqualTo(2);
+ assertThat(measure(testFile2, CoreMetrics.TESTS)).isEqualTo(8);
+
+ assertThat(measure(testFile1, CoreMetrics.SKIPPED_TESTS)).isEqualTo(0);
+ assertThat(measure(testFile2, CoreMetrics.SKIPPED_TESTS)).isEqualTo(2);
+
+ assertThat(measure(testFile1, CoreMetrics.TEST_ERRORS)).isEqualTo(0);
+ assertThat(measure(testFile2, CoreMetrics.TEST_ERRORS)).isEqualTo(0);
+
+ assertThat(measure(testFile1, CoreMetrics.TEST_FAILURES)).isEqualTo(1);
+ assertThat(measure(testFile2, CoreMetrics.TEST_FAILURES)).isEqualTo(1);
+ }
+
+ @Test
+ public void testNoTestReport() {
+ DefaultInputFile testFile2 = TestInputFileBuilder.create("", FILE_SAMPLE2).build();
+ fs.add(testFile2);
+ settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "xunit-reports/empty-xunit-result.xml");
+ settings.setProperty(PythonXUnitSensor.SKIP_DETAILS, true);
+ sensor.execute(context);
+
+ assertThat(context.measure(context.module().key(), CoreMetrics.TESTS)).isNull();
+ }
+
+ @Test
+ public void fallbackToTestsuiteName() {
+ DefaultInputFile testFile1 = TestInputFileBuilder.create("", FILE_SAMPLE1).build();
+ fs.add(testFile1);
+ settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "xunit-reports/no-classname-xunit-result.xml");
+ sensor.execute(context);
+
+ assertThat(measure(testFile1, CoreMetrics.TESTS)).isEqualTo(2);
+ assertThat(measure(testFile1, CoreMetrics.SKIPPED_TESTS)).isEqualTo(0);
+ assertThat(measure(testFile1, CoreMetrics.TEST_ERRORS)).isEqualTo(0);
+ assertThat(measure(testFile1, CoreMetrics.TEST_FAILURES)).isEqualTo(1);
+ }
+
+ @Test
+ public void malformedReport() {
+ DefaultInputFile testFile1 = TestInputFileBuilder.create("", FILE_SAMPLE1).build();
+ fs.add(testFile1);
+ settings.setProperty(PythonXUnitSensor.REPORT_PATH_KEY, "xunit-reports/malformed-xunit-result.xml");
+ sensor.execute(context);
+
+ assertThat(measure(testFile1, CoreMetrics.TESTS)).isEqualTo(2);
+ assertThat(measure(testFile1, CoreMetrics.SKIPPED_TESTS)).isEqualTo(0);
+ assertThat(measure(testFile1, CoreMetrics.TEST_ERRORS)).isEqualTo(0);
+ assertThat(measure(testFile1, CoreMetrics.TEST_FAILURES)).isEqualTo(0);
+ }
+
private Integer moduleMeasure(Metric metric) {
return measure(context.module(), metric);
}
diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestCaseTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestCaseTest.java
index 217dc51ba6..ea155b4867 100644
--- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestCaseTest.java
+++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestCaseTest.java
@@ -32,11 +32,11 @@ public void rendersRightDetails() {
Map ioMap = new HashMap();
ioMap.put("",
- new TestCase("name", 1, "ok", "", ""));
+ new TestCase("name", 1, "ok", "", "", null, null));
ioMap.put("",
- new TestCase("name", 1, "error", "stack", "errmsg"));
+ new TestCase("name", 1, "error", "stack", "errmsg", null, null));
ioMap.put("",
- new TestCase("name", 1, "failure", "stack", "errmsg"));
+ new TestCase("name", 1, "failure", "stack", "errmsg","file", "testClassname"));
for(Map.Entry entry: ioMap.entrySet()) {
assertEquals(entry.getKey(), entry.getValue().getDetails());
diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestResultTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestResultTest.java
new file mode 100644
index 0000000000..8d9449cab6
--- /dev/null
+++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestResultTest.java
@@ -0,0 +1,113 @@
+/*
+ * SonarQube Python Plugin
+ * Copyright (C) 2011-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.plugins.python.xunit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TestResultTest {
+
+ TestResult testResult;
+
+ @Before
+ public void setUp() {
+ testResult = new TestResult();
+ }
+
+ @Test
+ public void newBornSuiteShouldHaveVirginStatistics() {
+ assertThat(testResult.getTests()).isEqualTo(0);
+ assertThat(testResult.getExecutedTests()).isEqualTo(0);
+ assertThat(testResult.getErrors()).isEqualTo(0);
+ assertThat(testResult.getFailures()).isEqualTo(0);
+ assertThat(testResult.getSkipped()).isEqualTo(0);
+ assertThat(testResult.getTime()).isEqualTo(0);
+ }
+
+ @Test
+ public void addingTestCaseShouldIncrementStatistics() {
+ int testBefore = testResult.getTests();
+ int timeBefore = testResult.getTime();
+
+ final int EXEC_TIME = 10;
+ testResult.addTestCase(createTestCase(EXEC_TIME, "status"));
+
+ assertThat(testResult.getTests()).isEqualTo(testBefore + 1);
+ assertThat(testResult.getTime()).isEqualTo(timeBefore + EXEC_TIME);
+ }
+
+ @Test
+ public void executedTestsValue() {
+ testResult.addTestCase(createTestCase(1, "ok"));
+ testResult.addTestCase(createTestCase(2, "skipped"));
+ testResult.addTestCase(createTestCase(3, "ok"));
+ testResult.addTestCase(createTestCase(4, "error"));
+ testResult.addTestCase(createTestCase(5, "skipped"));
+
+ assertThat(testResult.getTests()).isEqualTo(5);
+ assertThat(testResult.getExecutedTests()).isEqualTo(3);
+ assertThat(testResult.getErrors()).isEqualTo(1);
+ assertThat(testResult.getFailures()).isEqualTo(0);
+ assertThat(testResult.getSkipped()).isEqualTo(2);
+ assertThat(testResult.getTime()).isEqualTo(15);
+ }
+
+ private static TestCase createTestCase(int time, String status) {
+ return new TestCase("name", time, status, "stack", "msg", "file", "testClassname");
+ }
+
+ @Test
+ public void addingAnErroneousTestCaseShouldIncrementErrorStatistic() {
+ int errorsBefore = testResult.getErrors();
+ TestCase error = mock(TestCase.class);
+ when(error.isError()).thenReturn(true);
+
+ testResult.addTestCase(error);
+
+ assertThat(testResult.getErrors()).isEqualTo(errorsBefore + 1);
+ }
+
+ @Test
+ public void addingAFailedestCaseShouldIncrementFailedStatistic() {
+ int failedBefore = testResult.getFailures();
+ TestCase failedTC = mock(TestCase.class);
+ when(failedTC.isFailure()).thenReturn(true);
+
+ testResult.addTestCase(failedTC);
+
+ assertThat(testResult.getFailures()).isEqualTo(failedBefore + 1);
+ }
+
+ @Test
+ public void addingASkippedTestCaseShouldIncrementSkippedStatistic() {
+ int skippedBefore = testResult.getSkipped();
+ TestCase skippedTC = mock(TestCase.class);
+ when(skippedTC.isSkipped()).thenReturn(true);
+
+ testResult.addTestCase(skippedTC);
+
+ assertThat(testResult.getSkipped()).isEqualTo(skippedBefore + 1);
+ }
+
+}
diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestSuiteTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestSuiteTest.java
index 5bd84ee9b2..f47109bd85 100644
--- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestSuiteTest.java
+++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/xunit/TestSuiteTest.java
@@ -19,120 +19,23 @@
*/
package org.sonar.plugins.python.xunit;
-import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
public class TestSuiteTest {
- TestSuite suite;
- TestSuite equalSuite;
- TestSuite otherSuite;
-
- @Before
- public void setUp() {
- suite = new TestSuite("key");
- equalSuite = new TestSuite("key");
- otherSuite = new TestSuite("otherkey");
- }
-
- @Test
- public void suiteDoesntEqualsNull() {
- assertThat(suite).isNotEqualTo(null);
- }
-
- @Test
- public void suiteDoesntEqualsMiscObject() {
- assertThat(suite).isNotEqualTo("string");
- }
-
- @Test
- public void suiteEqualityIsReflexive() {
- assertThat(suite).isEqualTo(suite);
- assertThat(otherSuite).isEqualTo(otherSuite);
- assertThat(equalSuite).isEqualTo(equalSuite);
- }
-
- @Test
- public void suiteEqualityWorksAsExpected() {
- assertThat(suite).isEqualTo(equalSuite);
- assertThat(suite).isNotEqualTo(otherSuite);
- }
-
- @Test
- public void suiteHashWorksAsExpected() {
- assertThat(suite.hashCode()).isEqualTo(equalSuite.hashCode());
- assertThat(suite.hashCode()).isNotEqualTo(otherSuite.hashCode());
- }
@Test
- public void newBornSuiteShouldHaveVirginStatistics() {
- assertThat(suite.getTests()).isEqualTo(0);
- assertThat(suite.getErrors()).isEqualTo(0);
- assertThat(suite.getFailures()).isEqualTo(0);
- assertThat(suite.getSkipped()).isEqualTo(0);
- assertThat(suite.getTime()).isEqualTo(0);
+ public void test() {
+ TestSuite suite = new TestSuite("key");
+ assertThat(suite.getKey()).isEqualTo("key");
+ assertThat(suite.getTestCases()).isEmpty();
assertThat(suite.getDetails()).isEqualTo("");
- }
-
- @Test
- public void addingTestCaseShouldIncrementStatistics() {
- int testBefore = suite.getTests();
- int timeBefore = suite.getTime();
- final int EXEC_TIME = 10;
- suite.addTestCase(new TestCase("name", EXEC_TIME, "status", "stack", "msg"));
-
- assertThat(suite.getTests()).isEqualTo(testBefore + 1);
- assertThat(suite.getTime()).isEqualTo(timeBefore + EXEC_TIME);
- }
-
- @Test
- public void addingAnErroneousTestCaseShouldIncrementErrorStatistic() {
- int errorsBefore = suite.getErrors();
- TestCase error = mock(TestCase.class);
- when(error.isError()).thenReturn(true);
-
- suite.addTestCase(error);
-
- assertThat(suite.getErrors()).isEqualTo(errorsBefore + 1);
- }
-
- @Test
- public void addingAFailedestCaseShouldIncrementFailedStatistic() {
- int failedBefore = suite.getFailures();
- TestCase failedTC = mock(TestCase.class);
- when(failedTC.isFailure()).thenReturn(true);
-
- suite.addTestCase(failedTC);
-
- assertThat(suite.getFailures()).isEqualTo(failedBefore + 1);
- }
-
- @Test
- public void addingASkippedTestCaseShouldIncrementSkippedStatistic() {
- int skippedBefore = suite.getSkipped();
- TestCase skippedTC = mock(TestCase.class);
- when(skippedTC.isSkipped()).thenReturn(true);
-
- suite.addTestCase(skippedTC);
-
- assertThat(suite.getSkipped()).isEqualTo(skippedBefore + 1);
+ TestCase testCase = new TestCase("name", 1, "status", "stack", "msg", "file", "testClassname");
+ suite.addTestCase(testCase);
+ assertThat(suite.getTestCases()).containsExactly(testCase);
+ assertThat(suite.getDetails()).isEqualTo("");
}
- @Test
- public void addingAnotherTestSuiteShouldMaintainStatistics() {
- TestCase tc = mock(TestCase.class);
- when(tc.isSkipped()).thenReturn(true);
- TestSuite ts1 = new TestSuite("1");
- TestSuite ts2 = new TestSuite("2");
- ts1.addTestCase(tc);
- ts2.addTestCase(tc);
-
- TestSuite summedUp = ts1.addMeasures(ts2);
-
- assertThat(summedUp.getSkipped()).isEqualTo(2);
- }
}
diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/empty-xunit-result.xml b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/empty-xunit-result.xml
new file mode 100644
index 0000000000..7421e40aef
--- /dev/null
+++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/empty-xunit-result.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/malformed-xunit-result.xml b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/malformed-xunit-result.xml
new file mode 100644
index 0000000000..3443a2e6af
--- /dev/null
+++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/malformed-xunit-result.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ Failure message 1 more info
+
+
+
diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/no-classname-xunit-result.xml b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/no-classname-xunit-result.xml
new file mode 100644
index 0000000000..e6736b6f03
--- /dev/null
+++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/no-classname-xunit-result.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ Failure message 1 more info
+
+
+
diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/pytest-xunit-result.xml b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/pytest-xunit-result.xml
new file mode 100644
index 0000000000..a348062de6
--- /dev/null
+++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/xunit-reports/pytest-xunit-result.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ Failure message 1 more info
+
+
+
+
+
+
+
+
+
+
+ more info
+
+
+
+
+
+
+ Failure message 2 more info
+
+
+
\ No newline at end of file