SHARE
TWEET

Untitled

a guest Jul 16th, 2019 68 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ##
  2. # Print JUnit Test Results.
  3. #
  4. # - Description: Only prints iOS-formatted results (AllTests > Suites > Subsuites > Cases).
  5. #
  6. # - Usage:
  7. #   - `python3 PrintBluepillJUnitResults.py <XML file>`
  8. #
  9. # - Arguments:
  10. #   - JUnit results XML file
  11. #   - [optional] Format (e.g. "terminal" or "slack") (default: "terminal")
  12. #
  13. # - Dependencies:
  14. #   - Python 3
  15. #   - Requires junitparser (`pip3 install junitparser`)
  16. ##
  17.  
  18. import sys
  19. from junitparser import TestCase, TestSuite, JUnitXml, Skipped, Error, Failure
  20.  
  21. class TestError(object):
  22.     """A nice error wrapper
  23.  
  24.     Properties:
  25.         - name          e.g. "MyCareTeamTests/testRemoveFromMyCareTeam"
  26.  
  27.     Attributes:
  28.         - type          e.g. "Failure" or "Error"
  29.         - message       e.g. "No matches found for first match sequence"
  30.         - location      e.g. "XCUIElement+Extension.swift:122"
  31.         - trace         Full stacktrace (as reported by XCTest)
  32.     """
  33.  
  34.     def __init__(self, testCase, error):
  35.         self.name = "{}/{}".format(testCase.classname, testCase.name)
  36.         self.message = error.message
  37.         self.type = error.type
  38.         self.location = stripAndRemoveNewlines(error._elem.text)
  39.         self.trace = stripAndRemoveNewlines(testCase.system_out)
  40.  
  41.     def __eq__(self, other):
  42.         return self.uniqueId==other.uniqueId
  43.  
  44.     def __hash__(self):
  45.         return hash(self.uniqueId)
  46.  
  47.     @property
  48.     def uniqueId(self):
  49.         """Used for mapping duplicate failures on the same test case.
  50.  
  51.         Bluepill runs will often retry failed tests.
  52.         String in format: "MyCareTeamTests/testAddToMyCareTeam:Out of bounds exception"
  53.         """
  54.         return "{}:{}".format(self.name, self.message)
  55.  
  56. class BluepillJUnitReport(JUnitXml):
  57.     """Fix baby"""
  58.     def __iter__(self):
  59.         return super(JUnitXml, self).iterchildren(ParentTestSuite)
  60.  
  61. class ParentTestSuite(TestSuite):
  62.     """Fix baby"""
  63.     def __iter__(self):
  64.         return super(TestSuite, self).iterchildren(TestSuite)
  65.  
  66. def stripAndRemoveNewlines(text):
  67.     """Removes empty newlines, and removes leading whitespace.
  68.     """
  69.     no_empty_newlines = "\n".join([ll.rstrip() for ll in text.splitlines() if ll.strip()])
  70.     return no_empty_newlines.strip()
  71.  
  72. def getLastXLines(string, x):
  73.     return "\n".join(string.split("\n")[-x:])
  74.  
  75. def getTestErrors(testSuites):
  76.     """Get a collection of bad results in these test suites
  77.     """
  78.  
  79.     if testSuites.errors == 0 and testSuites.failures == 0:
  80.         return [] # early exit
  81.  
  82.     testErrors = []
  83.  
  84.     # Suites (e.g. "UITests.xctest")
  85.     for testSuite in testSuites:
  86.         if testSuite.errors == 0 and testSuite.failures == 0:
  87.             continue # skip
  88.  
  89.         # Groups (e.g. "MarketplaceTests")
  90.         for testGroup in testSuite:
  91.             if testGroup.errors == 0 and testGroup.failures == 0:
  92.                 continue # skip
  93.  
  94.             # (e.g. "testOpenXMLReader")
  95.             for testCase in testGroup:
  96.                 testResult = testCase.result
  97.  
  98.                 if isinstance(testResult, Error) or isinstance(testResult, Failure):
  99.                     testError = TestError(testCase, testResult)
  100.                     testErrors.append(testError)
  101.  
  102.     return testErrors
  103.  
  104. def printForTerminal(testErrors):
  105.     """Formatted for terminal
  106.  
  107.     MyCareTeamTests/testAddToMyCareTeamButton (1)
  108.     Error: "No matches found for first match sequence"
  109.     Location: XCUIElement+Extension.swift:122
  110.     Trackback:
  111.     XCTestOutputBarrier    t =    14.25s Assertion Failure: XCUIElement+Extension.swift:122: No matches found for first match sequence (
  112.         "<XCTElementFilteringTransformer: 0x6000009d7f90 'Find: Descendants matching type Button'>",
  113.         "<XCTElementFilteringTransformer: 0x6000009e4360 'Find: Elements matching predicate '\"Book\" IN identifiers''>"
  114.     ) from input
  115.         t =    14.32s Tear Down
  116.     """
  117.     uniqueTestErrors = set(testErrors)
  118.  
  119.     if len(testErrors) == 0:
  120.         print("All tests passed.")
  121.     else:
  122.         print("There were {} errors/failures ({} unique):\n".format(len(testErrors),
  123.                                                                     len(uniqueTestErrors)))
  124.  
  125.     for error in uniqueTestErrors:
  126.         occurences = testErrors.count(error)
  127.  
  128.         print("""\n{e.name} ({occurences})
  129. {e.type}: \t\t"{e.message}"
  130. Location: \t{e.location}
  131. Traceback:\n{trace}\n""".format(e=error,
  132.                                 occurences=occurences,
  133.                                 trace=error.trace))
  134.  
  135. def printForSlack(testErrors):
  136.     """Formatted for slack
  137.  
  138.     MyCareTeamTests/testAddToMyCareTeamButton (1)
  139.     Error: "No matches found for first match sequence"
  140.     Location: XCUIElement+Extension.swift:122
  141.     Trackback:
  142.     XCTestOutputBarrier    t =    14.25s Assertion Failure: XCUIElement+Extension.swift:122: No matches found for first match sequence (
  143.         "<XCTElementFilteringTransformer: 0x6000009d7f90 'Find: Descendants matching type Button'>",
  144.         "<XCTElementFilteringTransformer: 0x6000009e4360 'Find: Elements matching predicate '\"Book\" IN identifiers''>"
  145.     ) from input
  146.         t =    14.32s Tear Down
  147.     """
  148.     uniqueTestErrors = set(testErrors)
  149.  
  150.     if len(testErrors) == 0:
  151.         print("All tests passed.")
  152.     else:
  153.         print("There were {} errors/failures ({} unique):\n".format(len(testErrors),
  154.                                                                     len(uniqueTestErrors)))
  155.  
  156.     for error in uniqueTestErrors:
  157.         occurences = testErrors.count(error)
  158.  
  159.         print(""">*{e.name} ({occurences})*
  160. >{e.message}
  161. >```{trace}```\n""".format(e=error,
  162.                          occurences=occurences,
  163.                          trace=getLastXLines(error.trace, 5)))
  164.  
  165. def main():
  166.     """python3 PrintBluepillJUnitResults.py <bluepill_junit_result_file.xml>
  167.     """
  168.  
  169.     if len(sys.argv) < 2:
  170.         print("Missing argument 1: Input file")
  171.  
  172.     file = sys.argv[1]
  173.     results = BluepillJUnitReport.fromfile(file)
  174.     testErrors = getTestErrors(results)
  175.  
  176.  
  177.     if len(sys.argv) < 3:
  178.         mode = "terminal"
  179.     else:
  180.         mode = sys.argv[2]
  181.  
  182.     if mode == "slack":
  183.         printForSlack(testErrors)
  184.     else:
  185.         printForTerminal(testErrors)
  186.  
  187. if __name__ == "__main__":
  188.     main()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top