diff options
author | Nadia Agueeva <agueeva@google.com> | 2024-04-23 23:41:51 +0000 |
---|---|---|
committer | Nadia Agueeva <agueeva@google.com> | 2024-04-29 23:28:26 +0000 |
commit | bd7a02e5737d167c32cab8778bf66d109d687389 (patch) | |
tree | 57b08bccc51b18741d08abfa7af7915a16f6e581 | |
parent | 15bf7ff6d2cb5b56bfa2e02326f99513bec151a6 (diff) | |
download | tradefederation-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.java | 59 | ||||
-rw-r--r-- | test_framework/com/android/tradefed/testtype/PythonUnitTestResultParser.java | 25 |
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 |