def validate_force_sandbox_requirement(name, value, test_size, is_force_sandbox, in_autocheck, is_fuzzing, is_kvm, check_func): if is_force_sandbox or not in_autocheck or is_fuzzing: if value == 'all': return return validate_numerical_requirement(name, value) error_msg = validate_numerical_requirement(name, value) if error_msg: return error_msg return check_func(mr.resolve_value(value), test_size, is_kvm)
def validate_numerical_requirement(name, value): if mr.resolve_value(value) is None: return "Cannot convert [[imp]]{}[[rst]] to the proper [[imp]]{}[[rst]] requirement value".format(value, name)
def validate_test(kw, is_fuzz_test): def get_list(key): return deserialize_list(kw.get(key, "")) valid_kw = copy.deepcopy(kw) errors = [] has_fatal_error = False if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith( ("mail", "maps", "metrika", "devtools")): errors.append("BOOSTTEST is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'ytest.py': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith( "yweb/antispam") and not project_path.startswith("devtools"): errors.append("FLEUR test is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'gtest': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("mail", "devtools")): errors.append("GTEST is not allowed here") has_fatal_error = True size_timeout = collections.OrderedDict( sorted(consts.TestSize.DefaultTimeouts.items(), key=lambda t: t[1])) size = valid_kw.get('SIZE', consts.TestSize.Small).lower() tags = get_list("TAG") is_fat = 'ya:fat' in tags requirements = {} valid_requirements = { 'cpu', 'disk_usage', 'ram', 'ram_disk', 'container', 'sb', 'sb_vault' } for req in get_list("REQUIREMENTS"): if ":" in req: req_name, req_value = req.split(":", 1) if req_name not in valid_requirements: errors.append( "Unknown requirement: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]" .format(req_name, ", ".join(sorted(valid_requirements)))) continue elif req_name in ('disk_usage', 'ram_disk'): if not mr.resolve_value(req_value.lower()): errors.append( "Cannot convert [[imp]]{}[[rst]] to the proper requirement value" .format(req_value)) continue # TODO: Remove this special rules for ram and cpu requirements of FAT-tests elif is_fat and req_name in ('ram', 'cpu'): if req_value.strip() == 'all': pass elif mr.resolve_value(req_value) is None: errors.append( "Cannot convert [[imp]]{}[[rst]]: [[imp]]{}[[rst]] to the proper requirement value" .format(req_name, req_value)) continue elif req_name == 'ram': if req_value.strip() == 'all': pass else: ram_errors = reqs.check_ram(mr.resolve_value(req_value), size) errors += ram_errors if ram_errors: req_value = str( consts.TestSize.get_default_requirements(size).get( consts.TestRequirements.Ram)) elif req_name == 'cpu': if req_value.strip() == 'all' and is_fuzz_test: pass # XXX # errors += reqs.check_cpu(mr.resolve_value(req_value), size) elif reqs.check_cpu(mr.resolve_value(req_value), size): req_value = str( consts.TestSize.get_default_requirements(size).get( consts.TestRequirements.Cpu)) elif req_name == "sb_vault": if not re.match("\w+=(value|file)\:\w+\:\w+", req_value): errors.append( "sb_vault value '{}' should follow pattern <ENV_NAME>=:<value|file>:<owner>:<vault key>" .format(req_value)) continue req_value = ",".join( filter(None, [requirements.get(req_name), req_value])) requirements[req_name] = req_value else: errors.append( "Invalid requirement syntax [[imp]]{}[[rst]]: expect <requirement>:<value>" .format(req)) in_autocheck = "ya:not_autocheck" not in tags and 'ya:manual' not in tags invalid_requirements_for_distbuild = [ requirement for requirement in requirements.keys() if requirement not in ('ram', 'cpu') ] sb_tags = [tag for tag in tags if tag.startswith('sb:')] # XXX remove when the dust settles if 'ya:force_distbuild' in tags: errors.append( "Don't use ya:force_distbuild tag. Distbuild is default build system for autocheck. You can specify only ya:force_sandbox if you want" ) has_fatal_error = True if is_fat: if in_autocheck and 'ya:force_sandbox' not in tags: if invalid_requirements_for_distbuild: errors.append( "'{}' REQUIREMENTS options can be used only for FAT tests with ya:force_sandbox tag. Add TAG(ya:force_sandbox) or remove option." .format(invalid_requirements_for_distbuild)) has_fatal_error = True if sb_tags: errors.append( "You can set sandbox tags '{}' only for FAT tests with ya:force_sandbox. Add TAG(ya:force_sandbox) or remove sandbox tags." .format(sb_tags)) has_fatal_error = True else: if 'ya:force_sandbox' in tags: errors.append('ya:force_sandbox can be used with LARGE tests only') has_fatal_error = True if 'ya:privileged' in tags and 'container' not in requirements: errors.append( "Only tests with 'container' requirement can have 'ya:privileged' tag" ) has_fatal_error = True if 'ya:privileged' in tags and not is_fat: errors.append("Only fat tests can have 'ya:privileged' tag") has_fatal_error = True if size not in size_timeout: errors.append( "Unknown test size: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]" .format(size.upper(), ", ".join([sz.upper() for sz in size_timeout.keys()]))) has_fatal_error = True else: try: timeout = int( valid_kw.get('TEST-TIMEOUT', size_timeout[size]) or size_timeout[size]) script_rel_path = valid_kw.get('SCRIPT-REL-PATH') if timeout < 0: raise Exception("Timeout must be > 0") if size_timeout[ size] < timeout and in_autocheck and script_rel_path != 'java.style': suggested_size = None for s, t in size_timeout.items(): if timeout <= t: suggested_size = s break if suggested_size: suggested_size = ", suggested size: [[imp]]{}[[rst]]".format( suggested_size.upper()) else: suggested_size = "" errors.append( "Max allowed timeout for test size [[imp]]{}[[rst]] is [[imp]]{} sec[[rst]]{}" .format(size.upper(), size_timeout[size], suggested_size)) has_fatal_error = True except Exception as e: errors.append( "Error when parsing test timeout: [[bad]]{}[[rst]]".format(e)) has_fatal_error = True for req in ["container", "disk"]: if req in requirements and not is_fat: errors.append( "Only [[imp]]FAT[[rst]] tests can have [[imp]]{}[[rst]] requirement" .format(req)) has_fatal_error = True if in_autocheck and size == consts.TestSize.Large and not is_fat: errors.append("LARGE test must have ya:fat tag") has_fatal_error = True if is_fat and size != consts.TestSize.Large: errors.append("Only LARGE test may have ya:fat tag") has_fatal_error = True requiremtens_list = [] for req_name, req_value in requirements.iteritems(): requiremtens_list.append(req_name + ":" + req_value) valid_kw['REQUIREMENTS'] = serialize_list(requiremtens_list) if valid_kw.get("FUZZ-OPTS"): for option in get_list("FUZZ-OPTS"): if not option.startswith("-"): errors.append( "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should start with '-'" .format(option)) has_fatal_error = True break eqpos = option.find("=") if eqpos == -1 or len(option) == eqpos + 1: errors.append( "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should obtain value specified after '='" .format(option)) has_fatal_error = True break if option[eqpos - 1] == " " or option[eqpos + 1] == " ": errors.append( "Spaces are not allowed: '[[imp]]{}[[rst]]'".format( option)) has_fatal_error = True break if option[:eqpos] in ("-runs", "-dict", "-jobs", "-workers", "-artifact_prefix", "-print_final_stats"): errors.append( "You can't use '[[imp]]{}[[rst]]' - it will be automatically calculated or configured during run" .format(option)) has_fatal_error = True break if valid_kw.get("USE_ARCADIA_PYTHON") == "yes" and valid_kw.get( "SCRIPT-REL-PATH") == "py.test": errors.append("PYTEST_SCRIPT is deprecated") has_fatal_error = True if valid_kw.get('SPLIT-FACTOR'): if valid_kw.get('FORK-MODE') == 'none': errors.append( 'SPLIT_FACTOR must be use with FORK_TESTS() or FORK_SUBTESTS() macro' ) has_fatal_error = True try: value = int(valid_kw.get('SPLIT-FACTOR')) if value <= 0: raise ValueError("must be > 0") except ValueError as e: errors.append('Incorrect SPLIT_FACTOR value: {}'.format(e)) has_fatal_error = True if has_fatal_error: return None, errors return valid_kw, errors
def validate_test(kw): def get_list(key): return deserialize_list(kw.get(key, "")) valid_kw = copy.deepcopy(kw) errors = [] has_fatal_error = False if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("maps", "metrika", "devtools")): errors.append("BOOSTTEST is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'ytest.py': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith( "yweb/antispam") and not project_path.startswith( "devtools") and not project_path.startswith( "robot/gemini/pygemini/test"): errors.append("FLEUR test is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'gtest': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("mail", "devtools")): errors.append("GTEST is not allowed here") has_fatal_error = True size_timeout = collections.OrderedDict( sorted(consts.TestSize.DefaultTimeouts.items(), key=lambda t: t[1])) size = valid_kw.get('SIZE', consts.TestSize.Small).lower() tags = get_list("TAG") requirements = {} valid_requirements = { 'cpu', 'disk_usage', 'ram', 'ram_disk', 'container', 'sb' } for req in get_list("REQUIREMENTS"): if ":" in req: req_name, req_value = req.split(":", 1) if req_name not in valid_requirements: errors.append( "Unknown requirement: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]" .format(req_name, ", ".join(sorted(valid_requirements)))) continue elif req_name in ('disk_usage', 'ram_disk'): if not mr.resolve_size_metric(req_value.lower()): errors.append( "Cannot convert [[imp]]{}[[rst]] to the proper requirement value" .format(req_value)) continue # TODO: Remove this special rules for ram and cpu requirements of FAT-tests elif size == consts.TestSize.Fat and req_name in ('ram', 'cpu'): if req_name == 'cpu': if req_value.strip() == 'all': pass elif mr.resolve_value(req_value) is None: errors.append( "Cannot convert [[imp]]{}[[rst]] to the proper requirement value" .format(req_value)) continue elif req_name == 'ram': if not mr.resolve_size_metric(req_value.lower()): errors.append( "Cannot convert [[imp]]{}[[rst]] to the proper requirement value" .format(req_value)) continue elif req_name == 'ram': ram_errors = reqs.check_ram(mr.resolve_value(req_value), size) errors += ram_errors if ram_errors: req_value = str( consts.TestSize.get_default_requirements(size).get( consts.TestRequirements.Ram)) elif req_name == 'cpu': # XXX # errors += reqs.check_cpu(mr.resolve_value(req_value), size) if reqs.check_cpu(mr.resolve_value(req_value), size): req_value = str( consts.TestSize.get_default_requirements(size).get( consts.TestRequirements.Cpu)) requirements[req_name] = req_value else: errors.append( "Invalid requirement syntax [[imp]]{}[[rst]]: expect <requirement>:<value>" .format(req)) in_autocheck = "ya:not_autocheck" not in tags and 'ya:manual' not in tags if size not in size_timeout: errors.append( "Unknown test size: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]" .format(size, ", ".join(size_timeout.keys()))) has_fatal_error = True else: try: timeout = int( valid_kw.get('TEST-TIMEOUT', size_timeout[size]) or size_timeout[size]) if timeout < 0: raise Exception("Timeout must be > 0") if size_timeout[size] < timeout and in_autocheck: suggested_size = None for s, t in size_timeout.items(): if timeout <= t: suggested_size = s break if suggested_size: suggested_size = ", suggested size: [[imp]]{}[[rst]]".format( suggested_size) else: suggested_size = "" errors.append( "Max allowed timeout for test size [[imp]]{}[[rst]] is [[imp]]{} sec[[rst]]{}" .format(size, size_timeout[size], suggested_size)) has_fatal_error = True except Exception as e: errors.append( "Error when parsing test timeout: [[bad]]{}[[rst]]".format(e)) has_fatal_error = True is_fat = size == consts.TestSize.Fat or 'ya:fat' in tags for req in ["container", "disk"]: if req in requirements and not is_fat: errors.append( "Only [[imp]]FAT[[rst]] tests can have [[imp]]{}[[rst]] requirement" .format(req)) if 'ya:privileged' in tags and 'container' not in requirements: errors.append( "Only tests with 'container' requirement can have 'ya:privileged' tag" ) if 'ya:privileged' in tags and not is_fat: errors.append("Only fat tests can have 'ya:privileged' tag") if in_autocheck and size == consts.TestSize.Large and not is_fat: errors.append("LARGE test must have ya:fat tag") has_fatal_error = True requiremtens_list = [] for req_name, req_value in requirements.iteritems(): requiremtens_list.append(req_name + ":" + req_value) valid_kw['REQUIREMENTS'] = serialize_list(requiremtens_list) data = get_list("TEST-DATA") data_prefixes = ["arcadia", "arcadia_tests_data", "sbr://"] validate_re = re.compile("^({})".format("|".join(data_prefixes))) for d in data: if not validate_re.match(d): errors.append( "Path [[imp]]{}[[rst]] in the test data section should start with one of the following prefixes: [[imp]]{}[[rst]]" .format(d, ", ".join(data_prefixes))) has_fatal_error = True if valid_kw.get("FUZZ-OPTS"): for option in get_list("FUZZ-OPTS"): if not option.startswith("-"): errors.append( "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should start with '-'" .format(option)) has_fatal_error = True break eqpos = option.find("=") if eqpos == -1 or len(option) == eqpos + 1: errors.append( "Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should obtain value specified after '='" .format(option)) has_fatal_error = True break if option[eqpos - 1] == " " or option[eqpos + 1] == " ": errors.append( "Spaces are not allowed: '[[imp]]{}[[rst]]'".format( option)) has_fatal_error = True break if option[:eqpos] in ("-runs", "-dict", "-jobs", "-workers", "-artifact_prefix", "-print_final_stats"): errors.append( "You can't use '[[imp]]{}[[rst]]' - it will be automatically calculated or configured during run" .format(option)) has_fatal_error = True break if valid_kw.get("USE_ARCADIA_PYTHON") == "yes" and valid_kw.get( "SCRIPT-REL-PATH") == "py.test": errors.append("PYTEST_SCRIPT is deprecated") has_fatal_error = True if valid_kw.get('SPLIT-FACTOR'): if valid_kw.get('FORK-MODE') == 'none': errors.append( 'SPLIT_FACTOR must be use with FORK_TESTS() or FORK_SUBTESTS() macro' ) has_fatal_error = True try: value = int(valid_kw.get('SPLIT-FACTOR')) if value <= 0: raise ValueError("must be > 0") except ValueError as e: errors.append('Incorrect SPLIT_FACTOR value: {}'.format(e)) has_fatal_error = True if has_fatal_error: return None, errors return valid_kw, errors
def validate_test(kw, is_fuzz_test): def get_list(key): return deserialize_list(kw.get(key, "")) valid_kw = copy.deepcopy(kw) errors = [] has_fatal_error = False if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("mail", "maps", "metrika", "devtools")): errors.append("BOOSTTEST is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'ytest.py': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith("yweb/antispam") and not project_path.startswith("devtools"): errors.append("FLEUR test is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'gtest': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("mail", "devtools")): errors.append("GTEST is not allowed here") has_fatal_error = True size_timeout = collections.OrderedDict(sorted(consts.TestSize.DefaultTimeouts.items(), key=lambda t: t[1])) size = valid_kw.get('SIZE', consts.TestSize.Small).lower() tags = get_list("TAG") is_fat = 'ya:fat' in tags requirements = {} valid_requirements = {'cpu', 'disk_usage', 'ram', 'ram_disk', 'container', 'sb', 'sb_vault'} for req in get_list("REQUIREMENTS"): if ":" in req: req_name, req_value = req.split(":", 1) if req_name not in valid_requirements: errors.append("Unknown requirement: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]".format(req_name, ", ".join(sorted(valid_requirements)))) continue elif req_name in ('disk_usage', 'ram_disk'): if not mr.resolve_value(req_value.lower()): errors.append("Cannot convert [[imp]]{}[[rst]] to the proper requirement value".format(req_value)) continue # TODO: Remove this special rules for ram and cpu requirements of FAT-tests elif is_fat and req_name in ('ram', 'cpu'): if req_value.strip() == 'all': pass elif mr.resolve_value(req_value) is None: errors.append("Cannot convert [[imp]]{}[[rst]]: [[imp]]{}[[rst]] to the proper requirement value".format(req_name, req_value)) continue elif req_name == 'ram': if req_value.strip() == 'all': pass else: ram_errors = reqs.check_ram(mr.resolve_value(req_value), size) errors += ram_errors if ram_errors: req_value = str(consts.TestSize.get_default_requirements(size).get(consts.TestRequirements.Ram)) elif req_name == 'cpu': if req_value.strip() == 'all' and is_fuzz_test: pass # XXX # errors += reqs.check_cpu(mr.resolve_value(req_value), size) elif reqs.check_cpu(mr.resolve_value(req_value), size): req_value = str(consts.TestSize.get_default_requirements(size).get(consts.TestRequirements.Cpu)) elif req_name == "sb_vault": if not re.match("\w+=(value|file)\:\w+\:\w+", req_value): errors.append("sb_vault value '{}' should follow pattern <ENV_NAME>=:<value|file>:<owner>:<vault key>".format(req_value)) continue req_value = ",".join(filter(None, [requirements.get(req_name), req_value])) requirements[req_name] = req_value else: errors.append("Invalid requirement syntax [[imp]]{}[[rst]]: expect <requirement>:<value>".format(req)) tags_changed = False if ('ya:force_distbuild' in tags or 'ya:force_sandbox' in tags) and ('ya:not_autocheck' in tags or 'ya:manual' in tags): errors.append('Unable to use ya:force_distbuild or ya:force_sandbox with ya:not_autocheck or ya:manual tags simultaniously. ya:force_distbuild and ya:force_sandbox will be skipped.') tags = filter(lambda o: o not in ('ya:force_distbuild', 'ya:force_sandbox'), tags) tags_changed = True if 'ya:force_distbuild' in tags and 'ya:force_sandbox' in tags: errors.append('Unable to use ya:force_distbuild and ya:force_sandbox tags simultaniously. ya:force_sandbox will be used.') tags = filter(lambda o: o != "ya:force_distbuild", tags) tags_changed = True has_sb_tags = any([tag.startswith('sb:') for tag in tags]) if 'ya:force_distbuild' in tags and has_sb_tags: errors.append('Unable to use ya:force_distbuild with sb:**** tags simultaniously. ya:force_sandbox will be used.') tags = filter(lambda o: o != "ya:force_distbuild", tags) tags.append('ya:force_sandbox') tags_changed = True if "ya:force_distbuild" in tags: invalid_requirements_for_distbuild = [requirement for requirement in requirements.keys() if requirement not in ('ram', 'cpu')] if invalid_requirements_for_distbuild: errors.append('Invalid requirement for distbuild mode (tag ya:force_distbuild): {}'.format(', '.join(invalid_requirements_for_distbuild))) has_fatal_error = True if tags_changed: valid_kw['TAG'] = serialize_list(tags) in_autocheck = "ya:not_autocheck" not in tags and 'ya:manual' not in tags if size not in size_timeout: errors.append("Unknown test size: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]".format(size.upper(), ", ".join([sz.upper() for sz in size_timeout.keys()]))) has_fatal_error = True else: try: timeout = int(valid_kw.get('TEST-TIMEOUT', size_timeout[size]) or size_timeout[size]) script_rel_path = valid_kw.get('SCRIPT-REL-PATH') if timeout < 0: raise Exception("Timeout must be > 0") if size_timeout[size] < timeout and in_autocheck and script_rel_path != 'java.style': suggested_size = None for s, t in size_timeout.items(): if timeout <= t: suggested_size = s break if suggested_size: suggested_size = ", suggested size: [[imp]]{}[[rst]]".format(suggested_size.upper()) else: suggested_size = "" errors.append("Max allowed timeout for test size [[imp]]{}[[rst]] is [[imp]]{} sec[[rst]]{}".format(size.upper(), size_timeout[size], suggested_size)) has_fatal_error = True except Exception as e: errors.append("Error when parsing test timeout: [[bad]]{}[[rst]]".format(e)) has_fatal_error = True for req in ["container", "disk"]: if req in requirements and not is_fat: errors.append("Only [[imp]]FAT[[rst]] tests can have [[imp]]{}[[rst]] requirement".format(req)) has_fatal_error = True if 'ya:privileged' in tags and 'container' not in requirements: errors.append("Only tests with 'container' requirement can have 'ya:privileged' tag") has_fatal_error = True if 'ya:privileged' in tags and not is_fat: errors.append("Only fat tests can have 'ya:privileged' tag") has_fatal_error = True if in_autocheck and size == consts.TestSize.Large and not is_fat: errors.append("LARGE test must have ya:fat tag") has_fatal_error = True if is_fat and size != consts.TestSize.Large: errors.append("Only LARGE test may have ya:fat tag") has_fatal_error = True requiremtens_list = [] for req_name, req_value in requirements.iteritems(): requiremtens_list.append(req_name + ":" + req_value) valid_kw['REQUIREMENTS'] = serialize_list(requiremtens_list) if valid_kw.get("FUZZ-OPTS"): for option in get_list("FUZZ-OPTS"): if not option.startswith("-"): errors.append("Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should start with '-'".format(option)) has_fatal_error = True break eqpos = option.find("=") if eqpos == -1 or len(option) == eqpos + 1: errors.append("Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should obtain value specified after '='".format(option)) has_fatal_error = True break if option[eqpos - 1] == " " or option[eqpos + 1] == " ": errors.append("Spaces are not allowed: '[[imp]]{}[[rst]]'".format(option)) has_fatal_error = True break if option[:eqpos] in ("-runs", "-dict", "-jobs", "-workers", "-artifact_prefix", "-print_final_stats"): errors.append("You can't use '[[imp]]{}[[rst]]' - it will be automatically calculated or configured during run".format(option)) has_fatal_error = True break if valid_kw.get("USE_ARCADIA_PYTHON") == "yes" and valid_kw.get("SCRIPT-REL-PATH") == "py.test": errors.append("PYTEST_SCRIPT is deprecated") has_fatal_error = True if valid_kw.get('SPLIT-FACTOR'): if valid_kw.get('FORK-MODE') == 'none': errors.append('SPLIT_FACTOR must be use with FORK_TESTS() or FORK_SUBTESTS() macro') has_fatal_error = True try: value = int(valid_kw.get('SPLIT-FACTOR')) if value <= 0: raise ValueError("must be > 0") except ValueError as e: errors.append('Incorrect SPLIT_FACTOR value: {}'.format(e)) has_fatal_error = True if has_fatal_error: return None, errors return valid_kw, errors
def validate_test(kw): def get_list(key): return deserialize_list(kw.get(key, "")) valid_kw = copy.deepcopy(kw) errors = [] has_fatal_error = False if valid_kw.get('SCRIPT-REL-PATH') == 'boost.test': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("maps", "metrika", "devtools")): errors.append("BOOSTTEST is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'ytest.py': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith("yweb/antispam") and not project_path.startswith("devtools") and not project_path.startswith("robot/gemini/pygemini/test"): errors.append("FLEUR test is not allowed here") has_fatal_error = True elif valid_kw.get('SCRIPT-REL-PATH') == 'gtest': project_path = valid_kw.get('BUILD-FOLDER-PATH', "") if not project_path.startswith(("mail", "devtools")): errors.append("GTEST is not allowed here") has_fatal_error = True size_timeout = collections.OrderedDict(sorted(consts.TestSize.DefaultTimeouts.items(), key=lambda t: t[1])) size = valid_kw.get('SIZE', consts.TestSize.Small).lower() tags = get_list("TAG") is_fat = 'ya:fat' in tags requirements = {} valid_requirements = {'cpu', 'disk_usage', 'ram', 'ram_disk', 'container', 'sb'} for req in get_list("REQUIREMENTS"): if ":" in req: req_name, req_value = req.split(":", 1) if req_name not in valid_requirements: errors.append("Unknown requirement: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]".format(req_name, ", ".join(sorted(valid_requirements)))) continue elif req_name in ('disk_usage', 'ram_disk'): if not mr.resolve_value(req_value.lower()): errors.append("Cannot convert [[imp]]{}[[rst]] to the proper requirement value".format(req_value)) continue # TODO: Remove this special rules for ram and cpu requirements of FAT-tests elif is_fat and req_name in ('ram', 'cpu'): if req_name == 'cpu': if req_value.strip() == 'all': pass elif mr.resolve_value(req_value) is None: errors.append("Cannot convert [[imp]]{}[[rst]] to the proper requirement value".format(req_value)) continue elif req_name == 'ram': if not mr.resolve_value(req_value.lower()): errors.append("Cannot convert [[imp]]{}[[rst]] to the proper requirement value".format(req_value)) continue elif req_name == 'ram': ram_errors = reqs.check_ram(mr.resolve_value(req_value), size) errors += ram_errors if ram_errors: req_value = str(consts.TestSize.get_default_requirements(size).get(consts.TestRequirements.Ram)) elif req_name == 'cpu': # XXX # errors += reqs.check_cpu(mr.resolve_value(req_value), size) if reqs.check_cpu(mr.resolve_value(req_value), size): req_value = str(consts.TestSize.get_default_requirements(size).get(consts.TestRequirements.Cpu)) requirements[req_name] = req_value else: errors.append("Invalid requirement syntax [[imp]]{}[[rst]]: expect <requirement>:<value>".format(req)) in_autocheck = "ya:not_autocheck" not in tags and 'ya:manual' not in tags if size not in size_timeout: errors.append("Unknown test size: [[imp]]{}[[rst]], choose from [[imp]]{}[[rst]]".format(size.upper(), ", ".join([sz.upper() for sz in size_timeout.keys()]))) has_fatal_error = True else: try: timeout = int(valid_kw.get('TEST-TIMEOUT', size_timeout[size]) or size_timeout[size]) if timeout < 0: raise Exception("Timeout must be > 0") if size_timeout[size] < timeout and in_autocheck: suggested_size = None for s, t in size_timeout.items(): if timeout <= t: suggested_size = s break if suggested_size: suggested_size = ", suggested size: [[imp]]{}[[rst]]".format(suggested_size.upper()) else: suggested_size = "" errors.append("Max allowed timeout for test size [[imp]]{}[[rst]] is [[imp]]{} sec[[rst]]{}".format(size.upper(), size_timeout[size], suggested_size)) has_fatal_error = True except Exception as e: errors.append("Error when parsing test timeout: [[bad]]{}[[rst]]".format(e)) has_fatal_error = True for req in ["container", "disk"]: if req in requirements and not is_fat: errors.append("Only [[imp]]FAT[[rst]] tests can have [[imp]]{}[[rst]] requirement".format(req)) has_fatal_error = True if 'ya:privileged' in tags and 'container' not in requirements: errors.append("Only tests with 'container' requirement can have 'ya:privileged' tag") has_fatal_error = True if 'ya:privileged' in tags and not is_fat: errors.append("Only fat tests can have 'ya:privileged' tag") has_fatal_error = True if in_autocheck and size == consts.TestSize.Large and not is_fat: errors.append("LARGE test must have ya:fat tag") has_fatal_error = True if is_fat and size != consts.TestSize.Large: errors.append("Only LARGE test may have ya:fat tag") has_fatal_error = True requiremtens_list = [] for req_name, req_value in requirements.iteritems(): requiremtens_list.append(req_name + ":" + req_value) valid_kw['REQUIREMENTS'] = serialize_list(requiremtens_list) data = get_list("TEST-DATA") data_prefixes = ["arcadia", "arcadia_tests_data", "sbr://"] validate_re = re.compile("^({})".format("|".join(data_prefixes))) for d in data: if not validate_re.match(d): errors.append("Path [[imp]]{}[[rst]] in the test data section should start with one of the following prefixes: [[imp]]{}[[rst]]".format(d, ", ".join(data_prefixes))) has_fatal_error = True if valid_kw.get("FUZZ-OPTS"): for option in get_list("FUZZ-OPTS"): if not option.startswith("-"): errors.append("Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should start with '-'".format(option)) has_fatal_error = True break eqpos = option.find("=") if eqpos == -1 or len(option) == eqpos + 1: errors.append("Unrecognized fuzzer option '[[imp]]{}[[rst]]'. All fuzzer options should obtain value specified after '='".format(option)) has_fatal_error = True break if option[eqpos - 1] == " " or option[eqpos + 1] == " ": errors.append("Spaces are not allowed: '[[imp]]{}[[rst]]'".format(option)) has_fatal_error = True break if option[:eqpos] in ("-runs", "-dict", "-jobs", "-workers", "-artifact_prefix", "-print_final_stats"): errors.append("You can't use '[[imp]]{}[[rst]]' - it will be automatically calculated or configured during run".format(option)) has_fatal_error = True break if valid_kw.get("USE_ARCADIA_PYTHON") == "yes" and valid_kw.get("SCRIPT-REL-PATH") == "py.test": errors.append("PYTEST_SCRIPT is deprecated") has_fatal_error = True if valid_kw.get('SPLIT-FACTOR'): if valid_kw.get('FORK-MODE') == 'none': errors.append('SPLIT_FACTOR must be use with FORK_TESTS() or FORK_SUBTESTS() macro') has_fatal_error = True try: value = int(valid_kw.get('SPLIT-FACTOR')) if value <= 0: raise ValueError("must be > 0") except ValueError as e: errors.append('Incorrect SPLIT_FACTOR value: {}'.format(e)) has_fatal_error = True if has_fatal_error: return None, errors return valid_kw, errors