get_muted_tests.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #!/usr/bin/env python3
  2. import argparse
  3. import configparser
  4. import datetime
  5. import os
  6. import posixpath
  7. import re
  8. import ydb
  9. from get_diff_lines_of_file import get_diff_lines_of_file
  10. from mute_utils import pattern_to_re
  11. from transform_ya_junit import YaMuteCheck
  12. dir = os.path.dirname(__file__)
  13. config = configparser.ConfigParser()
  14. config_file_path = f"{dir}/../../config/ydb_qa_db.ini"
  15. repo_path = f"{dir}/../../../"
  16. muted_ya_path = '.github/config/muted_ya.txt'
  17. config.read(config_file_path)
  18. DATABASE_ENDPOINT = config["QA_DB"]["DATABASE_ENDPOINT"]
  19. DATABASE_PATH = config["QA_DB"]["DATABASE_PATH"]
  20. def get_all_tests(job_id=None, branch=None):
  21. print(f'Getting all tests')
  22. with ydb.Driver(
  23. endpoint=DATABASE_ENDPOINT,
  24. database=DATABASE_PATH,
  25. credentials=ydb.credentials_from_env_variables(),
  26. ) as driver:
  27. driver.wait(timeout=10, fail_fast=True)
  28. # settings, paths, consts
  29. tc_settings = ydb.TableClientSettings().with_native_date_in_result_sets(enabled=True)
  30. table_client = ydb.TableClient(driver, tc_settings)
  31. # geting last date from history
  32. today = datetime.date.today().strftime('%Y-%m-%d')
  33. if job_id and branch: # extend all tests from main by new tests from pr
  34. tests = f"""
  35. SELECT * FROM (
  36. SELECT
  37. suite_folder,
  38. test_name,
  39. full_name
  40. from `test_results/analytics/testowners`
  41. WHERE
  42. run_timestamp_last >= Date('{today}') - 6*Interval("P1D")
  43. and run_timestamp_last <= Date('{today}') + Interval("P1D")
  44. UNION
  45. SELECT DISTINCT
  46. suite_folder,
  47. test_name,
  48. suite_folder || '/' || test_name as full_name
  49. FROM `test_results/test_runs_column`
  50. WHERE
  51. job_id = {job_id}
  52. and branch = '{branch}'
  53. )
  54. """
  55. else: # only all tests from main
  56. tests = f"""
  57. SELECT
  58. suite_folder,
  59. test_name,
  60. full_name,
  61. owners,
  62. run_timestamp_last,
  63. Date('{today}') as date
  64. FROM `test_results/analytics/testowners`
  65. WHERE
  66. run_timestamp_last >= Date('{today}') - 6*Interval("P1D")
  67. and run_timestamp_last <= Date('{today}') + Interval("P1D")
  68. """
  69. query = ydb.ScanQuery(tests, {})
  70. it = table_client.scan_query(query)
  71. results = []
  72. while True:
  73. try:
  74. result = next(it)
  75. results = results + result.result_set.rows
  76. except StopIteration:
  77. break
  78. return results
  79. def create_tables(pool, table_path):
  80. print(f"> create table if not exists:'{table_path}'")
  81. def callee(session):
  82. session.execute_scheme(
  83. f"""
  84. CREATE table IF NOT EXISTS `{table_path}` (
  85. `date` Date NOT NULL,
  86. `test_name` Utf8 NOT NULL,
  87. `suite_folder` Utf8 NOT NULL,
  88. `full_name` Utf8 NOT NULL,
  89. `run_timestamp_last` Timestamp NOT NULL,
  90. `owners` Utf8,
  91. `branch` Utf8 NOT NULL,
  92. `is_muted` Uint32 ,
  93. PRIMARY KEY (`date`,branch, `test_name`, `suite_folder`, `full_name`)
  94. )
  95. PARTITION BY HASH(date,branch)
  96. WITH (STORE = COLUMN)
  97. """
  98. )
  99. return pool.retry_operation_sync(callee)
  100. def bulk_upsert(table_client, table_path, rows):
  101. print(f"> bulk upsert: {table_path}")
  102. column_types = (
  103. ydb.BulkUpsertColumns()
  104. .add_column("date", ydb.OptionalType(ydb.PrimitiveType.Date))
  105. .add_column("test_name", ydb.OptionalType(ydb.PrimitiveType.Utf8))
  106. .add_column("suite_folder", ydb.OptionalType(ydb.PrimitiveType.Utf8))
  107. .add_column("full_name", ydb.OptionalType(ydb.PrimitiveType.Utf8))
  108. .add_column("run_timestamp_last", ydb.OptionalType(ydb.PrimitiveType.Timestamp))
  109. .add_column("owners", ydb.OptionalType(ydb.PrimitiveType.Utf8))
  110. .add_column("branch", ydb.OptionalType(ydb.PrimitiveType.Utf8))
  111. .add_column("is_muted", ydb.OptionalType(ydb.PrimitiveType.Uint32))
  112. )
  113. table_client.bulk_upsert(table_path, rows, column_types)
  114. def write_to_file(text, file):
  115. os.makedirs(os.path.dirname(file), exist_ok=True)
  116. with open(file, 'w') as f:
  117. f.writelines(text)
  118. def upload_muted_tests(tests):
  119. with ydb.Driver(
  120. endpoint=DATABASE_ENDPOINT,
  121. database=DATABASE_PATH,
  122. credentials=ydb.credentials_from_env_variables(),
  123. ) as driver:
  124. driver.wait(timeout=10, fail_fast=True)
  125. # settings, paths, consts
  126. tc_settings = ydb.TableClientSettings().with_native_date_in_result_sets(enabled=True)
  127. table_client = ydb.TableClient(driver, tc_settings)
  128. table_path = f'test_results/all_tests_with_owner_and_mute'
  129. with ydb.SessionPool(driver) as pool:
  130. create_tables(pool, table_path)
  131. full_path = posixpath.join(DATABASE_PATH, table_path)
  132. bulk_upsert(driver.table_client, full_path, tests)
  133. def to_str(data):
  134. if isinstance(data, str):
  135. return data
  136. elif isinstance(data, bytes):
  137. return data.decode('utf-8')
  138. else:
  139. raise ValueError("Unsupported type")
  140. def mute_applier(args):
  141. output_path = args.output_folder
  142. all_tests_file = os.path.join(output_path, '1_all_tests.txt')
  143. all_muted_tests_file = os.path.join(output_path, '1_all_muted_tests.txt')
  144. if "CI_YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS" not in os.environ:
  145. print("Error: Env variable CI_YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS is missing, skipping")
  146. return 1
  147. else:
  148. # Do not set up 'real' variable from gh workflows because it interfere with ydb tests
  149. # So, set up it locally
  150. os.environ["YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS"] = os.environ[
  151. "CI_YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS"
  152. ]
  153. # all muted
  154. mute_check = YaMuteCheck()
  155. mute_check.load(muted_ya_path)
  156. if args.mode == 'upload_muted_tests':
  157. all_tests = get_all_tests(branch=args.branch)
  158. for test in all_tests:
  159. testsuite = to_str(test['suite_folder'])
  160. testcase = to_str(test['test_name'])
  161. test['branch'] = 'main'
  162. test['is_muted'] = int(mute_check(testsuite, testcase))
  163. upload_muted_tests(all_tests)
  164. elif args.mode == 'get_mute_diff':
  165. all_tests = get_all_tests(job_id=args.job_id, branch=args.branch)
  166. all_tests.sort(key=lambda test: test['full_name'])
  167. muted_tests = []
  168. all_tests_names_and_suites = []
  169. for test in all_tests:
  170. testsuite = to_str(test['suite_folder'])
  171. testcase = to_str(test['test_name'])
  172. all_tests_names_and_suites.append(testsuite + ' ' + testcase + '\n')
  173. if mute_check(testsuite, testcase):
  174. muted_tests.append(testsuite + ' ' + testcase + '\n')
  175. write_to_file(all_tests_names_and_suites, all_tests_file)
  176. write_to_file(muted_tests, all_muted_tests_file)
  177. added_mute_lines_file = os.path.join(output_path, '2_added_mute_lines.txt')
  178. new_muted_tests_file = os.path.join(output_path, '2_new_muted_tests.txt')
  179. removed_mute_lines_file = os.path.join(output_path, '3_removed_mute_lines.txt')
  180. unmuted_tests_file = os.path.join(output_path, '3_unmuted_tests.txt')
  181. added_lines, removed_lines = get_diff_lines_of_file(args.base_sha, args.head_sha, muted_ya_path)
  182. # checking added lines
  183. write_to_file('\n'.join(added_lines), added_mute_lines_file)
  184. mute_check = YaMuteCheck()
  185. mute_check.load(added_mute_lines_file)
  186. added_muted_tests = []
  187. print("New muted tests captured")
  188. for test in all_tests:
  189. testsuite = to_str(test['suite_folder'])
  190. testcase = to_str(test['test_name'])
  191. if mute_check(testsuite, testcase):
  192. added_muted_tests.append(testsuite + ' ' + testcase + '\n')
  193. # checking removed lines
  194. write_to_file('\n'.join(removed_lines), removed_mute_lines_file)
  195. mute_check = YaMuteCheck()
  196. mute_check.load(removed_mute_lines_file)
  197. removed_muted_tests = []
  198. print("Unmuted tests captured")
  199. for test in all_tests:
  200. testsuite = to_str(test['suite_folder'])
  201. testcase = to_str(test['test_name'])
  202. if mute_check(testsuite, testcase):
  203. removed_muted_tests.append(testsuite + ' ' + testcase + '\n')
  204. # geting only uniq items in both lists because not uniq items= this tests was muted before
  205. added_set = set(added_muted_tests)
  206. removed_set = set(removed_muted_tests)
  207. added_unique = added_set - removed_set
  208. removed_unique = removed_set - added_set
  209. added_muted_tests = list(sorted(added_unique))
  210. removed_muted_tests = list(sorted(removed_unique))
  211. write_to_file(added_muted_tests, new_muted_tests_file)
  212. write_to_file(removed_muted_tests, unmuted_tests_file)
  213. print(f"All tests have been written to {all_tests_file}.")
  214. print(f"All mutes tests have been written to {all_muted_tests_file}.")
  215. print(f"Added lines have been written to {added_mute_lines_file}.")
  216. print(f"New muted tests have been written to {new_muted_tests_file}.")
  217. print(f"Removed lines have been written to {removed_mute_lines_file}.")
  218. print(f"Unmuted tests have been written to {unmuted_tests_file}.")
  219. if __name__ == "__main__":
  220. parser = argparse.ArgumentParser(description="Generate diff files for mute_ya.txt")
  221. parser.add_argument(
  222. '--output_folder',
  223. type=str,
  224. default=repo_path + '.github/config/mute_info/',
  225. help=f'The folder to output results. Default is the value of repo_path = {repo_path}.github/config/mute_info/.',
  226. )
  227. subparsers = parser.add_subparsers(dest='mode', help="Mode to perform")
  228. upload_muted_tests_parser = subparsers.add_parser(
  229. 'upload_muted_tests', help='apply mute rules for all tests in main and upload to database'
  230. )
  231. upload_muted_tests_parser.add_argument(
  232. '--branch', required=True, default='main', help='branch for getting all tests'
  233. )
  234. get_mute_details_parser = subparsers.add_parser(
  235. 'get_mute_diff',
  236. help='apply mute rules for all tests in main extended by new tests from pr and collect new muted and unmuted',
  237. )
  238. get_mute_details_parser.add_argument('--base_sha', required=True, help='Base sha of PR')
  239. get_mute_details_parser.add_argument('--head_sha', required=True, help='Head sha of PR')
  240. get_mute_details_parser.add_argument(
  241. '--branch',
  242. required=True,
  243. help='pass branch to extend list of tests by new tests from this pr (by job-id of PR-check and branch)',
  244. )
  245. get_mute_details_parser.add_argument(
  246. '--job-id',
  247. required=True,
  248. help='pass job-id to extend list of tests by new tests from this pr (by job-id of PR-check and branch)',
  249. )
  250. args = parser.parse_args()
  251. mute_applier(args)