Пример #1
0
class NPMPackage(RequiredTool):
    def __init__(self, tool_name, package_name, tools_dir, node_tool, npm_tool, parent_logger):
        super(NPMPackage, self).__init__(tool_name, "")
        self.package_name = package_name
        self.tools_dir = tools_dir
        self.node_tool = node_tool
        self.npm_tool = npm_tool
        self.log = parent_logger.getChild(self.__class__.__name__)
        self.env = Environment(self.log, dict(os.environ))
        self.env.add_path({"NODE_PATH": os.path.join(tools_dir, "node_modules")})

    def check_if_installed(self):
        try:
            node_binary = self.node_tool.executable
            package = self.package_name
            cmdline = [node_binary, '-e', "require('%s'); console.log('%s is installed');" % (package, package)]
            self.log.debug("%s check cmdline: %s", package, cmdline)
            self.log.debug("NODE_PATH for check: %s", self.env.get("NODE_PATH"))
            output = subprocess.check_output(cmdline, env=self.env.get(), stderr=subprocess.STDOUT)
            self.log.debug("%s check output: %s", self.package_name, output)
            return True
        except (CalledProcessError, OSError):
            self.log.debug("%s check failed: %s", self.package_name, traceback.format_exc())
            return False

    def install(self):
        try:
            cmdline = [self.npm_tool.executable, 'install', self.package_name, '--prefix', self.tools_dir]
            output = subprocess.check_output(cmdline, stderr=subprocess.STDOUT)
            self.log.debug("%s install output: %s", self.tool_name, output)
            return True
        except (CalledProcessError, OSError):
            self.log.debug("%s install failed: %s", self.package_name, traceback.format_exc())
            return False
Пример #2
0
class NPMPackage(RequiredTool):
    def __init__(self, tool_name, package_name, tools_dir, node_tool, npm_tool, parent_logger):
        super(NPMPackage, self).__init__(tool_name, "")
        self.package_name = package_name
        self.tools_dir = tools_dir
        self.node_tool = node_tool
        self.npm_tool = npm_tool
        self.log = parent_logger.getChild(self.__class__.__name__)
        self.env = Environment(self.log, dict(os.environ))
        self.env.add_path({"NODE_PATH": os.path.join(tools_dir, "node_modules")})

    def check_if_installed(self):
        try:
            node_binary = self.node_tool.executable
            package = self.package_name
            cmdline = [node_binary, '-e', "require('%s'); console.log('%s is installed');" % (package, package)]
            self.log.debug("%s check cmdline: %s", package, cmdline)
            self.log.debug("NODE_PATH for check: %s", self.env.get("NODE_PATH"))
            output = subprocess.check_output(cmdline, env=self.env.get(), stderr=subprocess.STDOUT)
            self.log.debug("%s check output: %s", self.package_name, output)
            return True
        except (CalledProcessError, OSError):
            self.log.debug("%s check failed: %s", self.package_name, traceback.format_exc())
            return False

    def install(self):
        try:
            cmdline = [self.npm_tool.executable, 'install', self.package_name, '--prefix', self.tools_dir]
            output = subprocess.check_output(cmdline, stderr=subprocess.STDOUT)
            self.log.debug("%s install output: %s", self.tool_name, output)
            return True
        except (CalledProcessError, OSError):
            self.log.debug("%s install failed: %s", self.package_name, traceback.format_exc())
            return False
Пример #3
0
    def _load_tasks(self, stage, container):
        if not isinstance(self.parameters.get(stage, []), list):
            self.parameters[stage] = [self.parameters[stage]]

        for index, stage_task in enumerate(self.parameters.get(stage, [])):
            stage_task = ensure_is_dict(self.parameters[stage], index,
                                        "command")
            task_config = self.parameters[stage][index]
            default_cwd = self.settings.get("default-cwd", None)
            cwd = self.engine.find_file(task_config.get("cwd", default_cwd))
            if cwd is None:
                working_dir = self.engine.default_cwd
            elif cwd == 'artifacts-dir':
                working_dir = self.engine.artifacts_dir
            else:
                working_dir = cwd

            # make copy of env for every task
            env = Environment(self.log, self.env.get())
            env.set(task_config.get('env'))
            env.add_path({"PYTHONPATH": working_dir})

            task = Task(task_config, self.log, working_dir, env)
            container.append(task)
            self.log.debug("Added %s task: %s", stage, stage_task)
Пример #4
0
class NPMPackage(RequiredTool):
    def __init__(self, tool_name, package_name, tools_dir, node_tool, npm_tool,
                 parent_logger):
        super(NPMPackage, self).__init__(tool_name, "")

        if "@" in package_name:
            self.package_name, self.version = package_name.split("@")
        else:
            self.package_name = package_name
            self.version = None

        self.tools_dir = tools_dir
        self.node_tool = node_tool
        self.npm_tool = npm_tool
        self.log = parent_logger.getChild(self.__class__.__name__)
        self.env = Environment(self.log, dict(os.environ))
        self.env.add_path(
            {"NODE_PATH": os.path.join(tools_dir, "node_modules")})

    def check_if_installed(self):
        try:
            cmdline = [self.node_tool.executable, "-e"]
            ok_msg = "%s is installed" % self.package_name
            cmdline.append("require('%s'); console.log('%s');" %
                           (self.package_name, ok_msg))
            self.log.debug("%s check cmdline: %s", self.package_name, cmdline)

            self.log.debug("NODE_PATH for check: %s",
                           self.env.get("NODE_PATH"))
            output = sync_run(cmdline, env=self.env.get())
            return ok_msg in output

        except CALL_PROBLEMS:
            self.log.debug("%s check failed: %s", self.package_name,
                           traceback.format_exc())
            return False

    def install(self):
        try:
            package_name = self.package_name
            if self.version:
                package_name += "@" + self.version
            cmdline = [
                self.npm_tool.executable, 'install', package_name, '--prefix',
                self.tools_dir, '--no-save'
            ]
            output = sync_run(cmdline)
            self.log.debug("%s install output: %s", self.tool_name, output)
            return True

        except CALL_PROBLEMS:
            self.log.debug("%s install failed: %s", self.package_name,
                           traceback.format_exc())
            return False
Пример #5
0
    def _load_tasks(self, stage, container):
        if not isinstance(self.parameters.get(stage, []), list):
            self.parameters[stage] = [self.parameters[stage]]

        for index, stage_task in enumerate(self.parameters.get(stage, [])):
            stage_task = ensure_is_dict(self.parameters[stage], index,
                                        "command")
            task_config = self.parameters[stage][index]
            default_cwd = self.settings.get("default-cwd", None)
            cwd = self.engine.find_file(task_config.get("cwd", default_cwd))
            if cwd is None:
                working_dir = self.engine.default_cwd
            elif cwd == 'artifacts-dir':
                working_dir = self.engine.artifacts_dir
            else:
                working_dir = cwd

            # make copy of env for every task
            env = Environment(self.log, self.env)
            env.set(task_config.get('env'))
            env.add_path({"PYTHONPATH": working_dir})

            task = Task(task_config, self.log, working_dir, env)

            f_name = 'shellexec_%s_%s' % (stage, index)
            if task.out:
                task.out = open(task.out, 'at')
            else:
                if task.is_background:
                    out = self.engine.create_artifact(f_name, '.out')
                    self.log.debug(
                        'STDOUT of background task "%s" redirected to "%s"' %
                        (task, out))
                    task.out = open(out, 'at')
                else:
                    task.out = PIPE

            if task.err:
                task.err = open(task.err, 'at')
            else:
                if task.is_background:
                    err = self.engine.create_artifact(f_name, '.err')
                    self.log.debug(
                        'STDERR of background task "%s" redirected to "%s"' %
                        (task, err))
                    task.err = open(err, 'at')
                else:
                    task.err = PIPE

            container.append(task)
            self.log.debug("Added %s task: %s", stage, stage_task)
Пример #6
0
    def _load_tasks(self, stage, container):
        if not isinstance(self.parameters.get(stage, []), list):
            self.parameters[stage] = [self.parameters[stage]]

        for index, stage_task in enumerate(self.parameters.get(stage, [])):
            stage_task = ensure_is_dict(self.parameters[stage], index, "command")
            task_config = self.parameters[stage][index]
            default_cwd = self.settings.get("default-cwd", None)
            cwd = self.engine.find_file(task_config.get("cwd", default_cwd))
            if cwd is None:
                working_dir = self.engine.default_cwd
            elif cwd == 'artifacts-dir':
                working_dir = self.engine.artifacts_dir
            else:
                working_dir = cwd

            # make copy of env for every task
            env = Environment(self.log, self.env)
            env.set(task_config.get('env'))
            env.add_path({"PYTHONPATH": working_dir})

            task = Task(task_config, self.log, working_dir, env)

            f_name = 'shellexec_%s_%s' % (stage, index)
            if task.out:
                task.out = open(task.out, 'at')
            else:
                if task.is_background:
                    out = self.engine.create_artifact(f_name, '.out')
                    self.log.debug('STDOUT of background task "%s" redirected to "%s"' % (task, out))
                    task.out = open(out, 'at')
                else:
                    task.out = PIPE

            if task.err:
                task.err = open(task.err, 'at')
            else:
                if task.is_background:
                    err = self.engine.create_artifact(f_name, '.err')
                    self.log.debug('STDERR of background task "%s" redirected to "%s"' % (task, err))
                    task.err = open(err, 'at')
                else:
                    task.err = PIPE

            container.append(task)
            self.log.debug("Added %s task: %s", stage, stage_task)
    def test_nesting(self):
        v1 = 'val_param_name'
        v2 = 'path_param_name'
        v3 = 'const_val'
        os.environ[v1] = 'v1.1'
        os.environ[v2] = 'v1.2'
        os.environ[v3] = 'v1.3'

        e1 = Environment()
        e1.set({v1: 'local_val1.1'})
        e1.add_path({v2: 'param_val1.1'}, finish=True)
        e2 = Environment(parent=e1)
        e1.add_path({v2: 'param_val1.3'}, finish=True)
        os.environ[v1] = 'v2.1'
        os.environ[v2] = 'v2.2'
        os.environ[v3] = 'v2.3'
        e1.set({v1: 'local_val1.2'})
        e2.add_path({v2: 'param_val1.2'}, finish=True)

        self.assertEqual(e1.get(v1), 'local_val1.2')
        self.assertEqual(e2.get(v1), 'local_val1.1')
        self.assertEqual(
            e1.get(v2),
            os.pathsep.join(('v2.2', 'param_val1.1', 'param_val1.3')))
        self.assertEqual(
            e2.get(v2),
            os.pathsep.join(('v2.2', 'param_val1.1', 'param_val1.2')))
        self.assertEqual(e1.get(v3), 'v2.3')
        self.assertEqual(e2.get(v3), 'v2.3')
Пример #8
0
    def test_nesting(self):
        v1 = 'val_param_name'
        v2 = 'path_param_name'
        v3 = 'const_val'
        os.environ[v1] = 'v1.1'
        os.environ[v2] = 'v1.2'
        os.environ[v3] = 'v1.3'

        e1 = Environment()
        e1.set({v1: 'local_val1.1'})
        e1.add_path({v2: 'param_val1.1'}, finish=True)
        e2 = Environment(parent=e1)
        e1.add_path({v2: 'param_val1.3'}, finish=True)
        os.environ[v1] = 'v2.1'
        os.environ[v2] = 'v2.2'
        os.environ[v3] = 'v2.3'
        e1.set({v1: 'local_val1.2'})
        e2.add_path({v2: 'param_val1.2'}, finish=True)

        self.assertEqual(e1.get(v1), 'local_val1.2')
        self.assertEqual(e2.get(v1), 'local_val1.1')
        self.assertEqual(e1.get(v2), os.pathsep.join(('v2.2', 'param_val1.1', 'param_val1.3')))
        self.assertEqual(e2.get(v2), os.pathsep.join(('v2.2', 'param_val1.1', 'param_val1.2')))
        self.assertEqual(e1.get(v3), 'v2.3')
        self.assertEqual(e2.get(v3), 'v2.3')
Пример #9
0
    def _load_tasks(self, stage, container):
        if not isinstance(self.parameters.get(stage, []), list):
            self.parameters[stage] = [self.parameters[stage]]

        for index, stage_task in enumerate(self.parameters.get(stage, [])):
            stage_task = ensure_is_dict(self.parameters[stage], index, "command")
            task_config = self.parameters[stage][index]
            default_cwd = self.settings.get("default-cwd", None)
            cwd = self.engine.find_file(task_config.get("cwd", default_cwd))
            if cwd is None:
                working_dir = self.engine.default_cwd
            elif cwd == 'artifacts-dir':
                working_dir = self.engine.artifacts_dir
            else:
                working_dir = cwd

            # make copy of env for every task
            env = Environment(self.log, self.env.get())
            env.set(task_config.get('env'))
            env.add_path({"PYTHONPATH": working_dir})

            task = Task(task_config, self.log, working_dir, env)
            container.append(task)
            self.log.debug("Added %s task: %s", stage, stage_task)
Пример #10
0
class SeleniumExecutor(AbstractSeleniumExecutor, WidgetProvider, FileLister, HavingInstallableTools, SelfDiagnosable):
    """
    Selenium executor
    :type runner: bzt.modules.SubprocessedExecutor
    """

    SUPPORTED_RUNNERS = ["nose", "junit", "testng", "rspec", "mocha", "nunit", "pytest", "wdio", "robot"]
    SELENIUM_TOOLS_DIR = "~/.bzt/selenium-taurus/tools"

    def __init__(self):
        super(SeleniumExecutor, self).__init__()
        self.end_time = None
        self.runner = None
        self.script = None
        self.runner_working_dir = None
        self.register_reader = True
        self.webdrivers = []

    def add_env(self, env):  # compatibility with taurus-cloud
        self.env.set(env)

    def get_runner_working_dir(self):
        if self.runner_working_dir is None:
            self.runner_working_dir = self.engine.create_artifact("classes", "")
        return self.runner_working_dir

    def subscribe_to_transactions(self, listener):
        self.runner.subscribe_to_transactions(listener)
        self.runner.set_source(self)

    def create_runner(self):
        runner_type = self.get_runner_type()
        self.runner = self.engine.instantiate_module(runner_type)
        self.runner.env = self.env
        self.runner.parameters = self.parameters
        self.runner.provisioning = self.provisioning
        self.runner.execution = copy.deepcopy(self.execution)
        self.runner.execution['files'] = self.execution.get('files', [], force_set=True)
        self.runner.execution['executor'] = runner_type
        self.runner.register_reader = self.register_reader
        self.runner.settings = self.settings.merge(self.runner.settings)

        if runner_type == "nose":
            self.runner.execution["test-mode"] = "selenium"

    def get_virtual_display(self):
        pass  # for compatibility with taurus server

    def install_required_tools(self):
        self.webdrivers = [self._get_tool(ChromeDriver, config=self.settings.get('chromedriver')),
                           self._get_tool(GeckoDriver, config=self.settings.get('geckodriver'))]

        for tool in self.webdrivers:
            if not tool.check_if_installed():
                self.log.info("Installing %s...", tool.tool_name)
                tool.install()

    def prepare(self):
        if self.env is None:
            self.env = Environment(self.log, self.engine.env.get())  # for backward compatibility with taurus-server

        self.install_required_tools()
        for driver in self.webdrivers:
            self.env.add_path({"PATH": driver.get_driver_dir()})

        if self.get_load().concurrency and self.get_load().concurrency > 1:
            msg = 'Selenium supports concurrency in cloud provisioning mode only\n'
            msg += 'For details look at http://gettaurus.org/docs/Cloud.md'
            self.log.warning(msg)

        self.create_runner()
        self.runner.prepare()
        self.script = self.runner.script

    def get_runner_type(self):
        if "runner" in self.execution:
            runner = self.execution["runner"]
            if runner not in SeleniumExecutor.SUPPORTED_RUNNERS:
                msg = "Runner '%s' is not supported. Supported runners: %s"
                raise TaurusConfigError(msg % (runner, SeleniumExecutor.SUPPORTED_RUNNERS))
            self.log.debug("Using script type: %s", runner)
            return runner

        script_name = self.get_script_path()
        if script_name:
            return self.detect_script_type(script_name)
        else:
            if "requests" in self.get_scenario():
                return "nose"
            else:
                raise TaurusConfigError("You must specify either script or list of requests to run Selenium")

    def resource_files(self):
        self.create_runner()
        return self.runner.resource_files()

    def detect_script_type(self, script_name):
        if not os.path.exists(script_name):
            raise TaurusConfigError("Script '%s' doesn't exist" % script_name)

        file_types = set()

        # gather file extensions and choose script_type according to priority
        if os.path.isfile(script_name):  # regular file received
            file_types.add(os.path.splitext(script_name)[1].lower())
        else:  # dir received: check contained files
            for file_name in get_files_recursive(script_name):
                file_types.add(os.path.splitext(file_name)[1].lower())

        if '.java' in file_types or '.jar' in file_types:
            # todo: next detection logic is duplicated in TestNGTester - can we avoid it?
            script_dir = get_full_path(self.get_script_path(), step_up=1)
            if os.path.exists(os.path.join(script_dir, 'testng.xml')) or self.execution.get('testng-xml'):
                script_type = 'testng'
            else:
                script_type = 'junit'
        elif '.py' in file_types:
            script_type = 'nose'
        elif '.rb' in file_types:
            script_type = 'rspec'
        elif '.js' in file_types:
            script_type = 'mocha'
        elif '.dll' in file_types or '.exe' in file_types:
            script_type = 'nunit'
        else:
            if os.path.isfile(script_name):
                message = "Unsupported script type: %r" % script_name
            else:
                message = "Directory %r doesn't contain supported scripts" % script_name
            raise TaurusConfigError(message)

        self.log.debug("Detected script type: %s", script_type)

        return script_type

    def startup(self):
        """
        Start runner
        :return:
        """
        self.start_time = time.time()
        self.runner.startup()

    def check(self):
        """
        check if test completed
        :return:
        """
        if self.widget:
            self.widget.update()

        return self.runner.check()

    def report_test_duration(self):
        if self.start_time:
            self.end_time = time.time()
            self.log.debug("Selenium tests ran for %s seconds", self.end_time - self.start_time)

    def shutdown(self):
        """
        shutdown test_runner
        :return:
        """
        self.runner.shutdown()
        self.report_test_duration()

    def post_process(self):
        self.runner.post_process()

        if os.path.exists("geckodriver.log"):
            self.engine.existing_artifact("geckodriver.log", True)

    def has_results(self):
        return self.runner.has_results()

    def get_widget(self):
        if not self.widget:
            self.widget = SeleniumWidget(self.script, self.runner.stdout_file)
        return self.widget

    def get_error_diagnostics(self):
        diagnostics = []
        if self.runner:
            diagnostics.extend(self.runner.get_error_diagnostics())
        gecko_logs = ["geckodriver.log", os.path.join(self.engine.artifacts_dir, "geckodriver.log")]
        for possible_log in gecko_logs:
            if os.path.exists(possible_log):
                with open(possible_log) as fds:
                    diagnostics.append("Geckodriver log:\n" + fds.read())
        return diagnostics
Пример #11
0
class SeleniumExecutor(AbstractSeleniumExecutor, WidgetProvider, FileLister,
                       HavingInstallableTools, SelfDiagnosable):
    """
    Selenium executor
    :type runner: bzt.modules.SubprocessedExecutor
    """

    SUPPORTED_RUNNERS = [
        "nose", "junit", "testng", "rspec", "mocha", "nunit", "pytest", "wdio",
        "robot"
    ]
    SELENIUM_TOOLS_DIR = "~/.bzt/selenium-taurus/tools"

    def __init__(self):
        super(SeleniumExecutor, self).__init__()
        self.end_time = None
        self.runner = None
        self.script = None
        self.runner_working_dir = None
        self.register_reader = True
        self.webdrivers = []

    def add_env(self, env):  # compatibility with taurus-cloud
        self.env.set(env)

    def get_runner_working_dir(self):
        if self.runner_working_dir is None:
            self.runner_working_dir = self.engine.create_artifact(
                "classes", "")
        return self.runner_working_dir

    def subscribe_to_transactions(self, listener):
        self.runner.subscribe_to_transactions(listener)
        self.runner.set_source(self)

    def create_runner(self):
        runner_type = self.get_runner_type()
        self.runner = self.engine.instantiate_module(runner_type)
        self.runner.env = self.env
        self.runner.parameters = self.parameters
        self.runner.provisioning = self.provisioning
        self.runner.execution = copy.deepcopy(self.execution)
        self.runner.execution['files'] = self.execution.get('files', [],
                                                            force_set=True)
        self.runner.execution['executor'] = runner_type
        self.runner.register_reader = self.register_reader
        self.runner.settings = self.settings.merge(self.runner.settings)

        if runner_type == "nose":
            self.runner.execution["test-mode"] = "selenium"

    def get_virtual_display(self):
        pass  # for compatibility with taurus server

    def install_required_tools(self):
        self.webdrivers = [
            self._get_tool(ChromeDriver,
                           config=self.settings.get('chromedriver')),
            self._get_tool(GeckoDriver,
                           config=self.settings.get('geckodriver'))
        ]

        for tool in self.webdrivers:
            if not tool.check_if_installed():
                self.log.info("Installing %s...", tool.tool_name)
                tool.install()

    def prepare(self):
        if self.env is None:
            self.env = Environment(self.log, self.engine.env.get(
            ))  # for backward compatibility with taurus-server

        self.install_required_tools()
        for driver in self.webdrivers:
            self.env.add_path({"PATH": driver.get_driver_dir()})

        if self.get_load().concurrency and self.get_load().concurrency > 1:
            msg = 'Selenium supports concurrency in cloud provisioning mode only\n'
            msg += 'For details look at http://gettaurus.org/docs/Cloud.md'
            self.log.warning(msg)

        self.create_runner()
        self.runner.prepare()
        self.script = self.runner.script

    def get_runner_type(self):
        if "runner" in self.execution:
            runner = self.execution["runner"]
            if runner not in SeleniumExecutor.SUPPORTED_RUNNERS:
                msg = "Runner '%s' is not supported. Supported runners: %s"
                raise TaurusConfigError(
                    msg % (runner, SeleniumExecutor.SUPPORTED_RUNNERS))
            self.log.debug("Using script type: %s", runner)
            return runner

        script_name = self.get_script_path()
        if script_name:
            return self.detect_script_type(script_name)
        else:
            if "requests" in self.get_scenario():
                return "nose"
            else:
                raise TaurusConfigError(
                    "You must specify either script or list of requests to run Selenium"
                )

    def resource_files(self):
        self.create_runner()
        return self.runner.resource_files()

    def detect_script_type(self, script_name):
        if not os.path.exists(script_name):
            raise TaurusConfigError("Script '%s' doesn't exist" % script_name)

        file_types = set()

        # gather file extensions and choose script_type according to priority
        if os.path.isfile(script_name):  # regular file received
            file_types.add(os.path.splitext(script_name)[1].lower())
        else:  # dir received: check contained files
            for file_name in get_files_recursive(script_name):
                file_types.add(os.path.splitext(file_name)[1].lower())

        if '.java' in file_types or '.jar' in file_types:
            # todo: next detection logic is duplicated in TestNGTester - can we avoid it?
            script_dir = get_full_path(self.get_script_path(), step_up=1)
            if os.path.exists(os.path.join(
                    script_dir,
                    'testng.xml')) or self.execution.get('testng-xml'):
                script_type = 'testng'
            else:
                script_type = 'junit'
        elif '.py' in file_types:
            script_type = 'nose'
        elif '.rb' in file_types:
            script_type = 'rspec'
        elif '.js' in file_types:
            script_type = 'mocha'
        elif '.dll' in file_types or '.exe' in file_types:
            script_type = 'nunit'
        else:
            if os.path.isfile(script_name):
                message = "Unsupported script type: %r" % script_name
            else:
                message = "Directory %r doesn't contain supported scripts" % script_name
            raise TaurusConfigError(message)

        self.log.debug("Detected script type: %s", script_type)

        return script_type

    def startup(self):
        """
        Start runner
        :return:
        """
        self.start_time = time.time()
        self.runner.startup()

    def check(self):
        """
        check if test completed
        :return:
        """
        if self.widget:
            self.widget.update()

        return self.runner.check()

    def report_test_duration(self):
        if self.start_time:
            self.end_time = time.time()
            self.log.debug("Selenium tests ran for %s seconds",
                           self.end_time - self.start_time)

    def shutdown(self):
        """
        shutdown test_runner
        :return:
        """
        self.runner.shutdown()
        self.report_test_duration()

    def post_process(self):
        self.runner.post_process()

        if os.path.exists("geckodriver.log"):
            self.engine.existing_artifact("geckodriver.log", True)

    def has_results(self):
        return self.runner.has_results()

    def get_widget(self):
        if not self.widget:
            self.widget = SeleniumWidget(self.script, self.runner.stdout_file)
        return self.widget

    def get_error_diagnostics(self):
        diagnostics = []
        if self.runner:
            diagnostics.extend(self.runner.get_error_diagnostics())
        gecko_logs = [
            "geckodriver.log",
            os.path.join(self.engine.artifacts_dir, "geckodriver.log")
        ]
        for possible_log in gecko_logs:
            if os.path.exists(possible_log):
                with open(possible_log) as fds:
                    diagnostics.append("Geckodriver log:\n" + fds.read())
        return diagnostics
Пример #12
0
class SeleniumExecutor(AbstractSeleniumExecutor, WidgetProvider, FileLister, HavingInstallableTools, SelfDiagnosable):
    """
    Selenium executor
    :type runner: bzt.modules.SubprocessedExecutor
    """

    SUPPORTED_RUNNERS = ["nose", "junit", "testng", "rspec", "mocha", "nunit", "pytest", "wdio", "robot"]

    CHROMEDRIVER_DOWNLOAD_LINK = "https://chromedriver.storage.googleapis.com/{version}/chromedriver_{arch}.zip"
    CHROMEDRIVER_VERSION = "2.33"

    GECKODRIVER_DOWNLOAD_LINK = "https://github.com/mozilla/geckodriver/releases/download/v{version}/" \
                                "geckodriver-v{version}-{arch}.{ext}"
    GECKODRIVER_VERSION = "0.19.0"

    SELENIUM_TOOLS_DIR = get_full_path("~/.bzt/selenium-taurus/tools")

    def __init__(self):
        super(SeleniumExecutor, self).__init__()
        self.end_time = None
        self.runner = None
        self.script = None
        self.runner_working_dir = None
        self.register_reader = True
        self.webdrivers = []

    def add_env(self, env):     # compatibility with taurus-server
        self.env.set(env)

    def get_runner_working_dir(self):
        if self.runner_working_dir is None:
            self.runner_working_dir = self.engine.create_artifact("classes", "")
        return self.runner_working_dir

    def create_runner(self):
        runner_type = self.get_runner_type()
        self.runner = self.engine.instantiate_module(runner_type)
        self.runner.env = self.env
        self.runner.parameters = self.parameters
        self.runner.provisioning = self.provisioning
        self.runner.execution = copy.deepcopy(self.execution)
        self.runner.execution['files'] = self.execution.get('files', [], force_set=True)
        self.runner.execution['executor'] = runner_type
        self.runner.register_reader = self.register_reader

        if runner_type == "nose":
            self.runner.execution["test-mode"] = "selenium"

    def get_virtual_display(self):
        pass    # for compatibility with taurus server

    def _get_chromedriver_link(self):
        settings = self.settings.get('chromedriver')
        link = settings.get('download-link', SeleniumExecutor.CHROMEDRIVER_DOWNLOAD_LINK)
        version = settings.get('version', SeleniumExecutor.CHROMEDRIVER_VERSION)
        if is_windows():
            arch = 'win32'  # no 64-bit windows builds, :(
        elif is_mac():
            arch = 'mac64'
        else:
            arch = 'linux32' if platform_bitness() == 32 else 'linux64'
        return link.format(version=version, arch=arch)

    def _get_chromedriver_path(self):
        base_dir = get_full_path(SeleniumExecutor.SELENIUM_TOOLS_DIR)
        settings = self.settings.get('chromedriver')
        version = settings.get('version', SeleniumExecutor.CHROMEDRIVER_VERSION)
        filename = 'chromedriver.exe' if is_windows() else 'chromedriver'
        return os.path.join(base_dir, 'chromedriver', version, filename)

    def _get_geckodriver_link(self):
        settings = self.settings.get('geckodriver')
        link = settings.get('download-link', SeleniumExecutor.GECKODRIVER_DOWNLOAD_LINK)
        version = settings.get('version', SeleniumExecutor.GECKODRIVER_VERSION)
        if is_windows():
            arch = 'win64'  # no 32-bit windows builds, :(
            ext = 'zip'
        elif is_mac():
            arch = 'macos'
            ext = 'tar.gz'
        else:
            arch = 'linux32' if platform_bitness() == 32 else 'linux64'
            ext = 'tar.gz'
        return link.format(version=version, arch=arch, ext=ext)

    def _get_geckodriver_path(self):
        base_dir = get_full_path(SeleniumExecutor.SELENIUM_TOOLS_DIR)
        settings = self.settings.get('geckodriver')
        version = settings.get('version', SeleniumExecutor.GECKODRIVER_VERSION)
        filename = 'geckodriver.exe' if is_windows() else 'geckodriver'
        return os.path.join(base_dir, 'geckodriver', version, filename)

    def install_required_tools(self):
        chromedriver_path = self._get_chromedriver_path()
        chromedriver_link = self._get_chromedriver_link()
        geckodriver_path = self._get_geckodriver_path()
        geckodriver_link = self._get_geckodriver_link()

        self.webdrivers = [ChromeDriver(chromedriver_path, self.log, chromedriver_link),
                           GeckoDriver(geckodriver_path, self.log, geckodriver_link)]

        for tool in self.webdrivers:
            if not tool.check_if_installed():
                self.log.info("Installing %s...", tool.tool_name)
                tool.install()

    def prepare(self):
        if self.env is None:
            self.env = Environment(self.log, self.engine.env.get())   # for backward compatibility with taurus-server

        self.install_required_tools()
        for driver in self.webdrivers:
            self.env.add_path({"PATH": driver.get_driver_dir()})

        if self.get_load().concurrency and self.get_load().concurrency > 1:
            msg = 'Selenium supports concurrency in cloud provisioning mode only\n'
            msg += 'For details look at http://gettaurus.org/docs/Cloud.md'
            self.log.warning(msg)

        self.create_runner()
        self.runner.prepare()
        self.script = self.runner.script

    def get_runner_type(self):
        if "runner" in self.execution:
            runner = self.execution["runner"]
            if runner not in SeleniumExecutor.SUPPORTED_RUNNERS:
                msg = "Runner '%s' is not supported. Supported runners: %s"
                raise TaurusConfigError(msg % (runner, SeleniumExecutor.SUPPORTED_RUNNERS))
            self.log.debug("Using script type: %s", runner)
            return runner

        script_name = self.get_script_path()
        if script_name:
            return self.detect_script_type(script_name)
        else:
            if "requests" in self.get_scenario():
                return "nose"
            else:
                raise TaurusConfigError("You must specify either script or list of requests to run Selenium")

    def resource_files(self):
        self.create_runner()
        return self.runner.resource_files()

    def detect_script_type(self, script_name):
        if not os.path.exists(script_name):
            raise TaurusConfigError("Script '%s' doesn't exist" % script_name)

        file_types = set()

        # gather file extensions and choose script_type according to priority
        if os.path.isfile(script_name):  # regular file received
            file_types.add(os.path.splitext(script_name)[1].lower())
        else:  # dir received: check contained files
            for file_name in get_files_recursive(script_name):
                file_types.add(os.path.splitext(file_name)[1].lower())

        if '.java' in file_types or '.jar' in file_types:
            # todo: next detection logic is duplicated in TestNGTester - can we avoid it?
            script_dir = get_full_path(self.get_script_path(), step_up=1)
            if os.path.exists(os.path.join(script_dir, 'testng.xml')) or self.execution.get('testng-xml'):
                script_type = 'testng'
            else:
                script_type = 'junit'
        elif '.py' in file_types:
            script_type = 'nose'
        elif '.rb' in file_types:
            script_type = 'rspec'
        elif '.js' in file_types:
            script_type = 'mocha'
        elif '.dll' in file_types or '.exe' in file_types:
            script_type = 'nunit'
        else:
            if os.path.isfile(script_name):
                message = "Unsupported script type: %r" % script_name
            else:
                message = "Directory %r doesn't contain supported scripts" % script_name
            raise TaurusConfigError(message)

        self.log.debug("Detected script type: %s", script_type)

        return script_type

    def startup(self):
        """
        Start runner
        :return:
        """
        self.start_time = time.time()
        self.runner.startup()

    def check(self):
        """
        check if test completed
        :return:
        """
        if self.widget:
            self.widget.update()

        return self.runner.check()

    def report_test_duration(self):
        if self.start_time:
            self.end_time = time.time()
            self.log.debug("Selenium tests ran for %s seconds", self.end_time - self.start_time)

    def shutdown(self):
        """
        shutdown test_runner
        :return:
        """
        self.runner.shutdown()
        self.report_test_duration()

    def post_process(self):
        self.runner.post_process()

        if os.path.exists("geckodriver.log"):
            self.engine.existing_artifact("geckodriver.log", True)

    def has_results(self):
        return self.runner.has_results()

    def get_widget(self):
        if not self.widget:
            self.widget = SeleniumWidget(self.script, self.runner.stdout_file)
        return self.widget

    def get_error_diagnostics(self):
        diagnostics = []
        if self.runner:
            diagnostics.extend(self.runner.get_error_diagnostics())
        gecko_logs = ["geckodriver.log", os.path.join(self.engine.artifacts_dir, "geckodriver.log")]
        for possible_log in gecko_logs:
            if os.path.exists(possible_log):
                with open(possible_log) as fds:
                    diagnostics.append("Geckodriver log:\n" + fds.read())
        return diagnostics
Пример #13
0
class AppiumLoader(Service):
    def __init__(self):
        super(AppiumLoader, self).__init__()
        self.env = Environment(log=self.log)
        self.appium_process = None
        self.tool_path = ''
        self.tools_dir = "~/.bzt/selenium-taurus/appium-server"
        self.default_path = None
        self.startup_timeout = None
        self.addr = ''
        self.port = ''
        self.stdout = None
        self.stderr = None
        self.appium_python = None
        self.appium_server = None

    def prepare(self):
        self.startup_timeout = self.settings.get('timeout', 30)
        self.addr = self.settings.get('addr', '127.0.0.1')
        self.port = self.settings.get('port', 4723)
        self.tool_path = self.settings.get('path', 'appium')
        self.tools_dir = get_full_path(
            self.settings.get("tools-dir", self.tools_dir))
        self.default_path = os.path.join(self.tools_dir,
                                         "node_modules/.bin/appium")
        self.env.add_path(
            {"NODE_PATH": os.path.join(self.tools_dir, "node_modules")})

        self.install_required_tools()

    def install_required_tools(self):
        node = Node(env=self.env, log=self.log)
        npm = NPM(env=self.env, log=self.log)
        self.appium_python = AppiumPython(engine=self.engine,
                                          settings=self.settings,
                                          log=self.log)
        self.appium_server = AppiumServer(path=self.tool_path,
                                          def_path=self.default_path,
                                          tools_dir=self.tools_dir,
                                          node_tool=node,
                                          npm_tool=npm)
        required_tools = [
            node, npm,
            JavaVM(log=self.log), self.appium_python, self.appium_server
        ]
        for tool in required_tools:
            if not tool.check_if_installed():
                tool.install()

    def startup(self):
        self.log.debug('Starting Appium...')
        self.stdout = open(
            os.path.join(self.engine.artifacts_dir, 'appium.out'), 'wt')
        self.stderr = open(
            os.path.join(self.engine.artifacts_dir, 'appium.err'), 'wt')
        self.appium_process = shell_exec(
            [self.appium_server.tool_path, "--log-no-colors"],
            stdout=self.stdout,
            stderr=self.stderr)

        start_time = time.time()
        while not self.tool_is_started():
            time.sleep(1)
            if time.time() - start_time > self.startup_timeout:
                raise ToolError("Appium cannot be loaded")

        self.log.info('Appium was started successfully')

    def tool_is_started(self):
        try:
            response = urlopen("http://%s:%s%s" %
                               (self.addr, self.port, '/wd/hub/sessions'))
            resp_str = response.read()
            if not isinstance(resp_str, str):
                resp_str = resp_str.decode()
            return isinstance(json.loads(resp_str), dict)
        except (URLError, ValueError):
            return False

    def shutdown(self):
        if self.appium_process:
            self.log.debug('Stopping appium...')
            shutdown_process(self.appium_process, self.log)
        if not self.stdout.closed:
            self.stdout.close()
        if not self.stderr.closed:
            self.stderr.close()
        _file = self.stdout.name
        if not os.stat(_file).st_size:
            os.remove(_file)
        _file = self.stderr.name
        if not os.stat(_file).st_size:
            os.remove(_file)

    def post_process(self):
        self.appium_python.post_process()