Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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