ytest.py 50 KB

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