aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadia Agueeva <agueeva@google.com>2024-04-23 23:41:51 +0000
committerNadia Agueeva <agueeva@google.com>2024-04-29 23:28:26 +0000
commitbd7a02e5737d167c32cab8778bf66d109d687389 (patch)
tree57b08bccc51b18741d08abfa7af7915a16f6e581
parent15bf7ff6d2cb5b56bfa2e02326f99513bec151a6 (diff)
downloadtradefederation-bd7a02e5737d167c32cab8778bf66d109d687389.tar.gz
Fix python result parser to correctly parse tests invoked with module:class
The current parsing doesn't take into account for when a user invokes atest with "module:class" and filters. As a result, tests are skipped that should be included in the results. This CL fixes the parsing so that the test class is correctly identified and can be filtered in. Bug: 327498768 Test: m tradefed tradefed-tests && ./javatests/run_tradefed_tests.sh --class com.android.tradefed.testtype.PythonUnitTestResultParserTest Test: ./javatests/run_tradefed_tests.sh Test: atest-beta-dev python_host_test_example:__main__.MyExampleTest -cv Change-Id: Ib845038bfe4cb4a287745172c90743c0089097a6
-rw-r--r--javatests/com/android/tradefed/testtype/PythonUnitTestResultParserTest.java59
-rw-r--r--test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java25
2 files changed, 80 insertions, 4 deletions
diff --git a/javatests/com/android/tradefed/testtype/PythonUnitTestResultParserTest.java b/javatests/com/android/tradefed/testtype/PythonUnitTestResultParserTest.java
index a5d4b5ad0..0cc8f7616 100644
--- a/javatests/com/android/tradefed/testtype/PythonUnitTestResultParserTest.java
+++ b/javatests/com/android/tradefed/testtype/PythonUnitTestResultParserTest.java
@@ -100,6 +100,8 @@ public class PythonUnitTestResultParserTest {
assertTrue(PythonUnitTestResultParser.PATTERN_FAIL_MESSAGE.matcher(s).matches());
s = "FAIL: a (b) (i=3)";
assertTrue(PythonUnitTestResultParser.PATTERN_FAIL_MESSAGE.matcher(s).matches());
+ s = "FAIL: my_test_method (__main__.MyExampleTest.my_test_method)";
+ assertTrue(PythonUnitTestResultParser.PATTERN_FAIL_MESSAGE.matcher(s).matches());
}
@Test
@@ -196,6 +198,28 @@ public class PythonUnitTestResultParserTest {
}
@Test
+ public void testParseSingleWithModuleClass() throws Exception {
+ String[] output = {
+ "test_1_pass (__main__.MyExampleTest.test_1_pass) ... ok",
+ "",
+ PythonUnitTestResultParser.DASH_LINE,
+ "Ran 1 test in 1s",
+ "",
+ "OK"
+ };
+ TestDescription id = new TestDescription("__main__.MyExampleTest", "test_1_pass");
+
+ mParser.processNewLines(output);
+
+ InOrder inOrder = Mockito.inOrder(mMockListener);
+ inOrder.verify(mMockListener, times(1)).testRunStarted("test", 1);
+ inOrder.verify(mMockListener, times(1)).testStarted(Mockito.eq(id));
+ inOrder.verify(mMockListener, times(1))
+ .testEnded(Mockito.eq(id), Mockito.<HashMap<String, Metric>>any());
+ inOrder.verify(mMockListener, times(1)).testRunEnded(1000L, new HashMap<String, Metric>());
+ }
+
+ @Test
public void testParseMultiTestPass() throws Exception {
String[] output = {
"b (a) ... ok",
@@ -378,6 +402,41 @@ public class PythonUnitTestResultParserTest {
}
@Test
+ public void testParseMultiTestWithModuleClassAndMixedResults() throws Exception {
+ String[] output = {
+ "test_example1_fail (__main__.MyExampleTest.test_example1_fail) ... FAIL",
+ "test_example1_pass (__main__.MyExampleTest.test_example1_pass) ... ok",
+ "",
+ PythonUnitTestResultParser.EQUAL_LINE,
+ "FAIL: test_example1_fail (__main__.MyExampleTest.test_example1_fail)",
+ PythonUnitTestResultParser.DASH_LINE,
+ "Traceback (most recent call last",
+ " File example.py line 43, in test_example1_fail",
+ " self.assertEqual(1, 2)",
+ "AssertionError: 1 != 2",
+ "",
+ PythonUnitTestResultParser.DASH_LINE,
+ "Ran 2 test in 1s",
+ "",
+ "FAILED (failures=1)"
+ };
+ TestDescription id = new TestDescription("__main__.MyExampleTest", "test_example1_fail");
+ TestDescription id2 = new TestDescription("__main__.MyExampleTest", "test_example1_pass");
+
+ mParser.processNewLines(output);
+
+ verify(mMockListener, times(1)).testRunStarted("test", 2);
+ verify(mMockListener, times(1)).testStarted(Mockito.eq(id));
+ verify(mMockListener, times(1))
+ .testEnded(Mockito.eq(id), Mockito.<HashMap<String, Metric>>any());
+ verify(mMockListener, times(1)).testStarted(Mockito.eq(id2));
+ verify(mMockListener, times(1)).testFailed(Mockito.any(), (String) Mockito.any());
+ verify(mMockListener, times(1))
+ .testEnded(Mockito.eq(id2), Mockito.<HashMap<String, Metric>>any());
+ verify(mMockListener, times(1)).testRunEnded(1000L, new HashMap<String, Metric>());
+ }
+
+ @Test
public void testParseSingleTestUnexpectedSuccess() throws Exception {
String[] output = {
"b (a) ... unexpected success",
diff --git a/test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java b/test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java
index a5240cb39..435887c41 100644
--- a/test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java
+++ b/test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java
@@ -146,6 +146,7 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
Pattern.DOTALL);
static final Pattern PATTERN_FAIL_MESSAGE =
Pattern.compile("(FAIL|ERROR): (\\S*) \\((\\S*)\\)( \\(.*\\))?");
+
static final Pattern PATTERN_RUN_SUMMARY =
Pattern.compile("Ran (\\d+) tests? in (\\d+(.\\d*)?)s(.*)");
@@ -312,7 +313,9 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
!lineMatchesPattern(line, PATTERN_MULTILINE_RESULT_FIRST_NEGATIVE);
if (canBeMultiline && lineMatchesPattern(line, PATTERN_MULTILINE_RESULT_FIRST)) {
mCurrentTestName = mCurrentMatcher.group(1);
- mCurrentTestClass = mCurrentMatcher.group(2);
+ mCurrentTestClass =
+ removeTestNameFromClassNameGroup(
+ mCurrentMatcher.group(2), mCurrentTestName);
mCurrentTestCaseString = null;
}
return; // The entire line doesn't match so just ignore it.
@@ -320,7 +323,7 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
for (MatchResult r : matchResults) {
mCurrentTestName = r.group(1);
- mCurrentTestClass = r.group(2);
+ mCurrentTestClass = removeTestNameFromClassNameGroup(r.group(2), mCurrentTestName);
mCurrentTestStatus = r.group(3);
// Tests with failed subtests have no status printed so we add an entry with 'FAIL'
@@ -341,7 +344,8 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
mCurrentTestCaseString = null;
} else if (lineMatchesPattern(line, PATTERN_TWO_LINE_RESULT_FIRST)) {
mCurrentTestName = mCurrentMatcher.group(1);
- mCurrentTestClass = mCurrentMatcher.group(2);
+ mCurrentTestClass =
+ removeTestNameFromClassNameGroup(mCurrentMatcher.group(2), mCurrentTestName);
mCurrentTestCaseString = null;
} else if (lineMatchesPattern(line, PATTERN_TWO_LINE_RESULT_SECOND)) {
mCurrentTestStatus = mCurrentMatcher.group(2);
@@ -363,6 +367,18 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
}
}
+ String removeTestNameFromClassNameGroup(String classNameGroup, String testName) {
+ if (!classNameGroup.endsWith(testName)) {
+ return classNameGroup;
+ }
+ Pattern p = Pattern.compile("(.*)\\." + testName);
+ Matcher matcher = p.matcher(classNameGroup);
+ if (!matcher.matches()) {
+ return classNameGroup;
+ }
+ return matcher.group(1);
+ }
+
/** Process a fail message line and collect the test name, class, and status. */
void processFailMessage(String line) {
if (isDashLine(line)) {
@@ -371,7 +387,8 @@ public class PythonUnitTestResultParser extends MultiLineReceiver {
mCurrentTraceback = new StringBuilder();
} else if (lineMatchesPattern(line, PATTERN_FAIL_MESSAGE)) {
mCurrentTestName = mCurrentMatcher.group(2);
- mCurrentTestClass = mCurrentMatcher.group(3);
+ mCurrentTestClass =
+ removeTestNameFromClassNameGroup(mCurrentMatcher.group(3), mCurrentTestName);
mCurrentTestStatus = mCurrentMatcher.group(1);
}
// optional docstring - do nothing