Example #1
0
    def _run_task(self):
        kwfiles = []
        processed_files = []
        for library_name in self.options["path"]:
            kwfile = KeywordFile(library_name)
            try:
                if self.is_pageobject_library(library_name):
                    PageObjects._reset()
                    module = Importer().import_class_or_module_by_path(
                        os.path.abspath(library_name))
                    kwfile.doc = module.__doc__
                    if hasattr(module, "TITLE"):
                        kwfile.title = module.TITLE

                    for pobj_name, pobj in sorted(
                            PageObjects.registry.items()):
                        pobj = PageObjects.registry[pobj_name]
                        libname = "{}.{}".format(pobj.__module__,
                                                 pobj.__name__)
                        libdoc = LibraryDocBuilder().build(libname)
                        libdoc.src = os.path.basename(library_name)
                        libdoc.pobj = libname
                        kwfile.add_keywords(libdoc, pobj_name)

                else:
                    libdoc = DocumentationBuilder(library_name).build(
                        library_name)
                    kwfile.add_keywords(libdoc)

                # if we get here, we were able to process the file correctly
                kwfiles.append(kwfile)
                processed_files.append(library_name)

            except RobotNotRunningError as e:
                # oddly, robot's exception has a traceback embedded in the message, so we'll
                # only print out the first line to hide most of the noise
                self.logger.warn("unexpected error: {}".format(
                    str(e).split("\n")[0]))

        try:
            with open(self.options["output"], "w") as f:
                html = self._render_html(kwfiles)
                f.write(html)
                self.logger.info("created {}".format(f.name))
        except Exception as e:
            raise TaskOptionsError(
                "Unable to create output file '{}' ({})".format(
                    self.options["output"], e.strerror))

        return {"files": processed_files, "html": html}
Example #2
0
def get_lib_keywords(lib_name):
    """Get keywords of library lib_name"""
    lib = LibraryDocBuilder().build(lib_name)
    keywords = []
    for keyword in lib.keywords:
        doc = keyword.doc.split('\n')[0]
        keywords.append({'name': keyword.name, 'lib': lib_name, 'doc': doc})
    return keywords
Example #3
0
def create_python_tests(fout):
    """
    Walk the python keywords and create tests
    """
    for keyword in LibraryDocBuilder().build('FusionLibrary').keywords:
        print(keyword.name)
        fout.write('Test {}\n'.format(keyword.name))
        fout.write('    [Documentation]    {}\n'.format(keyword.name))
        fout.write('    {} '.format(keyword.name))
        for arg in keyword.args:
            if '=' in arg:
                fout.write('    {}    '.format(arg.split('=')[-1]))
            else:
                if arg.startswith('&'):
                    fout.write('    testk=testv    ')
                else:
                    fout.write('    test    ')
        fout.write('\n\n')
Example #4
0
 def __init__(self):
     self.file_path = None
     self.rf_variables = Variables()
     self.rf_var_storage = VariableStore(self.rf_variables)
     self.libdoc = LibraryDocBuilder()
Example #5
0
class DataParser():
    """ This class is used to parse different tables in test data.

    Class will return the the test data as in json format. Can parse
    Python libraries, library xml documentation generated by the libdoc
    resource and test suite files.
    """
    def __init__(self):
        self.file_path = None
        self.rf_variables = Variables()
        self.rf_var_storage = VariableStore(self.rf_variables)
        self.libdoc = LibraryDocBuilder()

    def parse_resource(self, file_path):
        self.file_path = file_path
        if path.exists(file_path):
            if '__init__.' in file_path:
                folder = path.dirname(file_path)
                model = parsing.TestDataDirectory(source=folder).populate()
            else:
                model = parsing.ResourceFile(file_path).populate()
            return self._parse_robot_data(file_path, model)
        else:
            logging.error('File %s could not be found', file_path)
            raise ValueError('File does not exist: {0}'.format(file_path))

    def parse_suite(self, file_path):
        self.file_path = file_path
        if path.exists(file_path):
            model = parsing.TestCaseFile(source=file_path).populate()
            return self._parse_robot_data(file_path, model)
        else:
            logging.error('File %s could not be found', file_path)
            raise ValueError('File does not exist: {0}'.format(file_path))

    def parse_variable_file(self, file_path, args=None):
        if not args:
            args = []
        data = {}
        data[DBJsonSetting.file_name] = path.basename(file_path)
        data[DBJsonSetting.file_path] = normalise_path(file_path)
        self.file_path = file_path
        setter = VariableFileSetter(self.rf_var_storage)
        var_list = []
        try:
            variables = setter.set(file_path, args)
        except DataError:
            variables = []
        for variable in variables:
            var_list.append(variable[0])
        data[DBJsonSetting.variables] = sorted(var_list)
        return data

    def parse_library(self, library, args=None):
        """Parses RF library to dictionary

        Uses internally libdoc modules to parse the library.
        Possible arguments to the library are provided in the
        args parameter.
        """
        data = {}
        if not args:
            data[DBJsonSetting.arguments] = []
        else:
            arg_list = []
            for arg in args:
                arg_list.append(arg)
            data[DBJsonSetting.arguments] = arg_list
        if path.isfile(library):
            data[DBJsonSetting.file_path] = normalise_path(library)
            if library.endswith('.xml'):
                library_module, keywords = self._parse_xml_doc(library)
                data[DBJsonSetting.keywords] = keywords
                data[DBJsonSetting.library_module] = library_module
            elif library.endswith('.py'):
                data[DBJsonSetting.file_name] = path.basename(library)
                data[DBJsonSetting.library_module] = path.splitext(
                    data[DBJsonSetting.file_name])[0]
                data[DBJsonSetting.keywords] = self._parse_python_lib(
                    library, data[DBJsonSetting.arguments])
            else:
                raise ValueError('Unknown library')
        else:
            library, data = self._locate_file_from_path(library, data)
            data[DBJsonSetting.keywords] = self._parse_python_lib(
                library, data[DBJsonSetting.arguments])
        if data[DBJsonSetting.keywords] is None:
            raise ValueError('Library did not contain keywords')
        else:
            return data

    @staticmethod
    def _locate_file_from_path(library, data):
        file_name = path.basename(library)
        for path_ in sys.path:
            file_path = path.join(path_, file_name)
            if path.isfile(file_path):
                data[DBJsonSetting.file_name] = path.basename(file_path)
                data[DBJsonSetting.library_module] = path.splitext(
                    data[DBJsonSetting.file_name])[0]
                return file_path, data
        data[DBJsonSetting.library_module] = library
        return library, data

    def register_console_logger(self):
        ROBOT_LOGGER.register_console_logger()

    def unregister_console_logger(self):
        ROBOT_LOGGER.unregister_console_logger()

    def close_logger(self):
        ROBOT_LOGGER.close()

    def _parse_python_lib(self, library, args):
        lib_with_args = self._lib_arg_formatter(library, args)
        kws = {}
        try:
            lib = self.libdoc.build(lib_with_args)
        except DataError:
            raise ValueError('Library does not exist: {0}'.format(library))
        if library in STDLIBS:
            import_name = 'robot.libraries.' + library
        else:
            import_name = library
        importer = Importer('test library')
        libcode = importer.import_class_or_module(import_name,
                                                  return_source=False)
        kw_with_deco = self._get_keywords_with_robot_name(libcode)
        for keyword in lib.keywords:
            kw = {}
            kw[DBJsonSetting.keyword_name] = keyword.name
            kw[DBJsonSetting.tags] = list(keyword.tags._tags)
            kw[DBJsonSetting.keyword_arguments] = keyword.args
            kw[DBJsonSetting.documentation] = keyword.doc
            if keyword.name in kw_with_deco:
                function_name = kw_with_deco[keyword.name]
            else:
                function_name = keyword.name
            kw[DBJsonSetting.keyword_file] = self._get_library_kw_source(
                libcode, function_name)
            kws[strip_and_lower(keyword.name)] = kw
        return kws

    def _get_keywords_with_robot_name(self, libcode):
        """Returns keywords which uses Robot keyword decorator with robot_name

        The keyword name can be chaned with Robot Framework keyword decorator
        and by using the robot_name attribute. Return dictinionary which key is
        the value of the robot_name attribute and the orinal function name.
        """
        kw_deco = {}
        for key in libcode.__dict__:
            if callable(libcode.__dict__[key]):
                try:
                    if 'robot_name' in libcode.__dict__[key].__dict__:
                        kw = libcode.__dict__[key].__dict__['robot_name']
                        kw_deco[kw] = key
                except AttributeError:
                    pass
        return kw_deco

    def _get_library_kw_source(self, libcode, keyword):
        kw_func = keyword.lower().replace(' ', '_')
        func = None
        func_file = None
        if hasattr(libcode, kw_func):
            func = getattr(libcode, kw_func)
        if func:
            kw_class = self.get_class_that_defined_method(func)
            if kw_class:
                func_file = self.get_function_file(kw_class)
            else:
                func_file = self.get_function_file(func)
        return func_file

    def get_class_that_defined_method(self, meth):
        try:
            class_mro = inspect.getmro(meth.im_class)
        except AttributeError:
            return None
        for cls in class_mro:
            if meth.__name__ in cls.__dict__:
                return cls
        return None

    def get_function_file(self, kw_class):
        file_ = inspect.getsourcefile(kw_class)
        if file_ and path.exists(file_):
            return normalise_path(file_)
        else:
            return None

    def _lib_arg_formatter(self, library, args):
        args = self._argument_path_formatter(library, args)
        if not args:
            return library
        else:
            for item in args:
                library = '{lib}::{item}'.format(lib=library, item=item)
            return library

    def _argument_path_formatter(self, library, args):
        """Replace robot folder with real path

        If ${/}, ${OUTPUT_DIR} or ${EXECDIR} is found from args then
        a temporary directory is created and that one is used instead."""
        arguments = []
        for arg in args:
            if '${/}' in arg or '${OUTPUT_DIR}' in arg or '${EXECDIR}' in arg:
                f = mkdtemp()
                logging.info(
                    'Possible robot path encountered in library arguments')
                logging.debug('In library %s', library)
                logging.debug('Instead of %s using: %s', arg, f)
                arguments.append(f)
            else:
                arguments.append(arg)
        return arguments

    def _parse_xml_doc(self, library):
        root = ET.parse(library).getroot()
        if ('type', DBJsonSetting.library) in root.items():
            return root.attrib['name'], self._parse_xml_lib(root)
        else:
            raise ValueError('XML file is not library: {0}'.format(
                root.attrib['name']))

    def _parse_xml_lib(self, root):
        kws = {}
        for element in root.findall('kw'):
            kw = {}
            kw[DBJsonSetting.keyword_file] = None
            kw[DBJsonSetting.keyword_name] = element.attrib['name']
            kw[DBJsonSetting.documentation] = element.find('doc').text
            tags = []
            [tags.append(tag.text) for tag in element.findall('.//tags/tag')]
            kw[DBJsonSetting.tags] = tags
            arg = []
            [
                arg.append(tag.text)
                for tag in element.findall('.//arguments/arg')
            ]
            kw[DBJsonSetting.keyword_arguments] = arg
            kws[strip_and_lower(kw[DBJsonSetting.keyword_name])] = kw
        return kws

    def _parse_robot_data(self, file_path, model):
        data = {}
        data[DBJsonSetting.file_name] = path.basename(file_path)
        data[DBJsonSetting.file_path] = normalise_path(file_path)
        data[DBJsonSetting.keywords] = self._get_keywords(model)
        data[DBJsonSetting.variables] = self._get_global_variables(model)
        lib, res, v_files = self._get_imports(
            model, path.dirname(normalise_path(file_path)), file_path)
        data[DBJsonSetting.resources] = res
        data[DBJsonSetting.libraries] = lib
        data[DBJsonSetting.variable_files] = v_files
        return data

    def _get_keywords(self, model):
        kw_data = {}
        for kw in model.keywords:
            tmp = {}
            tmp[DBJsonSetting.keyword_arguments] = kw.args.value
            tmp[DBJsonSetting.documentation] = kw.doc.value
            tmp[DBJsonSetting.tags] = kw.tags.value
            tmp[DBJsonSetting.keyword_name] = kw.name
            kw_data[strip_and_lower(kw.name)] = tmp
        return kw_data

    def _get_imports(self, model, file_dir, file_path):
        lib = []
        res = []
        var_files = []
        for setting in model.setting_table.imports:
            if setting.type == 'Library':
                lib.append(self._format_library(setting, file_dir))
            elif setting.type == 'Resource':
                res.append(self._format_resource(setting, file_path))
            elif setting.type == 'Variables':
                var_files.append(self._format_variable_file(setting))
        return lib, res, var_files

    def _format_library(self, setting, file_dir):
        data = {}
        lib_name = setting.name
        if lib_name.endswith('.py') and not path.isfile(lib_name):
            lib_path = path.abspath(path.join(file_dir, lib_name))
            lib_name = path.basename(lib_path)
        elif lib_name.endswith('.py') and path.isfile(lib_name):
            lib_path = normalise_path(lib_name)
            lib_name = path.basename(lib_name)
        else:
            lib_path = None
        data[DBJsonSetting.library_name] = lib_name
        data[DBJsonSetting.library_alias] = setting.alias
        data[DBJsonSetting.library_arguments] = setting.args
        data[DBJsonSetting.library_path] = lib_path
        return data

    def _format_resource(self, setting, file_path):
        if path.isabs(setting.name):
            return setting.name
        else:
            c_dir = path.dirname(self.file_path)
            resource_path = normalise_path(path.join(c_dir, setting.name))
            if not path.isfile(resource_path):
                print('Import failure on file: {0},'.format(file_path),
                      'could not locate: {0}'.format(setting.name))
            return resource_path

    def _format_variable_file(self, setting):
        data = {}
        v_path = normalise_path(
            path.join(path.dirname(self.file_path), setting.name))
        args = {}
        args['variable_file_arguments'] = setting.args
        data[v_path] = args
        return data

    def _get_global_variables(self, model):
        var_data = []
        for var in model.variable_table.variables:
            if var:
                var_data.append(var.name)
        return var_data
 def __init__(self):
     self.file_path = None
     self.rf_variables = Variables()
     self.rf_var_storage = VariableStore(self.rf_variables)
     self.libdoc = LibraryDocBuilder()
class DataParser():
    """ This class is used to parse different tables in test data.

    Class will return the the test data as in json format. Can parse
    Python libraries, library xml documentation generated by the libdoc
    resource and test suite files.
    """

    def __init__(self):
        self.file_path = None
        self.rf_variables = Variables()
        self.rf_var_storage = VariableStore(self.rf_variables)
        self.libdoc = LibraryDocBuilder()

    def parse_resource(self, file_path):
        self.file_path = file_path
        if path.exists(file_path):
            if '__init__.' in file_path:
                folder = path.dirname(file_path)
                model = parsing.TestDataDirectory(source=folder).populate()
            else:
                model = parsing.ResourceFile(file_path).populate()
            return self._parse_robot_data(file_path, model)
        else:
            logging.error('File %s could not be found', file_path)
            raise ValueError(
                'File does not exist: {0}'.format(file_path))

    def parse_suite(self, file_path):
        self.file_path = file_path
        if path.exists(file_path):
            model = parsing.TestCaseFile(source=file_path).populate()
            return self._parse_robot_data(file_path, model)
        else:
            logging.error('File %s could not be found', file_path)
            raise ValueError(
                'File does not exist: {0}'.format(file_path))

    def parse_variable_file(self, file_path, args=None):
        if not args:
            args = []
        data = {}
        data[DBJsonSetting.file_name] = path.basename(file_path)
        data[DBJsonSetting.file_path] = normalise_path(file_path)
        self.file_path = file_path
        setter = VariableFileSetter(self.rf_var_storage)
        var_list = []
        try:
            variables = setter.set(file_path, args)
        except DataError:
            variables = []
        for variable in variables:
            var_list.append(variable[0])
        data[DBJsonSetting.variables] = sorted(var_list)
        return data

    def parse_library(self, library, args=None):
        """Parses RF library to dictionary

        Uses internally libdoc modules to parse the library.
        Possible arguments to the library are provided in the
        args parameter.
        """
        data = {}
        if not args:
            data[DBJsonSetting.arguments] = []
        else:
            arg_list = []
            for arg in args:
                arg_list.append(arg)
            data[DBJsonSetting.arguments] = arg_list
        if path.isfile(library):
            data[DBJsonSetting.file_path] = normalise_path(library)
            if library.endswith('.xml'):
                library_module, keywords = self._parse_xml_doc(library)
                data[DBJsonSetting.keywords] = keywords
                data[DBJsonSetting.library_module] = library_module
            elif library.endswith('.py'):
                data[DBJsonSetting.file_name] = path.basename(library)
                data[DBJsonSetting.library_module] = path.splitext(
                    data[DBJsonSetting.file_name])[0]
                data[DBJsonSetting.keywords] = self._parse_python_lib(
                    library, data[DBJsonSetting.arguments])
            else:
                raise ValueError('Unknown library')
        else:
            data[DBJsonSetting.library_module] = library
            data[DBJsonSetting.keywords] = self._parse_python_lib(
                library, data[DBJsonSetting.arguments])
        if data[DBJsonSetting.keywords] is None:
            raise ValueError('Library did not contain keywords')
        else:
            return data

    def register_console_logger(self):
        ROBOT_LOGGER.register_console_logger()

    def unregister_console_logger(self):
        ROBOT_LOGGER.unregister_console_logger()

    def close_logger(self):
        ROBOT_LOGGER.close()

    def _parse_python_lib(self, library, args):
        lib_with_args = self._lib_arg_formatter(library, args)
        kws = {}
        try:
            lib = self.libdoc.build(lib_with_args)
        except DataError:
            raise ValueError(
                'Library does not exist: {0}'.format(library))
        if library in STDLIBS:
            import_name = 'robot.libraries.' + library
        else:
            import_name = library
        importer = Importer('test library')
        libcode = importer.import_class_or_module(
            import_name, return_source=False)
        kw_with_deco = self._get_keywords_with_robot_name(libcode)
        for keyword in lib.keywords:
            kw = {}
            kw[DBJsonSetting.keyword_name] = keyword.name
            kw[DBJsonSetting.tags] = list(keyword.tags._tags)
            kw[DBJsonSetting.keyword_arguments] = keyword.args
            kw[DBJsonSetting.documentation] = keyword.doc
            if keyword.name in kw_with_deco:
                function_name = kw_with_deco[keyword.name]
            else:
                function_name = keyword.name
            kw[DBJsonSetting.keyword_file] = self._get_library_kw_source(
                libcode, function_name)
            kws[strip_and_lower(keyword.name)] = kw
        return kws

    def _get_keywords_with_robot_name(self, libcode):
        """Returns keywords which uses Robot keyword decorator with robot_name

        The keyword name can be chaned with Robot Framework keyword decorator
        and by using the robot_name attribute. Return dictinionary which key is
        the value of the robot_name attribute and the orinal function name.
        """
        kw_deco = {}
        for key in libcode.__dict__:
            if callable(libcode.__dict__[key]):
                try:
                    if 'robot_name' in libcode.__dict__[key].__dict__:
                        kw = libcode.__dict__[key].__dict__['robot_name']
                        kw_deco[kw] = key
                except AttributeError:
                    pass
        return kw_deco

    def _get_library_kw_source(self, libcode, keyword):
        kw_func = keyword.lower().replace(' ', '_')
        func = None
        func_file = None
        if hasattr(libcode, kw_func):
            func = getattr(libcode, kw_func)
        if func:
            kw_class = self.get_class_that_defined_method(func)
            if kw_class:
                func_file = self.get_function_file(kw_class)
            else:
                func_file = self.get_function_file(func)
        return func_file

    def get_class_that_defined_method(self, meth):
        try:
            class_mro = inspect.getmro(meth.im_class)
        except AttributeError:
            return None
        for cls in class_mro:
            if meth.__name__ in cls.__dict__:
                return cls
        return None

    def get_function_file(self, kw_class):
        file_ = inspect.getsourcefile(kw_class)
        if file_ and path.exists(file_):
            return normalise_path(file_)
        else:
            return None

    def _lib_arg_formatter(self, library, args):
        args = self._argument_path_formatter(library, args)
        if not args:
            return library
        else:
            for item in args:
                library = '{lib}::{item}'.format(lib=library, item=item)
            return library

    def _argument_path_formatter(self, library, args):
        """Replace robot folder with real path

        If ${/}, ${OUTPUT_DIR} or ${EXECDIR} is found from args then
        a temporary directory is created and that one is used instead."""
        arguments = []
        for arg in args:
            if '${/}' in arg or '${OUTPUT_DIR}' in arg or '${EXECDIR}' in arg:
                f = mkdtemp()
                logging.info(
                    'Possible robot path encountered in library arguments')
                logging.debug('In library %s', library)
                logging.debug('Instead of %s using: %s', arg, f)
                arguments.append(f)
            else:
                arguments.append(arg)
        return arguments

    def _parse_xml_doc(self, library):
        root = ET.parse(library).getroot()
        if ('type', DBJsonSetting.library) in root.items():
            return root.attrib['name'], self._parse_xml_lib(root)
        else:
            raise ValueError('XML file is not library: {0}'.format(
                root.attrib['name'])
            )

    def _parse_xml_lib(self, root):
        kws = {}
        for element in root.findall('kw'):
            kw = {}
            kw[DBJsonSetting.keyword_file] = None
            kw[DBJsonSetting.keyword_name] = element.attrib['name']
            kw[DBJsonSetting.documentation] = element.find('doc').text
            tags = []
            [tags.append(tag.text) for tag in element.findall('.//tags/tag')]
            kw[DBJsonSetting.tags] = tags
            arg = []
            [arg.append(tag.text) for tag in element.findall('.//arguments/arg')]
            kw[DBJsonSetting.keyword_arguments] = arg
            kws[strip_and_lower(kw[DBJsonSetting.keyword_name])] = kw
        return kws

    def _parse_robot_data(self, file_path, model):
        data = {}
        data[DBJsonSetting.file_name] = path.basename(file_path)
        data[DBJsonSetting.file_path] = normalise_path(file_path)
        data[DBJsonSetting.keywords] = self._get_keywords(model)
        data[DBJsonSetting.variables] = self._get_global_variables(model)
        lib, res, v_files = self._get_imports(
            model,
            path.dirname(normalise_path(file_path)),
            file_path
        )
        data[DBJsonSetting.resources] = res
        data[DBJsonSetting.libraries] = lib
        data[DBJsonSetting.variable_files] = v_files
        return data

    def _get_keywords(self, model):
        kw_data = {}
        for kw in model.keywords:
            tmp = {}
            tmp[DBJsonSetting.keyword_arguments] = kw.args.value
            tmp[DBJsonSetting.documentation] = kw.doc.value
            tmp[DBJsonSetting.tags] = kw.tags.value
            tmp[DBJsonSetting.keyword_name] = kw.name
            kw_data[strip_and_lower(kw.name)] = tmp
        return kw_data

    def _get_imports(self, model, file_dir, file_path):
        lib = []
        res = []
        var_files = []
        for setting in model.setting_table.imports:
            if setting.type == 'Library':
                lib.append(self._format_library(setting, file_dir))
            elif setting.type == 'Resource':
                res.append(self._format_resource(setting, file_path))
            elif setting.type == 'Variables':
                var_files.append(self._format_variable_file(setting))
        return lib, res, var_files

    def _format_library(self, setting, file_dir):
        data = {}
        lib_name = setting.name
        if lib_name.endswith('.py') and not path.isfile(lib_name):
            lib_path = path.abspath(path.join(file_dir, lib_name))
            lib_name = path.basename(lib_path)
        elif lib_name.endswith('.py') and path.isfile(lib_name):
            lib_path = normalise_path(lib_name)
            lib_name = path.basename(lib_name)
        else:
            lib_path = None
        data[DBJsonSetting.library_name] = lib_name
        data[DBJsonSetting.library_alias] = setting.alias
        data[DBJsonSetting.library_arguments] = setting.args
        data[DBJsonSetting.library_path] = lib_path
        return data

    def _format_resource(self, setting, file_path):
        if path.isabs(setting.name):
            return setting.name
        else:
            c_dir = path.dirname(self.file_path)
            resource_path = normalise_path(path.join(c_dir, setting.name))
            if not path.isfile(resource_path):
                print ('Import failure on file: {0},'.format(file_path),
                       'could not locate: {0}'.format(setting.name))
            return resource_path

    def _format_variable_file(self, setting):
        data = {}
        v_path = normalise_path(path.join(
            path.dirname(self.file_path), setting.name))
        args = {}
        args['variable_file_arguments'] = setting.args
        data[v_path] = args
        return data

    def _get_global_variables(self, model):
        var_data = []
        for var in model.variable_table.variables:
            if var:
                var_data.append(var.name)
        return var_data