예제 #1
0
파일: core.py 프로젝트: mxie91/avocado
def get_methods_info(statement_body, class_tags, class_dependencies):
    """Returns information on test methods.

    :param statement_body: the body of a "class" statement
    :param class_tags: the tags at the class level, to be combined with the
                       tags at the method level.
    :param class_dependencies: the dependencies at the class level, to be
                               combined with the dependencies at the method
                               level.
    """
    methods_info = []
    for st in statement_body:
        if not (isinstance(st, (ast.FunctionDef, ast.AsyncFunctionDef))):
            continue

        decorators = getattr(st, "decorator_list", [])
        decorator_names = [getattr(_, "id", None) for _ in decorators]
        if "property" in decorator_names:
            continue

        if not st.name.startswith("test"):
            continue

        docstring = ast.get_docstring(st)

        mt_tags = get_docstring_directives_tags(docstring)
        mt_tags.update(class_tags)

        mt_dependencies = get_docstring_directives_dependencies(docstring)
        mt_dependencies.extend(class_dependencies)

        methods = [method for method, _, _ in methods_info]
        if st.name not in methods:
            methods_info.append((st.name, mt_tags, mt_dependencies))

    return methods_info
예제 #2
0
 def test_dependency_double(self):
     raw = ':avocado: dependency={"foo":"bar"}\n:avocado: dependency={"uri":"http://foo.bar"}'
     exp = [{"foo": "bar"}, {"uri": "http://foo.bar"}]
     self.assertEqual(get_docstring_directives_dependencies(raw), exp)
예제 #3
0
 def test_dependency_single(self):
     raw = ':avocado: dependency={"foo":"bar"}'
     exp = [{"foo": "bar"}]
     self.assertEqual(get_docstring_directives_dependencies(raw), exp)
예제 #4
0
 def test_get_dependency_empty(self):
     for dep in self.NO_REQS:
         self.assertEqual([], get_docstring_directives_dependencies(dep))
예제 #5
0
파일: core.py 프로젝트: mxie91/avocado
def find_python_tests(target_module, target_class, determine_match, path):
    """
    Attempts to find Python tests from source files

    A Python test in this context is a method within a specific type
    of class (or that inherits from a specific class).

    :param target_module: the name of the module from which a class should
                          have come from.  When attempting to find a Python
                          unittest, the target_module will most probably
                          be "unittest", as per the standard library module
                          name.  When attempting to find Avocado tests, the
                          target_module will most probably be "avocado".
    :type target_module: str
    :param target_class: the name of the class that is considered to contain
                         test methods.  When attempting to find Python
                         unittests, the target_class will most probably be
                         "TestCase".  When attempting to find Avocado tests,
                         the target_class  will most probably be "Test".
    :type target_class: str
    :type determine_match: a callable that will determine if a given module
                           and class is contains valid Python tests
    :type determine_match: function
    :param path: path to a Python source code file
    :type path: str
    :returns: tuple where first item is dict with class name and additional
              info such as method names and tags; the second item is
              set of class names which look like Python tests but have been
              forcefully disabled.
    :rtype: tuple
    """
    module = PythonModule(path, target_module, target_class)
    # The resulting test classes
    result = collections.OrderedDict()
    disabled = set()

    for klass in module.iter_classes():
        docstring = ast.get_docstring(klass)
        # Looking for a class that has in the docstring either
        # ":avocado: enable" or ":avocado: disable
        if check_docstring_directive(docstring, "disable"):
            disabled.add(klass.name)
            continue

        if check_docstring_directive(docstring, "enable"):
            info = get_methods_info(
                klass.body,
                get_docstring_directives_tags(docstring),
                get_docstring_directives_dependencies(docstring),
            )
            result[klass.name] = info
            continue

        # From this point onwards we want to do recursive discovery, but
        # for now we don't know whether it is avocado.Test inherited
        # (Ifs are optimized for readability, not speed)

        # If "recursive" tag is specified, it is forced as test
        if check_docstring_directive(docstring, "recursive"):
            match = True
        else:
            match = module.is_matching_klass(klass)
        info = get_methods_info(
            klass.body,
            get_docstring_directives_tags(docstring),
            get_docstring_directives_dependencies(docstring),
        )
        # Getting the list of parents of the current class
        parents = klass.bases

        match = _examine_same_module(
            parents,
            info,
            disabled,
            match,
            module,
            target_module,
            target_class,
            determine_match,
        )

        # If there are parents left to be discovered, they
        # might be in a different module.
        for parent in parents:
            try:
                (
                    parent_class,
                    imported_symbol,
                    symbol_is_module,
                ) = _get_attributes_for_further_examination(parent, module)

                found_spec = imported_symbol.get_importable_spec(
                    symbol_is_module)
                if found_spec is None:
                    continue

            except ClassNotSuitable:
                continue

            _info, _dis, _match = _examine_class(
                target_module,
                target_class,
                determine_match,
                found_spec.origin,
                parent_class,
                match,
            )
            if _info:
                info.extend(_info)
                disabled.update(_dis)
            if _match is not match:
                match = _match

        # Only update the results if this was detected as 'avocado.Test'
        if match:
            result[klass.name] = info

    return result, disabled
예제 #6
0
파일: core.py 프로젝트: mxie91/avocado
def _examine_class(target_module, target_class, determine_match, path,
                   class_name, match):
    """
    Examine a class from a given path

    :param target_module: the name of the module from which a class should
                          have come from.  When attempting to find a Python
                          unittest, the target_module will most probably
                          be "unittest", as per the standard library module
                          name.  When attempting to find Avocado tests, the
                          target_module will most probably be "avocado".
    :type target_module: str
    :param target_class: the name of the class that is considered to contain
                         test methods.  When attempting to find Python
                         unittests, the target_class will most probably be
                         "TestCase".  When attempting to find Avocado tests,
                         the target_class  will most probably be "Test".
    :type target_class: str
    :param determine_match: a callable that will determine if a match has
                            occurred or not
    :type determine_match: function
    :param path: path to a Python source code file
    :type path: str
    :param class_name: the specific class to be found
    :type path: str
    :param match: whether the inheritance from <target_module.target_class> has
                  been determined or not
    :type match: bool
    :returns: tuple where first item is a list of test methods detected
              for given class; second item is set of class names which
              look like avocado tests but are force-disabled.
    :rtype: tuple
    """
    module = PythonModule(path, target_module, target_class)
    info = []
    disabled = set()

    for klass in module.iter_classes(class_name):
        if class_name != klass.name:
            continue

        docstring = ast.get_docstring(klass)

        if match is False:
            match = module.is_matching_klass(klass)

        info = get_methods_info(
            klass.body,
            get_docstring_directives_tags(docstring),
            get_docstring_directives_dependencies(docstring),
        )

        # Getting the list of parents of the current class
        parents = klass.bases

        match = _examine_same_module(
            parents,
            info,
            disabled,
            match,
            module,
            target_module,
            target_class,
            determine_match,
        )

        # If there are parents left to be discovered, they
        # might be in a different module.
        for parent in parents:
            try:
                (
                    parent_class,
                    imported_symbol,
                    symbol_is_module,
                ) = _get_attributes_for_further_examination(parent, module)

                found_spec = imported_symbol.get_importable_spec(
                    symbol_is_module)
                if found_spec is None:
                    continue

            except ClassNotSuitable:
                continue

            _info, _disabled, _match = _examine_class(
                target_module,
                target_class,
                determine_match,
                found_spec.origin,
                parent_class,
                match,
            )
            if _info:
                _extend_test_list(info, _info)
                disabled.update(_disabled)
            if _match is not match:
                match = _match

    if not match and module.interesting_klass_found:
        imported_symbol = module.imported_symbols[class_name]
        if imported_symbol:
            found_spec = imported_symbol.get_importable_spec()
            if found_spec:
                _info, _disabled, _match = _examine_class(
                    target_module,
                    target_class,
                    determine_match,
                    found_spec.origin,
                    class_name,
                    match,
                )
                if _info:
                    _extend_test_list(info, _info)
                    disabled.update(_disabled)
                if _match is not match:
                    match = _match

    return info, disabled, match