Пример #1
0
def get_methods_info(statement_body, class_tags, class_requirements):
    """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_requirements: the requirements at the class level, to be
                               combined with the requirements 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_requirements = get_docstring_directives_requirements(docstring)
        mt_requirements.extend(class_requirements)

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

    return methods_info
Пример #2
0
 def test_tag_keyval_duplicate(self):
     raw = ":avocado: tags=fast,arch:x86_64,arch:ppc64,arch:x86_64"
     exp = {"fast": None, "arch": set(["x86_64", "ppc64"])}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #3
0
 def test_tag_newline_after(self):
     raw = ":avocado: tags=fast,slow\n:avocado: enable"
     exp = {"fast": None, "slow": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #4
0
 def test_tag_newline_before(self):
     raw = ":avocado: enable\n:avocado: tags=fast"
     exp = {"fast": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #5
0
 def test_tag_empty(self):
     raw = ":avocado: tags="
     exp = {}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #6
0
 def test_tag_tab_separator(self):
     raw = ":avocado:\ttags=FAST"
     exp = {"FAST": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #7
0
 def test_tag_duplicate(self):
     raw = ":avocado: tags=SLOW,disk,disk"
     exp = {"SLOW": None, "disk": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #8
0
 def test_tag_lowercase_uppercase(self):
     raw = ":avocado: tags=slow,DISK"
     exp = {"slow": None, "DISK": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #9
0
 def test_tag_double_with_empty(self):
     raw = ":avocado: tags=fast,,network"
     exp = {"fast": None, "network": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #10
0
 def test_tag_single(self):
     raw = ":avocado: tags=fast"
     exp = {"fast": None}
     self.assertEqual(get_docstring_directives_tags(raw), exp)
Пример #11
0
 def test_get_tags_empty(self):
     for tag in self.NO_TAGS:
         self.assertEqual({}, get_docstring_directives_tags(tag))
Пример #12
0
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_requirements(
                                        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_requirements(
                                    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
Пример #13
0
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_requirements(
                                    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