python_test_impact.py 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #
  2. # Copyright (c) Contributors to the Open 3D Engine Project.
  3. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0 OR MIT
  6. #
  7. #
  8. from pathlib import Path
  9. import xml.etree.ElementTree as ET
  10. import tiaf_report_constants as constants
  11. from test_impact import BaseTestImpact, RuntimeArgs
  12. from tiaf_logger import get_logger
  13. logger = get_logger(__file__)
  14. class PythonTestImpact(BaseTestImpact):
  15. _runtime_type = "python"
  16. _default_sequence_type = "regular"
  17. ARG_TEST_RUNNER_POLICY = RuntimeArgs.PYTHON_TEST_RUNNER.driver_argument
  18. def __init__(self, args: dict):
  19. """
  20. Initializes the test impact model with the commit, branches as runtime configuration.
  21. @param args: The arguments to be parsed and applied to this TestImpact object.
  22. """
  23. self._test_runner_policy = args.get(self.ARG_TEST_RUNNER_POLICY, None)
  24. super(PythonTestImpact, self).__init__(args)
  25. def _cross_check_tests(self, report: dict):
  26. """
  27. Function to compare our report with the report provided by another test runner. Will perform a comparison and return a list of any tests that weren't selected by TIAF that failed in the other test runner.
  28. Returns an empty list if not overloaded by a specialised test impact class.
  29. @param report: Dictionary containing the report provided by TIAF binary
  30. @return: List of tests that failed in test runner but did not fail in TIAF or weren't selected by TIAF.
  31. """
  32. mismatched_test_suites = []
  33. # If test runner policy is any other setting or not set, we will not have Pytest xml files to consume and comparison is not possible.
  34. if self._test_runner_policy == "on":
  35. ERRORS_KEY = 'errors'
  36. FAILURES_KEY = 'failures'
  37. xml_report_map = self._parse_xml_report(
  38. self._runtime_artifact_directory)
  39. for not_selected_test_name in report[constants.SELECTED_TEST_RUNS_KEY][constants.EXCLUDED_TEST_RUNS_KEY]:
  40. try:
  41. xml_report = xml_report_map[not_selected_test_name]
  42. # If either of these are greater than zero, then a test failed or errored out, and thus this test shouldn't have passed.
  43. if int(xml_report[ERRORS_KEY]) > 0 or int(xml_report[FAILURES_KEY]) > 0:
  44. logger.info(
  45. f"Mismatch found between the XML report for {not_selected_test_name}, logging for reporting.")
  46. mismatched_test_suites.append(not_selected_test_name)
  47. except KeyError as e:
  48. logger.warning(
  49. f"Error, {e} not found in our xml_reports. Maybe it wasn't run by the other test runner.")
  50. continue
  51. return mismatched_test_suites
  52. def _parse_xml_report(self, path):
  53. """
  54. Parse JUnit xml reports at the specified path and return a dictionary mapping the test suite name to the xml properties of that test suite.
  55. @param path: Path to the folder containing Pytest JUnit artifacts.
  56. @return test_results: A dictionary mapping each test suite name to the corresponding properties(tests passed, failed etc) of that test suite.
  57. """
  58. test_results = {}
  59. path_to_report_dir = Path(path)
  60. for report in path_to_report_dir.iterdir():
  61. tree = ET.parse(report)
  62. root = tree.getroot()
  63. for child in root:
  64. test_results[report.stem] = child.attrib
  65. return test_results
  66. @property
  67. def default_sequence_type(self):
  68. """
  69. The default sequence type for this TestImpact class. Must be implemented by subclass.
  70. """
  71. return self._default_sequence_type
  72. @property
  73. def runtime_type(self):
  74. """
  75. The runtime this TestImpact supports. Must be implemented by subclass.
  76. Current options are "native" or "python".
  77. """
  78. return self._runtime_type