ytest.py 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  1. from __future__ import print_function
  2. import os
  3. import re
  4. import sys
  5. import six
  6. import json
  7. import copy
  8. import base64
  9. import shlex
  10. import _common
  11. import lib.test_const as consts
  12. import _requirements as reqs
  13. try:
  14. from StringIO import StringIO
  15. except ImportError:
  16. from io import StringIO
  17. import subprocess
  18. import collections
  19. import ymake
  20. CANON_DATA_DIR_NAME = 'canondata'
  21. CANON_OUTPUT_STORAGE = 'canondata_storage'
  22. CANON_RESULT_FILE_NAME = 'result.json'
  23. BLOCK_SEPARATOR = '============================================================='
  24. SPLIT_FACTOR_MAX_VALUE = 1000
  25. SPLIT_FACTOR_TEST_FILES_MAX_VALUE = 4250
  26. PARTITION_MODS = ('SEQUENTIAL', 'MODULO')
  27. DEFAULT_TIDY_CONFIG = "build/config/tests/clang_tidy/config.yaml"
  28. DEFAULT_TIDY_CONFIG_MAP_PATH = "build/yandex_specific/config/clang_tidy/tidy_default_map.json"
  29. PROJECT_TIDY_CONFIG_MAP_PATH = "build/yandex_specific/config/clang_tidy/tidy_project_map.json"
  30. KTLINT_CURRENT_EDITOR_CONFIG = "arcadia/build/platform/java/ktlint/.editorconfig"
  31. KTLINT_OLD_EDITOR_CONFIG = "arcadia/build/platform/java/ktlint_old/.editorconfig"
  32. tidy_config_map = None
  33. def ontest_data(unit, *args):
  34. ymake.report_configure_error("TEST_DATA is removed in favour of DATA")
  35. def format_recipes(data: str | None) -> str:
  36. if not data:
  37. return ""
  38. data = data.replace('"USE_RECIPE_DELIM"', "\n")
  39. data = data.replace("$TEST_RECIPES_VALUE", "")
  40. return data
  41. def prepare_recipes(data: str | None) -> str:
  42. formatted = format_recipes(data)
  43. return base64.b64encode(six.ensure_binary(formatted))
  44. def prepare_env(data):
  45. data = data.replace("$TEST_ENV_VALUE", "")
  46. return serialize_list(shlex.split(data))
  47. def is_yt_spec_contain_pool_info(filename): # XXX switch to yson in ymake + perf test for configure
  48. pool_re = re.compile(r"""['"]*pool['"]*\s*?=""")
  49. cypress_root_re = re.compile(r"""['"]*cypress_root['"]*\s*=""")
  50. with open(filename, 'r') as afile:
  51. yt_spec = afile.read()
  52. return pool_re.search(yt_spec) and cypress_root_re.search(yt_spec)
  53. def validate_test(unit, kw):
  54. def get_list(key):
  55. return deserialize_list(kw.get(key, ""))
  56. valid_kw = copy.deepcopy(kw)
  57. errors = []
  58. warnings = []
  59. if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test':
  60. project_path = valid_kw.get('BUILD-FOLDER-PATH', "")
  61. if not project_path.startswith(
  62. ("contrib", "mail", "maps", "tools/idl", "metrika", "devtools", "mds", "yandex_io", "smart_devices")
  63. ):
  64. errors.append("BOOSTTEST is not allowed here")
  65. size_timeout = collections.OrderedDict(sorted(consts.TestSize.DefaultTimeouts.items(), key=lambda t: t[1]))
  66. size = valid_kw.get('SIZE', consts.TestSize.Small).lower()
  67. tags = set(get_list("TAG"))
  68. requirements_orig = get_list("REQUIREMENTS")
  69. in_autocheck = consts.YaTestTags.NotAutocheck not in tags and consts.YaTestTags.Manual not in tags
  70. is_fat = consts.YaTestTags.Fat in tags
  71. is_force_sandbox = consts.YaTestTags.ForceDistbuild not in tags and is_fat
  72. is_ytexec_run = consts.YaTestTags.YtRunner in tags
  73. is_fuzzing = valid_kw.get("FUZZING", False)
  74. is_kvm = 'kvm' in requirements_orig
  75. requirements = {}
  76. secret_requirements = ('sb_vault', 'yav')
  77. list_requirements = secret_requirements
  78. for req in requirements_orig:
  79. if req in ('kvm',):
  80. requirements[req] = str(True)
  81. continue
  82. if ":" in req:
  83. req_name, req_value = req.split(":", 1)
  84. if req_name in list_requirements:
  85. requirements[req_name] = ",".join(filter(None, [requirements.get(req_name), req_value]))
  86. else:
  87. if req_name in requirements:
  88. if req_value in ["0"]:
  89. warnings.append(
  90. "Requirement [[imp]]{}[[rst]] is dropped [[imp]]{}[[rst]] -> [[imp]]{}[[rst]]".format(
  91. req_name, requirements[req_name], req_value
  92. )
  93. )
  94. del requirements[req_name]
  95. elif requirements[req_name] != req_value:
  96. warnings.append(
  97. "Requirement [[imp]]{}[[rst]] is redefined [[imp]]{}[[rst]] -> [[imp]]{}[[rst]]".format(
  98. req_name, requirements[req_name], req_value
  99. )
  100. )
  101. requirements[req_name] = req_value
  102. else:
  103. requirements[req_name] = req_value
  104. else:
  105. errors.append("Invalid requirement syntax [[imp]]{}[[rst]]: expect <requirement>:<value>".format(req))
  106. if not errors:
  107. for req_name, req_value in requirements.items():
  108. try:
  109. error_msg = reqs.validate_requirement(
  110. req_name,
  111. req_value,
  112. size,
  113. is_force_sandbox,
  114. in_autocheck,
  115. is_fuzzing,
  116. is_kvm,
  117. is_ytexec_run,
  118. requirements,
  119. )
  120. except Exception as e:
  121. error_msg = str(e)
  122. if error_msg:
  123. errors += [error_msg]
  124. invalid_requirements_for_distbuild = [
  125. requirement for requirement in requirements.keys() if requirement not in ('ram', 'ram_disk', 'cpu', 'network')
  126. ]
  127. sb_tags = []
  128. # XXX Unfortunately, some users have already started using colons
  129. # in their tag names. Use skip set to avoid treating their tag as system ones.
  130. # Remove this check when all such user tags are removed.
  131. skip_set = ('ynmt_benchmark', 'bert_models', 'zeliboba_map')
  132. # Verify the prefixes of the system tags to avoid pointless use of the REQUIREMENTS macro parameters in the TAG macro.
  133. for tag in tags:
  134. if tag.startswith('sb:'):
  135. sb_tags.append(tag)
  136. elif ':' in tag and not tag.startswith('ya:') and tag.split(':')[0] not in skip_set:
  137. errors.append(
  138. "Only [[imp]]sb:[[rst]] and [[imp]]ya:[[rst]] prefixes are allowed in system tags: {}".format(tag)
  139. )
  140. if is_fat:
  141. if size != consts.TestSize.Large:
  142. errors.append("Only LARGE test may have ya:fat tag")
  143. if in_autocheck and not is_force_sandbox:
  144. if invalid_requirements_for_distbuild:
  145. errors.append(
  146. "'{}' REQUIREMENTS options can be used only for FAT tests without ya:force_distbuild tag. Remove TAG(ya:force_distbuild) or an option.".format(
  147. invalid_requirements_for_distbuild
  148. )
  149. )
  150. if sb_tags:
  151. errors.append(
  152. "You can set sandbox tags '{}' only for FAT tests without ya:force_distbuild. Remove TAG(ya:force_sandbox) or sandbox tags.".format(
  153. sb_tags
  154. )
  155. )
  156. if consts.YaTestTags.SandboxCoverage in tags:
  157. errors.append("You can set 'ya:sandbox_coverage' tag only for FAT tests without ya:force_distbuild.")
  158. if is_ytexec_run:
  159. errors.append(
  160. "Running LARGE tests over YT (ya:yt) on Distbuild (ya:force_distbuild) is forbidden. Consider removing TAG(ya:force_distbuild)."
  161. )
  162. else:
  163. if is_force_sandbox:
  164. errors.append('ya:force_sandbox can be used with LARGE tests only')
  165. if consts.YaTestTags.NoFuse in tags:
  166. errors.append('ya:nofuse can be used with LARGE tests only')
  167. if consts.YaTestTags.Privileged in tags:
  168. errors.append("ya:privileged can be used with LARGE tests only")
  169. if in_autocheck and size == consts.TestSize.Large:
  170. errors.append("LARGE test must have ya:fat tag")
  171. if consts.YaTestTags.Privileged in tags and 'container' not in requirements:
  172. errors.append("Only tests with 'container' requirement can have 'ya:privileged' tag")
  173. if size not in size_timeout:
  174. errors.append(
  175. "Unknown test size: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]".format(
  176. size.upper(), ", ".join([sz.upper() for sz in size_timeout.keys()])
  177. )
  178. )
  179. else:
  180. try:
  181. timeout = int(valid_kw.get('TEST-TIMEOUT', size_timeout[size]) or size_timeout[size])
  182. script_rel_path = valid_kw.get('SCRIPT-REL-PATH')
  183. if timeout < 0:
  184. raise Exception("Timeout must be > 0")
  185. skip_timeout_verification = script_rel_path in ('java.style', 'ktlint')
  186. if size_timeout[size] < timeout and in_autocheck and not skip_timeout_verification:
  187. suggested_size = None
  188. for s, t in size_timeout.items():
  189. if timeout <= t:
  190. suggested_size = s
  191. break
  192. if suggested_size:
  193. suggested_size = ", suggested size: [[imp]]{}[[rst]]".format(suggested_size.upper())
  194. else:
  195. suggested_size = ""
  196. errors.append(
  197. "Max allowed timeout for test size [[imp]]{}[[rst]] is [[imp]]{} sec[[rst]]{}".format(
  198. size.upper(), size_timeout[size], suggested_size
  199. )
  200. )
  201. except Exception as e:
  202. errors.append("Error when parsing test timeout: [[bad]]{}[[rst]]".format(e))
  203. requirements_list = []
  204. for req_name, req_value in six.iteritems(requirements):
  205. requirements_list.append(req_name + ":" + req_value)
  206. valid_kw['REQUIREMENTS'] = serialize_list(sorted(requirements_list))
  207. # Mark test with ya:external tag if it requests any secret from external storages
  208. # It's not stable and nonreproducible by definition
  209. for x in secret_requirements:
  210. if x in requirements:
  211. tags.add(consts.YaTestTags.External)
  212. if valid_kw.get("FUZZ-OPTS"):
  213. for option in get_list("FUZZ-OPTS"):
  214. if not option.startswith("-"):
  215. errors.append(
  216. "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should start with '-'".format(
  217. option
  218. )
  219. )
  220. break
  221. eqpos = option.find("=")
  222. if eqpos == -1 or len(option) == eqpos + 1:
  223. errors.append(
  224. "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should obtain value specified after '='".format(
  225. option
  226. )
  227. )
  228. break
  229. if option[eqpos - 1] == " " or option[eqpos + 1] == " ":
  230. errors.append("Spaces are not allowed: '[[imp]]{}[[rst]]'".format(option))
  231. break
  232. if option[:eqpos] in ("-runs", "-dict", "-jobs", "-workers", "-artifact_prefix", "-print_final_stats"):
  233. errors.append(
  234. "You can't use '[[imp]]{}[[rst]]' - it will be automatically calculated or configured during run".format(
  235. option
  236. )
  237. )
  238. break
  239. if valid_kw.get("YT-SPEC"):
  240. if not is_ytexec_run:
  241. errors.append("You can use YT_SPEC macro only tests marked with ya:yt tag")
  242. else:
  243. for filename in get_list("YT-SPEC"):
  244. filename = unit.resolve('$S/' + filename)
  245. if not os.path.exists(filename):
  246. errors.append("File '{}' specified in the YT_SPEC macro doesn't exist".format(filename))
  247. continue
  248. if not is_yt_spec_contain_pool_info(filename):
  249. tags.add(consts.YaTestTags.External)
  250. tags.add("ya:yt_research_pool")
  251. partition = valid_kw.get('TEST_PARTITION', 'SEQUENTIAL')
  252. if partition not in PARTITION_MODS:
  253. raise ValueError('partition mode should be one of {}, detected: {}'.format(PARTITION_MODS, partition))
  254. if valid_kw.get('SPLIT-FACTOR'):
  255. if valid_kw.get('FORK-MODE') == 'none':
  256. errors.append('SPLIT_FACTOR must be use with FORK_TESTS() or FORK_SUBTESTS() macro')
  257. value = 1
  258. try:
  259. value = int(valid_kw.get('SPLIT-FACTOR'))
  260. if value <= 0:
  261. raise ValueError("must be > 0")
  262. if value > SPLIT_FACTOR_MAX_VALUE:
  263. raise ValueError("the maximum allowed value is {}".format(SPLIT_FACTOR_MAX_VALUE))
  264. except ValueError as e:
  265. errors.append('Incorrect SPLIT_FACTOR value: {}'.format(e))
  266. if valid_kw.get('FORK-TEST-FILES') and size != consts.TestSize.Large:
  267. nfiles = count_entries(valid_kw.get('TEST-FILES'))
  268. if nfiles * value > SPLIT_FACTOR_TEST_FILES_MAX_VALUE:
  269. errors.append(
  270. 'Too much chunks generated:{} (limit: {}). Remove FORK_TEST_FILES() macro or reduce SPLIT_FACTOR({}).'.format(
  271. nfiles * value, SPLIT_FACTOR_TEST_FILES_MAX_VALUE, value
  272. )
  273. )
  274. if tags:
  275. valid_kw['TAG'] = serialize_list(sorted(tags))
  276. unit_path = _common.get_norm_unit_path(unit)
  277. if (
  278. not is_fat
  279. and consts.YaTestTags.Noretries in tags
  280. and not is_ytexec_run
  281. and not unit_path.startswith("devtools/dummy_arcadia/test/noretries")
  282. ):
  283. errors.append("Only LARGE tests can have 'ya:noretries' tag")
  284. if errors:
  285. return None, warnings, errors
  286. return valid_kw, warnings, errors
  287. def dump_test(unit, kw):
  288. valid_kw, warnings, errors = validate_test(unit, kw)
  289. for w in warnings:
  290. unit.message(['warn', w])
  291. for e in errors:
  292. ymake.report_configure_error(e)
  293. if valid_kw is None:
  294. return None
  295. string_handler = StringIO()
  296. for k, v in six.iteritems(valid_kw):
  297. print(k + ': ' + six.ensure_str(v), file=string_handler)
  298. print(BLOCK_SEPARATOR, file=string_handler)
  299. data = string_handler.getvalue()
  300. string_handler.close()
  301. return data
  302. def serialize_list(lst):
  303. lst = list(filter(None, lst))
  304. return '\"' + ';'.join(lst) + '\"' if lst else ''
  305. def deserialize_list(val):
  306. return list(filter(None, val.replace('"', "").split(";")))
  307. def get_correct_expression_for_group_var(varname):
  308. return r"\"${join=\;:" + varname + "}\""
  309. def count_entries(x):
  310. # see (de)serialize_list
  311. assert x is None or isinstance(x, str), type(x)
  312. if not x:
  313. return 0
  314. return x.count(";") + 1
  315. def get_values_list(unit, key):
  316. res = map(str.strip, (unit.get(key) or '').replace('$' + key, '').strip().split())
  317. return [r for r in res if r and r not in ['""', "''"]]
  318. def get_norm_paths(unit, key):
  319. # return paths without trailing (back)slash
  320. return [x.rstrip('\\/').replace('${ARCADIA_ROOT}/', '') for x in get_values_list(unit, key)]
  321. def get_unit_list_variable(unit, name):
  322. items = unit.get(name)
  323. if items:
  324. items = items.split(' ')
  325. assert items[0] == "${}".format(name), (items, name)
  326. return items[1:]
  327. return []
  328. def implies(a, b):
  329. return bool((not a) or b)
  330. def match_coverage_extractor_requirements(unit):
  331. # we shouldn't add test if
  332. return all(
  333. [
  334. # tests are not requested
  335. unit.get("TESTS_REQUESTED") == "yes",
  336. # build doesn't imply clang coverage, which supports segment extraction from the binaries
  337. unit.get("CLANG_COVERAGE") == "yes",
  338. # contrib wasn't requested
  339. implies(
  340. _common.get_norm_unit_path(unit).startswith("contrib/"), unit.get("ENABLE_CONTRIB_COVERAGE") == "yes"
  341. ),
  342. ]
  343. )
  344. def get_tidy_config_map(unit, map_path):
  345. config_map_path = unit.resolve(os.path.join("$S", map_path))
  346. config_map = {}
  347. try:
  348. with open(config_map_path, 'r') as afile:
  349. config_map = json.load(afile)
  350. except ValueError:
  351. ymake.report_configure_error("{} is invalid json".format(map_path))
  352. except Exception as e:
  353. ymake.report_configure_error(str(e))
  354. return config_map
  355. def get_default_tidy_config(unit):
  356. unit_path = _common.get_norm_unit_path(unit)
  357. tidy_default_config_map = get_tidy_config_map(unit, DEFAULT_TIDY_CONFIG_MAP_PATH)
  358. for project_prefix, config_path in tidy_default_config_map.items():
  359. if unit_path.startswith(project_prefix):
  360. return config_path
  361. return DEFAULT_TIDY_CONFIG
  362. ordered_tidy_map = None
  363. def get_project_tidy_config(unit):
  364. global ordered_tidy_map
  365. if ordered_tidy_map is None:
  366. ordered_tidy_map = list(reversed(sorted(get_tidy_config_map(unit, PROJECT_TIDY_CONFIG_MAP_PATH).items())))
  367. unit_path = _common.get_norm_unit_path(unit)
  368. for project_prefix, config_path in ordered_tidy_map:
  369. if unit_path.startswith(project_prefix):
  370. return config_path
  371. else:
  372. return get_default_tidy_config(unit)
  373. def onadd_ytest(unit, *args):
  374. keywords = {
  375. "DEPENDS": -1,
  376. "DATA": -1,
  377. "TIMEOUT": 1,
  378. "FORK_MODE": 1,
  379. "SPLIT_FACTOR": 1,
  380. "FORK_SUBTESTS": 0,
  381. "FORK_TESTS": 0,
  382. }
  383. flat_args, spec_args = _common.sort_by_keywords(keywords, args)
  384. is_implicit_data_needed = flat_args[1] in (
  385. "unittest.py",
  386. "gunittest",
  387. "g_benchmark",
  388. "go.test",
  389. "boost.test",
  390. "fuzz.test",
  391. )
  392. if is_implicit_data_needed and unit.get('ADD_SRCDIR_TO_TEST_DATA') == "yes":
  393. unit.ondata_files(_common.get_norm_unit_path(unit))
  394. if flat_args[1] == "fuzz.test":
  395. unit.ondata_files("fuzzing/{}/corpus.json".format(_common.get_norm_unit_path(unit)))
  396. if not flat_args[1] in ("unittest.py", "gunittest", "g_benchmark"):
  397. unit.ondata_files(get_unit_list_variable(unit, 'TEST_YT_SPEC_VALUE'))
  398. test_data = sorted(
  399. _common.filter_out_by_keyword(
  400. spec_args.get('DATA', []) + get_norm_paths(unit, 'TEST_DATA_VALUE'), 'AUTOUPDATED'
  401. )
  402. )
  403. if flat_args[1] == "go.test":
  404. data, _ = get_canonical_test_resources(unit)
  405. test_data += data
  406. elif flat_args[1] == "coverage.extractor" and not match_coverage_extractor_requirements(unit):
  407. # XXX
  408. # Current ymake implementation doesn't allow to call macro inside the 'when' body
  409. # that's why we add ADD_YTEST(coverage.extractor) to every PROGRAM entry and check requirements later
  410. return
  411. elif flat_args[1] == "clang_tidy" and unit.get("TIDY_ENABLED") != "yes":
  412. # Graph is not prepared
  413. return
  414. elif unit.get("TIDY") == "yes" and unit.get("TIDY_ENABLED") != "yes":
  415. # clang_tidy disabled for module
  416. return
  417. elif flat_args[1] == "no.test":
  418. return
  419. test_size = ''.join(spec_args.get('SIZE', [])) or unit.get('TEST_SIZE_NAME') or ''
  420. test_tags = serialize_list(sorted(_get_test_tags(unit, spec_args)))
  421. test_timeout = ''.join(spec_args.get('TIMEOUT', [])) or unit.get('TEST_TIMEOUT') or ''
  422. test_requirements = spec_args.get('REQUIREMENTS', []) + get_values_list(unit, 'TEST_REQUIREMENTS_VALUE')
  423. if flat_args[1] != "clang_tidy" and unit.get("TIDY_ENABLED") == "yes":
  424. # graph changed for clang_tidy tests
  425. if flat_args[1] in ("unittest.py", "gunittest", "g_benchmark", "boost.test"):
  426. flat_args[1] = "clang_tidy"
  427. test_size = 'SMALL'
  428. test_tags = ''
  429. test_timeout = "60"
  430. test_requirements = []
  431. unit.set(["TEST_YT_SPEC_VALUE", ""])
  432. else:
  433. return
  434. if flat_args[1] == "clang_tidy" and unit.get("TIDY_ENABLED") == "yes":
  435. if unit.get("TIDY_CONFIG"):
  436. default_config_path = unit.get("TIDY_CONFIG")
  437. project_config_path = unit.get("TIDY_CONFIG")
  438. else:
  439. default_config_path = get_default_tidy_config(unit)
  440. project_config_path = get_project_tidy_config(unit)
  441. unit.set(["DEFAULT_TIDY_CONFIG", default_config_path])
  442. unit.set(["PROJECT_TIDY_CONFIG", project_config_path])
  443. fork_mode = []
  444. if 'FORK_SUBTESTS' in spec_args:
  445. fork_mode.append('subtests')
  446. if 'FORK_TESTS' in spec_args:
  447. fork_mode.append('tests')
  448. fork_mode = fork_mode or spec_args.get('FORK_MODE', []) or unit.get('TEST_FORK_MODE').split()
  449. fork_mode = ' '.join(fork_mode) if fork_mode else ''
  450. unit_path = _common.get_norm_unit_path(unit)
  451. test_record = {
  452. 'TEST-NAME': flat_args[0],
  453. 'SCRIPT-REL-PATH': flat_args[1],
  454. 'TESTED-PROJECT-NAME': unit.name(),
  455. 'TESTED-PROJECT-FILENAME': unit.filename(),
  456. 'SOURCE-FOLDER-PATH': unit_path,
  457. # TODO get rid of BUILD-FOLDER-PATH
  458. 'BUILD-FOLDER-PATH': unit_path,
  459. 'BINARY-PATH': "{}/{}".format(unit_path, unit.filename()),
  460. 'GLOBAL-LIBRARY-PATH': unit.global_filename(),
  461. 'CUSTOM-DEPENDENCIES': ' '.join(spec_args.get('DEPENDS', []) + get_values_list(unit, 'TEST_DEPENDS_VALUE')),
  462. 'TEST-RECIPES': prepare_recipes(unit.get("TEST_RECIPES_VALUE")),
  463. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  464. # 'TEST-PRESERVE-ENV': 'da',
  465. 'TEST-DATA': serialize_list(sorted(test_data)),
  466. 'TEST-TIMEOUT': test_timeout,
  467. 'FORK-MODE': fork_mode,
  468. 'SPLIT-FACTOR': ''.join(spec_args.get('SPLIT_FACTOR', [])) or unit.get('TEST_SPLIT_FACTOR') or '',
  469. 'SIZE': test_size,
  470. 'TAG': test_tags,
  471. 'REQUIREMENTS': serialize_list(test_requirements),
  472. 'TEST-CWD': unit.get('TEST_CWD_VALUE') or '',
  473. 'FUZZ-DICTS': serialize_list(
  474. spec_args.get('FUZZ_DICTS', []) + get_unit_list_variable(unit, 'FUZZ_DICTS_VALUE')
  475. ),
  476. 'FUZZ-OPTS': serialize_list(spec_args.get('FUZZ_OPTS', []) + get_unit_list_variable(unit, 'FUZZ_OPTS_VALUE')),
  477. 'YT-SPEC': serialize_list(spec_args.get('YT_SPEC', []) + get_unit_list_variable(unit, 'TEST_YT_SPEC_VALUE')),
  478. 'BLOB': unit.get('TEST_BLOB_DATA') or '',
  479. 'SKIP_TEST': unit.get('SKIP_TEST_VALUE') or '',
  480. 'TEST_IOS_DEVICE_TYPE': unit.get('TEST_IOS_DEVICE_TYPE_VALUE') or '',
  481. 'TEST_IOS_RUNTIME_TYPE': unit.get('TEST_IOS_RUNTIME_TYPE_VALUE') or '',
  482. 'ANDROID_APK_TEST_ACTIVITY': unit.get('ANDROID_APK_TEST_ACTIVITY_VALUE') or '',
  483. 'TEST_PARTITION': unit.get("TEST_PARTITION") or 'SEQUENTIAL',
  484. 'GO_BENCH_TIMEOUT': unit.get('GO_BENCH_TIMEOUT') or '',
  485. }
  486. if flat_args[1] == "go.bench":
  487. if "ya:run_go_benchmark" not in test_record["TAG"]:
  488. return
  489. else:
  490. test_record["TEST-NAME"] += "_bench"
  491. elif flat_args[1] in ("g_benchmark", "y_benchmark"):
  492. benchmark_opts = get_unit_list_variable(unit, 'BENCHMARK_OPTS_VALUE')
  493. test_record['BENCHMARK-OPTS'] = serialize_list(benchmark_opts)
  494. elif flat_args[1] == 'fuzz.test' and unit.get('FUZZING') == 'yes':
  495. test_record['FUZZING'] = '1'
  496. # use all cores if fuzzing requested
  497. test_record['REQUIREMENTS'] = serialize_list(
  498. filter(None, deserialize_list(test_record['REQUIREMENTS']) + ["cpu:all", "ram:all"])
  499. )
  500. data = dump_test(unit, test_record)
  501. if data:
  502. unit.set_property(["DART_DATA", data])
  503. def java_srcdirs_to_data(unit, var):
  504. extra_data = []
  505. for srcdir in (unit.get(var) or '').replace('$' + var, '').split():
  506. if srcdir == '.':
  507. srcdir = unit.get('MODDIR')
  508. if srcdir.startswith('${ARCADIA_ROOT}/') or srcdir.startswith('$ARCADIA_ROOT/'):
  509. srcdir = srcdir.replace('${ARCADIA_ROOT}/', '$S/')
  510. srcdir = srcdir.replace('$ARCADIA_ROOT/', '$S/')
  511. if srcdir.startswith('${CURDIR}') or srcdir.startswith('$CURDIR'):
  512. srcdir = srcdir.replace('${CURDIR}', os.path.join('$S', unit.get('MODDIR')))
  513. srcdir = srcdir.replace('$CURDIR', os.path.join('$S', unit.get('MODDIR')))
  514. srcdir = unit.resolve_arc_path(srcdir)
  515. if not srcdir.startswith('$'):
  516. srcdir = os.path.join('$S', unit.get('MODDIR'), srcdir)
  517. if srcdir.startswith('$S'):
  518. extra_data.append(srcdir.replace('$S', 'arcadia'))
  519. return serialize_list(extra_data)
  520. def onadd_check(unit, *args):
  521. if unit.get("TIDY") == "yes":
  522. # graph changed for clang_tidy tests
  523. return
  524. flat_args, spec_args = _common.sort_by_keywords(
  525. {
  526. "DEPENDS": -1,
  527. "TIMEOUT": 1,
  528. "DATA": -1,
  529. "TAG": -1,
  530. "REQUIREMENTS": -1,
  531. "FORK_MODE": 1,
  532. "SPLIT_FACTOR": 1,
  533. "FORK_SUBTESTS": 0,
  534. "FORK_TESTS": 0,
  535. "SIZE": 1,
  536. },
  537. args,
  538. )
  539. check_type = flat_args[0]
  540. if check_type in ("check.data", "check.resource") and unit.get('VALIDATE_DATA') == "no":
  541. return
  542. if check_type == "check.external" and (len(flat_args) == 1 or not flat_args[1]):
  543. return
  544. test_dir = _common.get_norm_unit_path(unit)
  545. test_timeout = ''
  546. fork_mode = ''
  547. extra_test_data = ''
  548. extra_test_dart_data = {}
  549. ymake_java_test = unit.get('YMAKE_JAVA_TEST') == 'yes'
  550. use_arcadia_python = unit.get('USE_ARCADIA_PYTHON')
  551. uid_ext = ''
  552. script_rel_path = check_type
  553. test_files = flat_args[1:]
  554. if check_type in ["check.data", "check.resource"]:
  555. uid_ext = unit.get("SBR_UID_EXT").split(" ", 1)[-1] # strip variable name
  556. if check_type in ["flake8.py2", "flake8.py3", "black"]:
  557. fork_mode = unit.get('TEST_FORK_MODE') or ''
  558. elif check_type == "ktlint":
  559. test_timeout = '120'
  560. if unit.get('_USE_KTLINT_OLD') == 'yes':
  561. extra_test_data = serialize_list([KTLINT_OLD_EDITOR_CONFIG])
  562. extra_test_dart_data['KTLINT_BINARY'] = '$(KTLINT_OLD)/run.bat'
  563. extra_test_dart_data['USE_KTLINT_OLD'] = 'yes'
  564. else:
  565. data_list = [KTLINT_CURRENT_EDITOR_CONFIG]
  566. baseline_path_relative = unit.get('_KTLINT_BASELINE_FILE')
  567. if baseline_path_relative:
  568. baseline_path = unit.resolve_arc_path(baseline_path_relative).replace('$S', 'arcadia')
  569. data_list += [baseline_path]
  570. extra_test_dart_data['KTLINT_BASELINE_FILE'] = baseline_path_relative
  571. extra_test_data = serialize_list(data_list)
  572. extra_test_dart_data['KTLINT_BINARY'] = '$(KTLINT)/run.bat'
  573. elif check_type == "JAVA_STYLE":
  574. if ymake_java_test and not unit.get('ALL_SRCDIRS'):
  575. return
  576. if len(flat_args) < 2:
  577. raise Exception("Not enough arguments for JAVA_STYLE check")
  578. check_level = flat_args[1]
  579. allowed_levels = {
  580. 'base': '/yandex_checks.xml',
  581. 'strict': '/yandex_checks_strict.xml',
  582. 'extended': '/yandex_checks_extended.xml',
  583. 'library': '/yandex_checks_library.xml',
  584. }
  585. if check_level not in allowed_levels:
  586. raise Exception("'{}' is not allowed in LINT(), use one of {}".format(check_level, allowed_levels.keys()))
  587. test_files[0] = allowed_levels[check_level] # replace check_level with path to config file
  588. script_rel_path = "java.style"
  589. test_timeout = '240'
  590. fork_mode = unit.get('TEST_FORK_MODE') or ''
  591. if ymake_java_test:
  592. extra_test_data = java_srcdirs_to_data(unit, 'ALL_SRCDIRS')
  593. # jstyle should use the latest jdk
  594. unit.onpeerdir([unit.get('JDK_LATEST_PEERDIR')])
  595. extra_test_dart_data['JDK_LATEST_VERSION'] = unit.get('JDK_LATEST_VERSION')
  596. # TODO remove when ya-bin will be released (https://st.yandex-team.ru/DEVTOOLS-9611)
  597. extra_test_dart_data['JDK_RESOURCE'] = 'JDK' + (
  598. unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION') or '_DEFAULT'
  599. )
  600. elif check_type == "gofmt":
  601. if test_files:
  602. test_dir = os.path.dirname(test_files[0]).lstrip("$S/")
  603. elif check_type == "check.data":
  604. data_re = re.compile(r"sbr:/?/?(\d+)=?.*")
  605. data = flat_args[1:]
  606. resources = []
  607. for f in data:
  608. matched = re.match(data_re, f)
  609. if matched:
  610. resources.append(matched.group(1))
  611. if resources:
  612. test_files = resources
  613. else:
  614. return
  615. serialized_test_files = serialize_list(test_files)
  616. test_record = {
  617. 'TEST-NAME': check_type.lower(),
  618. 'TEST-TIMEOUT': test_timeout,
  619. 'SCRIPT-REL-PATH': script_rel_path,
  620. 'TESTED-PROJECT-NAME': os.path.basename(test_dir),
  621. 'SOURCE-FOLDER-PATH': test_dir,
  622. 'CUSTOM-DEPENDENCIES': " ".join(spec_args.get('DEPENDS', [])),
  623. 'TEST-DATA': extra_test_data,
  624. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  625. 'SBR-UID-EXT': uid_ext,
  626. 'SPLIT-FACTOR': '',
  627. 'TEST_PARTITION': 'SEQUENTIAL',
  628. 'FORK-MODE': fork_mode,
  629. 'FORK-TEST-FILES': '',
  630. 'SIZE': 'SMALL',
  631. 'TAG': '',
  632. 'REQUIREMENTS': " ".join(spec_args.get('REQUIREMENTS', [])),
  633. 'USE_ARCADIA_PYTHON': use_arcadia_python or '',
  634. 'OLD_PYTEST': 'no',
  635. 'PYTHON-PATHS': '',
  636. # TODO remove FILES, see DEVTOOLS-7052
  637. 'FILES': serialized_test_files,
  638. 'TEST-FILES': serialized_test_files,
  639. }
  640. test_record.update(extra_test_dart_data)
  641. data = dump_test(unit, test_record)
  642. if data:
  643. unit.set_property(["DART_DATA", data])
  644. def on_register_no_check_imports(unit):
  645. s = unit.get('NO_CHECK_IMPORTS_FOR_VALUE')
  646. if s not in ('', 'None'):
  647. unit.onresource(['-', 'py/no_check_imports/{}="{}"'.format(_common.pathid(s), s)])
  648. def onadd_check_py_imports(unit, *args):
  649. if unit.get("TIDY") == "yes":
  650. # graph changed for clang_tidy tests
  651. return
  652. if unit.get('NO_CHECK_IMPORTS_FOR_VALUE').strip() == "":
  653. return
  654. unit.onpeerdir(['library/python/testing/import_test'])
  655. check_type = "py.imports"
  656. test_dir = _common.get_norm_unit_path(unit)
  657. use_arcadia_python = unit.get('USE_ARCADIA_PYTHON')
  658. test_files = serialize_list([_common.get_norm_unit_path(unit, unit.filename())])
  659. test_record = {
  660. 'TEST-NAME': "pyimports",
  661. 'TEST-TIMEOUT': '',
  662. 'SCRIPT-REL-PATH': check_type,
  663. 'TESTED-PROJECT-NAME': os.path.basename(test_dir),
  664. 'SOURCE-FOLDER-PATH': test_dir,
  665. 'CUSTOM-DEPENDENCIES': '',
  666. 'TEST-DATA': '',
  667. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  668. 'SPLIT-FACTOR': '',
  669. 'TEST_PARTITION': 'SEQUENTIAL',
  670. 'FORK-MODE': '',
  671. 'FORK-TEST-FILES': '',
  672. 'SIZE': 'SMALL',
  673. 'TAG': '',
  674. 'USE_ARCADIA_PYTHON': use_arcadia_python or '',
  675. 'OLD_PYTEST': 'no',
  676. 'PYTHON-PATHS': '',
  677. # TODO remove FILES, see DEVTOOLS-7052
  678. 'FILES': test_files,
  679. 'TEST-FILES': test_files,
  680. }
  681. if unit.get('NO_CHECK_IMPORTS_FOR_VALUE') != "None":
  682. test_record["NO-CHECK"] = serialize_list(get_values_list(unit, 'NO_CHECK_IMPORTS_FOR_VALUE') or ["*"])
  683. else:
  684. test_record["NO-CHECK"] = ''
  685. data = dump_test(unit, test_record)
  686. if data:
  687. unit.set_property(["DART_DATA", data])
  688. def onadd_pytest_script(unit, *args):
  689. if unit.get("TIDY") == "yes":
  690. # graph changed for clang_tidy tests
  691. return
  692. unit.set(["PYTEST_BIN", "no"])
  693. custom_deps = get_values_list(unit, 'TEST_DEPENDS_VALUE')
  694. timeout = list(filter(None, [unit.get(["TEST_TIMEOUT"])]))
  695. if unit.get('ADD_SRCDIR_TO_TEST_DATA') == "yes":
  696. unit.ondata_files(_common.get_norm_unit_path(unit))
  697. if timeout:
  698. timeout = timeout[0]
  699. else:
  700. timeout = '0'
  701. test_type = args[0]
  702. fork_mode = unit.get('TEST_FORK_MODE').split() or ''
  703. split_factor = unit.get('TEST_SPLIT_FACTOR') or ''
  704. test_size = unit.get('TEST_SIZE_NAME') or ''
  705. test_files = get_values_list(unit, 'TEST_SRCS_VALUE')
  706. tags = _get_test_tags(unit)
  707. requirements = get_values_list(unit, 'TEST_REQUIREMENTS_VALUE')
  708. test_data = get_norm_paths(unit, 'TEST_DATA_VALUE')
  709. data, data_files = get_canonical_test_resources(unit)
  710. test_data += data
  711. python_paths = get_values_list(unit, 'TEST_PYTHON_PATH_VALUE')
  712. binary_path = os.path.join(_common.get_norm_unit_path(unit), unit.filename())
  713. test_cwd = unit.get('TEST_CWD_VALUE') or ''
  714. _dump_test(
  715. unit,
  716. test_type,
  717. test_files,
  718. timeout,
  719. _common.get_norm_unit_path(unit),
  720. custom_deps,
  721. test_data,
  722. python_paths,
  723. split_factor,
  724. fork_mode,
  725. test_size,
  726. tags,
  727. requirements,
  728. binary_path,
  729. test_cwd=test_cwd,
  730. data_files=data_files,
  731. )
  732. def onadd_pytest_bin(unit, *args):
  733. if unit.get("TIDY") == "yes":
  734. # graph changed for clang_tidy tests
  735. return
  736. flat, kws = _common.sort_by_keywords({'RUNNER_BIN': 1}, args)
  737. if flat:
  738. ymake.report_configure_error('Unknown arguments found while processing add_pytest_bin macro: {!r}'.format(flat))
  739. runner_bin = kws.get('RUNNER_BIN', [None])[0]
  740. test_type = 'py3test.bin' if (unit.get("PYTHON3") == 'yes') else "pytest.bin"
  741. add_test_to_dart(unit, test_type, runner_bin=runner_bin)
  742. def add_test_to_dart(unit, test_type, binary_path=None, runner_bin=None):
  743. if unit.get("TIDY") == "yes":
  744. # graph changed for clang_tidy tests
  745. return
  746. if unit.get('ADD_SRCDIR_TO_TEST_DATA') == "yes":
  747. unit.ondata_files(_common.get_norm_unit_path(unit))
  748. custom_deps = get_values_list(unit, 'TEST_DEPENDS_VALUE')
  749. timeout = list(filter(None, [unit.get(["TEST_TIMEOUT"])]))
  750. if timeout:
  751. timeout = timeout[0]
  752. else:
  753. timeout = '0'
  754. fork_mode = unit.get('TEST_FORK_MODE').split() or ''
  755. split_factor = unit.get('TEST_SPLIT_FACTOR') or ''
  756. test_size = unit.get('TEST_SIZE_NAME') or ''
  757. test_cwd = unit.get('TEST_CWD_VALUE') or ''
  758. yt_spec = get_values_list(unit, 'TEST_YT_SPEC_VALUE')
  759. unit.ondata_files(yt_spec)
  760. unit_path = unit.path()
  761. test_files = get_values_list(unit, 'TEST_SRCS_VALUE')
  762. tags = _get_test_tags(unit)
  763. requirements = get_values_list(unit, 'TEST_REQUIREMENTS_VALUE')
  764. test_data = get_norm_paths(unit, 'TEST_DATA_VALUE')
  765. data, data_files = get_canonical_test_resources(unit)
  766. test_data += data
  767. python_paths = get_values_list(unit, 'TEST_PYTHON_PATH_VALUE')
  768. if not binary_path:
  769. binary_path = os.path.join(unit_path, unit.filename())
  770. _dump_test(
  771. unit,
  772. test_type,
  773. test_files,
  774. timeout,
  775. _common.get_norm_unit_path(unit),
  776. custom_deps,
  777. test_data,
  778. python_paths,
  779. split_factor,
  780. fork_mode,
  781. test_size,
  782. tags,
  783. requirements,
  784. binary_path,
  785. test_cwd=test_cwd,
  786. runner_bin=runner_bin,
  787. yt_spec=yt_spec,
  788. data_files=data_files,
  789. )
  790. def extract_java_system_properties(unit, args):
  791. if len(args) % 2:
  792. return [], 'Wrong use of SYSTEM_PROPERTIES in {}: odd number of arguments'.format(unit.path())
  793. props = []
  794. for x, y in zip(args[::2], args[1::2]):
  795. if x == 'FILE':
  796. if y.startswith('${BINDIR}') or y.startswith('${ARCADIA_BUILD_ROOT}') or y.startswith('/'):
  797. return [], 'Wrong use of SYSTEM_PROPERTIES in {}: absolute/build file path {}'.format(unit.path(), y)
  798. y = _common.rootrel_arc_src(y, unit)
  799. if not os.path.exists(unit.resolve('$S/' + y)):
  800. return [], 'Wrong use of SYSTEM_PROPERTIES in {}: can\'t resolve {}'.format(unit.path(), y)
  801. y = '${ARCADIA_ROOT}/' + y
  802. props.append({'type': 'file', 'path': y})
  803. else:
  804. props.append({'type': 'inline', 'key': x, 'value': y})
  805. return props, None
  806. def onjava_test(unit, *args):
  807. if unit.get("TIDY") == "yes":
  808. # graph changed for clang_tidy tests
  809. return
  810. assert unit.get('MODULE_TYPE') is not None
  811. if unit.get('MODULE_TYPE') == 'JTEST_FOR':
  812. if not unit.get('UNITTEST_DIR'):
  813. ymake.report_configure_error('skip JTEST_FOR in {}: no args provided'.format(unit.path()))
  814. return
  815. java_cp_arg_type = unit.get('JAVA_CLASSPATH_CMD_TYPE_VALUE') or 'MANIFEST'
  816. if java_cp_arg_type not in ('MANIFEST', 'COMMAND_FILE', 'LIST'):
  817. ymake.report_configure_error(
  818. '{}: TEST_JAVA_CLASSPATH_CMD_TYPE({}) are invalid. Choose argument from MANIFEST, COMMAND_FILE or LIST)'.format(
  819. unit.path(), java_cp_arg_type
  820. )
  821. )
  822. return
  823. unit_path = unit.path()
  824. path = _common.strip_roots(unit_path)
  825. if unit.get('ADD_SRCDIR_TO_TEST_DATA') == "yes":
  826. unit.ondata_files(_common.get_norm_unit_path(unit))
  827. yt_spec_values = get_unit_list_variable(unit, 'TEST_YT_SPEC_VALUE')
  828. unit.ondata_files(yt_spec_values)
  829. test_data = get_norm_paths(unit, 'TEST_DATA_VALUE')
  830. test_data.append('arcadia/build/scripts/run_junit.py')
  831. test_data.append('arcadia/build/scripts/unpacking_jtest_runner.py')
  832. data, data_files = get_canonical_test_resources(unit)
  833. test_data += data
  834. props, error_mgs = extract_java_system_properties(unit, get_values_list(unit, 'SYSTEM_PROPERTIES_VALUE'))
  835. if error_mgs:
  836. ymake.report_configure_error(error_mgs)
  837. return
  838. for prop in props:
  839. if prop['type'] == 'file':
  840. test_data.append(prop['path'].replace('${ARCADIA_ROOT}', 'arcadia'))
  841. props = base64.b64encode(six.ensure_binary(json.dumps(props)))
  842. test_cwd = unit.get('TEST_CWD_VALUE') or '' # TODO: validate test_cwd value
  843. if unit.get('MODULE_TYPE') == 'JUNIT5':
  844. script_rel_path = 'junit5.test'
  845. else:
  846. script_rel_path = 'junit.test'
  847. ymake_java_test = unit.get('YMAKE_JAVA_TEST') == 'yes'
  848. test_record = {
  849. 'SOURCE-FOLDER-PATH': path,
  850. 'TEST-NAME': '-'.join([os.path.basename(os.path.dirname(path)), os.path.basename(path)]),
  851. 'SCRIPT-REL-PATH': script_rel_path,
  852. 'TEST-TIMEOUT': unit.get('TEST_TIMEOUT') or '',
  853. 'TESTED-PROJECT-NAME': path,
  854. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  855. # 'TEST-PRESERVE-ENV': 'da',
  856. 'TEST-DATA': serialize_list(sorted(_common.filter_out_by_keyword(test_data, 'AUTOUPDATED'))),
  857. 'FORK-MODE': unit.get('TEST_FORK_MODE') or '',
  858. 'SPLIT-FACTOR': unit.get('TEST_SPLIT_FACTOR') or '',
  859. 'CUSTOM-DEPENDENCIES': ' '.join(get_values_list(unit, 'TEST_DEPENDS_VALUE')),
  860. 'TAG': serialize_list(sorted(_get_test_tags(unit))),
  861. 'SIZE': unit.get('TEST_SIZE_NAME') or '',
  862. 'REQUIREMENTS': serialize_list(get_values_list(unit, 'TEST_REQUIREMENTS_VALUE')),
  863. 'TEST-RECIPES': prepare_recipes(unit.get("TEST_RECIPES_VALUE")),
  864. # JTEST/JTEST_FOR only
  865. 'MODULE_TYPE': unit.get('MODULE_TYPE'),
  866. 'UNITTEST_DIR': unit.get('UNITTEST_DIR') or '',
  867. 'JVM_ARGS': serialize_list(get_values_list(unit, 'JVM_ARGS_VALUE')),
  868. 'SYSTEM_PROPERTIES': props,
  869. 'TEST-CWD': test_cwd,
  870. 'SKIP_TEST': unit.get('SKIP_TEST_VALUE') or '',
  871. 'JAVA_CLASSPATH_CMD_TYPE': java_cp_arg_type,
  872. 'JDK_RESOURCE': 'JDK' + (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION') or '_DEFAULT'),
  873. 'JDK_FOR_TESTS': 'JDK' + (unit.get('JDK_VERSION') or unit.get('JDK_REAL_VERSION') or '_DEFAULT') + '_FOR_TESTS',
  874. 'YT-SPEC': serialize_list(yt_spec_values),
  875. }
  876. test_classpath_origins = unit.get('TEST_CLASSPATH_VALUE')
  877. if test_classpath_origins:
  878. test_record['TEST_CLASSPATH_ORIGINS'] = test_classpath_origins
  879. test_record['TEST_CLASSPATH'] = '${TEST_CLASSPATH_MANAGED}'
  880. elif ymake_java_test:
  881. test_record['TEST_CLASSPATH'] = '${DART_CLASSPATH}'
  882. test_record['TEST_CLASSPATH_DEPS'] = '${DART_CLASSPATH_DEPS}'
  883. if unit.get('UNITTEST_DIR'):
  884. test_record['TEST_JAR'] = '${UNITTEST_MOD}'
  885. else:
  886. test_record['TEST_JAR'] = '{}/{}.jar'.format(unit.get('MODDIR'), unit.get('REALPRJNAME'))
  887. data = dump_test(unit, test_record)
  888. if data:
  889. unit.set_property(['DART_DATA', data])
  890. def onjava_test_deps(unit, *args):
  891. if unit.get("TIDY") == "yes":
  892. # graph changed for clang_tidy tests
  893. return
  894. assert unit.get('MODULE_TYPE') is not None
  895. assert len(args) == 1
  896. mode = args[0]
  897. path = _common.get_norm_unit_path(unit)
  898. ymake_java_test = unit.get('YMAKE_JAVA_TEST') == 'yes'
  899. test_record = {
  900. 'SOURCE-FOLDER-PATH': path,
  901. 'TEST-NAME': '-'.join([os.path.basename(os.path.dirname(path)), os.path.basename(path), 'dependencies']).strip(
  902. '-'
  903. ),
  904. 'SCRIPT-REL-PATH': 'java.dependency.test',
  905. 'TEST-TIMEOUT': '',
  906. 'TESTED-PROJECT-NAME': path,
  907. 'TEST-DATA': '',
  908. 'TEST_PARTITION': 'SEQUENTIAL',
  909. 'FORK-MODE': '',
  910. 'SPLIT-FACTOR': '',
  911. 'CUSTOM-DEPENDENCIES': ' '.join(get_values_list(unit, 'TEST_DEPENDS_VALUE')),
  912. 'TAG': '',
  913. 'SIZE': 'SMALL',
  914. 'IGNORE_CLASSPATH_CLASH': ' '.join(get_values_list(unit, 'JAVA_IGNORE_CLASSPATH_CLASH_VALUE')),
  915. # JTEST/JTEST_FOR only
  916. 'MODULE_TYPE': unit.get('MODULE_TYPE'),
  917. 'UNITTEST_DIR': '',
  918. 'SYSTEM_PROPERTIES': '',
  919. 'TEST-CWD': '',
  920. }
  921. if mode == 'strict':
  922. test_record['STRICT_CLASSPATH_CLASH'] = 'yes'
  923. if ymake_java_test:
  924. test_record['CLASSPATH'] = '$B/{}/{}.jar ${{DART_CLASSPATH}}'.format(
  925. unit.get('MODDIR'), unit.get('REALPRJNAME')
  926. )
  927. data = dump_test(unit, test_record)
  928. unit.set_property(['DART_DATA', data])
  929. def _get_test_tags(unit, spec_args=None):
  930. if spec_args is None:
  931. spec_args = {}
  932. tags = spec_args.get('TAG', []) + get_values_list(unit, 'TEST_TAGS_VALUE')
  933. tags = set(tags)
  934. if unit.get('EXPORT_SEM') == 'yes':
  935. filter_only_tags = sorted(t for t in tags if ':' not in t)
  936. unit.set(['FILTER_ONLY_TEST_TAGS', ' '.join(filter_only_tags)])
  937. # DEVTOOLS-7571
  938. if unit.get('SKIP_TEST_VALUE') and consts.YaTestTags.Fat in tags:
  939. tags.add(consts.YaTestTags.NotAutocheck)
  940. return tags
  941. def _dump_test(
  942. unit,
  943. test_type,
  944. test_files,
  945. timeout,
  946. test_dir,
  947. custom_deps,
  948. test_data,
  949. python_paths,
  950. split_factor,
  951. fork_mode,
  952. test_size,
  953. tags,
  954. requirements,
  955. binary_path='',
  956. old_pytest=False,
  957. test_cwd=None,
  958. runner_bin=None,
  959. yt_spec=None,
  960. data_files=None,
  961. ):
  962. if test_type == "PY_TEST":
  963. script_rel_path = "py.test"
  964. else:
  965. script_rel_path = test_type
  966. unit_path = unit.path()
  967. fork_test_files = unit.get('FORK_TEST_FILES_MODE')
  968. fork_mode = ' '.join(fork_mode) if fork_mode else ''
  969. use_arcadia_python = unit.get('USE_ARCADIA_PYTHON')
  970. if test_cwd:
  971. test_cwd = test_cwd.replace("$TEST_CWD_VALUE", "").replace('"MACRO_CALLS_DELIM"', "").strip()
  972. test_name = os.path.basename(binary_path)
  973. test_record = {
  974. 'TEST-NAME': os.path.splitext(test_name)[0],
  975. 'TEST-TIMEOUT': timeout,
  976. 'SCRIPT-REL-PATH': script_rel_path,
  977. 'TESTED-PROJECT-NAME': test_name,
  978. 'SOURCE-FOLDER-PATH': test_dir,
  979. 'CUSTOM-DEPENDENCIES': " ".join(custom_deps),
  980. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  981. # 'TEST-PRESERVE-ENV': 'da',
  982. 'TEST-DATA': serialize_list(sorted(_common.filter_out_by_keyword(test_data, 'AUTOUPDATED'))),
  983. 'TEST-RECIPES': prepare_recipes(unit.get("TEST_RECIPES_VALUE")),
  984. 'SPLIT-FACTOR': split_factor,
  985. 'TEST_PARTITION': unit.get('TEST_PARTITION') or 'SEQUENTIAL',
  986. 'FORK-MODE': fork_mode,
  987. 'FORK-TEST-FILES': fork_test_files,
  988. 'TEST-FILES': serialize_list(test_files),
  989. 'SIZE': test_size,
  990. 'TAG': serialize_list(sorted(tags)),
  991. 'REQUIREMENTS': serialize_list(requirements),
  992. 'USE_ARCADIA_PYTHON': use_arcadia_python or '',
  993. 'OLD_PYTEST': 'yes' if old_pytest else 'no',
  994. 'PYTHON-PATHS': serialize_list(python_paths),
  995. 'TEST-CWD': test_cwd or '',
  996. 'SKIP_TEST': unit.get('SKIP_TEST_VALUE') or '',
  997. 'BUILD-FOLDER-PATH': _common.strip_roots(unit_path),
  998. 'BLOB': unit.get('TEST_BLOB_DATA') or '',
  999. 'CANONIZE_SUB_PATH': unit.get('CANONIZE_SUB_PATH') or '',
  1000. }
  1001. if binary_path:
  1002. test_record['BINARY-PATH'] = _common.strip_roots(binary_path)
  1003. if runner_bin:
  1004. test_record['TEST-RUNNER-BIN'] = runner_bin
  1005. if yt_spec:
  1006. test_record['YT-SPEC'] = serialize_list(yt_spec)
  1007. data = dump_test(unit, test_record)
  1008. if data:
  1009. unit.set_property(["DART_DATA", data])
  1010. def onsetup_pytest_bin(unit, *args):
  1011. use_arcadia_python = unit.get('USE_ARCADIA_PYTHON') == "yes"
  1012. if use_arcadia_python:
  1013. unit.onresource(['-', 'PY_MAIN={}'.format("library.python.pytest.main:main")]) # XXX
  1014. unit.onadd_pytest_bin(list(args))
  1015. else:
  1016. unit.onno_platform()
  1017. unit.onadd_pytest_script(["PY_TEST"])
  1018. def onrun(unit, *args):
  1019. exectest_cmd = unit.get(["EXECTEST_COMMAND_VALUE"]) or ''
  1020. exectest_cmd += "\n" + subprocess.list2cmdline(args)
  1021. unit.set(["EXECTEST_COMMAND_VALUE", exectest_cmd])
  1022. def onsetup_exectest(unit, *args):
  1023. command = unit.get(["EXECTEST_COMMAND_VALUE"])
  1024. if command is None:
  1025. ymake.report_configure_error("EXECTEST must have at least one RUN macro")
  1026. return
  1027. command = command.replace("$EXECTEST_COMMAND_VALUE", "")
  1028. if "PYTHON_BIN" in command:
  1029. unit.ondepends('contrib/tools/python')
  1030. unit.set(["TEST_BLOB_DATA", base64.b64encode(six.ensure_binary(command))])
  1031. add_test_to_dart(unit, "exectest", binary_path=os.path.join(unit.path(), unit.filename()).replace(".pkg", ""))
  1032. def onsetup_run_python(unit):
  1033. if unit.get("USE_ARCADIA_PYTHON") == "yes":
  1034. unit.ondepends('contrib/tools/python')
  1035. def get_canonical_test_resources(unit):
  1036. unit_path = unit.path()
  1037. if unit.get("CUSTOM_CANONDATA_PATH"):
  1038. path_to_canondata = unit_path.replace("$S", unit.get("CUSTOM_CANONDATA_PATH"))
  1039. else:
  1040. path_to_canondata = unit.resolve(unit_path)
  1041. canon_data_dir = os.path.join(path_to_canondata, CANON_DATA_DIR_NAME, unit.get('CANONIZE_SUB_PATH') or '')
  1042. try:
  1043. _, dirs, files = next(os.walk(canon_data_dir))
  1044. except StopIteration:
  1045. # path doesn't exist
  1046. return [], []
  1047. if CANON_RESULT_FILE_NAME in files:
  1048. return _get_canonical_data_resources_v2(os.path.join(canon_data_dir, CANON_RESULT_FILE_NAME), unit_path)
  1049. return [], []
  1050. def _load_canonical_file(filename, unit_path):
  1051. try:
  1052. with open(filename, 'rb') as results_file:
  1053. return json.load(results_file)
  1054. except Exception as e:
  1055. print("malformed canonical data in {}: {} ({})".format(unit_path, e, filename), file=sys.stderr)
  1056. return {}
  1057. def _get_resource_from_uri(uri):
  1058. m = consts.CANON_MDS_RESOURCE_REGEX.match(uri)
  1059. if m:
  1060. key = m.group(1)
  1061. return "{}:{}".format(consts.MDS_SCHEME, key)
  1062. m = consts.CANON_BACKEND_RESOURCE_REGEX.match(uri)
  1063. if m:
  1064. key = m.group(1)
  1065. return "{}:{}".format(consts.MDS_SCHEME, key)
  1066. m = consts.CANON_SBR_RESOURCE_REGEX.match(uri)
  1067. if m:
  1068. # There might be conflict between resources, because all resources in sandbox have 'resource.tar.gz' name
  1069. # That's why we use notation with '=' to specify specific path for resource
  1070. uri = m.group(1)
  1071. res_id = m.group(2)
  1072. return "{}={}".format(uri, '/'.join([CANON_OUTPUT_STORAGE, res_id]))
  1073. def _get_external_resources_from_canon_data(data):
  1074. # Method should work with both canonization versions:
  1075. # result.json: {'uri':X 'checksum':Y}
  1076. # result.json: {'testname': {'uri':X 'checksum':Y}}
  1077. # result.json: {'testname': [{'uri':X 'checksum':Y}]}
  1078. # Also there is a bug - if user returns {'uri': 1} from test - machinery will fail
  1079. # That's why we check 'uri' and 'checksum' fields presence
  1080. # (it's still a bug - user can return {'uri':X, 'checksum': Y}, we need to unify canonization format)
  1081. res = set()
  1082. if isinstance(data, dict):
  1083. if 'uri' in data and 'checksum' in data:
  1084. resource = _get_resource_from_uri(data['uri'])
  1085. if resource:
  1086. res.add(resource)
  1087. else:
  1088. for k, v in six.iteritems(data):
  1089. res.update(_get_external_resources_from_canon_data(v))
  1090. elif isinstance(data, list):
  1091. for e in data:
  1092. res.update(_get_external_resources_from_canon_data(e))
  1093. return res
  1094. def _get_canonical_data_resources_v2(filename, unit_path):
  1095. return (_get_external_resources_from_canon_data(_load_canonical_file(filename, unit_path)), [filename])
  1096. def on_add_linter_check(unit, *args):
  1097. if unit.get("TIDY") == "yes":
  1098. return
  1099. source_root_from_prefix = '${ARCADIA_ROOT}/'
  1100. source_root_to_prefix = '$S/'
  1101. unlimited = -1
  1102. no_lint_value = _common.get_no_lint_value(unit)
  1103. if no_lint_value in ("none", "none_internal"):
  1104. return
  1105. keywords = {
  1106. "DEPENDS": unlimited,
  1107. "FILES": unlimited,
  1108. "CONFIGS": unlimited,
  1109. "GLOBAL_RESOURCES": unlimited,
  1110. "FILE_PROCESSING_TIME": 1,
  1111. "EXTRA_PARAMS": unlimited,
  1112. }
  1113. flat_args, spec_args = _common.sort_by_keywords(keywords, args)
  1114. if len(flat_args) != 2:
  1115. unit.message(['ERROR', '_ADD_LINTER_CHECK params: expected 2 free parameters'])
  1116. return
  1117. configs = []
  1118. for cfg in spec_args.get('CONFIGS', []):
  1119. filename = unit.resolve(source_root_to_prefix + cfg)
  1120. if not os.path.exists(filename):
  1121. unit.message(['ERROR', 'Configuration file {} is not found'.format(filename)])
  1122. return
  1123. configs.append(cfg)
  1124. deps = []
  1125. lint_name, linter = flat_args
  1126. deps.append(os.path.dirname(linter))
  1127. test_files = []
  1128. for path in spec_args.get('FILES', []):
  1129. if path.startswith(source_root_from_prefix):
  1130. test_files.append(path.replace(source_root_from_prefix, source_root_to_prefix, 1))
  1131. elif path.startswith(source_root_to_prefix):
  1132. test_files.append(path)
  1133. if not test_files:
  1134. unit.message(['WARN', 'No files to lint for {}'.format(lint_name)])
  1135. return
  1136. for arg in spec_args.get('EXTRA_PARAMS', []):
  1137. if '=' not in arg:
  1138. unit.message(['WARN', 'Wrong EXTRA_PARAMS value: "{}". Values must have format "name=value".'.format(arg)])
  1139. return
  1140. deps += spec_args.get('DEPENDS', [])
  1141. for dep in deps:
  1142. unit.ondepends(dep)
  1143. for resource in spec_args.get('GLOBAL_RESOURCES', []):
  1144. unit.onpeerdir(resource)
  1145. test_record = {
  1146. 'TEST-NAME': lint_name,
  1147. 'SCRIPT-REL-PATH': 'custom_lint',
  1148. 'TESTED-PROJECT-NAME': unit.name(),
  1149. 'SOURCE-FOLDER-PATH': _common.get_norm_unit_path(unit),
  1150. 'CUSTOM-DEPENDENCIES': " ".join(deps),
  1151. 'TEST-DATA': '',
  1152. 'TEST-ENV': prepare_env(unit.get("TEST_ENV_VALUE")),
  1153. 'TEST-TIMEOUT': '',
  1154. 'SPLIT-FACTOR': '',
  1155. 'TEST_PARTITION': 'SEQUENTIAL',
  1156. 'FORK-MODE': '',
  1157. 'FORK-TEST-FILES': '',
  1158. 'SIZE': 'SMALL',
  1159. 'TAG': '',
  1160. 'USE_ARCADIA_PYTHON': unit.get('USE_ARCADIA_PYTHON') or '',
  1161. 'OLD_PYTEST': 'no',
  1162. 'PYTHON-PATHS': '',
  1163. # TODO remove FILES, see DEVTOOLS-7052
  1164. 'FILES': serialize_list(test_files),
  1165. 'TEST-FILES': serialize_list(test_files),
  1166. # Linter specific parameters
  1167. # TODO Add configs to DATA. See YMAKE-427
  1168. 'LINT-CONFIGS': serialize_list(configs),
  1169. 'LINT-NAME': lint_name,
  1170. 'LINT-FILE-PROCESSING-TIME': spec_args.get('FILE_PROCESSING_TIME', [''])[0],
  1171. 'LINT-EXTRA-PARAMS': serialize_list(spec_args.get('EXTRA_PARAMS', [])),
  1172. 'LINTER': linter,
  1173. }
  1174. data = dump_test(unit, test_record)
  1175. if data:
  1176. unit.set_property(["DART_DATA", data])