tiaf_driver.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. import argparse
  9. import mars_utils
  10. import sys
  11. import pathlib
  12. import traceback
  13. from test_impact import NativeTestImpact, PythonTestImpact
  14. from tiaf_logger import get_logger
  15. import tiaf_report_constants as constants
  16. logger = get_logger(__file__)
  17. class PruneAndSortMultiValues(argparse.Action):
  18. def __call__(self, parser, namespace, values, option_string=None):
  19. # Remove the suite duplicates and sort alphabetically
  20. values = sorted(set(values), key = lambda x: x[1])
  21. setattr(namespace, self.dest, values)
  22. def parse_args():
  23. def valid_file_path(value):
  24. if pathlib.Path(value).is_file():
  25. return value
  26. else:
  27. raise FileNotFoundError(value)
  28. def valid_timout_type(value):
  29. value = int(value)
  30. if value <= 0:
  31. raise ValueError("Timer values must be positive integers")
  32. return value
  33. def valid_test_failure_policy(value):
  34. if value == "continue" or value == "abort" or value == "ignore":
  35. return value
  36. else:
  37. raise ValueError(
  38. "Test failure policy must be 'abort', 'continue' or 'ignore'")
  39. parser = argparse.ArgumentParser()
  40. # Configuration file path
  41. parser.add_argument(
  42. '--config',
  43. type=valid_file_path,
  44. help="Path to the test impact analysis framework configuration file",
  45. required=True
  46. )
  47. # Source branch
  48. parser.add_argument(
  49. '--src-branch',
  50. help="Branch that is being built",
  51. required=True
  52. )
  53. # Destination branch
  54. parser.add_argument(
  55. '--dst-branch',
  56. help="For PR builds, the destination branch to be merged to, otherwise empty",
  57. required=False
  58. )
  59. # Commit hash
  60. parser.add_argument(
  61. '--commit',
  62. help="Commit that is being built",
  63. required=True
  64. )
  65. # S3 bucket name
  66. parser.add_argument(
  67. '--s3-bucket',
  68. help="Location of S3 bucket to use for persistent storage, otherwise local disk storage will be used",
  69. required=False
  70. )
  71. # S3 bucket top level directory
  72. parser.add_argument(
  73. '--s3-top-level-dir',
  74. help="The top level directory to use in the S3 bucket",
  75. required=False
  76. )
  77. # MARS index prefix
  78. parser.add_argument(
  79. '--mars-index-prefix',
  80. help="Index prefix to use for MARS, otherwise no data will be tramsmitted to MARS",
  81. required=False
  82. )
  83. # Build number
  84. parser.add_argument(
  85. '--build-number',
  86. help="The build number this run of TIAF corresponds to",
  87. required=True
  88. )
  89. # Test suite
  90. parser.add_argument(
  91. '--suites',
  92. help="Test suites to run",
  93. nargs='+',
  94. action=PruneAndSortMultiValues,
  95. required=True,
  96. )
  97. # Test label excludes
  98. parser.add_argument(
  99. '--label-excludes',
  100. help="CTest suite labels to exclude if matched",
  101. nargs='*',
  102. action=PruneAndSortMultiValues,
  103. required=False
  104. )
  105. # Test failure policy
  106. parser.add_argument(
  107. '--test-failure-policy',
  108. type=valid_test_failure_policy,
  109. help="Test failure policy for regular and test impact sequences (ignored when seeding)",
  110. required=True
  111. )
  112. # Safe mode
  113. parser.add_argument(
  114. '--safe-mode',
  115. action='store_const',
  116. const="on",
  117. help="Run impact analysis tests in safe mode (ignored when seeding)",
  118. required=False
  119. )
  120. # Test timeout
  121. parser.add_argument(
  122. '--test-timeout',
  123. type=valid_timout_type,
  124. help="Maximum run time (in seconds) of any test target before being terminated",
  125. required=False
  126. )
  127. # Global timeout
  128. parser.add_argument(
  129. '--global-timeout',
  130. type=valid_timout_type,
  131. help="Maximum run time of the sequence before being terminated",
  132. required=False
  133. )
  134. # Target exclusion JSON file
  135. parser.add_argument(
  136. '--exclude-file',
  137. type=valid_file_path,
  138. help="Path to file containing tests to exclude from this run",
  139. required=False
  140. )
  141. # Runtime type (native or python)
  142. parser.add_argument(
  143. "--runtime-type",
  144. choices=SUPPORTED_RUNTIMES.keys(),
  145. help="The runtime TIAF should run tests for",
  146. required=True
  147. )
  148. # Runtime sequence override
  149. parser.add_argument(
  150. "--sequence-override",
  151. help="Override sequence type",
  152. required=False
  153. )
  154. # Python test runner policy
  155. parser.add_argument(
  156. "--testrunner-policy",
  157. choices=["live","null"],
  158. help="Test runner policy for TIAF",
  159. required=False
  160. )
  161. # Test target output routing
  162. parser.add_argument(
  163. "--target-output",
  164. choices=["stdout"],
  165. help="Test target std/error output routing",
  166. required=False
  167. )
  168. args = parser.parse_args()
  169. return args
  170. SUPPORTED_RUNTIMES = {
  171. "python": PythonTestImpact,
  172. "native": NativeTestImpact
  173. }
  174. def main(args: dict):
  175. try:
  176. tiaf_class = SUPPORTED_RUNTIMES[args.pop("runtime_type")]
  177. tiaf = tiaf_class(args)
  178. if not tiaf.enabled:
  179. logger.info("TIAF has been disabled for this runtime type.")
  180. sys.exit(0)
  181. tiaf_result = tiaf.run()
  182. if args.get('mars_index_prefix'):
  183. logger.info("Transmitting report to MARS...")
  184. mars_utils.transmit_report_to_mars(
  185. args['mars_index_prefix'], tiaf_result, sys.argv, args['build_number'])
  186. logger.info("Complete!")
  187. sys.exit(tiaf_result[constants.RUNTIME_RETURN_CODE_KEY])
  188. except Exception as e:
  189. # Non-gating will be removed from this script and handled at the job level in SPEC-7413
  190. logger.error(f"Exception caught by TIAF driver: '{e}'.")
  191. traceback.print_exc()
  192. sys.exit(1)
  193. if __name__ == "__main__":
  194. args = vars(parse_args())
  195. main(args)