Ejemplo n.º 1
0
def validate_yaml_syntax(root_dir: str, grep_tags: List[str],
                         source_tags: List[str]) -> Tuple[bool, List[str]]:
    """Validate a given directory's .drift-data.yml files

    This method validates that a given directory's .drift-data.yml files are
    semantically correct, and match up with the directory's source code.

    Args:
        yaml_paths: A list of .drift-data.yml filepaths to validate
        grep_tags: A list of region tags (parsed *and* unparsed) that exist
                   in the target directory
        source_tags: A list of successfully-parsed region tags in the target
                     directory

    Returns:
        A 2-tuple containing the following:
         - A boolean including file validity (True if all .drift-data.yml
           files passed validation, False otherwise)
         - A list of validation error messages (if any) that were raised
    """
    yaml_paths = file_utils.get_drift_yaml_files(root_dir)

    tags_are_valid, tags_violations = (_get_region_tag_errors(
        yaml_paths, grep_tags, source_tags))
    attrs_are_valid, attrs_violations = _get_attr_errors(yaml_paths, grep_tags)

    violations = tags_violations + attrs_violations
    output = [str(violation) for violation in violations]

    is_valid = tags_are_valid and attrs_are_valid

    return is_valid, output
Ejemplo n.º 2
0
def get_untested_region_tags(root_dir: str) -> List[str]:
    """Get the 'untested' region tags for a given directory

    In this method, 'untested' region tags are those *explicitly marked*
    as untested in .drift-data.yml. (In other words, these are *not* region
    tags for which a test exists, but could not be found by automatically.)

    Args:
        root_dir: A directory containing snippets and .drift-data.yml files
    """
    yaml_paths = file_utils.get_drift_yaml_files(root_dir)
    all_untested_tags = []

    for path in yaml_paths:
        with open(path, 'r') as file:
            yaml_contents = '\n'.join(file.readlines())
            parsed_yaml = yaml.safe_load(yaml_contents)

            local_untested_tags = [
                key for key in parsed_yaml.keys()
                if parsed_yaml[key].get('tested') is False
            ]
            all_untested_tags += local_untested_tags

    return list(set(all_untested_tags))
Ejemplo n.º 3
0
def _handle_additions_clause(source_methods_json: List[
    polyglot_drift_data.PolyglotDriftData], root_dir: str) -> None:
    """Handle additions clause in .drift-data.yml files

    The "additions" clause is used to specify lists of region tags
    that correspond to the same set of tests. If a given snippet
    method contains one of these region tags, it will be deemed to
    contain *all* of them!

    This is useful for multi-region-tag samples like the following:
     - my_sample_setup
     - my_sample_do_thing_1
     - my_sample_do_thing_2
     - my_sample_teardown

    Args:
        source_methods_json: A list of language-agnostic snippet methods
        root_dir: A path to the directory source_methods_json was created from

    Modifies:
        source_methods_json: Adds grouped region tags to source_methods_json
                             entries with corresponding 'additions' YAML
                             keyword exists
    """
    yaml_paths = file_utils.get_drift_yaml_files(root_dir)

    for path in yaml_paths:
        with open(path, 'r') as file:
            yaml_contents = '\n'.join(file.readlines())
            parsed_yaml = yaml.safe_load(yaml_contents)

            additions_tags = [
                key for key in parsed_yaml.keys()
                if key not in constants.RESERVED_YAML_KEYS
            ]

            for tag in additions_tags:
                yaml_entry = parsed_yaml[tag]
                if 'additions' in yaml_entry and \
                   isinstance(yaml_entry['additions'], list):

                    added_tags = set(yaml_entry['additions'])
                    added_tags.add(tag)

                    for idx, method in enumerate(source_methods_json):
                        method_tags = set(method.region_tags)

                        if added_tags.intersection(method_tags):
                            new_method_tags = list(
                                set(method.region_tags).union(added_tags))
                            # _replace() creates a *copy*
                            # (named-tuples themselves are immutable)
                            # so we must update the underlying array
                            source_methods_json[idx] = \
                                method._replace(
                                    region_tags=new_method_tags)
Ejemplo n.º 4
0
def _handle_overwrites(source_methods_json: List[
    polyglot_drift_data.PolyglotDriftData], root_dir: str) -> None:
    """Handle overwrites in .drift-data.yml files

    The "overwrite" clause is used to denote when a .drift-data.yml
    entry should overwrite any automatically-detected test methods.
    If an overwrite entry is present for a given region tag, this
    method clears all previously-detected tests for snippet methods
    containing that region tag.

    Args:
        source_methods_json: A list of language-agnostic snippet methods
        root_dir: A path to the directory source_methods_json was created from

    Modifies:
        source_methods_json: Clears test_methods attribute on
                             source_methods_json entries for which an
                             'overwrite' YAML keyword exists
    """
    yaml_paths = file_utils.get_drift_yaml_files(root_dir)

    overwritten_tags = set()

    for path in yaml_paths:
        with open(path, 'r') as file:
            yaml_contents = '\n'.join(file.readlines())
            parsed_yaml = yaml.safe_load(yaml_contents)

            for tag in parsed_yaml.keys():
                yaml_entry = parsed_yaml[tag]
                if 'overwrite' in yaml_entry:
                    if yaml_entry.get('overwrite', False) is True:
                        overwritten_tags.add(tag)

    for idx, method in enumerate(source_methods_json):
        if set(method.region_tags).intersection(overwritten_tags):
            # _replace() creates a *copy*
            # (named-tuples themselves are immutable)
            # so we must update the underlying array
            source_methods_json[idx] = \
                method._replace(test_methods=[])
Ejemplo n.º 5
0
def _handle_manually_specified_tests(source_methods_json: List[
    polyglot_drift_data.PolyglotDriftData], root_dir: str) -> None:
    """Handle manually specified tests in .drift-data.yml files

    This method allows users to manually specify which tests (file
    paths and method names) correspond to a given region tag. This
    is useful in two cases:
     - automatic detection doesn't detect a snippet <-> test mapping
     - automatic detection detects an incorrect mapping

    In the latter case, use the 'overwrite' attribute to ignore
    auto-detected mappings in favor of manually specified ones.

    Args:
        source_methods_json: A list of language-agnostic snippet methods
        root_dir: A path to the directory source_methods_json was created from

    Modifies:
        source_methods_json: Adds manually-specified test data to
                             source_methods_json entries that have a
                             corresponding manual-test-specifying YAML
                             entry.
    """
    def _no_banned_subkeys(yaml_entry: Dict[str, Any],
                           banned_keys: Set[str]) -> bool:
        """Determine whether a given yaml entry has "banned" keys

        (Banned keys cannot exist in a manually-specified test entry.)
        """
        return not set(yaml_entry.keys()).intersection(banned_keys)

    yaml_paths = file_utils.get_drift_yaml_files(root_dir)

    test_tag_map: Dict[str, List[Tuple[str, str]]] = {}

    for path in yaml_paths:
        with open(path, 'r') as file:
            yaml_dir = os.path.dirname(path)
            yaml_contents = '\n'.join(file.readlines())
            parsed_yaml = yaml.safe_load(yaml_contents)

            # Manually specified tests cannot contain any keys in
            # RESERVED_YAML_KEYS (except for "overwrite"), so filter
            # out any yaml entries that *do* have these keys
            banned_keys = set(constants.RESERVED_YAML_KEYS)
            banned_keys.remove('overwrite')

            manual_tags = [
                tag for tag in parsed_yaml.keys()
                if _no_banned_subkeys(parsed_yaml[tag], banned_keys)
            ]

            for tag in manual_tags:
                yaml_entry = parsed_yaml[tag]
                test_tag_map[tag] = []

                for test_rel_path in yaml_entry.keys():
                    test_path = os.path.join(yaml_dir, test_rel_path)
                    if test_path and os.path.exists(test_path):
                        for test_name in yaml_entry[test_rel_path]:
                            test_tag_map[tag].append((test_path, test_name))

    for method in source_methods_json:
        for tag in method.region_tags:
            if tag in test_tag_map and test_tag_map[tag]:
                method.test_methods.append(test_tag_map[tag])